aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/tunnel/winipcfg/netsh.go
diff options
context:
space:
mode:
Diffstat (limited to 'tunnel/winipcfg/netsh.go')
-rw-r--r--tunnel/winipcfg/netsh.go82
1 files changed, 69 insertions, 13 deletions
diff --git a/tunnel/winipcfg/netsh.go b/tunnel/winipcfg/netsh.go
index 1962266e..4f8e5b13 100644
--- a/tunnel/winipcfg/netsh.go
+++ b/tunnel/winipcfg/netsh.go
@@ -1,52 +1,108 @@
/* SPDX-License-Identifier: MIT
*
- * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
+ * Copyright (C) 2019-2022 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"
)
-// I wish we didn't have to do this. netiohlp.dll (what's used by netsh.exe) has some nice tricks with writing directly
-// to the registry and the nsi kernel object, but it's not clear copying those makes for a stable interface. WMI doesn't
-// work with v6. CMI isn't in Windows 7.
func runNetsh(cmds []string) error {
system32, err := windows.GetSystemDirectory()
if err != nil {
return err
}
- cmd := exec.Command(filepath.Join(system32, "netsh.exe")) // I wish we could append (, "-f", "CONIN$") but Go sets up the process context wrong.
+ 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 - %v", err)
+ 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()
- if err != nil {
- return fmt.Errorf("runNetsh run - %v", err)
- }
// Horrible kludges, sorry.
- cleaned := bytes.ReplaceAll(output, []byte("netsh>"), []byte{})
+ 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 {
- return fmt.Errorf("runNetsh returned error strings.\ninput:\n%s\noutput\n:%s",
- strings.Join(cmds, "\n"), bytes.ReplaceAll(output, []byte{'\r', '\n'}, []byte{'\n'}))
+ 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 := 0; i < len(dnses); i++ {
+ 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
+}