diff --git a/lib/Froxlor/Dns/Dns.php b/lib/Froxlor/Dns/Dns.php index b229eaaa..a8ca8771 100644 --- a/lib/Froxlor/Dns/Dns.php +++ b/lib/Froxlor/Dns/Dns.php @@ -212,7 +212,7 @@ class Dns } if (Settings::Get('dmarc.use_dmarc') == '1') { // check for DMARC content later - self::addRequiredEntry('@DMARC@.' . $sub_record, 'TXT', $required_entries); + self::addRequiredEntry('@DMARC@', 'TXT', $required_entries); } if (Settings::Get('antispam.activated') == '1' && $domain['dkim'] == '1') { // check for DKIM content later @@ -239,19 +239,28 @@ class Dns } if (Settings::Get('spf.use_spf') == '1' && $entry['type'] == 'TXT' - && $entry['record'] == '@' && (strtolower(substr($entry['content'], 0, 7)) == '"v=spf1' || strtolower(substr($entry['content'], 0, 6)) == 'v=spf1') ) { // unset special spf required-entry - unset($required_entries[$entry['type']][md5("@SPF@")]); + if ($entry['record'] == '@') { + unset($required_entries[$entry['type']][md5("@SPF@")]); + } else { + // subdomain + unset($required_entries[$entry['type']][md5("@SPF@." . $entry['record'])]); + } } if (Settings::Get('dmarc.use_dmarc') == '1' && $entry['type'] == 'TXT' - && $entry['record'] == '@' + && ($entry['record'] == '_dmarc' || substr($entry['record'], 0, 7) == '_dmarc.') && (strtolower(substr($entry['content'], 0, 9)) == '"v=dmarc1' || strtolower(substr($entry['content'], 0, 8)) == 'v=dmarc1') ) { // unset special dmarc required-entry - unset($required_entries[$entry['type']][md5("@DMARC@")]); + if ($entry['record'] == '_dmarc') { + unset($required_entries[$entry['type']][md5("@DMARC@")]); + } else { + // subdomain + unset($required_entries[$entry['type']][md5("@DMARC@" . substr($entry['record'], 6))]); + } } if (empty($primary_ns) && $entry['record'] == '@' && $entry['type'] == 'NS') { // use the first NS entry pertaining to the current domain as primary ns @@ -392,12 +401,12 @@ class Dns } elseif ($record == '@DMARC@') { // dmarc for main-domain $txt_content = Settings::Get('dmarc.dmarc_entry'); - $zonerecords[] = new DnsEntry('@', 'TXT', self::encloseTXTContent($txt_content)); + $zonerecords[] = new DnsEntry('_dmarc', 'TXT', self::encloseTXTContent($txt_content)); } elseif (strlen($record) > 8 && substr($record, 0, 8) == '@DMARC@.') { // dmarc for subdomain $txt_content = Settings::Get('dmarc.dmarc_entry'); $sub_record = substr($record, 8); - $zonerecords[] = new DnsEntry($sub_record, 'TXT', self::encloseTXTContent($txt_content)); + $zonerecords[] = new DnsEntry('_dmarc.' . $sub_record, 'TXT', self::encloseTXTContent($txt_content)); } elseif (!empty($dkim_entries)) { // DKIM entries $dkim_record = 'dkim' . $domain['dkim_id'] . '._domainkey'; diff --git a/tests/DomainZones/DomainZonesTest.php b/tests/DomainZones/DomainZonesTest.php index b4abc13b..113d765c 100644 --- a/tests/DomainZones/DomainZonesTest.php +++ b/tests/DomainZones/DomainZonesTest.php @@ -2,9 +2,11 @@ use PHPUnit\Framework\TestCase; use Froxlor\Settings; +use Froxlor\Api\Commands\Admins; use Froxlor\Api\Commands\Customers; use Froxlor\Api\Commands\DomainZones; use Froxlor\Api\Commands\Domains; +use Froxlor\Api\Commands\SubDomains; /** * @@ -63,6 +65,89 @@ class DomainZonesTest extends TestCase DomainZones::getLocal($customer_userdata, $data)->get(); } + /** + * + * @depends testCustomerDomainZonesGet + */ + public function testCustomerDomainZonesGetWithDMARC() + { + global $admin_userdata; + + Settings::Set('dmarc.use_dmarc', 1, true); + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'domainname' => 'test2.local' + ]; + $json_result = DomainZones::getLocal($customer_userdata, $data)->get(); + $result = json_decode($json_result, true)['data']; + $this->assertTrue(count($result) > 1); + $foundCnt = 0; + $foundStr = ''; + foreach ($result as $entry) { + if (substr($entry, 0, 7) == '_dmarc ') { + $foundCnt++; + $foundStr = $entry; + } + } + $this->assertEquals(1, $foundCnt); + $resstr = preg_replace('/\s+/', '', $foundStr); + $against = preg_replace('/\s+/', '', '_dmarc 604800 IN TXT v=DMARC1;p=none;'); + $this->assertEquals($against, $resstr); + } + + /** + * + * @depends testCustomerDomainZonesGetWithDMARC + */ + public function testCustomerDomainZonesGetWithDMARCSubdomain() + { + global $admin_userdata; + + // enable isemaildomain for subdomain + $json_result = Admins::getLocal($admin_userdata, array( + 'loginname' => 'reseller' + ))->get(); + $reseller_userdata = json_decode($json_result, true)['data']; + $reseller_userdata['adminsession'] = 1; + $data = [ + 'domainname' => 'mysub2.test2.local', + 'isemaildomain' => 1, + 'customerid' => 1 + ]; + $json_result = SubDomains::getLocal($reseller_userdata, $data)->update(); + + // get customer + $json_result = Customers::getLocal($admin_userdata, array( + 'loginname' => 'test1' + ))->get(); + $customer_userdata = json_decode($json_result, true)['data']; + + $data = [ + 'domainname' => 'test2.local' + ]; + $json_result = DomainZones::getLocal($customer_userdata, $data)->get(); + $result = json_decode($json_result, true)['data']; + $foundCnt = 0; + $foundStr = ''; + $this->assertTrue(count($result) > 1); + foreach ($result as $entry) { + if (substr($entry, 0, 14) == '_dmarc.mysub2 ') { + $foundCnt++; + $foundStr = $entry; + } + } + $this->assertEquals(1, $foundCnt); + $resstr = preg_replace('/\s+/', '', $foundStr); + $against = preg_replace('/\s+/', '', '_dmarc.mysub2 604800 IN TXT v=DMARC1;p=none;'); + $this->assertEquals($against, $resstr); + } + public function testAdminDomainZonesUpdate() { global $admin_userdata; @@ -872,6 +957,68 @@ class DomainZonesTest extends TestCase $this->assertEquals('_test1 18000 IN TXT aw yeah', $entry); } + /** + * + * @depends testCustomerDomainZonesGetWithDMARC + */ + public function testAdminDomainZonesAddTXTCustomDMARC() + { + global $admin_userdata; + + $data = [ + 'domainname' => 'test2.local', + 'record' => '_dmarc', + 'type' => 'TXT', + 'content' => 'v=DMARC1;p=none;overwrite=TRUE' + ]; + $json_result = DomainZones::getLocal($admin_userdata, $data)->add(); + $result = json_decode($json_result, true)['data']; + $this->assertTrue(count($result) > 1); + $foundCnt = 0; + $foundStr = ''; + foreach ($result as $entry) { + if (substr($entry, 0, 7) == '_dmarc ') { + $foundCnt++; + $foundStr = $entry; + } + } + $this->assertEquals(1, $foundCnt); + $resstr = preg_replace('/\s+/', '', $foundStr); + $against = preg_replace('/\s+/', '', '_dmarc 18000 IN TXT v=DMARC1;p=none;overwrite=TRUE'); + $this->assertEquals($against, $resstr); + } + + /** + * + * @depends testCustomerDomainZonesGetWithDMARCSubdomain + */ + public function testAdminDomainZonesAddTXTCustomDMARCSubdomain() + { + global $admin_userdata; + + $data = [ + 'domainname' => 'test2.local', + 'record' => '_dmarc.mysub2', + 'type' => 'TXT', + 'content' => 'v=DMARC1;p=none;overwrite=TRUE' + ]; + $json_result = DomainZones::getLocal($admin_userdata, $data)->add(); + $result = json_decode($json_result, true)['data']; + $this->assertTrue(count($result) > 1); + $foundCnt = 0; + $foundStr = ''; + foreach ($result as $entry) { + if (substr($entry, 0, 14) == '_dmarc.mysub2 ') { + $foundCnt++; + $foundStr = $entry; + } + } + $this->assertEquals(1, $foundCnt); + $resstr = preg_replace('/\s+/', '', $foundStr); + $against = preg_replace('/\s+/', '', '_dmarc.mysub2 18000 IN TXT v=DMARC1;p=none;overwrite=TRUE'); + $this->assertEquals($against, $resstr); + } + public function testAdminDomainZonesAddSRV() { global $admin_userdata;