From 55957a1960a3bda96f84a9fdc6cbeabba2896153 Mon Sep 17 00:00:00 2001 From: Viktor Liu <17948409+lixmal@users.noreply.github.com> Date: Thu, 5 Jun 2025 12:40:23 +0200 Subject: [PATCH] [client] Run registerdns before flushing (#3926) * Run registerdns before flushing * Disable WINS, dynamic updates and registration --- client/internal/dns/host_windows.go | 125 +++++++++++++++++++++++++--- 1 file changed, 113 insertions(+), 12 deletions(-) diff --git a/client/internal/dns/host_windows.go b/client/internal/dns/host_windows.go index cfba29501..f8939328a 100644 --- a/client/internal/dns/host_windows.go +++ b/client/internal/dns/host_windows.go @@ -1,11 +1,14 @@ package dns import ( + "context" "errors" "fmt" "io" + "os/exec" "strings" "syscall" + "time" "github.com/hashicorp/go-multierror" log "github.com/sirupsen/logrus" @@ -41,6 +44,20 @@ const ( interfaceConfigNameServerKey = "NameServer" interfaceConfigSearchListKey = "SearchList" + // Network interface DNS registration settings + disableDynamicUpdateKey = "DisableDynamicUpdate" + registrationEnabledKey = "RegistrationEnabled" + maxNumberOfAddressesToRegisterKey = "MaxNumberOfAddressesToRegister" + + // NetBIOS/WINS settings + netbtInterfacePath = `SYSTEM\CurrentControlSet\Services\NetBT\Parameters\Interfaces` + netbiosOptionsKey = "NetbiosOptions" + + // NetBIOS option values: 0 = from DHCP, 1 = enabled, 2 = disabled + netbiosFromDHCP = 0 + netbiosEnabled = 1 + netbiosDisabled = 2 + // RP_FORCE: Reapply all policies even if no policy change was detected rpForce = 0x1 ) @@ -67,16 +84,85 @@ func newHostManager(wgInterface WGIface) (*registryConfigurator, error) { log.Infof("detected GPO DNS policy configuration, using policy store") } - return ®istryConfigurator{ + configurator := ®istryConfigurator{ guid: guid, gpo: useGPO, - }, nil + } + + if err := configurator.configureInterface(); err != nil { + log.Errorf("failed to configure interface settings: %v", err) + } + + return configurator, nil } func (r *registryConfigurator) supportCustomPort() bool { return false } +func (r *registryConfigurator) configureInterface() error { + var merr *multierror.Error + + if err := r.disableDNSRegistrationForInterface(); err != nil { + merr = multierror.Append(merr, fmt.Errorf("disable DNS registration: %w", err)) + } + + if err := r.disableWINSForInterface(); err != nil { + merr = multierror.Append(merr, fmt.Errorf("disable WINS: %w", err)) + } + + return nberrors.FormatErrorOrNil(merr) +} + +func (r *registryConfigurator) disableDNSRegistrationForInterface() error { + regKey, err := r.getInterfaceRegistryKey() + if err != nil { + return fmt.Errorf("get interface registry key: %w", err) + } + defer closer(regKey) + + var merr *multierror.Error + + if err := regKey.SetDWordValue(disableDynamicUpdateKey, 1); err != nil { + merr = multierror.Append(merr, fmt.Errorf("set %s: %w", disableDynamicUpdateKey, err)) + } + + if err := regKey.SetDWordValue(registrationEnabledKey, 0); err != nil { + merr = multierror.Append(merr, fmt.Errorf("set %s: %w", registrationEnabledKey, err)) + } + + if err := regKey.SetDWordValue(maxNumberOfAddressesToRegisterKey, 0); err != nil { + merr = multierror.Append(merr, fmt.Errorf("set %s: %w", maxNumberOfAddressesToRegisterKey, err)) + } + + if merr == nil || len(merr.Errors) == 0 { + log.Infof("disabled DNS registration for interface %s", r.guid) + } + + return nberrors.FormatErrorOrNil(merr) +} + +func (r *registryConfigurator) disableWINSForInterface() error { + netbtKeyPath := fmt.Sprintf(`%s\Tcpip_%s`, netbtInterfacePath, r.guid) + + regKey, err := registry.OpenKey(registry.LOCAL_MACHINE, netbtKeyPath, registry.SET_VALUE) + if err != nil { + regKey, _, err = registry.CreateKey(registry.LOCAL_MACHINE, netbtKeyPath, registry.SET_VALUE) + if err != nil { + return fmt.Errorf("create NetBT interface key %s: %w", netbtKeyPath, err) + } + } + defer closer(regKey) + + // NetbiosOptions: 2 = disabled + if err := regKey.SetDWordValue(netbiosOptionsKey, netbiosDisabled); err != nil { + return fmt.Errorf("set %s: %w", netbiosOptionsKey, err) + } + + log.Infof("disabled WINS/NetBIOS for interface %s", r.guid) + return nil +} + func (r *registryConfigurator) applyDNSConfig(config HostDNSConfig, stateManager *statemanager.Manager) error { if config.RouteAll { if err := r.addDNSSetupForAll(config.ServerIP); err != nil { @@ -119,9 +205,7 @@ func (r *registryConfigurator) applyDNSConfig(config HostDNSConfig, stateManager return fmt.Errorf("update search domains: %w", err) } - if err := r.flushDNSCache(); err != nil { - log.Errorf("failed to flush DNS cache: %v", err) - } + go r.flushDNSCache() return nil } @@ -191,7 +275,25 @@ func (r *registryConfigurator) string() string { return "registry" } -func (r *registryConfigurator) flushDNSCache() error { +func (r *registryConfigurator) registerDNS() { + ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) + defer cancel() + + // nolint:misspell + cmd := exec.CommandContext(ctx, "ipconfig", "/registerdns") + out, err := cmd.CombinedOutput() + + if err != nil { + log.Errorf("failed to register DNS: %v, output: %s", err, out) + return + } + + log.Info("registered DNS names") +} + +func (r *registryConfigurator) flushDNSCache() { + r.registerDNS() + // dnsFlushResolverCacheFn.Call() may panic if the func is not found defer func() { if rec := recover(); rec != nil { @@ -202,13 +304,14 @@ func (r *registryConfigurator) flushDNSCache() error { ret, _, err := dnsFlushResolverCacheFn.Call() if ret == 0 { if err != nil && !errors.Is(err, syscall.Errno(0)) { - return fmt.Errorf("DnsFlushResolverCache failed: %w", err) + log.Errorf("DnsFlushResolverCache failed: %v", err) + return } - return fmt.Errorf("DnsFlushResolverCache failed") + log.Errorf("DnsFlushResolverCache failed") + return } log.Info("flushed DNS cache") - return nil } func (r *registryConfigurator) updateSearchDomains(domains []string) error { @@ -263,9 +366,7 @@ func (r *registryConfigurator) restoreHostDNS() error { return fmt.Errorf("remove interface registry key: %w", err) } - if err := r.flushDNSCache(); err != nil { - log.Errorf("failed to flush DNS cache: %v", err) - } + go r.flushDNSCache() return nil }