diff --git a/records.go b/records.go index 4a414f7..319a801 100644 --- a/records.go +++ b/records.go @@ -9,6 +9,8 @@ import ( "github.com/miekg/dns" ) +const maxCnameStackDepth = 10 + // Records is the plugin handler. type Records struct { origins []string // for easy matching, these strings are the index in the map m. @@ -34,15 +36,38 @@ func (re *Records) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Ms nxdomain := true var soa dns.RR + cnameStack := make(map[string]struct{}, 0) + +resolveLoop: for _, r := range re.m[zone] { + if _, ok := cnameStack[qname]; ok { + log.Errorf("detected loop in CNAME chain, name [%s] already processed", qname) + goto servfail + } + if len(cnameStack) > maxCnameStackDepth { + log.Errorf("maximum CNAME stack depth of %d exceeded", maxCnameStackDepth) + goto servfail + } + if r.Header().Rrtype == dns.TypeSOA && soa == nil { soa = r } if r.Header().Name == qname { nxdomain = false - if r.Header().Rrtype == state.QType() { + if r.Header().Rrtype == state.QType() || r.Header().Rrtype == dns.TypeCNAME || state.QType() == dns.TypeANY { m.Answer = append(m.Answer, r) } + if r.Header().Rrtype == dns.TypeCNAME { + cnameStack[qname] = struct{}{} + qname = r.(*dns.CNAME).Target + if plugin.Zones(re.origins).Matches(qname) == "" { + // if the CNAME target isn't a record in this zone, break and return. + // The administrator can configure the `finalize` plugin (https://coredns.io/explugins/finalize/) + // to complete resolution of these names. + break resolveLoop + } + goto resolveLoop + } } } @@ -64,6 +89,15 @@ func (re *Records) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Ms w.WriteMsg(m) return dns.RcodeSuccess, nil + +servfail: + m.Rcode = dns.RcodeServerFailure + m.Answer = nil + if soa != nil { + m.Ns = []dns.RR{soa} + } + w.WriteMsg(m) + return dns.RcodeServerFailure, nil } // Name implements the plugin.Handle interface. diff --git a/records_test.go b/records_test.go index ef9d11d..fda394d 100644 --- a/records_test.go +++ b/records_test.go @@ -7,7 +7,7 @@ import ( "github.com/coredns/coredns/plugin/pkg/dnstest" "github.com/coredns/coredns/plugin/test" - "github.com/caddyserver/caddy" + "github.com/coredns/caddy" "github.com/miekg/dns" ) @@ -76,6 +76,25 @@ func TestLookupNoSOA(t *testing.T) { records { example.org. 60 IN MX 10 mx.example.org. mx.example.org. 60 IN A 127.0.0.1 + cname.example.org. 60 IN CNAME mx.example.org. + dualstack.example.org. 60 IN A 127.0.0.1 + dualstack.example.org. 60 IN AAAA ::1 + cnameloop1.example.org. 60 IN CNAME cnameloop2.example.org. + cnameloop2.example.org. 60 IN CNAME cnameloop1.example.org. + cnameext.example.org. 60 IN CNAME mx.example.net. + + cnamedepth.example.org. 60 IN CNAME cnamedepth1.example.org. + cnamedepth1.example.org. 60 IN CNAME cnamedepth2.example.org. + cnamedepth2.example.org. 60 IN CNAME cnamedepth3.example.org. + cnamedepth3.example.org. 60 IN CNAME cnamedepth4.example.org. + cnamedepth4.example.org. 60 IN CNAME cnamedepth5.example.org. + cnamedepth5.example.org. 60 IN CNAME cnamedepth6.example.org. + cnamedepth6.example.org. 60 IN CNAME cnamedepth7.example.org. + cnamedepth7.example.org. 60 IN CNAME cnamedepth8.example.org. + cnamedepth8.example.org. 60 IN CNAME cnamedepth9.example.org. + cnamedepth9.example.org. 60 IN CNAME cnamedepth10.example.org. + cnamedepth10.example.org. 60 IN CNAME cnamedepth11.example.org. + cnamedepth11.example.org. 60 IN A 127.0.0.1 } ` @@ -122,6 +141,34 @@ var testCasesNoSOA = []test.Case{ { Qname: "mx.example.org.", Qtype: dns.TypeAAAA, }, + { + Qname: "dualstack.example.org.", Qtype: dns.TypeANY, + Answer: []dns.RR{ + test.A("dualstack.example.org. 60 IN A 127.0.0.1"), + test.AAAA("dualstack.example.org. 60 IN AAAA ::1"), + }, + }, + { + Qname: "cname.example.org.", Qtype: dns.TypeA, + Answer: []dns.RR{ + test.CNAME("cname.example.org. 60 IN CNAME mx.example.org."), + test.A("mx.example.org. 60 IN A 127.0.0.1"), + }, + }, + { + Qname: "cnameext.example.org.", Qtype: dns.TypeA, + Answer: []dns.RR{ + test.CNAME("cnameext.example.org. 60 IN CNAME mx.example.net."), + }, + }, + { + Rcode: dns.RcodeServerFailure, + Qname: "cnameloop1.example.org.", Qtype: dns.TypeA, + }, + { + Rcode: dns.RcodeServerFailure, + Qname: "cnamedepth.example.org.", Qtype: dns.TypeA, + }, } func TestLookupMultipleOrigins(t *testing.T) {