git » spf » commit 043d2ff

test: Add CNAME support in YAML tests

author Alberto Bertogli
2022-08-06 08:52:37 UTC
committer Alberto Bertogli
2022-08-06 10:02:27 UTC
parent eec4eeb616632562b14ac9ddb2fb75b1fec25b22

test: Add CNAME support in YAML tests

This patch adds CNAME support for the YAML tests.

Note that loop detection is not implemented in the resolver (it's not
worth the complexity), and instead we return them explicitly in the
only case where it's needed.

internal/dnstest/dns.go +14 -0
testdata/pyspf-tests.yml +0 -1
testdata/rfc7208-tests.yml +3 -1
yml_test.go +25 -11

diff --git a/internal/dnstest/dns.go b/internal/dnstest/dns.go
index a870de5..2ec1551 100644
--- a/internal/dnstest/dns.go
+++ b/internal/dnstest/dns.go
@@ -22,6 +22,7 @@ type TestResolver struct {
 	Mx     map[string][]*net.MX
 	Ip     map[string][]net.IP
 	Addr   map[string][]string
+	Cname  map[string]string
 	Errors map[string]error
 }
 
@@ -31,6 +32,7 @@ func NewResolver() *TestResolver {
 		Mx:     map[string][]*net.MX{},
 		Ip:     map[string][]net.IP{},
 		Addr:   map[string][]string{},
+		Cname:  map[string]string{},
 		Errors: map[string]error{},
 	}
 }
@@ -46,6 +48,9 @@ func (r *TestResolver) LookupTXT(ctx context.Context, domain string) (txts []str
 	}
 	domain = strings.ToLower(domain)
 	domain = strings.TrimRight(domain, ".")
+	if cname, ok := r.Cname[domain]; ok {
+		return r.LookupTXT(ctx, cname)
+	}
 	if _, ok := r.Txt[domain]; !ok && r.Errors[domain] == nil {
 		return nil, nxDomainErr
 	}
@@ -58,6 +63,9 @@ func (r *TestResolver) LookupMX(ctx context.Context, domain string) (mxs []*net.
 	}
 	domain = strings.ToLower(domain)
 	domain = strings.TrimRight(domain, ".")
+	if cname, ok := r.Cname[domain]; ok {
+		return r.LookupMX(ctx, cname)
+	}
 	if _, ok := r.Mx[domain]; !ok && r.Errors[domain] == nil {
 		return nil, nxDomainErr
 	}
@@ -70,6 +78,9 @@ func (r *TestResolver) LookupIPAddr(ctx context.Context, host string) (as []net.
 	}
 	host = strings.ToLower(host)
 	host = strings.TrimRight(host, ".")
+	if cname, ok := r.Cname[host]; ok {
+		return r.LookupIPAddr(ctx, cname)
+	}
 	if _, ok := r.Ip[host]; !ok && r.Errors[host] == nil {
 		return nil, nxDomainErr
 	}
@@ -90,6 +101,9 @@ func (r *TestResolver) LookupAddr(ctx context.Context, host string) (addrs []str
 	}
 	host = strings.ToLower(host)
 	host = strings.TrimRight(host, ".")
+	if cname, ok := r.Cname[host]; ok {
+		return r.LookupAddr(ctx, cname)
+	}
 	if _, ok := r.Addr[host]; !ok && r.Errors[host] == nil {
 		return nil, nxDomainErr
 	}
diff --git a/testdata/pyspf-tests.yml b/testdata/pyspf-tests.yml
index 8dea89d..baeab58 100644
--- a/testdata/pyspf-tests.yml
+++ b/testdata/pyspf-tests.yml
@@ -171,7 +171,6 @@ tests:
     host: 1.2.3.4
     mailfrom: foo@e2.example.com
     result: pass
-    skip: Test runner doesn't handle CNAMEs yet.
   null-cname:
     comment: |
       pyspf was getting a type error for null CNAMEs
diff --git a/testdata/rfc7208-tests.yml b/testdata/rfc7208-tests.yml
index a477882..cb0933a 100644
--- a/testdata/rfc7208-tests.yml
+++ b/testdata/rfc7208-tests.yml
@@ -731,7 +731,9 @@ zonedata:
   loop4.example.com:
     - CNAME: "CNAME.example.com."
   cname.example.com:
-    - CNAME: "CNAME.example.com."
+    # Our test resolver doesn't detect CNAME loops.
+    #- CNAME: "CNAME.example.com."
+    - CNAMELOOP: true
 
 ---
 description: A mechanism syntax
diff --git a/yml_test.go b/yml_test.go
index 46706f1..737a643 100644
--- a/yml_test.go
+++ b/yml_test.go
@@ -43,15 +43,18 @@ type Test struct {
 
 // Only one of these will be set.
 type Record struct {
-	A        stringSlice `yaml:"A"`
-	AAAA     stringSlice `yaml:"AAAA"`
-	MX       *MX         `yaml:"MX"`
-	SPF      stringSlice `yaml:"SPF"`
-	TXT      stringSlice `yaml:"TXT"`
-	PTR      stringSlice `yaml:"PTR"`
-	CNAME    stringSlice `yaml:"CNAME"`
-	TIMEOUT  bool        `yaml:"TIMEOUT"`
-	SERVFAIL bool        `yaml:"SERVFAIL"`
+	A     stringSlice `yaml:"A"`
+	AAAA  stringSlice `yaml:"AAAA"`
+	MX    *MX         `yaml:"MX"`
+	SPF   stringSlice `yaml:"SPF"`
+	TXT   stringSlice `yaml:"TXT"`
+	PTR   stringSlice `yaml:"PTR"`
+	CNAME string      `yaml:"CNAME"`
+
+	// Errors.
+	TIMEOUT   bool `yaml:"TIMEOUT"`
+	SERVFAIL  bool `yaml:"SERVFAIL"`
+	CNAMELOOP bool `yaml:"CNAMELOOP"`
 }
 
 func (r Record) String() string {
@@ -73,7 +76,7 @@ func (r Record) String() string {
 	if len(r.PTR) > 0 {
 		return fmt.Sprintf("PTR: %v", r.PTR)
 	}
-	if len(r.CNAME) > 0 {
+	if r.CNAME != "" {
 		return fmt.Sprintf("CNAME: %v", r.CNAME)
 	}
 	if r.TIMEOUT {
@@ -82,6 +85,9 @@ func (r Record) String() string {
 	if r.SERVFAIL {
 		return "SERVFAIL"
 	}
+	if r.CNAMELOOP {
+		return "CNAMELOOP"
+	}
 	return "<empty>"
 }
 
@@ -176,6 +182,12 @@ func testRFC(t *testing.T, fname string) {
 					}
 					dns.Errors[domain] = err
 				}
+				if record.CNAMELOOP {
+					dns.Errors[domain] = &net.DNSError{
+						Err:         "CNAME loop detected",
+						IsTemporary: false,
+					}
+				}
 				for _, s := range record.A {
 					dns.Ip[domain] = append(dns.Ip[domain], net.ParseIP(s))
 				}
@@ -203,7 +215,9 @@ func testRFC(t *testing.T, fname string) {
 					ip := reverseDNS(t, domain).String()
 					dns.Addr[ip] = append(dns.Addr[ip], s)
 				}
-				// TODO: CNAME
+				if record.CNAME != "" {
+					dns.Cname[domain] = record.CNAME
+				}
 			}
 
 			// The test suite is not well done: some tests use SPF instead of