Skip to content

Commit f70b873

Browse files
committed
Support CNAMEs and the ANY query type
Add support for CNAMEs both inside and outside of the zones set by this plugin. Add support for the ANY question type. CNAME support is robust against excessive stack depth and loops. Includes tests for all changes. Signed-off-by: Dan Fuhry <dan@fuhry.com>
1 parent e8b4cfd commit f70b873

File tree

2 files changed

+83
-2
lines changed

2 files changed

+83
-2
lines changed

records.go

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import (
99
"github.com/miekg/dns"
1010
)
1111

12+
const maxCnameStackDepth = 10
13+
1214
// Records is the plugin handler.
1315
type Records struct {
1416
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
3436

3537
nxdomain := true
3638
var soa dns.RR
39+
cnameStack := make(map[string]struct{}, 0)
40+
41+
resolveLoop:
3742
for _, r := range re.m[zone] {
43+
if _, ok := cnameStack[qname]; ok {
44+
log.Errorf("detected loop in CNAME chain, name [%s] already processed", qname)
45+
goto servfail
46+
}
47+
if len(cnameStack) > maxCnameStackDepth {
48+
log.Errorf("maximum CNAME stack depth of %d exceeded", maxCnameStackDepth)
49+
goto servfail
50+
}
51+
3852
if r.Header().Rrtype == dns.TypeSOA && soa == nil {
3953
soa = r
4054
}
4155
if r.Header().Name == qname {
4256
nxdomain = false
43-
if r.Header().Rrtype == state.QType() {
57+
if r.Header().Rrtype == state.QType() || r.Header().Rrtype == dns.TypeCNAME || state.QType() == dns.TypeANY {
4458
m.Answer = append(m.Answer, r)
4559
}
60+
if r.Header().Rrtype == dns.TypeCNAME {
61+
cnameStack[qname] = struct{}{}
62+
qname = r.(*dns.CNAME).Target
63+
if plugin.Zones(re.origins).Matches(qname) == "" {
64+
// if the CNAME target isn't a record in this zone, break and return.
65+
// The administrator can configure the `finalize` plugin (https://coredns.io/explugins/finalize/)
66+
// to complete resolution of these names.
67+
break resolveLoop
68+
}
69+
goto resolveLoop
70+
}
4671
}
4772
}
4873

@@ -64,6 +89,15 @@ func (re *Records) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Ms
6489

6590
w.WriteMsg(m)
6691
return dns.RcodeSuccess, nil
92+
93+
servfail:
94+
m.Rcode = dns.RcodeServerFailure
95+
m.Answer = nil
96+
if soa != nil {
97+
m.Ns = []dns.RR{soa}
98+
}
99+
w.WriteMsg(m)
100+
return dns.RcodeServerFailure, nil
67101
}
68102

69103
// Name implements the plugin.Handle interface.

records_test.go

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
"github.com/coredns/coredns/plugin/pkg/dnstest"
88
"github.com/coredns/coredns/plugin/test"
99

10-
"github.com/caddyserver/caddy"
10+
"github.com/coredns/caddy"
1111
"github.com/miekg/dns"
1212
)
1313

@@ -76,6 +76,25 @@ func TestLookupNoSOA(t *testing.T) {
7676
records {
7777
example.org. 60 IN MX 10 mx.example.org.
7878
mx.example.org. 60 IN A 127.0.0.1
79+
cname.example.org. 60 IN CNAME mx.example.org.
80+
dualstack.example.org. 60 IN A 127.0.0.1
81+
dualstack.example.org. 60 IN AAAA ::1
82+
cnameloop1.example.org. 60 IN CNAME cnameloop2.example.org.
83+
cnameloop2.example.org. 60 IN CNAME cnameloop1.example.org.
84+
cnameext.example.org. 60 IN CNAME mx.example.net.
85+
86+
cnamedepth.example.org. 60 IN CNAME cnamedepth1.example.org.
87+
cnamedepth1.example.org. 60 IN CNAME cnamedepth2.example.org.
88+
cnamedepth2.example.org. 60 IN CNAME cnamedepth3.example.org.
89+
cnamedepth3.example.org. 60 IN CNAME cnamedepth4.example.org.
90+
cnamedepth4.example.org. 60 IN CNAME cnamedepth5.example.org.
91+
cnamedepth5.example.org. 60 IN CNAME cnamedepth6.example.org.
92+
cnamedepth6.example.org. 60 IN CNAME cnamedepth7.example.org.
93+
cnamedepth7.example.org. 60 IN CNAME cnamedepth8.example.org.
94+
cnamedepth8.example.org. 60 IN CNAME cnamedepth9.example.org.
95+
cnamedepth9.example.org. 60 IN CNAME cnamedepth10.example.org.
96+
cnamedepth10.example.org. 60 IN CNAME cnamedepth11.example.org.
97+
cnamedepth11.example.org. 60 IN A 127.0.0.1
7998
}
8099
`
81100

@@ -122,6 +141,34 @@ var testCasesNoSOA = []test.Case{
122141
{
123142
Qname: "mx.example.org.", Qtype: dns.TypeAAAA,
124143
},
144+
{
145+
Qname: "dualstack.example.org.", Qtype: dns.TypeANY,
146+
Answer: []dns.RR{
147+
test.A("dualstack.example.org. 60 IN A 127.0.0.1"),
148+
test.AAAA("dualstack.example.org. 60 IN AAAA ::1"),
149+
},
150+
},
151+
{
152+
Qname: "cname.example.org.", Qtype: dns.TypeA,
153+
Answer: []dns.RR{
154+
test.CNAME("cname.example.org. 60 IN CNAME mx.example.org."),
155+
test.A("mx.example.org. 60 IN A 127.0.0.1"),
156+
},
157+
},
158+
{
159+
Qname: "cnameext.example.org.", Qtype: dns.TypeA,
160+
Answer: []dns.RR{
161+
test.CNAME("cnameext.example.org. 60 IN CNAME mx.example.net."),
162+
},
163+
},
164+
{
165+
Rcode: dns.RcodeServerFailure,
166+
Qname: "cnameloop1.example.org.", Qtype: dns.TypeA,
167+
},
168+
{
169+
Rcode: dns.RcodeServerFailure,
170+
Qname: "cnamedepth.example.org.", Qtype: dns.TypeA,
171+
},
125172
}
126173

127174
func TestLookupMultipleOrigins(t *testing.T) {

0 commit comments

Comments
 (0)