plugin: use Zones.Contains when any match suffices (#8505)

This commit is contained in:
Ilya Kulakov
2026-09-02 23:59:58 -07:00
committed by GitHub
parent f1d835aa51
commit c942ca7c36
16 changed files with 25 additions and 30 deletions

View File

@@ -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 {

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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
}

View File

@@ -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.

View File

@@ -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 {

View File

@@ -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{]

View File

@@ -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)
}

View File

@@ -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)

View File

@@ -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() {

View File

@@ -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.

View File

@@ -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) {

View File

@@ -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)
}

View File

@@ -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()])