diff --git a/src/DNSRecordGetter.php b/src/DNSRecordGetter.php index 8411d00..260aad2 100755 --- a/src/DNSRecordGetter.php +++ b/src/DNSRecordGetter.php @@ -13,6 +13,8 @@ class DNSRecordGetter implements DNSRecordGetterInterface { protected $requestCount = 0; + protected $requestMXCount = 0; + protected $requestPTRCount = 0; /** * @param $domain string The domain to get SPF record @@ -94,7 +96,7 @@ public function resolvePtr($ipAddress) return $e['target']; }, dns_get_record($revIp, DNS_PTR)); - return array_slice($revs, 0, 10); + return $revs; } public function exists($domain) @@ -106,14 +108,39 @@ public function exists($domain) } } + /** + * @codeCoverageIgnore + */ public function resetRequestCount() { - $this->requestCount = 0; + trigger_error('DNSRecordGetterInterface::resetRequestCount() is deprecated. Please use resetRequestCounts() instead', E_USER_DEPRECATED); + $this->resetRequestCounts(); } public function countRequest() { - if (++$this->requestCount > 10) { + if ($this->requestCount++ == 10) { + throw new DNSLookupLimitReachedException(); + } + } + + public function resetRequestCounts() + { + $this->requestCount = 0; + $this->requestMXCount = 0; + $this->requestPTRCount = 0; + } + + public function countMxRequest() + { + if (++$this->requestMXCount > 10) { + throw new DNSLookupLimitReachedException(); + } + } + + public function countPtrRequest() + { + if (++$this->requestPTRCount > 10) { throw new DNSLookupLimitReachedException(); } } diff --git a/src/DNSRecordGetterDirect.php b/src/DNSRecordGetterDirect.php index 398ca70..215a832 100644 --- a/src/DNSRecordGetterDirect.php +++ b/src/DNSRecordGetterDirect.php @@ -15,6 +15,8 @@ class DNSRecordGetterDirect implements DNSRecordGetterInterface { protected $requestCount = 0; + protected $requestMXCount = 0; + protected $requestPTRCount = 0; protected $nameserver = "8.8.8.8"; protected $port = 53; protected $timeout = 30; @@ -140,7 +142,7 @@ public function resolvePtr($ipAddress) return $e['target']; }, $this->dns_get_record($revIp, "PTR")); - return array_slice($revs, 0, 10); + return $revs; } public function exists($domain) @@ -152,18 +154,6 @@ public function exists($domain) } } - public function resetRequestCount() - { - $this->requestCount = 0; - } - - public function countRequest() - { - if (++$this->requestCount > 10) { - throw new DNSLookupLimitReachedException(); - } - } - public function dns_get_record($question, $type) { @@ -248,4 +238,41 @@ public function dns_get_record($question, $type) return $response; } + + /** + * @codeCoverageIgnore + */ + public function resetRequestCount() + { + trigger_error('DNSRecordGetterInterface::resetRequestCount() is deprecated. Please use resetRequestCounts() instead', E_USER_DEPRECATED); + $this->resetRequestCounts(); + } + + public function countRequest() + { + if (++$this->requestCount > 10) { + throw new DNSLookupLimitReachedException(); + } + } + + public function resetRequestCounts() + { + $this->requestCount = 0; + $this->requestMXCount = 0; + $this->requestPTRCount = 0; + } + + public function countMxRequest() + { + if (++$this->requestMXCount > 10) { + throw new DNSLookupLimitReachedException(); + } + } + + public function countPtrRequest() + { + if (++$this->requestPTRCount > 10) { + throw new DNSLookupLimitReachedException(); + } + } } \ No newline at end of file diff --git a/src/DNSRecordGetterInterface.php b/src/DNSRecordGetterInterface.php index 3c0d37f..532f203 100755 --- a/src/DNSRecordGetterInterface.php +++ b/src/DNSRecordGetterInterface.php @@ -7,13 +7,62 @@ namespace Mika56\SPFCheck; +use Mika56\SPFCheck\Exception\DNSLookupException; +use Mika56\SPFCheck\Exception\DNSLookupLimitReachedException; + interface DNSRecordGetterInterface { + /** + * @param $domain + * @return string[] + * @throws DNSLookupException + */ public function getSPFRecordForDomain($domain); + public function resolveA($domain, $ip4only = false); + public function resolveMx($domain); + public function resolvePtr($ipAddress); + + /** + * @param $domain + * @return boolean + * @throws DNSLookupException + */ public function exists($domain); + + /** + * @return void + * @deprecated {@see resetRequestCounts} + * @codeCoverageIgnore + */ public function resetRequestCount(); + + /** + * Reset all request counters (A/AAAA, MX, PTR) + * @return void + */ + public function resetRequestCounts(); + + /** + * Count a A/AAAA request + * @throws DNSLookupLimitReachedException + * @return void + */ public function countRequest(); + + /** + * Count an MX request + * @throws DNSLookupLimitReachedException + * @return void + */ + public function countMxRequest(); + + /** + * Count a PTR request + * @throws DNSLookupLimitReachedException + * @return void + */ + public function countPtrRequest(); } \ No newline at end of file diff --git a/src/SPFCheck.php b/src/SPFCheck.php index ef0bf39..6dcef73 100755 --- a/src/SPFCheck.php +++ b/src/SPFCheck.php @@ -76,7 +76,7 @@ protected function doIsIPAllowed($ipAddress, $domain, $resetRequestCount) $this->redirect = null; if ($resetRequestCount) { $this->voidLookup = 0; - $this->DNSRecordGetter->resetRequestCount(); + $this->DNSRecordGetter->resetRequestCounts(); } // Handle IPv4 address in IPv6 format @@ -92,6 +92,12 @@ protected function doIsIPAllowed($ipAddress, $domain, $resetRequestCount) return $result; } + /** + * @param $ipAddress + * @param $domain + * @return bool|string + * @throws DNSLookupException + */ private function doCheck($ipAddress, $domain) { try { @@ -141,6 +147,14 @@ private function doCheck($ipAddress, $domain) return self::RESULT_NEUTRAL; } + /** + * @param $ipAddress + * @param $part + * @param $matchingDomain + * @return bool + * @throws DNSLookupLimitReachedException + * @throws DNSLookupException + */ protected function ipMatchesPart($ipAddress, $part, $matchingDomain) { $qualifier = substr($part, 0, 1); @@ -242,10 +256,8 @@ protected function ipMatchesPart($ipAddress, $part, $matchingDomain) $validIpAddresses = []; $this->DNSRecordGetter->countRequest(); $mxServers = $this->DNSRecordGetter->resolveMx($domain); - if (count($mxServers) > 10) { - return self::RESULT_PERMERROR; - } foreach ($mxServers as $mxServer) { + $this->DNSRecordGetter->countMxRequest(); if (false !== filter_var($mxServer, FILTER_VALIDATE_IP)) { $validIpAddresses[] = $mxServer; } else { @@ -277,6 +289,7 @@ protected function ipMatchesPart($ipAddress, $part, $matchingDomain) $ptrRecords = $this->DNSRecordGetter->resolvePtr($ipAddress); $validatedSendingDomainNames = array(); foreach ($ptrRecords as $ptrRecord) { + $this->DNSRecordGetter->countPtrRequest(); $ptrRecord = strtolower($ptrRecord); $ipAddresses = $this->DNSRecordGetter->resolveA($ptrRecord); if (in_array($ipAddress, $ipAddresses)) { diff --git a/tests/DNSRecordGetterIssue3.php b/tests/DNSRecordGetterIssue3.php index 50dc8de..57a3a72 100644 --- a/tests/DNSRecordGetterIssue3.php +++ b/tests/DNSRecordGetterIssue3.php @@ -13,6 +13,8 @@ class DNSRecordGetterIssue3 implements DNSRecordGetterInterface { protected $requestCount = 0; + protected $requestMXCount = 0; + protected $requestPTRCount = 0; protected $spfRecords = [ 'domain.com' => 'v=spf1 include:domain.com ~all', @@ -49,12 +51,34 @@ public function exists($domain) public function resetRequestCount() { - $this->requestCount = 0; + trigger_error('DNSRecordGetterInterface::resetRequestCount() is deprecated. Please use resetRequestCounts() instead', E_USER_DEPRECATED); + $this->resetRequestCounts(); } public function countRequest() { - if (++$this->requestCount == 10) { + if (++$this->requestCount > 10) { + throw new DNSLookupLimitReachedException(); + } + } + + public function resetRequestCounts() + { + $this->requestCount = 0; + $this->requestMXCount = 0; + $this->requestPTRCount = 0; + } + + public function countMxRequest() + { + if (++$this->requestMXCount > 10) { + throw new DNSLookupLimitReachedException(); + } + } + + public function countPtrRequest() + { + if (++$this->requestPTRCount > 10) { throw new DNSLookupLimitReachedException(); } } diff --git a/tests/DNSRecordGetterIssue7.php b/tests/DNSRecordGetterIssue7.php index b15270d..f711fa9 100644 --- a/tests/DNSRecordGetterIssue7.php +++ b/tests/DNSRecordGetterIssue7.php @@ -12,6 +12,8 @@ class DNSRecordGetterIssue7 implements DNSRecordGetterInterface { protected $requestCount = 0; + protected $requestMXCount = 0; + protected $requestPTRCount = 0; protected $spfRecords = [ ]; @@ -39,12 +41,34 @@ public function exists($domain) public function resetRequestCount() { - $this->requestCount = 0; + trigger_error('DNSRecordGetterInterface::resetRequestCount() is deprecated. Please use resetRequestCounts() instead', E_USER_DEPRECATED); + $this->resetRequestCounts(); } public function countRequest() { - if (++$this->requestCount == 10) { + if (++$this->requestCount > 10) { + throw new DNSLookupLimitReachedException(); + } + } + + public function resetRequestCounts() + { + $this->requestCount = 0; + $this->requestMXCount = 0; + $this->requestPTRCount = 0; + } + + public function countMxRequest() + { + if (++$this->requestMXCount > 10) { + throw new DNSLookupLimitReachedException(); + } + } + + public function countPtrRequest() + { + if (++$this->requestPTRCount > 10) { throw new DNSLookupLimitReachedException(); } } diff --git a/tests/DNSRecordGetterOpenSPF.php b/tests/DNSRecordGetterOpenSPF.php index 1724164..81f5ac4 100644 --- a/tests/DNSRecordGetterOpenSPF.php +++ b/tests/DNSRecordGetterOpenSPF.php @@ -16,6 +16,8 @@ class DNSRecordGetterOpenSPF implements DNSRecordGetterInterface { protected $data; protected $requestCount; + protected $requestMXCount = 0; + protected $requestPTRCount = 0; public function __construct($data) { @@ -145,12 +147,34 @@ public function exists($domain) public function resetRequestCount() { - $this->requestCount = 0; + trigger_error('DNSRecordGetterInterface::resetRequestCount() is deprecated. Please use resetRequestCounts() instead', E_USER_DEPRECATED); + $this->resetRequestCounts(); } public function countRequest() { - if (++$this->requestCount == 11) { + if (++$this->requestCount > 10) { + throw new DNSLookupLimitReachedException(); + } + } + + public function resetRequestCounts() + { + $this->requestCount = 0; + $this->requestMXCount = 0; + $this->requestPTRCount = 0; + } + + public function countMxRequest() + { + if (++$this->requestMXCount > 10) { + throw new DNSLookupLimitReachedException(); + } + } + + public function countPtrRequest() + { + if (++$this->requestPTRCount > 10) { throw new DNSLookupLimitReachedException(); } } diff --git a/tests/DNSRecordGetterTest.php b/tests/DNSRecordGetterTest.php index d3e19a7..01f3266 100644 --- a/tests/DNSRecordGetterTest.php +++ b/tests/DNSRecordGetterTest.php @@ -174,10 +174,72 @@ public function testLookupLimitReset() for ($i = 0; $i < 10; $i++) { $dnsRecordGetter->countRequest(); } - $dnsRecordGetter->resetRequestCount(); + $dnsRecordGetter->resetRequestCounts(); for ($i = 0; $i < 10; $i++) { $dnsRecordGetter->countRequest(); } } + public function testMXLookupLimitEdge() + { + $dnsRecordGetter = new DNSRecordGetter(); + for ($i = 0; $i < 10; $i++) { + $dnsRecordGetter->countMxRequest(); + } + } + + /** + * @expectedException \Mika56\SPFCheck\Exception\DNSLookupLimitReachedException + */ + public function testMXLookupLimitExceed() + { + $dnsRecordGetter = new DNSRecordGetter(); + for ($i = 0; $i <= 10; $i++) { + $dnsRecordGetter->countMxRequest(); + } + } + + public function testMXLookupLimitReset() + { + $dnsRecordGetter = new DNSRecordGetter(); + for ($i = 0; $i < 10; $i++) { + $dnsRecordGetter->countMxRequest(); + } + $dnsRecordGetter->resetRequestCounts(); + for ($i = 0; $i < 10; $i++) { + $dnsRecordGetter->countMxRequest(); + } + } + + public function testPTRLookupLimitEdge() + { + $dnsRecordGetter = new DNSRecordGetter(); + for ($i = 0; $i < 10; $i++) { + $dnsRecordGetter->countPtrRequest(); + } + } + + /** + * @expectedException \Mika56\SPFCheck\Exception\DNSLookupLimitReachedException + */ + public function testPTRLookupLimitExceed() + { + $dnsRecordGetter = new DNSRecordGetter(); + for ($i = 0; $i <= 10; $i++) { + $dnsRecordGetter->countPtrRequest(); + } + } + + public function testPTRLookupLimitReset() + { + $dnsRecordGetter = new DNSRecordGetter(); + for ($i = 0; $i < 10; $i++) { + $dnsRecordGetter->countPtrRequest(); + } + $dnsRecordGetter->resetRequestCounts(); + for ($i = 0; $i < 10; $i++) { + $dnsRecordGetter->countPtrRequest(); + } + } + } diff --git a/tests/Issue25Test.php b/tests/Issue25Test.php new file mode 100644 index 0000000..a600980 --- /dev/null +++ b/tests/Issue25Test.php @@ -0,0 +1,88 @@ + [ + ['SPF' => ['v=spf1 a mx a mx a mx a mx a ptr ip4:1.2.3.4 -all']], + ['A' => '1.2.3.8'], + ['MX' => [10, 'e1.example.com']], + ], + ])); + $this->assertEquals(SPFCheck::RESULT_FAIL, $SPFCheck->isIPAllowed('127.0.0.1', 'e1.example.com')); + } + + public function testIssue7MXAtLimit() + { + $SPFCheck = new SPFCheck(new DNSRecordGetterOpenSPF([ + 'e1.example.com' => [ + ['SPF' => ['v=spf1 mx mx mx mx mx mx mx mx mx mx ip4:1.2.3.4 -all']], + ['A' => '1.2.3.8'], + ['MX' => [10, 'e1.example.com']], + ['MX' => [20, 'e1.example.com']], + ['MX' => [30, 'e1.example.com']], + ], + ])); + $this->assertEquals(SPFCheck::RESULT_PERMERROR, $SPFCheck->isIPAllowed('127.0.0.1', 'e1.example.com')); + } + + public function testIssue7MXExceeded() + { + $SPFCheck = new SPFCheck(new DNSRecordGetterOpenSPF([ + 'e1.example.com' => [ + ['SPF' => ['v=spf1 a mx a mx a mx a mx a ptr ip4:1.2.3.4 -all']], + ['A' => '1.2.3.8'], + ['MX' => [10, 'e1.example.com']], + ['MX' => [20, 'e1.example.com']], + ['MX' => [30, 'e1.example.com']], + ], + ])); + $this->assertEquals(SPFCheck::RESULT_PERMERROR, $SPFCheck->isIPAllowed('127.0.0.1', 'e1.example.com')); + } + + public function testIssue7PTR() + { + $SPFCheck = new SPFCheck(new DNSRecordGetterOpenSPF([ + 'e1.example.com' => [ + ['SPF' => ['v=spf1 ptr ptr ptr ptr ptr ptr ptr ptr ptr ptr -all']], + ['A' => '1.2.3.8'], + ], + ])); + $this->assertEquals(SPFCheck::RESULT_FAIL, $SPFCheck->isIPAllowed('127.0.0.1', 'e1.example.com')); + } + + public function testIssue7PTRAtLimit() + { + $SPFCheck = new SPFCheck(new DNSRecordGetterOpenSPF([ + 'e1.example.com' => [ + ['SPF' => ['v=spf1 ptr ptr ptr ptr ptr ptr ptr ptr ptr ptr -all']], + ['A' => '1.2.3.8'], + ], + ])); + $this->assertEquals(SPFCheck::RESULT_FAIL, $SPFCheck->isIPAllowed('127.0.0.1', 'e1.example.com')); + } + + public function testIssue7PTRExceeded() + { + $SPFCheck = new SPFCheck(new DNSRecordGetterOpenSPF([ + 'e1.example.com' => [ + ['SPF' => ['v=spf1 ptr ptr ptr ptr ptr ptr ptr ptr ptr ptr ptr -all']], + ['A' => '1.2.3.8'], + ], + ])); + $this->assertEquals(SPFCheck::RESULT_PERMERROR, $SPFCheck->isIPAllowed('127.0.0.1', 'e1.example.com')); + } +} \ No newline at end of file diff --git a/tests/SPFRecordGetterFixture.php b/tests/SPFRecordGetterFixture.php index 332b108..487e7f3 100755 --- a/tests/SPFRecordGetterFixture.php +++ b/tests/SPFRecordGetterFixture.php @@ -14,6 +14,8 @@ class DNSRecordGetterFixture implements DNSRecordGetterInterface { protected $requestCount = 0; + protected $requestMXCount = 0; + protected $requestPTRCount = 0; protected $spfRecords = [ 'test.com' => 'v=spf1 +ip4:127.0.0.0/8 +ip4:172.16.0.0/16 ip4:192.168.0.0/24 +ip6:fe80::/64 -all', @@ -101,14 +103,37 @@ public function exists($domain) return array_key_exists($domain, $this->aRecords) && count($this->aRecords) > 0; } + public function resetRequestCount() { - $this->requestCount = 0; + trigger_error('DNSRecordGetterInterface::resetRequestCount() is deprecated. Please use resetRequestCounts() instead', E_USER_DEPRECATED); + $this->resetRequestCounts(); } public function countRequest() { - if (++$this->requestCount == 10) { + if (++$this->requestCount > 10) { + throw new DNSLookupLimitReachedException(); + } + } + + public function resetRequestCounts() + { + $this->requestCount = 0; + $this->requestMXCount = 0; + $this->requestPTRCount = 0; + } + + public function countMxRequest() + { + if (++$this->requestMXCount > 10) { + throw new DNSLookupLimitReachedException(); + } + } + + public function countPtrRequest() + { + if (++$this->requestPTRCount > 10) { throw new DNSLookupLimitReachedException(); } } diff --git a/tests/SPFRecordGetterIssue1.php b/tests/SPFRecordGetterIssue1.php index b3d466a..34785dd 100755 --- a/tests/SPFRecordGetterIssue1.php +++ b/tests/SPFRecordGetterIssue1.php @@ -74,4 +74,16 @@ public function resetRequestCount() public function countRequest() { } + + public function resetRequestCounts() + { + } + + public function countMxRequest() + { + } + + public function countPtrRequest() + { + } } \ No newline at end of file