Resolve TXT records via CNAME (#3557)
* Add test case for TXT lookup via CNAME Signed-off-by: Jonathan Nagy <nagytech@users.noreply.github.com> * Return HostType of explicit TXT records Signed-off-by: Jonathan Nagy <nagytech@users.noreply.github.com> * Adapt TXT method lookup to allow lookup via CNAME Signed-off-by: Jonathan Nagy <nagytech@users.noreply.github.com> * Implement lookup of TXT records via CNAME Signed-off-by: Jonathan Nagy <nagytech@users.noreply.github.com>
This commit is contained in:
parent
acac649c1b
commit
e3266d24f7
6 changed files with 80 additions and 12 deletions
|
@ -325,15 +325,69 @@ func CNAME(ctx context.Context, b ServiceBackend, zone string, state request.Req
|
|||
}
|
||||
|
||||
// TXT returns TXT records from Backend or an error.
|
||||
func TXT(ctx context.Context, b ServiceBackend, zone string, state request.Request, opt Options) (records []dns.RR, err error) {
|
||||
services, err := b.Services(ctx, state, false, opt)
|
||||
func TXT(ctx context.Context, b ServiceBackend, zone string, state request.Request, previousRecords []dns.RR, opt Options) (records []dns.RR, err error) {
|
||||
|
||||
services, err := b.Services(ctx, state, true, opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dup := make(map[string]struct{})
|
||||
|
||||
for _, serv := range services {
|
||||
records = append(records, serv.NewTXT(state.QName()))
|
||||
|
||||
what, _ := serv.HostType()
|
||||
|
||||
switch what {
|
||||
case dns.TypeCNAME:
|
||||
if Name(state.Name()).Matches(dns.Fqdn(serv.Host)) {
|
||||
// x CNAME x is a direct loop, don't add those
|
||||
continue
|
||||
}
|
||||
|
||||
newRecord := serv.NewCNAME(state.QName(), serv.Host)
|
||||
if len(previousRecords) > 7 {
|
||||
// don't add it, and just continue
|
||||
continue
|
||||
}
|
||||
if dnsutil.DuplicateCNAME(newRecord, previousRecords) {
|
||||
continue
|
||||
}
|
||||
if dns.IsSubDomain(zone, dns.Fqdn(serv.Host)) {
|
||||
state1 := state.NewWithQuestion(serv.Host, state.QType())
|
||||
state1.Zone = zone
|
||||
nextRecords, err := TXT(ctx, b, zone, state1, append(previousRecords, newRecord), opt)
|
||||
|
||||
if err == nil {
|
||||
// Not only have we found something we should add the CNAME and the IP addresses.
|
||||
if len(nextRecords) > 0 {
|
||||
records = append(records, newRecord)
|
||||
records = append(records, nextRecords...)
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
// This means we can not complete the CNAME, try to look else where.
|
||||
target := newRecord.Target
|
||||
// Lookup
|
||||
m1, e1 := b.Lookup(ctx, state, target, state.QType())
|
||||
if e1 != nil {
|
||||
continue
|
||||
}
|
||||
// Len(m1.Answer) > 0 here is well?
|
||||
records = append(records, newRecord)
|
||||
records = append(records, m1.Answer...)
|
||||
continue
|
||||
|
||||
case dns.TypeTXT:
|
||||
if _, ok := dup[serv.Host]; !ok {
|
||||
dup[serv.Host] = struct{}{}
|
||||
return append(records, serv.NewTXT(state.QName())), nil
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return records, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -58,6 +58,9 @@ var servicesCname = []*msg.Service{
|
|||
{Host: "cname6.region2.skydns.test", Key: "cname5.region2.skydns.test."},
|
||||
{Host: "endpoint.region2.skydns.test", Key: "cname6.region2.skydns.test."},
|
||||
{Host: "mainendpoint.region2.skydns.test", Key: "region2.skydns.test."},
|
||||
{Host: "cname2.region3.skydns.test", Key: "cname3.region3.skydns.test."},
|
||||
{Host: "cname1.region3.skydns.test", Key: "cname2.region3.skydns.test."},
|
||||
{Host: "region3.skydns.test", Key: "cname1.region3.skydns.test."},
|
||||
{Host: "", Key: "region3.skydns.test.", Text: "SOME-RECORD-TEXT"},
|
||||
{Host: "10.240.0.1", Key: "endpoint.region2.skydns.test."},
|
||||
}
|
||||
|
@ -91,4 +94,13 @@ var dnsTestCasesCname = []test.Case{
|
|||
test.SOA("skydns.test. 303 IN SOA ns.dns.skydns.test. hostmaster.skydns.test. 1546424605 7200 1800 86400 30"),
|
||||
},
|
||||
},
|
||||
{
|
||||
Qname: "cname3.region3.skydns.test.", Qtype: dns.TypeTXT,
|
||||
Answer: []dns.RR{
|
||||
test.CNAME("cname3.region3.skydns.test. 300 IN CNAME cname2.region3.skydns.test."),
|
||||
test.CNAME("cname2.region3.skydns.test. 300 IN CNAME cname1.region3.skydns.test."),
|
||||
test.CNAME("cname1.region3.skydns.test. 300 IN CNAME region3.skydns.test."),
|
||||
test.TXT("region3.skydns.test. 300 IN TXT \"SOME-RECORD-TEXT\""),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -177,11 +177,9 @@ func (e *Etcd) TTL(kv *mvccpb.KeyValue, serv *msg.Service) uint32 {
|
|||
}
|
||||
|
||||
// shouldInclude returns true if the service should be included in a list of records, given the qType. For all the
|
||||
// currently supported lookup types, the only one to allow for an empty Host field in the service are TXT records.
|
||||
// Similarly, the TXT record in turn requires the Text field to be set.
|
||||
// currently supported lookup types, the only one to allow for an empty Host field in the service are TXT records
|
||||
// which resolve directly. If a TXT record is being resolved by CNAME, then we expect the Host field to have a
|
||||
// value while the TXT field will be empty.
|
||||
func shouldInclude(serv *msg.Service, qType uint16) bool {
|
||||
if qType == dns.TypeTXT {
|
||||
return serv.Text != ""
|
||||
}
|
||||
return serv.Host != ""
|
||||
return (qType == dns.TypeTXT && serv.Text != "") || serv.Host != ""
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ func (e *Etcd) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (
|
|||
case dns.TypeAAAA:
|
||||
records, err = plugin.AAAA(ctx, e, zone, state, nil, opt)
|
||||
case dns.TypeTXT:
|
||||
records, err = plugin.TXT(ctx, e, zone, state, opt)
|
||||
records, err = plugin.TXT(ctx, e, zone, state, nil, opt)
|
||||
case dns.TypeCNAME:
|
||||
records, err = plugin.CNAME(ctx, e, zone, state, opt)
|
||||
case dns.TypePTR:
|
||||
|
|
|
@ -19,8 +19,12 @@ func (s *Service) HostType() (what uint16, normalized net.IP) {
|
|||
ip := net.ParseIP(s.Host)
|
||||
|
||||
switch {
|
||||
|
||||
case ip == nil:
|
||||
if len(s.Text) == 0 {
|
||||
return dns.TypeCNAME, nil
|
||||
}
|
||||
return dns.TypeTXT, nil
|
||||
|
||||
case ip.To4() != nil:
|
||||
return dns.TypeA, ip.To4()
|
||||
|
|
|
@ -35,7 +35,7 @@ func (k Kubernetes) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.M
|
|||
case dns.TypeAAAA:
|
||||
records, err = plugin.AAAA(ctx, &k, zone, state, nil, plugin.Options{})
|
||||
case dns.TypeTXT:
|
||||
records, err = plugin.TXT(ctx, &k, zone, state, plugin.Options{})
|
||||
records, err = plugin.TXT(ctx, &k, zone, state, nil, plugin.Options{})
|
||||
case dns.TypeCNAME:
|
||||
records, err = plugin.CNAME(ctx, &k, zone, state, plugin.Options{})
|
||||
case dns.TypePTR:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue