This commit is contained in:
bluepython508 committed 2026-10-22 12:27:19 +01:00
1 parent 3530afb854
commit 6903fcff27
2146 files changed
+112256 -190703

No files matched your search

+137 -100
View File
@@ -1,4 +1,4 @@
// Copyright (c) Tailscale Inc & AUTHORS
// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause
package dns
@@ -8,15 +8,14 @@ import (
"bytes"
"errors"
"fmt"
"io/fs"
"maps"
"net/netip"
"os"
"os/exec"
"path/filepath"
"slices"
"sort"
"strings"
"syscall"
"time"
"golang.org/x/sys/windows"
@@ -34,6 +33,7 @@ import (
"tailscale.com/util/syspolicy/policyclient"
"tailscale.com/util/syspolicy/ptype"
"tailscale.com/util/winutil"
"tailscale.com/util/winutil/winenv"
)
const (
@@ -49,6 +49,9 @@ type windowsManager struct {
nrptDB *nrptRuleDatabase
wslManager *wslManager
polc policyclient.Client
doV4 bool
doV6 bool
doNetBIOS bool
unregisterPolicyChangeCb func() // called when the manager is closing
@@ -71,12 +74,16 @@ func NewOSConfigurator(logf logger.Logf, health *health.Tracker, bus *eventbus.B
wslManager: newWSLManager(logf, health),
}
if err := ret.checkInterfaces(); err != nil {
return nil, err
}
if isWindows10OrBetter() {
ret.nrptDB = newNRPTRuleDatabase(logf)
}
var err error
if ret.unregisterPolicyChangeCb, err = polc.RegisterChangeCallback(ret.sysPolicyChanged); err != nil {
if ret.unregisterPolicyChangeCb, err = polc.RegisterChangeCallback("", ret.sysPolicyChanged); err != nil {
logf("error registering policy change callback: %v", err) // non-fatal
}
@@ -92,22 +99,42 @@ func NewOSConfigurator(logf logger.Logf, health *health.Tracker, bus *eventbus.B
return ret, nil
}
func (m *windowsManager) openInterfaceKey(pfx winutil.RegistryPathPrefix) (registry.Key, error) {
var key registry.Key
var err error
path := pfx.WithSuffix(m.guid)
m.mu.Lock()
closing := m.closing
m.mu.Unlock()
if closing {
// Do not wait for the interface key to appear if the manager is being closed.
// If it's being closed due to the removal of the wintun adapter,
// the key would already be gone by now and will not reappear until tailscaled is restarted.
key, err = registry.OpenKey(registry.LOCAL_MACHINE, string(path), registry.SET_VALUE)
} else {
key, err = winutil.OpenKeyWait(registry.LOCAL_MACHINE, path, registry.SET_VALUE)
func (m *windowsManager) checkInterfaces() error {
guid, err := windows.GUIDFromString(m.guid)
if err != nil {
return err
}
luid, err := winipcfg.LUIDFromGUID(&guid)
if err != nil {
return err
}
_, err = luid.IPInterface(windows.AF_INET)
if err != nil && !errors.Is(err, windows.ERROR_NOT_FOUND) {
return fmt.Errorf("getting AF_INET interface: %w", err)
}
m.doV4 = err == nil
_, err = luid.IPInterface(windows.AF_INET6)
if err != nil && !errors.Is(err, windows.ERROR_NOT_FOUND) {
return fmt.Errorf("getting AF_INET6 interface: %w", err)
}
m.doV6 = err == nil
if !m.doV4 && !m.doV6 {
return errors.New("no IP interfaces available for dns configuration")
}
// NetBIOS is non-fatal
_, err = luid.IPInterface(windows.AF_NETBIOS)
m.doNetBIOS = err == nil
return nil
}
func (m *windowsManager) openInterfaceKey(pfx winutil.RegistryPathPrefix) (registry.Key, error) {
path := pfx.WithSuffix(m.guid)
key, err := registry.OpenKey(registry.LOCAL_MACHINE, string(path), registry.SET_VALUE)
if err != nil {
return 0, fmt.Errorf("opening %s: %w", path, err)
}
@@ -145,7 +172,7 @@ func (m *windowsManager) setSplitDNS(resolvers []netip.Addr, domains []dnsname.F
return fmt.Errorf("Split DNS unsupported on this Windows version")
}
defer m.nrptDB.Refresh()
defer m.nrptDB.NotifyPolicyChanged()
if len(resolvers) == 0 {
return m.nrptDB.DelAllRuleKeys()
}
@@ -245,7 +272,13 @@ func (m *windowsManager) setHosts(hosts []*HostEntry) error {
}
hostsFile := filepath.Join(systemDir, "drivers", "etc", "hosts")
b, err := os.ReadFile(hostsFile)
if err != nil {
switch {
case err == nil:
// Continue.
case errors.Is(err, fs.ErrNotExist):
// Non-fatal, we'll just create a new hosts file.
m.logf("failed to read the hosts file: %v", err)
default:
return err
}
outB, err := setTailscaleHosts(m.logf, b, hosts)
@@ -293,58 +326,66 @@ func (m *windowsManager) setPrimaryDNS(resolvers []netip.Addr, domains []dnsname
domStrs = append(domStrs, dom.WithoutTrailingDot())
}
key4, err := m.openInterfaceKey(winutil.IPv4TCPIPInterfacePrefix)
if err != nil {
return m.muteKeyNotFoundIfClosing(err)
}
defer key4.Close()
if m.doV4 {
key4, err := m.openInterfaceKey(winutil.IPv4TCPIPInterfacePrefix)
if err != nil {
return m.muteKeyNotFoundIfClosing(err)
}
defer key4.Close()
if len(ipsv4) == 0 {
if err := delValue(key4, "NameServer"); err != nil {
if len(ipsv4) == 0 {
if err := delValue(key4, "NameServer"); err != nil {
return err
}
} else if err := key4.SetStringValue("NameServer", strings.Join(ipsv4, ",")); err != nil {
return err
}
} else if err := key4.SetStringValue("NameServer", strings.Join(ipsv4, ",")); err != nil {
return err
}
if len(domains) == 0 {
if err := delValue(key4, "SearchList"); err != nil {
if len(domains) == 0 {
if err := delValue(key4, "SearchList"); err != nil {
return err
}
} else if err := key4.SetStringValue("SearchList", strings.Join(domStrs, ",")); err != nil {
return err
}
} else if err := key4.SetStringValue("SearchList", strings.Join(domStrs, ",")); err != nil {
return err
}
key6, err := m.openInterfaceKey(winutil.IPv6TCPIPInterfacePrefix)
if err != nil {
return m.muteKeyNotFoundIfClosing(err)
}
defer key6.Close()
if len(ipsv6) == 0 {
if err := delValue(key6, "NameServer"); err != nil {
// Disable LLMNR on the Tailscale interface. We don't do multicast, and we
// certainly don't do LLMNR, so it's pointless to make Windows try it. It is
// being deprecated.
if err := key4.SetDWordValue("EnableMulticast", 0); err != nil {
return err
}
} else if err := key6.SetStringValue("NameServer", strings.Join(ipsv6, ",")); err != nil {
return err
}
if len(domains) == 0 {
if err := delValue(key6, "SearchList"); err != nil {
if m.doV6 {
key6, err := m.openInterfaceKey(winutil.IPv6TCPIPInterfacePrefix)
if err != nil {
return m.muteKeyNotFoundIfClosing(err)
}
defer key6.Close()
if len(ipsv6) == 0 {
if err := delValue(key6, "NameServer"); err != nil {
return err
}
} else if err := key6.SetStringValue("NameServer", strings.Join(ipsv6, ",")); err != nil {
return err
}
} else if err := key6.SetStringValue("SearchList", strings.Join(domStrs, ",")); err != nil {
return err
}
// Disable LLMNR on the Tailscale interface. We don't do multicast, and we
// certainly don't do LLMNR, so it's pointless to make Windows try it. It is
// being deprecated.
if err := key4.SetDWordValue("EnableMulticast", 0); err != nil {
return err
}
if err := key6.SetDWordValue("EnableMulticast", 0); err != nil {
return err
if len(domains) == 0 {
if err := delValue(key6, "SearchList"); err != nil {
return err
}
} else if err := key6.SetStringValue("SearchList", strings.Join(domStrs, ",")); err != nil {
return err
}
// Disable LLMNR on the Tailscale interface. We don't do multicast, and we
// certainly don't do LLMNR, so it's pointless to make Windows try it. It is
// being deprecated.
if err := key6.SetDWordValue("EnableMulticast", 0); err != nil {
return err
}
}
return nil
@@ -354,6 +395,10 @@ func (m *windowsManager) disableLocalDNSOverrideViaNRPT() bool {
return m.knobs != nil && m.knobs.DisableLocalDNSOverrideViaNRPT.Load()
}
func (m *windowsManager) disableHostsFileUpdates() bool {
return m.knobs != nil && m.knobs.DisableHostsFileUpdates.Load()
}
func (m *windowsManager) SetDNS(cfg OSConfig) error {
// We can configure Windows DNS in one of two ways:
//
@@ -399,7 +444,15 @@ func (m *windowsManager) SetDNS(cfg OSConfig) error {
if err := m.setSplitDNS(resolvers, domains); err != nil {
return err
}
if err := m.setHosts(nil); err != nil {
var hosts []*HostEntry
if !m.disableHostsFileUpdates() && winenv.IsDomainJoined() {
// On domain-joined Windows devices the primary search domain (the one the device is joined to)
// always takes precedence over other search domains. This breaks MagicDNS when we are the primary
// resolver on the device (see #18712). To work around this Windows behavior, we should write MagicDNS
// host names the hosts file just as we do when we're not the primary resolver.
hosts = cfg.Hosts
}
if err := m.setHosts(hosts); err != nil {
return err
}
if err := m.setPrimaryDNS(cfg.Nameservers, cfg.SearchDomains); err != nil {
@@ -421,12 +474,14 @@ func (m *windowsManager) SetDNS(cfg OSConfig) error {
return err
}
// As we are not the primary resolver in this setup, we need to
// explicitly set some single name hosts to ensure that we can resolve
// them quickly and get around the 2.3s delay that otherwise occurs due
// to multicast timeouts.
if err := m.setHosts(cfg.Hosts); err != nil {
return err
if !m.disableHostsFileUpdates() {
// As we are not the primary resolver in this setup, we need to
// explicitly set some single name hosts to ensure that we can resolve
// them quickly and get around the 2.3s delay that otherwise occurs due
// to multicast timeouts.
if err := m.setHosts(cfg.Hosts); err != nil {
return err
}
}
}
@@ -436,40 +491,15 @@ func (m *windowsManager) SetDNS(cfg OSConfig) error {
// have changed, which makes the DNS settings actually take
// effect.
//
// This command can take a few seconds to run, so run it async, best effort.
//
// After re-registering DNS, also flush the DNS cache to clear out
// any cached split-horizon queries that are no longer the correct
// answer.
go func() {
t0 := time.Now()
m.logf("running ipconfig /registerdns ...")
cmd := exec.Command("ipconfig", "/registerdns")
cmd.SysProcAttr = &syscall.SysProcAttr{
CreationFlags: windows.DETACHED_PROCESS,
}
err := cmd.Run()
d := time.Since(t0).Round(time.Millisecond)
if err != nil {
m.logf("error running ipconfig /registerdns after %v: %v", d, err)
} else {
m.logf("ran ipconfig /registerdns in %v", d)
}
t0 = time.Now()
m.logf("running ipconfig /flushdns ...")
cmd = exec.Command("ipconfig", "/flushdns")
cmd.SysProcAttr = &syscall.SysProcAttr{
CreationFlags: windows.DETACHED_PROCESS,
}
err = cmd.Run()
d = time.Since(t0).Round(time.Millisecond)
if err != nil {
m.logf("error running ipconfig /flushdns after %v: %v", d, err)
} else {
m.logf("ran ipconfig /flushdns in %v", d)
}
}()
//
// Both operations are non-blocking and coalesced: at most one of
// each is in flight at a time, with at most one more queued.
m.logf("Re-registering DNS and flushing cache")
registerDNS()
Flush()
// On initial setup of WSL, the restart caused by --shutdown is slow,
// so we do it out-of-line.
@@ -551,9 +581,12 @@ func (m *windowsManager) reconfigureDNSRegistration() {
// the Windows DHCP client from registering Tailscale IP addresses with DNS
// and sending dynamic updates for our interface to AD domain controllers.
func (m *windowsManager) configureDNSRegistration(enabled bool) error {
prefixen := []winutil.RegistryPathPrefix{
winutil.IPv4TCPIPInterfacePrefix,
winutil.IPv6TCPIPInterfacePrefix,
prefixen := make([]winutil.RegistryPathPrefix, 0, 2)
if m.doV4 {
prefixen = append(prefixen, winutil.IPv4TCPIPInterfacePrefix)
}
if m.doV6 {
prefixen = append(prefixen, winutil.IPv6TCPIPInterfacePrefix)
}
var (
@@ -607,6 +640,10 @@ func (m *windowsManager) setSingleDWORD(prefix winutil.RegistryPathPrefix, value
// Further, LLMNR and NetBIOS are being deprecated anyway in favor of MDNS.
// https://techcommunity.microsoft.com/t5/networking-blog/aligning-on-mdns-ramping-down-netbios-name-resolution-and-llmnr/ba-p/3290816
func (m *windowsManager) disableNetBIOS() error {
if !m.doNetBIOS {
return nil
}
return m.setSingleDWORD(winutil.NetBTInterfacePrefix, "NetbiosOptions", 2)
}