diff --git a/cmd/cli/cli.go b/cmd/cli/cli.go index 987a470..4b5fbdf 100644 --- a/cmd/cli/cli.go +++ b/cmd/cli/cli.go @@ -2661,6 +2661,7 @@ func resetDnsTask(p *prog, s service.Service) task { iface = runningIface(s) } if isCtrldInstalled { + mainLog.Load().Debug().Msg("restore system DNS settings") resetDnsNoLog(p) } iface = oldIface diff --git a/cmd/cli/os_windows.go b/cmd/cli/os_windows.go index e5ac1d2..234764f 100644 --- a/cmd/cli/os_windows.go +++ b/cmd/cli/os_windows.go @@ -5,7 +5,7 @@ import ( "fmt" "net" "os" - "os/exec" + "slices" "strconv" "strings" "sync" @@ -30,6 +30,14 @@ func setDnsIgnoreUnusableInterface(iface *net.Interface, nameservers []string) e return setDNS(iface, nameservers) } +func setDnsPowershellCmd(iface *net.Interface, nameservers []string) string { + nss := make([]string, 0, len(nameservers)) + for _, ns := range nameservers { + nss = append(nss, strconv.Quote(ns)) + } + return fmt.Sprintf("Set-DnsClientServerAddress -InterfaceIndex %d -ServerAddresses (%s)", iface.Index, strings.Join(nss, ",")) +} + // setDNS sets the dns server for the provided network interface func setDNS(iface *net.Interface, nameservers []string) error { if len(nameservers) == 0 { @@ -41,22 +49,25 @@ func setDNS(iface *net.Interface, nameservers []string) error { if windowsHasLocalDnsServerRunning() { file := absHomeDir(windowsForwardersFilename) oldForwardersContent, _ := os.ReadFile(file) - if err := os.WriteFile(file, []byte(strings.Join(nameservers, ",")), 0600); err != nil { + hasLocalIPv6Listener := needLocalIPv6Listener() + forwarders := slices.DeleteFunc(slices.Clone(nameservers), func(s string) bool { + if !hasLocalIPv6Listener { + return false + } + return s == "::1" + }) + if err := os.WriteFile(file, []byte(strings.Join(forwarders, ",")), 0600); err != nil { mainLog.Load().Warn().Err(err).Msg("could not save forwarders settings") } oldForwarders := strings.Split(string(oldForwardersContent), ",") - if err := addDnsServerForwarders(nameservers, oldForwarders); err != nil { + if err := addDnsServerForwarders(forwarders, oldForwarders); err != nil { mainLog.Load().Warn().Err(err).Msg("could not set forwarders settings") } } }) - primaryDNS := nameservers[0] - if err := setPrimaryDNS(iface, primaryDNS, true); err != nil { - return err - } - if len(nameservers) > 1 { - secondaryDNS := nameservers[1] - _ = addSecondaryDNS(iface, secondaryDNS) + out, err := powershell(setDnsPowershellCmd(iface, nameservers)) + if err != nil { + return fmt.Errorf("%w: %s", err, string(out)) } return nil } @@ -85,17 +96,13 @@ func resetDNS(iface *net.Interface) error { } }) - // Restoring ipv6 first. - if ctrldnet.SupportsIPv6ListenLocal() { - if output, err := netsh("interface", "ipv6", "set", "dnsserver", strconv.Itoa(iface.Index), "dhcp"); err != nil { - mainLog.Load().Warn().Err(err).Msgf("failed to reset ipv6 DNS: %s", string(output)) - } - } - // Restoring ipv4 DHCP. - output, err := netsh("interface", "ipv4", "set", "dnsserver", strconv.Itoa(iface.Index), "dhcp") + // Restoring DHCP settings. + cmd := fmt.Sprintf("Set-DnsClientServerAddress -InterfaceIndex %d -ResetServerAddresses", iface.Index) + out, err := powershell(cmd) if err != nil { - return fmt.Errorf("%s: %w", string(output), err) + return fmt.Errorf("%w: %s", err, string(out)) } + // If there's static DNS saved, restoring it. if nss := savedStaticNameservers(iface); len(nss) > 0 { v4ns := make([]string, 0, 2) @@ -112,54 +119,14 @@ func resetDNS(iface *net.Interface) error { if len(ns) == 0 { continue } - primaryDNS := ns[0] - if err := setPrimaryDNS(iface, primaryDNS, false); err != nil { + if err := setDNS(iface, ns); err != nil { return err } - if len(ns) > 1 { - secondaryDNS := ns[1] - _ = addSecondaryDNS(iface, secondaryDNS) - } } } return nil } -func setPrimaryDNS(iface *net.Interface, dns string, disablev6 bool) error { - ipVer := "ipv4" - if ctrldnet.IsIPv6(dns) { - ipVer = "ipv6" - } - idx := strconv.Itoa(iface.Index) - output, err := netsh("interface", ipVer, "set", "dnsserver", idx, "static", dns) - if err != nil { - mainLog.Load().Error().Err(err).Msgf("failed to set primary DNS: %s", string(output)) - return err - } - if disablev6 && ipVer == "ipv4" && ctrldnet.SupportsIPv6ListenLocal() { - // Disable IPv6 DNS, so the query will be fallback to IPv4. - _, _ = netsh("interface", "ipv6", "set", "dnsserver", idx, "static", "::1", "primary") - } - - return nil -} - -func addSecondaryDNS(iface *net.Interface, dns string) error { - ipVer := "ipv4" - if ctrldnet.IsIPv6(dns) { - ipVer = "ipv6" - } - output, err := netsh("interface", ipVer, "add", "dns", strconv.Itoa(iface.Index), dns, "index=2") - if err != nil { - mainLog.Load().Warn().Err(err).Msgf("failed to add secondary DNS: %s", string(output)) - } - return nil -} - -func netsh(args ...string) ([]byte, error) { - return exec.Command("netsh", args...).Output() -} - func currentDNS(iface *net.Interface) []string { luid, err := winipcfg.LUIDFromIndex(uint32(iface.Index)) if err != nil { @@ -200,7 +167,9 @@ func currentStaticDNS(iface *net.Interface) ([]string, error) { out, err := powershell(cmd) if err == nil && len(out) > 0 { found = true - ns = append(ns, strings.Split(string(out), ",")...) + for _, e := range strings.Split(string(out), ",") { + ns = append(ns, strings.TrimRight(e, "\x00")) + } } } } diff --git a/cmd/cli/prog.go b/cmd/cli/prog.go index 82daa24..6018efb 100644 --- a/cmd/cli/prog.go +++ b/cmd/cli/prog.go @@ -588,6 +588,10 @@ func (p *prog) setDNS() { if needRFC1918Listeners(lc) { nameservers = append(nameservers, ctrld.Rfc1918Addresses()...) } + if needLocalIPv6Listener() { + nameservers = append(nameservers, "::1") + } + slices.Sort(nameservers) if err := setDNS(netIface, nameservers); err != nil { logger.Error().Err(err).Msgf("could not set DNS for interface") return @@ -665,7 +669,7 @@ func (p *prog) dnsWatchdog(iface *net.Interface, nameservers []string, allIfaces } } if allIfaces { - withEachPhysicalInterfaces(iface.Name, "re-applying DNS", func(i *net.Interface) error { + withEachPhysicalInterfaces(iface.Name, "", func(i *net.Interface) error { if dnsChanged(i, ns) { if err := setDnsIgnoreUnusableInterface(i, nameservers); err != nil { mainLog.Load().Error().Err(err).Str("iface", i.Name).Msgf("could not re-apply DNS settings") @@ -977,7 +981,11 @@ func savedStaticNameservers(iface *net.Interface) []string { func dnsChanged(iface *net.Interface, nameservers []string) bool { curNameservers, _ := currentStaticDNS(iface) slices.Sort(curNameservers) - return !slices.Equal(curNameservers, nameservers) + if !slices.Equal(curNameservers, nameservers) { + mainLog.Load().Debug().Msgf("interface %q current DNS settings: %v, expected: %v", iface.Name, curNameservers, nameservers) + return true + } + return false } // selfUninstallCheck checks if the error dues to controld.InvalidConfigCode, perform self-uninstall then.