aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2026-04-13 04:07:45 +0200
committerJason A. Donenfeld <Jason@zx2c4.com>2026-04-13 19:32:53 +0200
commitb7b300b88e8f825dd57d4b94bddd08b82b9d73c6 (patch)
treebf341b1870b21e9670f6537d0c6014679a91b02f
parentglobal: format code (diff)
downloadwireguard-windows-master.tar.xz
wireguard-windows-master.zip
tunnel: avoid calling netsh on <1809 for DNS changesHEADmaster
And it turns out we can use SearchList on old Windows 10 too. This gets total feature parity with the new DNS function. Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
Diffstat (limited to '')
-rw-r--r--tunnel/winipcfg/luid.go38
-rw-r--r--tunnel/winipcfg/netsh.go108
2 files changed, 32 insertions, 114 deletions
diff --git a/tunnel/winipcfg/luid.go b/tunnel/winipcfg/luid.go
index ade5a63f..f4c67f2b 100644
--- a/tunnel/winipcfg/luid.go
+++ b/tunnel/winipcfg/luid.go
@@ -7,10 +7,14 @@ package winipcfg
import (
"errors"
+ "fmt"
"net/netip"
"strings"
"golang.org/x/sys/windows"
+ "golang.org/x/sys/windows/registry"
+ "golang.org/x/sys/windows/svc"
+ "golang.org/x/sys/windows/svc/mgr"
)
// LUID represents a network interface.
@@ -370,15 +374,37 @@ func (luid LUID) SetDNS(family AddressFamily, servers []netip.Addr, domains []st
}
// For < Windows 10 1809
- err = luid.fallbackSetDNSForFamily(family, servers)
+ var regPath string
+ switch family {
+ case windows.AF_INET:
+ regPath = fmt.Sprintf("SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces\\%v", guid)
+ case windows.AF_INET6:
+ regPath = fmt.Sprintf("SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters\\Interfaces\\%v", guid)
+ }
+ key, err := registry.OpenKey(registry.LOCAL_MACHINE, regPath, registry.SET_VALUE)
if err != nil {
- return err
+ return fmt.Errorf("opening interface registry key: %w", err)
+ }
+ defer key.Close()
+ if err := key.SetStringValue("NameServer", strings.Join(filteredServers, ",")); err != nil {
+ return fmt.Errorf("setting NameServer registry value: %w", err)
+ }
+ if err := key.SetStringValue("SearchList", strings.Join(domains, ",")); err != nil {
+ return fmt.Errorf("setting SearchList registry value: %w", err)
}
- if len(domains) > 0 {
- return luid.fallbackSetDNSDomain(domains[0])
- } else {
- return luid.fallbackSetDNSDomain("")
+ scm, err := mgr.Connect()
+ if err != nil {
+ return nil
+ }
+ defer scm.Disconnect()
+ s := mgr.Service{Name: "dnscache"}
+ s.Handle, err = windows.OpenService(scm.Handle, windows.StringToUTF16Ptr(s.Name), windows.SERVICE_PAUSE_CONTINUE)
+ if err != nil {
+ return nil
}
+ defer s.Close()
+ s.Control(svc.ParamChange)
+ return nil
}
// FlushDNS method clears all DNS servers associated with the adapter.
diff --git a/tunnel/winipcfg/netsh.go b/tunnel/winipcfg/netsh.go
deleted file mode 100644
index 5f460957..00000000
--- a/tunnel/winipcfg/netsh.go
+++ /dev/null
@@ -1,108 +0,0 @@
-/* SPDX-License-Identifier: MIT
- *
- * Copyright (C) 2019-2026 WireGuard LLC. All Rights Reserved.
- */
-
-package winipcfg
-
-import (
- "bytes"
- "errors"
- "fmt"
- "io"
- "net/netip"
- "os/exec"
- "path/filepath"
- "strings"
- "syscall"
-
- "golang.org/x/sys/windows"
- "golang.org/x/sys/windows/registry"
-)
-
-func runNetsh(cmds []string) error {
- system32, err := windows.GetSystemDirectory()
- if err != nil {
- return err
- }
- cmd := exec.Command(filepath.Join(system32, "netsh.exe"))
- cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
-
- stdin, err := cmd.StdinPipe()
- if err != nil {
- return fmt.Errorf("runNetsh stdin pipe - %w", err)
- }
- go func() {
- defer stdin.Close()
- io.WriteString(stdin, strings.Join(append(cmds, "exit\r\n"), "\r\n"))
- }()
- output, err := cmd.CombinedOutput()
- // Horrible kludges, sorry.
- cleaned := bytes.ReplaceAll(output, []byte{'\r', '\n'}, []byte{'\n'})
- cleaned = bytes.ReplaceAll(cleaned, []byte("netsh>"), []byte{})
- cleaned = bytes.ReplaceAll(cleaned, []byte("There are no Domain Name Servers (DNS) configured on this computer."), []byte{})
- cleaned = bytes.TrimSpace(cleaned)
- if len(cleaned) != 0 && err == nil {
- return fmt.Errorf("netsh: %#q", string(cleaned))
- } else if err != nil {
- return fmt.Errorf("netsh: %v: %#q", err, string(cleaned))
- }
- return nil
-}
-
-const (
- netshCmdTemplateFlush4 = "interface ipv4 set dnsservers name=%d source=static address=none validate=no register=both"
- netshCmdTemplateFlush6 = "interface ipv6 set dnsservers name=%d source=static address=none validate=no register=both"
- netshCmdTemplateAdd4 = "interface ipv4 add dnsservers name=%d address=%s validate=no"
- netshCmdTemplateAdd6 = "interface ipv6 add dnsservers name=%d address=%s validate=no"
-)
-
-func (luid LUID) fallbackSetDNSForFamily(family AddressFamily, dnses []netip.Addr) error {
- var templateFlush string
- if family == windows.AF_INET {
- templateFlush = netshCmdTemplateFlush4
- } else if family == windows.AF_INET6 {
- templateFlush = netshCmdTemplateFlush6
- }
-
- cmds := make([]string, 0, 1+len(dnses))
- ipif, err := luid.IPInterface(family)
- if err != nil {
- return err
- }
- cmds = append(cmds, fmt.Sprintf(templateFlush, ipif.InterfaceIndex))
- for i := range dnses {
- if dnses[i].Is4() && family == windows.AF_INET {
- cmds = append(cmds, fmt.Sprintf(netshCmdTemplateAdd4, ipif.InterfaceIndex, dnses[i].String()))
- } else if dnses[i].Is6() && family == windows.AF_INET6 {
- cmds = append(cmds, fmt.Sprintf(netshCmdTemplateAdd6, ipif.InterfaceIndex, dnses[i].String()))
- }
- }
- return runNetsh(cmds)
-}
-
-func (luid LUID) fallbackSetDNSDomain(domain string) error {
- guid, err := luid.GUID()
- if err != nil {
- return fmt.Errorf("Error converting luid to guid: %w", err)
- }
- key, err := registry.OpenKey(registry.LOCAL_MACHINE, fmt.Sprintf("SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Adapters\\%v", guid), registry.QUERY_VALUE)
- if err != nil {
- return fmt.Errorf("Error opening adapter-specific TCP/IP network registry key: %w", err)
- }
- paths, _, err := key.GetStringsValue("IpConfig")
- key.Close()
- if err != nil {
- return fmt.Errorf("Error reading IpConfig registry key: %w", err)
- }
- if len(paths) == 0 {
- return errors.New("No TCP/IP interfaces found on adapter")
- }
- key, err = registry.OpenKey(registry.LOCAL_MACHINE, fmt.Sprintf("SYSTEM\\CurrentControlSet\\Services\\%s", paths[0]), registry.SET_VALUE)
- if err != nil {
- return fmt.Errorf("Unable to open TCP/IP network registry key: %w", err)
- }
- err = key.SetStringValue("Domain", domain)
- key.Close()
- return err
-}