diff --git a/plugin/file/lookup.go b/plugin/file/lookup.go index 28da3d4d0..e036d809a 100644 --- a/plugin/file/lookup.go +++ b/plugin/file/lookup.go @@ -307,8 +307,9 @@ func (z *Zone) externalLookup(ctx context.Context, state request.Request, elem * targetName := rrs[0].(*dns.CNAME).Target elem, _ = z.Tree.Search(targetName) if elem == nil { - rrs = append(rrs, z.doLookup(ctx, state, targetName, qtype)...) - return rrs, z.Apex.ns(do), nil, Success + lookupRRs, result := z.doLookup(ctx, state, targetName, qtype) + rrs = append(rrs, lookupRRs...) + return rrs, z.Apex.ns(do), nil, result } i := 0 @@ -326,8 +327,9 @@ Redo: targetName := cname[0].(*dns.CNAME).Target elem, _ = z.Tree.Search(targetName) if elem == nil { - rrs = append(rrs, z.doLookup(ctx, state, targetName, qtype)...) - return rrs, z.Apex.ns(do), nil, Success + lookupRRs, result := z.doLookup(ctx, state, targetName, qtype) + rrs = append(rrs, lookupRRs...) + return rrs, z.Apex.ns(do), nil, result } i++ @@ -352,15 +354,24 @@ Redo: return rrs, z.Apex.ns(do), nil, Success } -func (z *Zone) doLookup(ctx context.Context, state request.Request, target string, qtype uint16) []dns.RR { +func (z *Zone) doLookup(ctx context.Context, state request.Request, target string, qtype uint16) ([]dns.RR, Result) { m, e := z.Upstream.Lookup(ctx, state, target, qtype) if e != nil { - return nil + return nil, Success } if m == nil { - return nil + return nil, Success } - return m.Answer + if m.Rcode == dns.RcodeNameError { + return m.Answer, NameError + } + if m.Rcode == dns.RcodeServerFailure { + return m.Answer, ServerFailure + } + if m.Rcode == dns.RcodeSuccess && len(m.Answer) == 0 { + return m.Answer, NoData + } + return m.Answer, Success } // additionalProcessing checks the current answer section and retrieves A or AAAA records diff --git a/test/file_upstream_test.go b/test/file_upstream_test.go index 4d13a2ca0..6b796f90d 100644 --- a/test/file_upstream_test.go +++ b/test/file_upstream_test.go @@ -58,6 +58,113 @@ www 3600 IN CNAME www.example.net. } } +func TestFileUpstreamError(t *testing.T) { + cases := map[string]test.Case{ + "nxdomain": { + Qname: "nxdomain.example.org.", Qtype: dns.TypeA, + Answer: []dns.RR{ + test.CNAME("nxdomain.example.org. 3600 IN CNAME nxdomain.example.net"), + }, + Rcode: dns.RcodeNameError, + }, + "nxdomain-chain": { + Qname: "chain1.example.org.", Qtype: dns.TypeA, + Answer: []dns.RR{ + test.CNAME("chain1.example.org. 3600 IN CNAME nxdomain.example.org"), + test.CNAME("nxdomain.example.org. 3600 IN CNAME nxdomain.example.net"), + }, + Rcode: dns.RcodeNameError, + }, + "srvfail": { + Qname: "srvfail.example.org.", Qtype: dns.TypeA, + Rcode: dns.RcodeServerFailure, + }, + "srvfail-chain": { + Qname: "chain2.example.org.", Qtype: dns.TypeA, + Rcode: dns.RcodeServerFailure, + }, + "nodata": { + Qname: "nodata.example.org.", Qtype: dns.TypeA, + Answer: []dns.RR{ + test.CNAME("nodata.example.org. 3600 IN CNAME nodata.example.net"), + }, + Rcode: dns.RcodeSuccess, + }, + "nodata-chain": { + Qname: "chain3.example.org.", Qtype: dns.TypeA, + Answer: []dns.RR{ + test.CNAME("chain3.example.org. 3600 IN CNAME nodata.example.org"), + test.CNAME("nodata.example.org. 3600 IN CNAME nodata.example.net"), + }, + Rcode: dns.RcodeSuccess, + }, + } + name, rm, err := test.TempFile(".", `$ORIGIN example.org. +@ 3600 IN SOA sns.dns.icann.org. noc.dns.icann.org. ( + 2017042745 ; serial + 7200 ; refresh (2 hours) + 3600 ; retry (1 hour) + 1209600 ; expire (2 weeks) + 3600 ; minimum (1 hour) +) + + 3600 IN NS a.iana-servers.net. + 3600 IN NS b.iana-servers.net. + +chain1 3600 IN CNAME nxdomain +nxdomain 3600 IN CNAME nxdomain.example.net. +chain2 3600 IN CNAME srvfail +srvfail 3600 IN CNAME srvfail.example.net. +chain3 3600 IN CNAME nodata +nodata 3600 IN CNAME nodata.example.net. + +`) + if err != nil { + t.Fatalf("Failed to create zone: %s", err) + } + defer rm() + + corefile := `.:0 { + template ANY A nxdomain.example.net. { + rcode NXDOMAIN + } + template ANY A srvfail.example.net. { + rcode SERVFAIL + } + template ANY A nodata.example.net. { + } + file ` + name + ` example.org +}` + + i, udp, _, err := CoreDNSServerAndPorts(corefile) + if err != nil { + t.Fatalf("Could not get CoreDNS serving instance: %s", err) + } + defer i.Stop() + + for n, tc := range cases { + t.Run(n, func(t *testing.T) { + m := new(dns.Msg) + m.SetQuestion(tc.Qname, tc.Qtype) + m.SetEdns0(4096, true) + + r, err := dns.Exchange(m, udp) + if err != nil { + t.Fatalf("Could not exchange msg: %s", err) + } + if r.Rcode != tc.Rcode { + t.Fatalf("expected rcode %v, got %v", tc.Rcode, r.Rcode) + } + if n := len(r.Answer); n != len(tc.Answer) { + t.Fatalf("Expected %v answers, got %v", len(tc.Answer), n) + } + if err := test.Section(tc, test.Answer, r.Answer); err != nil { + t.Error(err) + } + }) + } +} + // TestFileUpstreamAdditional runs two CoreDNS servers that serve example.org and foo.example.org. // example.org contains a cname to foo.example.org; this should be resolved via upstream.Self. func TestFileUpstreamAdditional(t *testing.T) { diff --git a/test/go-test-tmpfile565156097 b/test/go-test-tmpfile565156097 new file mode 100644 index 000000000..752ec30cf --- /dev/null +++ b/test/go-test-tmpfile565156097 @@ -0,0 +1,19 @@ +$ORIGIN example.org. +@ 3600 IN SOA sns.dns.icann.org. noc.dns.icann.org. ( + 2017042745 ; serial + 7200 ; refresh (2 hours) + 3600 ; retry (1 hour) + 1209600 ; expire (2 weeks) + 3600 ; minimum (1 hour) +) + + 3600 IN NS a.iana-servers.net. + 3600 IN NS b.iana-servers.net. + +chain1 3600 IN CNAME nxdomain +nxdomain 3600 IN CNAME nxdomain.example.net. +chain2 3600 IN CNAME srvfail +srvfail 3600 IN CNAME srvfail.example.net. +chain3 3600 IN CNAME nodata +nodata 3600 IN CNAME nodata.example.net. +