From f767f82396786a0227519d5e433a20ee46034db2 Mon Sep 17 00:00:00 2001 From: riccardom Date: Mon, 24 Aug 2026 17:38:22 +0200 Subject: [PATCH] Install the catch-all rule where the adapter's DNS is set addDNSSetupForAll makes us the peer's main DNS forwarder, and the catch-all NRPT rule is the other half of that same job: without it the adapter's NameServer only adds one more resolver to the set Windows queries in parallel. Having the two in one place says that, where a separate block at the end of applyDNSConfig read as an afterthought. The block could not simply move up: removeDNSMatchPolicies deletes the catch-all key too, so installing the rule before it ran would have had the rule deleted moments later. The cleanup now runs first, which is what it was always for - it clears what the previous apply installed before this one installs anything - and keeps being unconditional, so a leftover rule from an earlier run cannot survive into a config that no longer wants it. --- client/internal/dns/host_windows.go | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/client/internal/dns/host_windows.go b/client/internal/dns/host_windows.go index d4b3a8674..f9560f1c4 100644 --- a/client/internal/dns/host_windows.go +++ b/client/internal/dns/host_windows.go @@ -287,6 +287,13 @@ func (r *registryConfigurator) disableWINSForInterface() error { } func (r *registryConfigurator) applyDNSConfig(config HostDNSConfig, stateManager *statemanager.Manager) error { + // Clear every rule the previous apply installed before installing any new + // one, including a leftover catch-all: removal is unconditional so a rule + // from an earlier run cannot survive into a config that no longer wants it. + if err := r.removeDNSMatchPolicies(); err != nil { + log.Errorf("cleanup old dns match policies: %s", err) + } + if config.RouteAll { if err := r.addDNSSetupForAll(config.ServerIP); err != nil { return fmt.Errorf("add dns setup: %w", err) @@ -312,10 +319,6 @@ func (r *registryConfigurator) applyDNSConfig(config HostDNSConfig, stateManager matchDomains = append(matchDomains, "."+strings.TrimSuffix(dConf.Domain, ".")) } - if err := r.removeDNSMatchPolicies(); err != nil { - log.Errorf("cleanup old dns match policies: %s", err) - } - if len(matchDomains) != 0 { count, err := r.addDNSMatchPolicy(matchDomains, config.ServerIP) // Update count even on error to ensure cleanup covers partially created rules @@ -329,12 +332,6 @@ func (r *registryConfigurator) applyDNSConfig(config HostDNSConfig, stateManager r.updateState(stateManager) - if config.RouteAll { - if err := r.addDNSCatchAllPolicy(config.ServerIP); err != nil { - return fmt.Errorf("add dns catch-all policy: %w", err) - } - } - if err := r.updateSearchDomains(searchDomains); err != nil { return fmt.Errorf("update search domains: %w", err) } @@ -360,7 +357,12 @@ func (r *registryConfigurator) addDNSSetupForAll(ip netip.Addr) error { } r.routingAll = true log.Infof("configured %s:%d as main DNS forwarder for this peer", ip, DefaultPort) - return nil + + // The adapter's NameServer alone does not make us the system resolver: + // Windows queries the resolvers of every adapter in parallel and takes the + // first answer back. A catch-all NRPT rule is evaluated before adapter + // selection and restricts every name to the servers it lists. + return r.addDNSCatchAllPolicy(ip) } func (r *registryConfigurator) addDNSMatchPolicy(domains []string, ip netip.Addr) (int, error) {