From 91d4432108a6a21eb8bbab84d789c8bd5deb5826 Mon Sep 17 00:00:00 2001 From: Michael Kaufmann Date: Mon, 15 Mar 2021 17:33:30 +0100 Subject: [PATCH] check rr against possible existing CNAME entries, fixes #927 Signed-off-by: Michael Kaufmann --- lib/Froxlor/Api/Commands/DomainZones.php | 28 ++++++++ lng/english.lng.php | 1 + lng/german.lng.php | 1 + tests/DomainZones/DomainZonesTest.php | 87 ++++++++++++++++-------- 4 files changed, 90 insertions(+), 27 deletions(-) diff --git a/lib/Froxlor/Api/Commands/DomainZones.php b/lib/Froxlor/Api/Commands/DomainZones.php index ddd45dfe..a51e59e0 100644 --- a/lib/Froxlor/Api/Commands/DomainZones.php +++ b/lib/Froxlor/Api/Commands/DomainZones.php @@ -136,8 +136,24 @@ class DomainZones extends \Froxlor\Api\ApiCommand implements \Froxlor\Api\Resour // types if ($type == 'A' && filter_var($content, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) === false) { $errors[] = $this->lng['error']['dns_arec_noipv4']; + } elseif ($type == 'A') { + // check whether there is a CNAME-record for the same resource + foreach ($dom_entries as $existing_entries) { + if ($existing_entries['type'] == 'CNAME' && $existing_entries['record'] == $record) { + $errors[] = $this->lng['error']['dns_other_nomorerr']; + break; + } + } } elseif ($type == 'AAAA' && filter_var($content, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) { $errors[] = $this->lng['error']['dns_aaaarec_noipv6']; + } elseif ($type == 'AAAA') { + // check whether there is a CNAME-record for the same resource + foreach ($dom_entries as $existing_entries) { + if ($existing_entries['type'] == 'CNAME' && $existing_entries['record'] == $record) { + $errors[] = $this->lng['error']['dns_other_nomorerr']; + break; + } + } } elseif ($type == 'CAA' && ! empty($content)) { $re = '/(?\'critical\'\d)\h*(?\'type\'iodef|issue|issuewild)\h*(?\'value\'(?\'issuevalue\'"(?\'domain\'(?=.{3,128}$)(?>(?>[a-zA-Z0-9]+[a-zA-Z0-9-]*[a-zA-Z0-9]+|[a-zA-Z0-9]+)\.)*(?>[a-zA-Z]{2,}|[a-zA-Z0-9]{2,}\.[a-zA-Z]{2,}))[;\h]*(?\'parameters\'(?>[a-zA-Z0-9]{1,60}=[a-zA-Z0-9]{1,60}\h*)+)?")|(?\'iodefvalue\'"(?\'url\'(mailto:.*|http:\/\/.*|https:\/\/.*))"))/'; preg_match($re, $content, $matches); @@ -198,6 +214,10 @@ class DomainZones extends \Froxlor\Api\ApiCommand implements \Froxlor\Api\Resour $errors[] = $this->lng['error']['dns_mx_noalias']; break; } + elseif ($existing_entries['type'] == 'CNAME' && $existing_entries['record'] == $record) { + $errors[] = $this->lng['error']['dns_other_nomorerr']; + break; + } } } // append trailing dot (again) @@ -210,6 +230,14 @@ class DomainZones extends \Froxlor\Api\ApiCommand implements \Froxlor\Api\Resour } if (! \Froxlor\Validate\Validate::validateDomain($content)) { $errors[] = $this->lng['error']['dns_ns_invaliddom']; + } else { + // check whether there is a CNAME-record for the same resource + foreach ($dom_entries as $existing_entries) { + if ($existing_entries['type'] == 'CNAME' && $existing_entries['record'] == $record) { + $errors[] = $this->lng['error']['dns_other_nomorerr']; + break; + } + } } // append trailing dot (again) $content .= '.'; diff --git a/lng/english.lng.php b/lng/english.lng.php index 3f76e3c6..dc3aa324 100644 --- a/lng/english.lng.php +++ b/lng/english.lng.php @@ -1908,6 +1908,7 @@ $lng['error']['dns_mx_needdom'] = 'The MX content value must be a valid domain-n $lng['error']['dns_mx_noalias'] = 'The MX-content value cannot be an CNAME entry.'; $lng['error']['dns_cname_invaliddom'] = 'Invalid domain-name for CNAME record'; $lng['error']['dns_cname_nomorerr'] = 'There already exists a resource-record with the same record-name. It can not be used as CNAME.'; +$lng['error']['dns_other_nomorerr'] = 'There already exists a CNAME record with the same record-name. It can not be used for another type.'; $lng['error']['dns_ns_invaliddom'] = 'Invalid domain-name for NS record'; $lng['error']['dns_srv_prioempty'] = 'Invalid SRV priority given'; $lng['error']['dns_srv_invalidcontent'] = 'Invalid SRV content, must contain of fields weight, port and target, e.g.: 5 5060 sipserver.example.com.'; diff --git a/lng/german.lng.php b/lng/german.lng.php index 3dc70156..bf01dbd0 100644 --- a/lng/german.lng.php +++ b/lng/german.lng.php @@ -1558,6 +1558,7 @@ $lng['error']['dns_mx_needdom'] = 'Der Wert des MX Eintrags muss ein gültiger D $lng['error']['dns_mx_noalias'] = 'Der MX Eintrag darf kein CNAME Eintrag sein.'; $lng['error']['dns_cname_invaliddom'] = 'Ungültiger Domain-Name für CNAME Eintrag'; $lng['error']['dns_cname_nomorerr'] = 'Es existiert bereits ein Eintrag mit dem gleichen Namen. Dieser Eintrag kann daher nicht für CNAME genutzt werden.'; +$lng['error']['dns_other_nomorerr'] = 'Es existiert bereits ein CNAME Eintrag mit dem gleichen Namen. Dieser Eintrag kann daher nicht für einen anderen genutzt werden.'; $lng['error']['dns_ns_invaliddom'] = 'Ungültiger Domain-Name für NS Eintrag'; $lng['error']['dns_srv_prioempty'] = 'Ungültige SRV Priorität angegeben'; $lng['error']['dns_srv_invalidcontent'] = 'Ungültiger Wert des SRV Eintrags, dieser muss aus den Feldern weight, port und target, bestehen. Bsp.: 5 5060 sipserver.example.com.'; diff --git a/tests/DomainZones/DomainZonesTest.php b/tests/DomainZones/DomainZonesTest.php index 21414c7f..73b21da8 100644 --- a/tests/DomainZones/DomainZonesTest.php +++ b/tests/DomainZones/DomainZonesTest.php @@ -101,6 +101,7 @@ class DomainZonesTest extends TestCase } /** + * * @depends testCustomerDomainZonesAddA */ public function testAdminDomainZonesListing() @@ -303,14 +304,14 @@ class DomainZonesTest extends TestCase 'domainname' => 'test2.local', 'record' => '@', 'type' => 'CAA', - 'content' => $content, + 'content' => $content ]; $json_result = DomainZones::getLocal($admin_userdata, $data)->add(); $result = json_decode($json_result, true)['data']; $this->assertTrue(count($result) > 1); $found = false; foreach ($result as $entry) { - if (substr($entry, -strlen($content)) == $content) { + if (substr($entry, - strlen($content)) == $content) { $found = true; break; } @@ -328,7 +329,7 @@ class DomainZonesTest extends TestCase 'domainname' => 'test2.local', 'record' => '@', 'type' => 'CAA', - 'content' => $content, + 'content' => $content ]; $json_result = DomainZones::getLocal($admin_userdata, $data)->add(); $result = json_decode($json_result, true)['data']; @@ -341,7 +342,7 @@ class DomainZonesTest extends TestCase } } $this->assertTrue($found); - $this->assertEquals('@ 18000 IN CAA '.$content, $entry); + $this->assertEquals('@ 18000 IN CAA ' . $content, $entry); } public function testAdminDomainZonesAddCAAIssueWithTwoParameters() @@ -353,7 +354,7 @@ class DomainZonesTest extends TestCase 'domainname' => 'test2.local', 'record' => '@', 'type' => 'CAA', - 'content' => $content, + 'content' => $content ]; $json_result = DomainZones::getLocal($admin_userdata, $data)->add(); $result = json_decode($json_result, true)['data']; @@ -366,7 +367,7 @@ class DomainZonesTest extends TestCase } } $this->assertTrue($found); - $this->assertEquals('@ 18000 IN CAA '.$content, $entry); + $this->assertEquals('@ 18000 IN CAA ' . $content, $entry); } public function testAdminDomainZonesAddCAAInvalidIssueValue() @@ -378,7 +379,7 @@ class DomainZonesTest extends TestCase 'domainname' => 'test2.local', 'record' => '@', 'type' => 'CAA', - 'content' => $content, + 'content' => $content ]; $this->expectExceptionMessage("DNS content invalid"); DomainZones::getLocal($admin_userdata, $data)->add(); @@ -393,7 +394,7 @@ class DomainZonesTest extends TestCase 'domainname' => 'test2.local', 'record' => '@', 'type' => 'CAA', - 'content' => $content, + 'content' => $content ]; $this->expectExceptionMessage("DNS content invalid"); DomainZones::getLocal($admin_userdata, $data)->add(); @@ -408,7 +409,7 @@ class DomainZonesTest extends TestCase 'domainname' => 'test2.local', 'record' => '@', 'type' => 'CAA', - 'content' => $content, + 'content' => $content ]; $this->expectExceptionMessage("DNS content invalid"); DomainZones::getLocal($admin_userdata, $data)->add(); @@ -423,7 +424,7 @@ class DomainZonesTest extends TestCase 'domainname' => 'test2.local', 'record' => '@', 'type' => 'CAA', - 'content' => $content, + 'content' => $content ]; $json_result = DomainZones::getLocal($admin_userdata, $data)->add(); $result = json_decode($json_result, true)['data']; @@ -436,7 +437,7 @@ class DomainZonesTest extends TestCase } } $this->assertTrue($found); - $this->assertEquals('@ 18000 IN CAA '.$content, $entry); + $this->assertEquals('@ 18000 IN CAA ' . $content, $entry); } public function testAdminDomainZonesAddCAAIssueWildWithParameters() @@ -448,7 +449,7 @@ class DomainZonesTest extends TestCase 'domainname' => 'test2.local', 'record' => '@', 'type' => 'CAA', - 'content' => $content, + 'content' => $content ]; $json_result = DomainZones::getLocal($admin_userdata, $data)->add(); $result = json_decode($json_result, true)['data']; @@ -461,7 +462,7 @@ class DomainZonesTest extends TestCase } } $this->assertTrue($found); - $this->assertEquals('@ 18000 IN CAA '.$content, $entry); + $this->assertEquals('@ 18000 IN CAA ' . $content, $entry); } public function testAdminDomainZonesAddCAAIssueWildWithTwoParameters() @@ -473,7 +474,7 @@ class DomainZonesTest extends TestCase 'domainname' => 'test2.local', 'record' => '@', 'type' => 'CAA', - 'content' => $content, + 'content' => $content ]; $json_result = DomainZones::getLocal($admin_userdata, $data)->add(); $result = json_decode($json_result, true)['data']; @@ -486,7 +487,7 @@ class DomainZonesTest extends TestCase } } $this->assertTrue($found); - $this->assertEquals('@ 18000 IN CAA '.$content, $entry); + $this->assertEquals('@ 18000 IN CAA ' . $content, $entry); } public function testAdminDomainZonesAddCAAInvalidIssueWildValue() @@ -498,7 +499,7 @@ class DomainZonesTest extends TestCase 'domainname' => 'test2.local', 'record' => '@', 'type' => 'CAA', - 'content' => $content, + 'content' => $content ]; $this->expectExceptionMessage("DNS content invalid"); DomainZones::getLocal($admin_userdata, $data)->add(); @@ -513,7 +514,7 @@ class DomainZonesTest extends TestCase 'domainname' => 'test2.local', 'record' => '@', 'type' => 'CAA', - 'content' => $content, + 'content' => $content ]; $this->expectExceptionMessage("DNS content invalid"); DomainZones::getLocal($admin_userdata, $data)->add(); @@ -528,7 +529,7 @@ class DomainZonesTest extends TestCase 'domainname' => 'test2.local', 'record' => '@', 'type' => 'CAA', - 'content' => $content, + 'content' => $content ]; $this->expectExceptionMessage("DNS content invalid"); DomainZones::getLocal($admin_userdata, $data)->add(); @@ -543,7 +544,7 @@ class DomainZonesTest extends TestCase 'domainname' => 'test2.local', 'record' => '@', 'type' => 'CAA', - 'content' => $content, + 'content' => $content ]; $json_result = DomainZones::getLocal($admin_userdata, $data)->add(); $result = json_decode($json_result, true)['data']; @@ -556,7 +557,7 @@ class DomainZonesTest extends TestCase } } $this->assertTrue($found); - $this->assertEquals('@ 18000 IN CAA '.$content, $entry); + $this->assertEquals('@ 18000 IN CAA ' . $content, $entry); } public function testAdminDomainZonesAddCAAIodefMailInvalid() @@ -568,7 +569,7 @@ class DomainZonesTest extends TestCase 'domainname' => 'test2.local', 'record' => '@', 'type' => 'CAA', - 'content' => $content, + 'content' => $content ]; $this->expectExceptionMessage("DNS content invalid"); DomainZones::getLocal($admin_userdata, $data)->add(); @@ -583,7 +584,7 @@ class DomainZonesTest extends TestCase 'domainname' => 'test2.local', 'record' => '@', 'type' => 'CAA', - 'content' => $content, + 'content' => $content ]; $json_result = DomainZones::getLocal($admin_userdata, $data)->add(); $result = json_decode($json_result, true)['data']; @@ -596,7 +597,7 @@ class DomainZonesTest extends TestCase } } $this->assertTrue($found); - $this->assertEquals('@ 18000 IN CAA '.$content, $entry); + $this->assertEquals('@ 18000 IN CAA ' . $content, $entry); } public function testAdminDomainZonesAddCAAIodefHttpInvalid() @@ -608,7 +609,7 @@ class DomainZonesTest extends TestCase 'domainname' => 'test2.local', 'record' => '@', 'type' => 'CAA', - 'content' => $content, + 'content' => $content ]; $this->expectExceptionMessage("DNS content invalid"); DomainZones::getLocal($admin_userdata, $data)->add(); @@ -623,7 +624,7 @@ class DomainZonesTest extends TestCase 'domainname' => 'test2.local', 'record' => '@', 'type' => 'CAA', - 'content' => $content, + 'content' => $content ]; $json_result = DomainZones::getLocal($admin_userdata, $data)->add(); $result = json_decode($json_result, true)['data']; @@ -636,7 +637,7 @@ class DomainZonesTest extends TestCase } } $this->assertTrue($found); - $this->assertEquals('@ 18000 IN CAA '.$content, $entry); + $this->assertEquals('@ 18000 IN CAA ' . $content, $entry); } public function testAdminDomainZonesAddCAAIodefHttpsInvalid() @@ -648,7 +649,7 @@ class DomainZonesTest extends TestCase 'domainname' => 'test2.local', 'record' => '@', 'type' => 'CAA', - 'content' => $content, + 'content' => $content ]; $this->expectExceptionMessage("DNS content invalid"); DomainZones::getLocal($admin_userdata, $data)->add(); @@ -745,6 +746,38 @@ class DomainZonesTest extends TestCase DomainZones::getLocal($admin_userdata, $data)->add(); } + /** + * + * @depends testAdminDomainZonesAddCname + */ + public function testAdminDomainZonesAddForExistingCname() + { + global $admin_userdata; + + // set domain to www-alias + $data = [ + 'domainname' => 'test2.local', + 'selectserveralias' => '1' + ]; + Domains::getLocal($admin_userdata, $data)->update(); + + foreach ([ + 'A' => '127.0.0.1', + 'AAAA' => '::1', + 'MX' => 'mail.example.com.', + 'NS' => 'ns.example.com.' + ] as $type => $val) { + $data = [ + 'domainname' => 'test2.local', + 'record' => 'db', + 'type' => $type, + 'content' => $val + ]; + $this->expectExceptionMessage('There already exists a CNAME record with the same record-name. It can not be used for another type.'); + DomainZones::getLocal($admin_userdata, $data)->add(); + } + } + /** * * @depends testAdminDomainZonesAddCname