client/dns: canonicalize pool-root membership check with toZone

GetPoolRootDomains returns what the extractor stored; GetCachedDomains
strips the trailing dot from question names. Today those happen to
produce the same domain.Domain string because both sources run through
domain.FromString, but relying on that parity is fragile.

Run both sides of the poolRootSet membership check through toZone so
the comparison is independent of each source's canonical form.
Prevents a future extractor or cache-entry path from silently routing
a pool-root into exactDomains, where a second registerHandler call
with the bare resolver would clobber the subdomainMatchHandler
registration (AddHandler replaces entries with the same pattern +
priority).
This commit is contained in:
Zoltán Papp
2026-04-24 22:33:41 +02:00
parent 14be474e3d
commit c89c30bb28

View File

@@ -632,10 +632,16 @@ func (s *DefaultServer) UpdateServerConfig(domains dnsconfig.ServerDomains) erro
// cache resolver, which resolves it on demand through the bypass
// resolver instead of falling through to the overlay-routed
// upstream handler.
// Canonicalize pool-root domains (toZone normalizes casing, IDNA,
// and trailing dot) so the membership check below is independent
// of the exact form each source hands back. GetPoolRootDomains
// returns whatever the extractor stored; GetCachedDomains strips
// the trailing dot from question names. Run both through toZone
// to guarantee they compare equal.
poolRoots := s.mgmtCacheResolver.GetPoolRootDomains()
poolRootSet := make(map[domain.Domain]struct{}, len(poolRoots))
for _, d := range poolRoots {
poolRootSet[d] = struct{}{}
poolRootSet[toZone(d)] = struct{}{}
}
if len(poolRoots) > 0 {
@@ -644,7 +650,7 @@ func (s *DefaultServer) UpdateServerConfig(domains dnsconfig.ServerDomains) erro
var exactDomains domain.List
for _, d := range s.mgmtCacheResolver.GetCachedDomains() {
if _, isPool := poolRootSet[d]; isPool {
if _, isPool := poolRootSet[toZone(d)]; isPool {
continue
}
exactDomains = append(exactDomains, d)