diff --git a/plugin/file/lookup.go b/plugin/file/lookup.go index 14dfb6f7d..9c2c1959b 100644 --- a/plugin/file/lookup.go +++ b/plugin/file/lookup.go @@ -3,6 +3,7 @@ package file import ( "context" + "github.com/coredns/coredns/plugin/file/rrutil" "github.com/coredns/coredns/plugin/file/tree" "github.com/coredns/coredns/request" @@ -32,31 +33,23 @@ func (z *Zone) Lookup(ctx context.Context, state request.Request, qname string) qtype := state.QType() do := state.Do() - if 0 < z.ReloadInterval { - z.reloadMu.RLock() - } - defer func() { - if 0 < z.ReloadInterval { - z.reloadMu.RUnlock() - } - }() - // If z is a secondary zone we might not have transferred it, meaning we have // all zone context setup, except the actual record. This means (for one thing) the apex // is empty and we don't have a SOA record. - z.apexMu.RLock() - soa := z.Apex.SOA - z.apexMu.RUnlock() - if soa == nil { + z.RLock() + ap := z.Apex + tr := z.Tree + z.RUnlock() + if ap.SOA == nil { return nil, nil, nil, ServerFailure } if qtype == dns.TypeSOA { - return z.soa(do), z.ns(do), nil, Success + return ap.soa(do), ap.ns(do), nil, Success } if qtype == dns.TypeNS && qname == z.origin { - nsrrs := z.ns(do) - glue := z.Glue(nsrrs, do) + nsrrs := ap.ns(do) + glue := tr.Glue(nsrrs, do) // technically this isn't glue return nsrrs, nil, glue, Success } @@ -87,14 +80,14 @@ func (z *Zone) Lookup(ctx context.Context, state request.Request, qname string) break } - elem, found = z.Tree.Search(parts) + elem, found = tr.Search(parts) if !found { // Apex will always be found, when we are here we can search for a wildcard // and save the result of that search. So when nothing match, but we have a // wildcard we should expand the wildcard. wildcard := replaceWithAsteriskLabel(parts) - if wild, found := z.Tree.Search(wildcard); found { + if wild, found := tr.Search(wildcard); found { wildElem = wild } @@ -110,11 +103,11 @@ func (z *Zone) Lookup(ctx context.Context, state request.Request, qname string) // Only one DNAME is allowed per name. We just pick the first one to synthesize from. dname := dnamerrs[0] if cname := synthesizeCNAME(state.Name(), dname.(*dns.DNAME)); cname != nil { - answer, ns, extra, rcode := z.additionalProcessing(ctx, state, elem, []dns.RR{cname}) + answer, ns, extra, rcode := z.externalLookup(ctx, state, elem, []dns.RR{cname}) if do { sigs := elem.Type(dns.TypeRRSIG) - sigs = signatureForSubType(sigs, dns.TypeDNAME) + sigs = rrutil.SubTypeSignature(sigs, dns.TypeDNAME) dnamerrs = append(dnamerrs, sigs...) } @@ -140,9 +133,9 @@ func (z *Zone) Lookup(ctx context.Context, state request.Request, qname string) continue } - glue := z.Glue(nsrrs, do) + glue := tr.Glue(nsrrs, do) if do { - dss := z.typeFromElem(elem, dns.TypeDS, do) + dss := typeFromElem(elem, dns.TypeDS, do) nsrrs = append(nsrrs, dss...) } @@ -161,16 +154,16 @@ func (z *Zone) Lookup(ctx context.Context, state request.Request, qname string) if found && shot { if rrs := elem.Type(dns.TypeCNAME); len(rrs) > 0 && qtype != dns.TypeCNAME { - return z.additionalProcessing(ctx, state, elem, rrs) + return z.externalLookup(ctx, state, elem, rrs) } rrs := elem.Type(qtype) // NODATA if len(rrs) == 0 { - ret := z.soa(do) + ret := ap.soa(do) if do { - nsec := z.typeFromElem(elem, dns.TypeNSEC, do) + nsec := typeFromElem(elem, dns.TypeNSEC, do) ret = append(ret, nsec...) } return nil, ret, nil, NoData @@ -178,15 +171,15 @@ func (z *Zone) Lookup(ctx context.Context, state request.Request, qname string) // Additional section processing for MX, SRV. Check response and see if any of the names are in baliwick - // if so add IP addresses to the additional section. - additional := additionalProcessing(z, rrs, do) + additional := z.additionalProcessing(rrs, do) if do { sigs := elem.Type(dns.TypeRRSIG) - sigs = signatureForSubType(sigs, qtype) + sigs = rrutil.SubTypeSignature(sigs, qtype) rrs = append(rrs, sigs...) } - return rrs, z.ns(do), additional, Success + return rrs, ap.ns(do), additional, Success } @@ -194,19 +187,19 @@ func (z *Zone) Lookup(ctx context.Context, state request.Request, qname string) // Found wildcard. if wildElem != nil { - auth := z.ns(do) + auth := ap.ns(do) if rrs := wildElem.TypeForWildcard(dns.TypeCNAME, qname); len(rrs) > 0 { - return z.additionalProcessing(ctx, state, wildElem, rrs) + return z.externalLookup(ctx, state, wildElem, rrs) } rrs := wildElem.TypeForWildcard(qtype, qname) // NODATA response. if len(rrs) == 0 { - ret := z.soa(do) + ret := ap.soa(do) if do { - nsec := z.typeFromElem(wildElem, dns.TypeNSEC, do) + nsec := typeFromElem(wildElem, dns.TypeNSEC, do) ret = append(ret, nsec...) } return nil, ret, nil, Success @@ -214,13 +207,13 @@ func (z *Zone) Lookup(ctx context.Context, state request.Request, qname string) if do { // An NSEC is needed to say no longer name exists under this wildcard. - if deny, found := z.Tree.Prev(qname); found { - nsec := z.typeFromElem(deny, dns.TypeNSEC, do) + if deny, found := tr.Prev(qname); found { + nsec := typeFromElem(deny, dns.TypeNSEC, do) auth = append(auth, nsec...) } sigs := wildElem.TypeForWildcard(dns.TypeRRSIG, qname) - sigs = signatureForSubType(sigs, qtype) + sigs = rrutil.SubTypeSignature(sigs, qtype) rrs = append(rrs, sigs...) } @@ -231,19 +224,19 @@ func (z *Zone) Lookup(ctx context.Context, state request.Request, qname string) // Hacky way to get around empty-non-terminals. If a longer name does exist, but this qname, does not, it // must be an empty-non-terminal. If so, we do the proper NXDOMAIN handling, but set the rcode to be success. - if x, found := z.Tree.Next(qname); found { + if x, found := tr.Next(qname); found { if dns.IsSubDomain(qname, x.Name()) { rcode = Success } } - ret := z.soa(do) + ret := ap.soa(do) if do { - deny, found := z.Tree.Prev(qname) + deny, found := tr.Prev(qname) if !found { goto Out } - nsec := z.typeFromElem(deny, dns.TypeNSEC, do) + nsec := typeFromElem(deny, dns.TypeNSEC, do) ret = append(ret, nsec...) if rcode != NameError { @@ -256,10 +249,10 @@ func (z *Zone) Lookup(ctx context.Context, state request.Request, qname string) if found { // wildcard denial wildcard := "*." + ce.Name() - if ss, found := z.Tree.Prev(wildcard); found { + if ss, found := tr.Prev(wildcard); found { // Only add this nsec if it is different than the one already added if ss.Name() != deny.Name() { - nsec := z.typeFromElem(ss, dns.TypeNSEC, do) + nsec := typeFromElem(ss, dns.TypeNSEC, do) ret = append(ret, nsec...) } } @@ -270,54 +263,50 @@ Out: return nil, ret, nil, rcode } -// Return type tp from e and add signatures (if they exists) and do is true. -func (z *Zone) typeFromElem(elem *tree.Elem, tp uint16, do bool) []dns.RR { +// typeFromElem returns the type tp from e and adds signatures (if they exist) and do is true. +func typeFromElem(elem *tree.Elem, tp uint16, do bool) []dns.RR { rrs := elem.Type(tp) if do { sigs := elem.Type(dns.TypeRRSIG) - sigs = signatureForSubType(sigs, tp) - if len(sigs) > 0 { - rrs = append(rrs, sigs...) - } + sigs = rrutil.SubTypeSignature(sigs, tp) + rrs = append(rrs, sigs...) } return rrs } -func (z *Zone) soa(do bool) []dns.RR { +func (a Apex) soa(do bool) []dns.RR { if do { - ret := append([]dns.RR{z.Apex.SOA}, z.Apex.SIGSOA...) + ret := append([]dns.RR{a.SOA}, a.SIGSOA...) return ret } - return []dns.RR{z.Apex.SOA} + return []dns.RR{a.SOA} } -func (z *Zone) ns(do bool) []dns.RR { +func (a Apex) ns(do bool) []dns.RR { if do { - ret := append(z.Apex.NS, z.Apex.SIGNS...) + ret := append(a.NS, a.SIGNS...) return ret } - return z.Apex.NS + return a.NS } -// aditionalProcessing adds signatures and tries to resolve CNAMEs that point to external names. -func (z *Zone) additionalProcessing(ctx context.Context, state request.Request, elem *tree.Elem, rrs []dns.RR) ([]dns.RR, []dns.RR, []dns.RR, Result) { +// externalLookup adds signatures and tries to resolve CNAMEs that point to external names. +func (z *Zone) externalLookup(ctx context.Context, state request.Request, elem *tree.Elem, rrs []dns.RR) ([]dns.RR, []dns.RR, []dns.RR, Result) { qtype := state.QType() do := state.Do() if do { sigs := elem.Type(dns.TypeRRSIG) - sigs = signatureForSubType(sigs, dns.TypeCNAME) - if len(sigs) > 0 { - rrs = append(rrs, sigs...) - } + sigs = rrutil.SubTypeSignature(sigs, dns.TypeCNAME) + rrs = append(rrs, sigs...) } targetName := rrs[0].(*dns.CNAME).Target elem, _ = z.Tree.Search(targetName) if elem == nil { - rrs = append(rrs, z.externalLookup(ctx, state, targetName, qtype)...) - return rrs, z.ns(do), nil, Success + rrs = append(rrs, z.doLookup(ctx, state, targetName, qtype)...) + return rrs, z.Apex.ns(do), nil, Success } i := 0 @@ -329,53 +318,39 @@ Redo: if do { sigs := elem.Type(dns.TypeRRSIG) - sigs = signatureForSubType(sigs, dns.TypeCNAME) - if len(sigs) > 0 { - rrs = append(rrs, sigs...) - } + sigs = rrutil.SubTypeSignature(sigs, dns.TypeCNAME) + rrs = append(rrs, sigs...) } targetName := cname[0].(*dns.CNAME).Target elem, _ = z.Tree.Search(targetName) if elem == nil { - rrs = append(rrs, z.externalLookup(ctx, state, targetName, qtype)...) - return rrs, z.ns(do), nil, Success + rrs = append(rrs, z.doLookup(ctx, state, targetName, qtype)...) + return rrs, z.Apex.ns(do), nil, Success } i++ - if i > maxChain { - return rrs, z.ns(do), nil, Success + if i > 8 { + return rrs, z.Apex.ns(do), nil, Success } goto Redo } - targets := cnameForType(elem.All(), qtype) + targets := rrutil.CNAMEForType(elem.All(), qtype) if len(targets) > 0 { rrs = append(rrs, targets...) if do { sigs := elem.Type(dns.TypeRRSIG) - sigs = signatureForSubType(sigs, qtype) - if len(sigs) > 0 { - rrs = append(rrs, sigs...) - } + sigs = rrutil.SubTypeSignature(sigs, qtype) + rrs = append(rrs, sigs...) } } - return rrs, z.ns(do), nil, Success + return rrs, z.Apex.ns(do), nil, Success } -func cnameForType(targets []dns.RR, origQtype uint16) []dns.RR { - ret := []dns.RR{} - for _, target := range targets { - if target.Header().Rrtype == origQtype { - ret = append(ret, target) - } - } - return ret -} - -func (z *Zone) externalLookup(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 { m, e := z.Upstream.Lookup(ctx, state, target, qtype) if e != nil { return nil @@ -386,59 +361,9 @@ func (z *Zone) externalLookup(ctx context.Context, state request.Request, target return m.Answer } -// signatureForSubType range through the signature and return the correct ones for the subtype. -func signatureForSubType(rrs []dns.RR, subtype uint16) []dns.RR { - sigs := []dns.RR{} - for _, sig := range rrs { - if s, ok := sig.(*dns.RRSIG); ok { - if s.TypeCovered == subtype { - sigs = append(sigs, s) - } - } - } - return sigs -} - -// Glue returns any potential glue records for nsrrs. -func (z *Zone) Glue(nsrrs []dns.RR, do bool) []dns.RR { - glue := []dns.RR{} - for _, rr := range nsrrs { - if ns, ok := rr.(*dns.NS); ok && dns.IsSubDomain(ns.Header().Name, ns.Ns) { - glue = append(glue, z.searchGlue(ns.Ns, do)...) - } - } - return glue -} - -// searchGlue looks up A and AAAA for name. -func (z *Zone) searchGlue(name string, do bool) []dns.RR { - glue := []dns.RR{} - - // A - if elem, found := z.Tree.Search(name); found { - glue = append(glue, elem.Type(dns.TypeA)...) - if do { - sigs := elem.Type(dns.TypeRRSIG) - sigs = signatureForSubType(sigs, dns.TypeA) - glue = append(glue, sigs...) - } - } - - // AAAA - if elem, found := z.Tree.Search(name); found { - glue = append(glue, elem.Type(dns.TypeAAAA)...) - if do { - sigs := elem.Type(dns.TypeRRSIG) - sigs = signatureForSubType(sigs, dns.TypeAAAA) - glue = append(glue, sigs...) - } - } - return glue -} - // additionalProcessing checks the current answer section and retrieves A or AAAA records // (and possible SIGs) to need to be put in the additional section. -func additionalProcessing(z *Zone, answer []dns.RR, do bool) (extra []dns.RR) { +func (z *Zone) additionalProcessing(answer []dns.RR, do bool) (extra []dns.RR) { for _, rr := range answer { name := "" switch x := rr.(type) { @@ -461,7 +386,7 @@ func additionalProcessing(z *Zone, answer []dns.RR, do bool) (extra []dns.RR) { if a := elem.Type(addr); a != nil { extra = append(extra, a...) if do { - sig := signatureForSubType(sigs, addr) + sig := rrutil.SubTypeSignature(sigs, addr) extra = append(extra, sig...) } } @@ -470,5 +395,3 @@ func additionalProcessing(z *Zone, answer []dns.RR, do bool) (extra []dns.RR) { return extra } - -const maxChain = 8 diff --git a/plugin/file/reload.go b/plugin/file/reload.go index ce5c81335..eb762ac52 100644 --- a/plugin/file/reload.go +++ b/plugin/file/reload.go @@ -13,10 +13,8 @@ func (z *Zone) Reload() error { tick := time.NewTicker(z.ReloadInterval) go func() { - for { select { - case <-tick.C: zFile := z.File() reader, err := os.Open(zFile) @@ -35,10 +33,10 @@ func (z *Zone) Reload() error { } // copy elements we need - z.reloadMu.Lock() + z.Lock() z.Apex = zone.Apex z.Tree = zone.Tree - z.reloadMu.Unlock() + z.Unlock() log.Infof("Successfully reloaded zone %q in %q with serial %d", z.origin, zFile, z.Apex.SOA.Serial) z.Notify() @@ -52,11 +50,10 @@ func (z *Zone) Reload() error { return nil } -// SOASerialIfDefined returns the SOA's serial if the zone has a SOA record in the Apex, or -// -1 otherwise. +// SOASerialIfDefined returns the SOA's serial if the zone has a SOA record in the Apex, or -1 otherwise. func (z *Zone) SOASerialIfDefined() int64 { - z.reloadMu.Lock() - defer z.reloadMu.Unlock() + z.RLock() + defer z.RUnlock() if z.Apex.SOA != nil { return int64(z.Apex.SOA.Serial) } diff --git a/plugin/file/rrutil/util.go b/plugin/file/rrutil/util.go new file mode 100644 index 000000000..63e447196 --- /dev/null +++ b/plugin/file/rrutil/util.go @@ -0,0 +1,29 @@ +// Package rrutil provides function to find certain RRs in slices. +package rrutil + +import "github.com/miekg/dns" + +// SubTypeSignature returns the RRSIG for the subtype. +func SubTypeSignature(rrs []dns.RR, subtype uint16) []dns.RR { + sigs := []dns.RR{} + // there may be multiple keys that have signed this subtype + for _, sig := range rrs { + if s, ok := sig.(*dns.RRSIG); ok { + if s.TypeCovered == subtype { + sigs = append(sigs, s) + } + } + } + return sigs +} + +// CNAMEForType returns the RR that have the qtype from targets. +func CNAMEForType(rrs []dns.RR, qtype uint16) []dns.RR { + ret := []dns.RR{} + for _, target := range rrs { + if target.Header().Rrtype == qtype { + ret = append(ret, target) + } + } + return ret +} diff --git a/plugin/file/secondary.go b/plugin/file/secondary.go index ed94daad6..408ee887a 100644 --- a/plugin/file/secondary.go +++ b/plugin/file/secondary.go @@ -51,11 +51,11 @@ Transfer: return Err } - z.apexMu.Lock() + z.Lock() z.Tree = z1.Tree z.Apex = z1.Apex *z.Expired = false - z.apexMu.Unlock() + z.Unlock() log.Infof("Transferred: %s from %s", z.origin, tr) return nil } diff --git a/plugin/file/tree/glue.go b/plugin/file/tree/glue.go new file mode 100644 index 000000000..937ae5482 --- /dev/null +++ b/plugin/file/tree/glue.go @@ -0,0 +1,44 @@ +package tree + +import ( + "github.com/coredns/coredns/plugin/file/rrutil" + + "github.com/miekg/dns" +) + +// Glue returns any potential glue records for nsrrs. +func (t *Tree) Glue(nsrrs []dns.RR, do bool) []dns.RR { + glue := []dns.RR{} + for _, rr := range nsrrs { + if ns, ok := rr.(*dns.NS); ok && dns.IsSubDomain(ns.Header().Name, ns.Ns) { + glue = append(glue, t.searchGlue(ns.Ns, do)...) + } + } + return glue +} + +// searchGlue looks up A and AAAA for name. +func (t *Tree) searchGlue(name string, do bool) []dns.RR { + glue := []dns.RR{} + + // A + if elem, found := t.Search(name); found { + glue = append(glue, elem.Type(dns.TypeA)...) + if do { + sigs := elem.Type(dns.TypeRRSIG) + sigs = rrutil.SubTypeSignature(sigs, dns.TypeA) + glue = append(glue, sigs...) + } + } + + // AAAA + if elem, found := t.Search(name); found { + glue = append(glue, elem.Type(dns.TypeAAAA)...) + if do { + sigs := elem.Type(dns.TypeRRSIG) + sigs = rrutil.SubTypeSignature(sigs, dns.TypeAAAA) + glue = append(glue, sigs...) + } + } + return glue +} diff --git a/plugin/file/zone.go b/plugin/file/zone.go index 3eb99ef66..1fcfc5d33 100644 --- a/plugin/file/zone.go +++ b/plugin/file/zone.go @@ -15,14 +15,15 @@ import ( "github.com/miekg/dns" ) -// Zone defines a structure that contains all data related to a DNS zone. +// Zone is a structure that contains all data related to a DNS zone. type Zone struct { origin string origLen int file string *tree.Tree Apex - apexMu sync.RWMutex + + sync.RWMutex TransferTo []string StartupOnce sync.Once @@ -30,9 +31,9 @@ type Zone struct { Expired *bool ReloadInterval time.Duration - reloadMu sync.RWMutex reloadShutdown chan bool - Upstream *upstream.Upstream // Upstream for looking up external names during the resolution process. + + Upstream *upstream.Upstream // Upstream for looking up external names during the resolution process. } // Apex contains the apex records of a zone: SOA, NS and their potential signatures. @@ -122,21 +123,18 @@ func (z *Zone) Insert(r dns.RR) error { return nil } -// Delete deletes r from z. -func (z *Zone) Delete(r dns.RR) { z.Tree.Delete(r) } - -// File retrieves the file path in a safe way +// File retrieves the file path in a safe way. func (z *Zone) File() string { - z.reloadMu.Lock() - defer z.reloadMu.Unlock() + z.RLock() + defer z.RUnlock() return z.file } -// SetFile updates the file path in a safe way +// SetFile updates the file path in a safe way. func (z *Zone) SetFile(path string) { - z.reloadMu.Lock() + z.Lock() z.file = path - z.reloadMu.Unlock() + z.Unlock() } // TransferAllowed checks if incoming request for transferring the zone is allowed according to the ACLs. @@ -162,18 +160,16 @@ func (z *Zone) TransferAllowed(state request.Request) bool { // All returns all records from the zone, the first record will be the SOA record, // otionally followed by all RRSIG(SOA)s. func (z *Zone) All() []dns.RR { - if z.ReloadInterval > 0 { - z.reloadMu.RLock() - defer z.reloadMu.RUnlock() - } - records := []dns.RR{} + z.RLock() allNodes := z.Tree.All() + z.RUnlock() + for _, a := range allNodes { records = append(records, a.All()...) } - // Either the entire Apex is filled or none it, this isn't enforced here though. + z.RLock() if len(z.Apex.SIGNS) > 0 { records = append(z.Apex.SIGNS, records...) } @@ -184,9 +180,12 @@ func (z *Zone) All() []dns.RR { if len(z.Apex.SIGSOA) > 0 { records = append(z.Apex.SIGSOA, records...) } + if z.Apex.SOA != nil { + z.RUnlock() return append([]dns.RR{z.Apex.SOA}, records...) } + z.RUnlock() return records }