sing-tun/internal/winipcfg/netsh.go
2023-10-26 14:08:25 +08:00

119 lines
3.9 KiB
Go

/* SPDX-License-Identifier: MIT
*
* 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"
)
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"
netshCmdTemplateFlush6 = "interface ipv6 set dnsservers name=%d source=static address=none validate=no"
netshCmdTemplateAdd4 = "interface ipv4 add dnsservers name=%d address=%s validate=no"
netshCmdTemplateAdd6 = "interface ipv6 add dnsservers name=%d address=%s validate=no"
netshCmdTemplateDisableRegistration = "interface ipv6 set dnsservers name=%d register=none"
)
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
}
func (luid LUID) fallbackDisableDNSRegistration() error {
// the DNS registration setting is shared for both IPv4 and IPv6
ipif, err := luid.IPInterface(windows.AF_INET)
if err != nil {
return err
}
cmd := fmt.Sprintf(netshCmdTemplateDisableRegistration, ipif.InterfaceIndex)
return runNetsh([]string{cmd})
}