From 283109bef499b46e8bb8372de84e4fb3a14360c2 Mon Sep 17 00:00:00 2001 From: Dominique Fournier Date: Fri, 21 Aug 2020 09:00:29 +0000 Subject: [PATCH] spfcheck : Add catching of sign (+-?~) before parts of SPF record git-svn-id: https://svn.fournier38.fr/svn/ProgSVN/trunk@6030 bf3deb0d-5f1a-0410-827f-c0cc1f45334c --- Tests/spfcheckTest.php | 15 ++++++++ spfcheck.php | 84 +++++++++++++++++++++++++++++------------- 2 files changed, 74 insertions(+), 25 deletions(-) diff --git a/Tests/spfcheckTest.php b/Tests/spfcheckTest.php index 30bad15..9e85d80 100644 --- a/Tests/spfcheckTest.php +++ b/Tests/spfcheckTest.php @@ -208,6 +208,21 @@ class spfcheckTest extends PHPUnit_Framework_TestCase ))); } + public function test_recordsWithPlus () + { + $spfcheck = new spfcheck (); + $res = $spfcheck->getRecords ("plus.spf.tester.fournier38.fr"); + $this->assertSame ($res, + array ("plus.spf.tester.fournier38.fr" => array ( + "+a" => array (), + "+mx" => array (), + "+ip4:178.33.236.5" => array ("178.33.236.5"), + "+ip4:137.74.69.64" => array ("137.74.69.64"), + "+ip4:51.254.45.81" => array ("51.254.45.81"), + "-all" => array (), + ))); + } + public function test_getRecords_Unknown () { $spfcheck = new spfcheck (); diff --git a/spfcheck.php b/spfcheck.php index f1da9aa..eb5d615 100644 --- a/spfcheck.php +++ b/spfcheck.php @@ -247,13 +247,22 @@ class spfcheck continue; // "redirect=" part $ips[$domain][$part] = array (); + // Manage the sign in front of part + $sign = ""; + if ($part{0} === "+" || $part{0} === "-" || $part{0} === "~" || + $part{0} === "?") + { + $sign = $part[0]; + $part = substr ($part, 1); + } if (stripos ($part, "redirect=") === 0) // {{{ { $ext = substr ($part, 9); if (! is_string ($ext) || trim ($ext) === "") { - $this->errors[$domain][$part] = sprintf (dgettext ("domframework", + $this->errors[$domain][$sign.$part] = + sprintf (dgettext ("domframework", "Invalid redirect set for domain '%s' : empty"), $domain); continue; } @@ -267,7 +276,8 @@ class spfcheck $ext = substr ($part, 8); if (! is_string ($ext) || trim ($ext) === "") { - $this->errors[$domain][$part] = sprintf (dgettext ("domframework", + $this->errors[$domain][$sign.$part] = + sprintf (dgettext ("domframework", "Invalid include set for domain '%s' : empty"), $domain); continue; } @@ -284,7 +294,8 @@ class spfcheck $ext = substr ($partWithDomain, 3); if (! is_string ($ext) || trim ($ext) === "") { - $this->errors[$domain][$part] = sprintf (dgettext ("domframework", + $this->errors[$domain][$sign.$part] = + sprintf (dgettext ("domframework", "Invalid mx set for domain '%s' : empty"), $domain); continue; } @@ -294,10 +305,18 @@ class spfcheck foreach ($this->dns_get_record ($record, DNS_A | DNS_AAAA, "$domain/$part/$record") as $ip) { - $ips[$domain][$part][] = $ip; + $ips[$domain][$sign.$part][] = $ip; } } - sort ($ips[$domain][$part]); + if (! isset ($ips[$domain][$sign.$part])) + { + $this->errors[$domain][$sign.$part] = + sprintf (dgettext ("domframework", + "Invalid mx set for domain '%s' : not available in DNS"), + $domain); + continue; + } + sort ($ips[$domain][$sign.$part]); } // }}} // "ip4:" part @@ -307,7 +326,8 @@ class spfcheck $ext = substr ($part, 4); if (! is_string ($ext) || trim ($ext) === "") { - $this->errors[$domain][$part] = sprintf (dgettext ("domframework", + $this->errors[$domain][$sign.$part] = + sprintf (dgettext ("domframework", "Invalid ip4 set for domain '%s' : empty"), $domain); continue; } @@ -315,7 +335,8 @@ class spfcheck $mask = ($mask === null) ? $mask = "" : $mask = "/$mask"; if (filter_var ($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) === false) { - $this->errors[$domain][$part] = sprintf (dgettext ("domframework", + $this->errors[$domain][$sign.$part] = + sprintf (dgettext ("domframework", "Invalid ip4 set for domain '%s' : Not a valid IPv4 '%s'"), $domain, $ext); continue; @@ -324,12 +345,13 @@ class spfcheck filter_var ($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) && substr ($mask, 1) < 16) { - $this->errors[$domain][$part] = sprintf (dgettext ("domframework", + $this->errors[$domain][$sign.$part] = + sprintf (dgettext ("domframework", "Invalid ip4 set for domain '%s' : Mask '%s' too wide"), $domain, $mask); continue; } - $ips[$domain][$part][] = $ip.$mask; + $ips[$domain][$sign.$part][] = $ip.$mask; } // }}} // "ip6:" part @@ -339,7 +361,8 @@ class spfcheck $ext = substr ($part, 4); if (! is_string ($ext) || trim ($ext) === "") { - $this->errors[$domain][$part] = sprintf (dgettext ("domframework", + $this->errors[$domain][$sign.$part] = + sprintf (dgettext ("domframework", "Invalid ip6 set for domain '%s' : empty"), $domain); continue; } @@ -347,7 +370,8 @@ class spfcheck $mask = ($mask === null) ? $mask = "" : $mask = "/$mask"; if (filter_var ($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) { - $this->errors[$domain][$part] = sprintf (dgettext ("domframework", + $this->errors[$domain][$sign.$part] = + sprintf (dgettext ("domframework", "Invalid ip6 set for domain '%s' : Not a valid IPv6 '%s'"), $domain, $ext); continue; @@ -356,19 +380,21 @@ class spfcheck filter_var ($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) && substr ($mask, 1) < 64) { - $this->errors[$domain][$part] = sprintf (dgettext ("domframework", + $this->errors[$domain][$sign.$part] = + sprintf (dgettext ("domframework", "Invalid ip6 set for domain '%s' : Mask '%s' too wide"), $domain, $mask); continue; } - $ips[$domain][$part][] = $ip.$mask; + $ips[$domain][$sign.$part][] = $ip.$mask; } // }}} // "ptr:" MUST NOT BE USED elseif (stripos ($part, "ptr:") === 0 || strtolower ($part) === "ptr") // {{{ { - $this->errors[$domain][$part] = sprintf (dgettext ("domframework", + $this->errors[$domain][$sign.$part] = + sprintf (dgettext ("domframework", "Invalid ptr set for domain '%s' : PTR must not be used anymore ". "(see RFC7208) : Skip it"), $domain, $part); continue; @@ -384,35 +410,42 @@ class spfcheck $ext = substr ($partWithDomain, 2); if (! is_string ($ext) || trim ($ext) === "") { - $this->errors[$domain][$part] = sprintf (dgettext ("domframework", + $this->errors[$domain][$sign.$part] = + sprintf (dgettext ("domframework", "Invalid A set for domain '%s' : empty"), $domain); continue; } foreach ($this->dns_get_record ($ext, DNS_A | DNS_AAAA, "$domain/$part") as $record) { - $ips[$domain][$part][] = $record; + $ips[$domain][$sign.$part][] = $record; } - sort ($ips[$domain][$part]); + if (! isset ($ips[$domain][$sign.$part])) + { + $this->errors[$domain][$sign.$part] = + sprintf (dgettext ("domframework", + "Invalid A set for domain '%s' : not available in DNS"), $domain); + continue; + } + sort ($ips[$domain][$sign.$part]); } // }}} // "-all" / "~all" / "+all" part - elseif (strtolower ($part) === "-all" || - strtolower ($part) === "~all" || - strtolower ($part) === "?all" || - strtolower ($part) === "+all") + elseif (strtolower ($part) === "all") // {{{ { - $ips[$domain][$part] = array (); + $ips[$domain][$sign.$part] = array (); if ($localAll !== "") { - $this->errors [$domain][$part] = sprintf (dgettext ("domframework", + $this->errors [$domain][$sign.$part] = + sprintf (dgettext ("domframework", "Multiple 'all' definitions for domain '%s'"), $domain); continue; } if ($nb < count ($split) -1) { - $this->errors [$domain][$part] = sprintf (dgettext ("domframework", + $this->errors [$domain][$sign.$part] = + sprintf (dgettext ("domframework", "'all' must be the last part of the record for domain '%s'"), $domain); } @@ -423,7 +456,8 @@ class spfcheck // }}} else { - $this->errors [$domain][$part] = sprintf (dgettext ("domframework", + $this->errors [$domain][$sign.$part] = + sprintf (dgettext ("domframework", "Unknown record part for domain '%s' : '%s'"), $domain, $part); } }