From 19020a96bfe526651b39aab3e373df322f0c1b11 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Wed, 22 May 2024 18:17:20 +0700 Subject: [PATCH] all: fix OS resolver looping issue on Windows By making dnsFromAdapter ignores DNS server which is the same IP address of the adapter. While at it, also changes OS resolver to use ctrld bootstrap DNS only if there's no available nameservers. --- cmd/cli/cli.go | 16 ++++++++++++++-- nameservers_windows.go | 42 +++++++++++++----------------------------- resolver.go | 7 +++++-- 3 files changed, 32 insertions(+), 33 deletions(-) diff --git a/cmd/cli/cli.go b/cmd/cli/cli.go index c0ef45c..b56cee2 100644 --- a/cmd/cli/cli.go +++ b/cmd/cli/cli.go @@ -507,7 +507,7 @@ func initCLI() { iface = runningIface(s) tasks := []task{ {s.Stop, false}, - {func() error { p.resetDNS(); return nil }, false}, + {func() error { resetDnsNoLog(p); return nil }, false}, {s.Start, true}, } if doTasks(tasks) { @@ -887,7 +887,10 @@ NOTE: Uninstalling will set DNS to values provided by DHCP.`, sc := &service.Config{} *sc = *svcConfig sc.Executable = bin - s, err := newService(&prog{}, sc) + readConfig(false) + v.Unmarshal(&cfg) + p := &prog{router: router.New(&cfg, runInCdMode())} + s, err := newService(p, sc) if err != nil { mainLog.Load().Error().Msg(err.Error()) return @@ -931,6 +934,7 @@ NOTE: Uninstalling will set DNS to values provided by DHCP.`, } tasks := []task{ {s.Stop, false}, + {func() error { resetDnsNoLog(p); return nil }, false}, {s.Start, false}, } if doTasks(tasks) { @@ -2541,3 +2545,11 @@ func runningIface(s service.Service) string { } return "" } + +// resetDnsNoLog performs resetting DNS with logging disable. +func resetDnsNoLog(p *prog) { + lvl := zerolog.GlobalLevel() + zerolog.SetGlobalLevel(zerolog.Disabled) + p.resetDNS() + zerolog.SetGlobalLevel(lvl) +} diff --git a/nameservers_windows.go b/nameservers_windows.go index ded4658..150f252 100644 --- a/nameservers_windows.go +++ b/nameservers_windows.go @@ -1,10 +1,8 @@ package ctrld import ( - "net" "syscall" - "golang.org/x/sys/windows" "golang.zx2c4.com/wireguard/windows/tunnel/winipcfg" ) @@ -19,37 +17,23 @@ func dnsFromAdapter() []string { } ns := make([]string, 0, len(aas)*2) seen := make(map[string]bool) - do := func(addr windows.SocketAddress) { - sa, err := addr.Sockaddr.Sockaddr() - if err != nil { - return + addressMap := make(map[string]struct{}) + for _, aa := range aas { + for a := aa.FirstUnicastAddress; a != nil; a = a.Next { + addressMap[a.Address.IP().String()] = struct{}{} } - var ip net.IP - switch sa := sa.(type) { - case *syscall.SockaddrInet4: - ip = net.IPv4(sa.Addr[0], sa.Addr[1], sa.Addr[2], sa.Addr[3]) - case *syscall.SockaddrInet6: - ip = make(net.IP, net.IPv6len) - copy(ip, sa.Addr[:]) - if ip[0] == 0xfe && ip[1] == 0xc0 { - // Ignore these fec0/10 ones. Windows seems to - // populate them as defaults on its misc rando - // interfaces. - return - } - default: - return - - } - if ip.IsLoopback() || seen[ip.String()] { - return - } - seen[ip.String()] = true - ns = append(ns, ip.String()) } for _, aa := range aas { for dns := aa.FirstDNSServerAddress; dns != nil; dns = dns.Next { - do(dns.Address) + ip := dns.Address.IP() + if ip == nil || ip.IsLoopback() || seen[ip.String()] { + continue + } + if _, ok := addressMap[ip.String()]; ok { + continue + } + seen[ip.String()] = true + ns = append(ns, ip.String()) } } return ns diff --git a/resolver.go b/resolver.go index f9951d7..bfb3394 100644 --- a/resolver.go +++ b/resolver.go @@ -39,10 +39,13 @@ var OsNameservers = defaultNameservers() // or is the Resolver used for ResolverTypeOS. var or = &osResolver{nameservers: OsNameservers} -// defaultNameservers returns OS nameservers plus ctrld bootstrap nameserver. +// defaultNameservers returns nameservers used by the OS. +// If no nameservers can be found, ctrld bootstrap nameserver will be used. func defaultNameservers() []string { ns := nameservers() - ns = append(ns, net.JoinHostPort(bootstrapDNS, "53")) + if len(ns) == 0 { + ns = append(ns, net.JoinHostPort(bootstrapDNS, "53")) + } return ns }