From c942ca7c3606601399ebea8ccddf8dbbf68e7fd1 Mon Sep 17 00:00:00 2001 From: Ilya Kulakov Date: Wed, 2 Sep 2026 23:59:58 -0700 Subject: [PATCH] plugin: use Zones.Contains when any match suffices (#8505) --- plugin/auto/auto.go | 5 ++--- plugin/autopath/autopath.go | 3 +-- plugin/cache/cache.go | 4 ++-- plugin/cache/handler.go | 3 +-- plugin/hosts/hosts.go | 3 +-- plugin/hosts/hostsfile.go | 2 +- plugin/kubernetes/kubernetes.go | 3 +-- plugin/kubernetes/metadata.go | 5 +---- plugin/kubernetes/xfr.go | 3 +-- plugin/loop/loop.go | 3 +-- plugin/metadata/metadata.go | 2 +- plugin/normalize.go | 10 ++++++++-- plugin/pkg/fall/fall.go | 2 +- plugin/secondary/catalog.go | 2 +- plugin/template/template.go | 3 +-- plugin/tsig/tsig.go | 2 +- 16 files changed, 25 insertions(+), 30 deletions(-) diff --git a/plugin/auto/auto.go b/plugin/auto/auto.go index fa62d8f14..a1cd9f35a 100644 --- a/plugin/auto/auto.go +++ b/plugin/auto/auto.go @@ -43,13 +43,12 @@ func (a Auto) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (i qname := state.Name() // Precheck with the origins, i.e. are we allowed to look here? - zone := plugin.Zones(a.Zones.Origins()).Matches(qname) - if zone == "" { + if !plugin.Zones(a.Zones.Origins()).Contains(qname) { return plugin.NextOrFailure(a.Name(), a.Next, ctx, w, r) } // Now the real zone. - zone = plugin.Zones(a.Zones.Names()).Matches(qname) + zone := plugin.Zones(a.Zones.Names()).Matches(qname) if zone == "" { // If no next plugin is configured, it's more correct to return REFUSED as auto acts as an authoritative server if a.Next == nil { diff --git a/plugin/autopath/autopath.go b/plugin/autopath/autopath.go index f6b3488e8..59d0282bc 100644 --- a/plugin/autopath/autopath.go +++ b/plugin/autopath/autopath.go @@ -68,8 +68,7 @@ type AutoPath struct { func (a *AutoPath) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { state := request.Request{W: w, Req: r} - zone := plugin.Zones(a.Zones).Matches(state.Name()) - if zone == "" { + if !plugin.Zones(a.Zones).Contains(state.Name()) { return plugin.NextOrFailure(a.Name(), a.Next, ctx, w, r) } diff --git a/plugin/cache/cache.go b/plugin/cache/cache.go index 9141661a7..b2a2f6520 100644 --- a/plugin/cache/cache.go +++ b/plugin/cache/cache.go @@ -468,7 +468,7 @@ func (w *ResponseWriter) set(m *dns.Msg, key uint64, mt response.Type, duration // and key is valid switch mt { case response.NoError, response.Delegation: - if plugin.Zones(w.pexcept).Matches(m.Question[0].Name) != "" { + if plugin.Zones(w.pexcept).Contains(m.Question[0].Name) { // zone is in exception list, do not cache return } @@ -493,7 +493,7 @@ func (w *ResponseWriter) set(m *dns.Msg, key uint64, mt response.Type, duration } case response.NameError, response.NoData, response.ServerError: - if plugin.Zones(w.nexcept).Matches(m.Question[0].Name) != "" { + if plugin.Zones(w.nexcept).Contains(m.Question[0].Name) { // zone is in exception list, do not cache return } diff --git a/plugin/cache/handler.go b/plugin/cache/handler.go index 18433f32e..117e87d5c 100644 --- a/plugin/cache/handler.go +++ b/plugin/cache/handler.go @@ -21,8 +21,7 @@ func (c *Cache) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) cd := r.CheckingDisabled ad := r.AuthenticatedData - zone := plugin.Zones(c.Zones).Matches(state.Name()) - if zone == "" { + if !plugin.Zones(c.Zones).Contains(state.Name()) { return plugin.NextOrFailure(c.Name(), c.Next, ctx, w, rc) } diff --git a/plugin/hosts/hosts.go b/plugin/hosts/hosts.go index b47f09bdf..0530ee9dd 100644 --- a/plugin/hosts/hosts.go +++ b/plugin/hosts/hosts.go @@ -35,8 +35,7 @@ func (h Hosts) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) ( if zones == nil { zones = plugin.Zones(h.Origins) } - zone := zones.Matches(qname) - if zone == "" { + if !zones.Contains(qname) { // PTR zones don't need to be specified in Origins. if state.QType() != dns.TypePTR { // if this doesn't match we need to fall through regardless of h.Fallthrough diff --git a/plugin/hosts/hostsfile.go b/plugin/hosts/hostsfile.go index e8f3a9dd7..169c0f22f 100644 --- a/plugin/hosts/hostsfile.go +++ b/plugin/hosts/hostsfile.go @@ -202,7 +202,7 @@ func (h *Hostsfile) parse(r io.Reader) *Map { for i := 1; i < len(f); i++ { name := plugin.Name(string(f[i])).Normalize() - if plugin.Zones(h.Origins).Matches(name) == "" { + if !plugin.Zones(h.Origins).Contains(name) { // name is not in Origins continue } diff --git a/plugin/kubernetes/kubernetes.go b/plugin/kubernetes/kubernetes.go index 940c0c3d8..f83d8f2e8 100644 --- a/plugin/kubernetes/kubernetes.go +++ b/plugin/kubernetes/kubernetes.go @@ -718,8 +718,7 @@ func (k *Kubernetes) Serial(state request.Request) uint32 { func (k *Kubernetes) MinTTL(_state request.Request) uint32 { return k.ttl } func (k *Kubernetes) isMultiClusterZone(zone string) bool { - z := plugin.Zones(k.opts.multiclusterZones).Matches(zone) - return z != "" + return plugin.Zones(k.opts.multiclusterZones).Contains(zone) } // match checks if a and b are equal. diff --git a/plugin/kubernetes/metadata.go b/plugin/kubernetes/metadata.go index 6832ee411..0eb6e1f8a 100644 --- a/plugin/kubernetes/metadata.go +++ b/plugin/kubernetes/metadata.go @@ -31,10 +31,7 @@ func (k *Kubernetes) Metadata(ctx context.Context, state request.Request) contex if zone == "" { return ctx } - multicluster := false - if z := plugin.Zones(k.opts.multiclusterZones).Matches(state.Zone); z != "" { - multicluster = true - } + multicluster := plugin.Zones(k.opts.multiclusterZones).Contains(state.Zone) // possible optimization: cache r so it doesn't need to be calculated again in ServeDNS r, err := parseRequest(state.Name(), zone, multicluster, k.opts.zonal) if err != nil { diff --git a/plugin/kubernetes/xfr.go b/plugin/kubernetes/xfr.go index ffbb4df91..8a792bda9 100644 --- a/plugin/kubernetes/xfr.go +++ b/plugin/kubernetes/xfr.go @@ -18,8 +18,7 @@ import ( // Transfer implements the transfer.Transfer interface. func (k *Kubernetes) Transfer(zone string, serial uint32) (<-chan []dns.RR, error) { - match := plugin.Zones(k.Zones).Matches(zone) - if match == "" { + if !plugin.Zones(k.Zones).Contains(zone) { return nil, transfer.ErrNotAuthoritative } // state is not used here, hence the empty request.Request{] diff --git a/plugin/loop/loop.go b/plugin/loop/loop.go index 8d29798ad..11f7acda2 100644 --- a/plugin/loop/loop.go +++ b/plugin/loop/loop.go @@ -40,8 +40,7 @@ func (l *Loop) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) ( state := request.Request{W: w, Req: r} - zone := plugin.Zones([]string{l.zone}).Matches(state.Name()) - if zone == "" { + if !plugin.Zones([]string{l.zone}).Contains(state.Name()) { return plugin.NextOrFailure(l.Name(), l.Next, ctx, w, r) } diff --git a/plugin/metadata/metadata.go b/plugin/metadata/metadata.go index 58e5ce2e2..db58b4503 100644 --- a/plugin/metadata/metadata.go +++ b/plugin/metadata/metadata.go @@ -34,7 +34,7 @@ func (m *Metadata) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Ms // Collect will retrieve metadata functions from each metadata provider and update the context func (m *Metadata) Collect(ctx context.Context, state request.Request) context.Context { ctx = ContextWithMetadata(ctx) - if plugin.Zones(m.Zones).Matches(state.Name()) != "" { + if plugin.Zones(m.Zones).Contains(state.Name()) { // Go through all Providers and collect metadata. for _, p := range m.Providers { ctx = p.Metadata(ctx, state) diff --git a/plugin/normalize.go b/plugin/normalize.go index 1c596ee19..f53d1ac6f 100644 --- a/plugin/normalize.go +++ b/plugin/normalize.go @@ -4,6 +4,7 @@ import ( "fmt" "net" "runtime" + "slices" "strconv" "strings" @@ -22,8 +23,7 @@ type Zones []string // Matches checks if qname is a subdomain of any of the zones in z. The match // will return the most specific zones that matches. The empty string // signals a not found condition. -func (z Zones) Matches(qname string) string { - zone := "" +func (z Zones) Matches(qname string) (zone string) { for _, zname := range z { if dns.IsSubDomain(zname, qname) { // We want the *longest* matching zone, otherwise we may end up in a parent @@ -35,6 +35,12 @@ func (z Zones) Matches(qname string) string { return zone } +func (z Zones) Contains(qname string) bool { + return slices.ContainsFunc(z, func(zname string) bool { + return dns.IsSubDomain(zname, qname) + }) +} + // Normalize fully qualifies all zones in z. The zones in Z must be domain names, without // a port or protocol prefix. func (z Zones) Normalize() { diff --git a/plugin/pkg/fall/fall.go b/plugin/pkg/fall/fall.go index db7b27fb1..ed04b65aa 100644 --- a/plugin/pkg/fall/fall.go +++ b/plugin/pkg/fall/fall.go @@ -27,7 +27,7 @@ type F struct { // Through will check if we should fallthrough for qname. Note that we've named the // variable in each plugin "Fall", so this then reads Fall.Through(). func (f F) Through(qname string) bool { - return plugin.Zones(f.Zones).Matches(qname) != "" + return plugin.Zones(f.Zones).Contains(qname) } // setZones will set zones in f. diff --git a/plugin/secondary/catalog.go b/plugin/secondary/catalog.go index 9a06b193f..15ce246f5 100644 --- a/plugin/secondary/catalog.go +++ b/plugin/secondary/catalog.go @@ -120,7 +120,7 @@ func (s *Secondary) applyCatalog(origin string, cat *catalog.Catalog, catalogZon func (s *Secondary) catalogMemberAllowed(origin, member string) bool { zones, ok := s.catalogZones[origin] - return ok && (len(zones) == 0 || zones.Matches(member) != "") + return ok && (len(zones) == 0 || zones.Contains(member)) } func catalogMember(cat *catalog.Catalog, zone string) (catalog.Member, bool) { diff --git a/plugin/template/template.go b/plugin/template/template.go index bacee6f98..936282cd7 100644 --- a/plugin/template/template.go +++ b/plugin/template/template.go @@ -103,8 +103,7 @@ func exprEnv(ctx context.Context, state *request.Request, data *templateData) ma func (h Handler) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (int, error) { state := request.Request{W: w, Req: r} - zone := plugin.Zones(h.Zones).Matches(state.Name()) - if zone == "" { + if !plugin.Zones(h.Zones).Contains(state.Name()) { return plugin.NextOrFailure(h.Name(), h.Next, ctx, w, r) } diff --git a/plugin/tsig/tsig.go b/plugin/tsig/tsig.go index d387c1d17..e240f856a 100644 --- a/plugin/tsig/tsig.go +++ b/plugin/tsig/tsig.go @@ -39,7 +39,7 @@ func (t *TSIGServer) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns. switch { case tsigRR == nil && !t.tsigRequired(state.QType(), r.Opcode): fallthrough - case plugin.Zones(t.Zones).Matches(state.Name()) == "": + case !plugin.Zones(t.Zones).Contains(state.Name()): return plugin.NextOrFailure(t.Name(), t.Next, ctx, w, r) case tsigRR == nil: log.Debugf("rejecting '%s' request without TSIG\n", dns.TypeToString[state.QType()])