plugin/file: Use NXDOMAIN response if CNAME target is NXDOMAIN (#4303)

* pass through nxdomain results

Signed-off-by: Chris O'Haver <cohaver@infoblox.com>

* return srvfail and nodata results

Signed-off-by: Chris O'Haver <cohaver@infoblox.com>

* add test

Signed-off-by: Chris O'Haver <cohaver@infoblox.com>

* cover more response cases

Signed-off-by: Chris O'Haver <cohaver@infoblox.com>
This commit is contained in:
Chris O'Haver 2020-12-09 03:44:31 -05:00 committed by GitHub
parent 5ecf23ae7b
commit 6bbb48d403
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 145 additions and 8 deletions

View file

@ -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

View file

@ -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) {

View file

@ -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.