git » spf » commit 1d5dff0

ptr: Validate entries via forward resolution

author Alberto Bertogli
2019-10-14 11:38:06 UTC
committer Alberto Bertogli
2019-10-14 12:35:33 UTC
parent a1b76d0b512ea7d50ffe27b5f85f8655e902ba7e

ptr: Validate entries via forward resolution

We should be validating ptr answers by doing a forward resolution on
them and checking if they have A/AAAA, which we're not doing today.

This patch fixes that by extending the ptr handling to do the extra
validation.

Found by the standard test suite.

spf.go +20 -4
spf_test.go +6 -1

diff --git a/spf.go b/spf.go
index 760ed9e..8b8d940 100644
--- a/spf.go
+++ b/spf.go
@@ -369,6 +369,7 @@ func (r *resolution) ptrField(res Result, field, domain string) (bool, Result, e
 	}
 
 	if r.ipNames == nil {
+		r.ipNames = []string{}
 		r.count++
 		ns, err := lookupAddr(r.ip.String())
 		if err != nil {
@@ -379,15 +380,30 @@ func (r *resolution) ptrField(res Result, field, domain string) (bool, Result, e
 			return false, "", err
 		}
 		for _, n := range ns {
-			// Append the lower-case variants so we do a case-insensitive
-			// lookup below.
-			r.ipNames = append(r.ipNames, strings.ToLower(n))
+			// Validate the record by doing a forward resolution: it has to
+			// have some A/AAAA.
+			// https://tools.ietf.org/html/rfc7208#section-5.5
+			if r.count > 10 {
+				return false, "", errLookupLimitReached
+			}
+			r.count++
+			addrs, err := lookupIP(n)
+			if err != nil {
+				// RFC explicitly says to skip domains which error here.
+				continue
+			}
+			trace("ptr forward resolution %q -> %q", n, addrs)
+			if len(addrs) > 0 {
+				// Append the lower-case variants so we do a case-insensitive
+				// lookup below.
+				r.ipNames = append(r.ipNames, strings.ToLower(n))
+			}
 		}
 	}
 
+	trace("ptr evaluating %q in %q", ptrDomain, r.ipNames)
 	ptrDomain = strings.ToLower(ptrDomain)
 	for _, n := range r.ipNames {
-		trace("ptr evaluating %q in %q", n, ptrDomain)
 		if strings.HasSuffix(n, ptrDomain+".") {
 			return true, res, errMatchedPTR
 		}
diff --git a/spf_test.go b/spf_test.go
index bbe3f42..f4c8065 100644
--- a/spf_test.go
+++ b/spf_test.go
@@ -66,7 +66,9 @@ func TestBasic(t *testing.T) {
 	dns.ip["d1111"] = []net.IP{ip1111}
 	dns.ip["d1110"] = []net.IP{ip1110}
 	dns.mx["d1110"] = []*net.MX{{"d1110", 5}, {"nothing", 10}}
-	dns.addr["1.1.1.1"] = []string{"lalala.", "domain.", "d1111."}
+	dns.addr["1.1.1.1"] = []string{"lalala.", "xx.domain.", "d1111."}
+	dns.ip["lalala"] = []net.IP{ip1111}
+	dns.ip["xx.domain"] = []net.IP{ip1111}
 
 	for _, c := range cases {
 		dns.txt["domain"] = []string{c.txt}
@@ -109,12 +111,15 @@ func TestIPv6(t *testing.T) {
 		{"v=spf1 ptr -all", Pass, errMatchedPTR},
 		{"v=spf1 ptr:d6666 -all", Pass, errMatchedPTR},
 		{"v=spf1 ptr:sonlas6 -all", Pass, errMatchedPTR},
+		{"v=spf1 ptr:sonlas7 -all", Fail, errMatchedAll},
 	}
 
 	dns.ip["d6666"] = []net.IP{ip6666}
 	dns.ip["d6660"] = []net.IP{ip6660}
 	dns.mx["d6660"] = []*net.MX{{"d6660", 5}, {"nothing", 10}}
 	dns.addr["2001:db8::68"] = []string{"sonlas6.", "domain.", "d6666."}
+	dns.ip["domain"] = []net.IP{ip1111}
+	dns.ip["sonlas6"] = []net.IP{ip6666}
 
 	for _, c := range cases {
 		dns.txt["domain"] = []string{c.txt}