From 3555a49518717acb6dcb84c15f01d998ecf5f060 Mon Sep 17 00:00:00 2001 From: Maisem Ali Date: Thu, 22 Sep 2022 14:38:09 -0700 Subject: [PATCH] net/dns: always attempt to read the OS config on macOS/iOS Also reconfigure DNS on iOS/macOS on link changes. Signed-off-by: Maisem Ali --- net/dns/manager.go | 32 +++++++++++++++++++++++++------- wgengine/userspace.go | 21 ++++++++++++--------- 2 files changed, 37 insertions(+), 16 deletions(-) diff --git a/net/dns/manager.go b/net/dns/manager.go index c766fd65c..c1086da7e 100644 --- a/net/dns/manager.go +++ b/net/dns/manager.go @@ -265,22 +265,40 @@ func (m *Manager) compileConfig(cfg Config) (rcfg resolver.Config, ocfg OSConfig rcfg.Routes = routes ocfg.Nameservers = []netip.Addr{cfg.serviceIP()} - if m.os.SupportsSplitDNS() { - ocfg.MatchDomains = cfg.matchDomains() - } else { + var baseCfg *OSConfig // base config; non-nil if/when known + + // Even though Apple devices can do split DNS, they don't provide a way to + // selectively answer ExtraRecords, and ignore other DNS traffic. As a + // workaround, we read the existing default resolver configuration and use + // that as the forwarder for all DNS traffic that quad-100 doesn't handle. + const isApple = runtime.GOOS == "darwin" || runtime.GOOS == "ios" + + if isApple || !m.os.SupportsSplitDNS() { // If the OS can't do native split-dns, read out the underlying // resolver config and blend it into our config. - bcfg, err := m.os.GetBaseConfig() - if err != nil { + cfg, err := m.os.GetBaseConfig() + if err == nil { + baseCfg = &cfg + } else if isApple && err == ErrGetBaseConfigNotSupported { + // This is currently (2022-10-13) expected on certain iOS and macOS + // builds. + } else { health.SetDNSOSHealth(err) return resolver.Config{}, OSConfig{}, err } + } + + if baseCfg == nil || isApple && len(baseCfg.Nameservers) == 0 { + // If there was no base config, or if we're on Apple and the base + // config is empty, then we need to fallback to SplitDNS mode. + ocfg.MatchDomains = cfg.matchDomains() + } else { var defaultRoutes []*dnstype.Resolver - for _, ip := range bcfg.Nameservers { + for _, ip := range baseCfg.Nameservers { defaultRoutes = append(defaultRoutes, &dnstype.Resolver{Addr: ip.String()}) } rcfg.Routes["."] = defaultRoutes - ocfg.SearchDomains = append(ocfg.SearchDomains, bcfg.SearchDomains...) + ocfg.SearchDomains = append(ocfg.SearchDomains, baseCfg.SearchDomains...) } return rcfg, ocfg, nil diff --git a/wgengine/userspace.go b/wgengine/userspace.go index 7cb5151ba..34f328513 100644 --- a/wgengine/userspace.go +++ b/wgengine/userspace.go @@ -1187,15 +1187,18 @@ func (e *userspaceEngine) linkChange(changed bool, cur *interfaces.State) { // suspend/resume or whenever NetworkManager is started, it // nukes all systemd-resolved configs. So reapply our DNS // config on major link change. - if (runtime.GOOS == "linux" || runtime.GOOS == "android") && changed { - e.wgLock.Lock() - dnsCfg := e.lastDNSConfig - e.wgLock.Unlock() - if dnsCfg != nil { - if err := e.dns.Set(*dnsCfg); err != nil { - e.logf("wgengine: error setting DNS config after major link change: %v", err) - } else { - e.logf("wgengine: set DNS config again after major link change") + if changed { + switch runtime.GOOS { + case "linux", "android", "ios", "darwin": + e.wgLock.Lock() + dnsCfg := e.lastDNSConfig + e.wgLock.Unlock() + if dnsCfg != nil { + if err := e.dns.Set(*dnsCfg); err != nil { + e.logf("wgengine: error setting DNS config after major link change: %v", err) + } else { + e.logf("wgengine: set DNS config again after major link change") + } } } }