github.com/apernet/sing-tun@v0.2.6-0.20240323130332-b9f6511036ad/internal/winipcfg/netsh.go (about) 1 /* SPDX-License-Identifier: MIT 2 * 3 * Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved. 4 */ 5 6 package winipcfg 7 8 import ( 9 "bytes" 10 "errors" 11 "fmt" 12 "io" 13 "net/netip" 14 "os/exec" 15 "path/filepath" 16 "strings" 17 "syscall" 18 19 "golang.org/x/sys/windows" 20 "golang.org/x/sys/windows/registry" 21 ) 22 23 func runNetsh(cmds []string) error { 24 system32, err := windows.GetSystemDirectory() 25 if err != nil { 26 return err 27 } 28 cmd := exec.Command(filepath.Join(system32, "netsh.exe")) 29 cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true} 30 31 stdin, err := cmd.StdinPipe() 32 if err != nil { 33 return fmt.Errorf("runNetsh stdin pipe - %w", err) 34 } 35 go func() { 36 defer stdin.Close() 37 io.WriteString(stdin, strings.Join(append(cmds, "exit\r\n"), "\r\n")) 38 }() 39 output, err := cmd.CombinedOutput() 40 // Horrible kludges, sorry. 41 cleaned := bytes.ReplaceAll(output, []byte{'\r', '\n'}, []byte{'\n'}) 42 cleaned = bytes.ReplaceAll(cleaned, []byte("netsh>"), []byte{}) 43 cleaned = bytes.ReplaceAll(cleaned, []byte("There are no Domain Name Servers (DNS) configured on this computer."), []byte{}) 44 cleaned = bytes.TrimSpace(cleaned) 45 if len(cleaned) != 0 && err == nil { 46 return fmt.Errorf("netsh: %#q", string(cleaned)) 47 } else if err != nil { 48 return fmt.Errorf("netsh: %v: %#q", err, string(cleaned)) 49 } 50 return nil 51 } 52 53 const ( 54 netshCmdTemplateFlush4 = "interface ipv4 set dnsservers name=%d source=static address=none validate=no" 55 netshCmdTemplateFlush6 = "interface ipv6 set dnsservers name=%d source=static address=none validate=no" 56 netshCmdTemplateAdd4 = "interface ipv4 add dnsservers name=%d address=%s validate=no" 57 netshCmdTemplateAdd6 = "interface ipv6 add dnsservers name=%d address=%s validate=no" 58 netshCmdTemplateDisableRegistration = "interface ipv6 set dnsservers name=%d register=none" 59 ) 60 61 func (luid LUID) fallbackSetDNSForFamily(family AddressFamily, dnses []netip.Addr) error { 62 var templateFlush string 63 if family == windows.AF_INET { 64 templateFlush = netshCmdTemplateFlush4 65 } else if family == windows.AF_INET6 { 66 templateFlush = netshCmdTemplateFlush6 67 } 68 69 cmds := make([]string, 0, 1+len(dnses)) 70 ipif, err := luid.IPInterface(family) 71 if err != nil { 72 return err 73 } 74 cmds = append(cmds, fmt.Sprintf(templateFlush, ipif.InterfaceIndex)) 75 for i := 0; i < len(dnses); i++ { 76 if dnses[i].Is4() && family == windows.AF_INET { 77 cmds = append(cmds, fmt.Sprintf(netshCmdTemplateAdd4, ipif.InterfaceIndex, dnses[i].String())) 78 } else if dnses[i].Is6() && family == windows.AF_INET6 { 79 cmds = append(cmds, fmt.Sprintf(netshCmdTemplateAdd6, ipif.InterfaceIndex, dnses[i].String())) 80 } 81 } 82 return runNetsh(cmds) 83 } 84 85 func (luid LUID) fallbackSetDNSDomain(domain string) error { 86 guid, err := luid.GUID() 87 if err != nil { 88 return fmt.Errorf("Error converting luid to guid: %w", err) 89 } 90 key, err := registry.OpenKey(registry.LOCAL_MACHINE, fmt.Sprintf("SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Adapters\\%v", guid), registry.QUERY_VALUE) 91 if err != nil { 92 return fmt.Errorf("Error opening adapter-specific TCP/IP network registry key: %w", err) 93 } 94 paths, _, err := key.GetStringsValue("IpConfig") 95 key.Close() 96 if err != nil { 97 return fmt.Errorf("Error reading IpConfig registry key: %w", err) 98 } 99 if len(paths) == 0 { 100 return errors.New("No TCP/IP interfaces found on adapter") 101 } 102 key, err = registry.OpenKey(registry.LOCAL_MACHINE, fmt.Sprintf("SYSTEM\\CurrentControlSet\\Services\\%s", paths[0]), registry.SET_VALUE) 103 if err != nil { 104 return fmt.Errorf("Unable to open TCP/IP network registry key: %w", err) 105 } 106 err = key.SetStringValue("Domain", domain) 107 key.Close() 108 return err 109 } 110 111 func (luid LUID) fallbackDisableDNSRegistration() error { 112 // the DNS registration setting is shared for both IPv4 and IPv6 113 ipif, err := luid.IPInterface(windows.AF_INET) 114 if err != nil { 115 return err 116 } 117 cmd := fmt.Sprintf(netshCmdTemplateDisableRegistration, ipif.InterfaceIndex) 118 return runNetsh([]string{cmd}) 119 }