diff --git a/cmd/tailscaled/depaware.txt b/cmd/tailscaled/depaware.txt index 93951c1bd..bd1490d40 100644 --- a/cmd/tailscaled/depaware.txt +++ b/cmd/tailscaled/depaware.txt @@ -92,7 +92,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de tailscale.com/logtail/filch from tailscale.com/logpolicy tailscale.com/metrics from tailscale.com/derp tailscale.com/net/dns from tailscale.com/ipn/ipnlocal+ - tailscale.com/net/dns/resolver from tailscale.com/wgengine + tailscale.com/net/dns/resolver from tailscale.com/wgengine+ tailscale.com/net/dnscache from tailscale.com/control/controlclient+ tailscale.com/net/dnsfallback from tailscale.com/control/controlclient tailscale.com/net/flowtrack from tailscale.com/wgengine/filter+ diff --git a/net/dns/config.go b/net/dns/config.go index b73667980..718a409e3 100644 --- a/net/dns/config.go +++ b/net/dns/config.go @@ -43,32 +43,3 @@ type OSConfig struct { // Domains are the search domains to use. Domains []string } - -// Equal determines whether its argument and receiver -// represent equivalent DNS configurations (then DNS reconfig is a no-op). -func (lhs OSConfig) Equal(rhs OSConfig) bool { - if len(lhs.Nameservers) != len(rhs.Nameservers) { - return false - } - - if len(lhs.Domains) != len(rhs.Domains) { - return false - } - - // With how we perform resolution order shouldn't matter, - // but it is unlikely that we will encounter different orders. - for i, server := range lhs.Nameservers { - if rhs.Nameservers[i] != server { - return false - } - } - - // The order of domains, on the other hand, is significant. - for i, domain := range lhs.Domains { - if rhs.Domains[i] != domain { - return false - } - } - - return true -} diff --git a/net/dns/manager.go b/net/dns/manager.go index d74c4d19a..ac8842ccf 100644 --- a/net/dns/manager.go +++ b/net/dns/manager.go @@ -7,7 +7,11 @@ package dns import ( "time" + "inet.af/netaddr" + "tailscale.com/net/dns/resolver" + "tailscale.com/net/tsaddr" "tailscale.com/types/logger" + "tailscale.com/wgengine/monitor" ) // We use file-ignore below instead of ignore because on some platforms, @@ -27,54 +31,82 @@ const reconfigTimeout = time.Second type Manager struct { logf logger.Logf - os OSConfigurator + resolver *resolver.Resolver + os OSConfigurator - config OSConfig + config Config } // NewManagers created a new manager from the given config. -func NewManager(logf logger.Logf, oscfg OSConfigurator) *Manager { +func NewManager(logf logger.Logf, oscfg OSConfigurator, linkMon *monitor.Mon) *Manager { logf = logger.WithPrefix(logf, "dns: ") m := &Manager{ - logf: logf, - os: oscfg, + logf: logf, + resolver: resolver.New(logf, linkMon), + os: oscfg, } m.logf("using %T", m.os) return m } -func (m *Manager) Set(config OSConfig) error { - if config.Equal(m.config) { - return nil - } +func (m *Manager) Set(cfg Config) error { + m.logf("Set: %+v", cfg) - m.logf("Set: %+v", config) + if len(cfg.DefaultResolvers) == 0 { + // TODO: make other settings work even if you didn't set a + // default resolver. For now, no default resolvers == no + // managed DNS config. + cfg = Config{} + } - if len(config.Nameservers) == 0 { - err := m.os.Set(OSConfig{}) - // If we save the config, we will not retry next time. Only do this on success. - if err == nil { - m.config = config + resolverCfg := resolver.Config{ + Hosts: cfg.Hosts, + LocalDomains: cfg.AuthoritativeSuffixes, + Routes: map[string][]netaddr.IPPort{}, + } + osCfg := OSConfig{ + Domains: cfg.SearchDomains, + } + // We must proxy through quad-100 if MagicDNS hosts are in + // use, or there are any per-domain routes. + mustProxy := len(cfg.Hosts) > 0 || len(cfg.Routes) > 0 + if mustProxy { + osCfg.Nameservers = []netaddr.IP{tsaddr.TailscaleServiceIP()} + resolverCfg.Routes["."] = cfg.DefaultResolvers + for suffix, resolvers := range cfg.Routes { + resolverCfg.Routes[suffix] = resolvers + } + } else { + for _, resolver := range cfg.DefaultResolvers { + osCfg.Nameservers = append(osCfg.Nameservers, resolver.IP) } - return err } - err := m.os.Set(config) - // If we save the config, we will not retry next time. Only do this on success. - if err == nil { - m.config = config + if err := m.resolver.SetConfig(resolverCfg); err != nil { + return err } + if err := m.os.Set(osCfg); err != nil { + return err + } + + return nil +} - return err +func (m *Manager) EnqueueRequest(bs []byte, from netaddr.IPPort) error { + return m.resolver.EnqueueRequest(bs, from) } -func (m *Manager) Up() error { - return m.os.Set(m.config) +func (m *Manager) NextResponse() ([]byte, netaddr.IPPort, error) { + return m.resolver.NextResponse() } func (m *Manager) Down() error { - return m.os.Close() + if err := m.os.Close(); err != nil { + return err + } + m.resolver.Close() + return nil } // Cleanup restores the system DNS configuration to its original state @@ -82,7 +114,7 @@ func (m *Manager) Down() error { // No other state needs to be instantiated before this runs. func Cleanup(logf logger.Logf, interfaceName string) { oscfg := NewOSConfigurator(logf, interfaceName) - dns := NewManager(logf, oscfg) + dns := NewManager(logf, oscfg, nil) if err := dns.Down(); err != nil { logf("dns down: %v", err) } diff --git a/wgengine/userspace.go b/wgengine/userspace.go index b28e1022d..b6c981f7b 100644 --- a/wgengine/userspace.go +++ b/wgengine/userspace.go @@ -85,7 +85,6 @@ type userspaceEngine struct { wgdev *device.Device router router.Router dns *dns.Manager - resolver *resolver.Resolver magicConn *magicsock.Conn linkMon *monitor.Mon linkMonOwned bool // whether we created linkMon (and thus need to close it) @@ -213,7 +212,6 @@ func NewUserspaceEngine(logf logger.Logf, conf Config) (_ Engine, reterr error) waitCh: make(chan struct{}), tundev: tsTUNDev, router: conf.Router, - dns: dns.NewManager(logf, conf.DNS), pingers: make(map[wgkey.Key]*pinger), } e.isLocalAddr.Store(genLocalAddrFunc(nil)) @@ -230,7 +228,7 @@ func NewUserspaceEngine(logf logger.Logf, conf Config) (_ Engine, reterr error) e.linkMonOwned = true } - e.resolver = resolver.New(logf, e.linkMon) + e.dns = dns.NewManager(logf, conf.DNS, e.linkMon) logf("link state: %+v", e.linkMon.InterfaceState()) @@ -443,7 +441,7 @@ func (e *userspaceEngine) handleLocalPackets(p *packet.Parsed, t *tstun.Wrapper) // handleDNS is an outbound pre-filter resolving Tailscale domains. func (e *userspaceEngine) handleDNS(p *packet.Parsed, t *tstun.Wrapper) filter.Response { if p.Dst.IP == magicDNSIP && p.Dst.Port == magicDNSPort && p.IPProto == ipproto.UDP { - err := e.resolver.EnqueueRequest(append([]byte(nil), p.Payload()...), p.Src) + err := e.dns.EnqueueRequest(append([]byte(nil), p.Payload()...), p.Src) if err != nil { e.logf("dns: enqueue: %v", err) } @@ -455,7 +453,7 @@ func (e *userspaceEngine) handleDNS(p *packet.Parsed, t *tstun.Wrapper) filter.R // pollResolver reads responses from the DNS resolver and injects them inbound. func (e *userspaceEngine) pollResolver() { for { - bs, to, err := e.resolver.NextResponse() + bs, to, err := e.dns.NextResponse() if err == resolver.ErrClosed { return } @@ -992,37 +990,8 @@ func (e *userspaceEngine) Reconfig(cfg *wgcfg.Config, routerCfg *router.Config, } if routerChanged { - resolverCfg := resolver.Config{ - Hosts: dnsCfg.Hosts, - LocalDomains: dnsCfg.AuthoritativeSuffixes, - Routes: map[string][]netaddr.IPPort{}, - } - osCfg := dns.OSConfig{ - Domains: dnsCfg.SearchDomains, - } - // We must proxy through quad-100 if MagicDNS hosts are in - // use, or there are any per-domain routes. - mustProxy := len(dnsCfg.Hosts) > 0 || len(dnsCfg.Routes) > 0 - if mustProxy { - osCfg.Nameservers = []netaddr.IP{tsaddr.TailscaleServiceIP()} - resolverCfg.Routes["."] = dnsCfg.DefaultResolvers - for suffix, resolvers := range dnsCfg.Routes { - resolverCfg.Routes[suffix] = resolvers - } - } else { - for _, resolver := range dnsCfg.DefaultResolvers { - osCfg.Nameservers = append(osCfg.Nameservers, resolver.IP) - } - } - osCfg.Domains = dnsCfg.SearchDomains - e.logf("wgengine: Reconfig: configuring DNS") - err := e.resolver.SetConfig(resolverCfg) - health.SetDNSHealth(err) - if err != nil { - return err - } - err = e.dns.Set(osCfg) + err := e.dns.Set(*dnsCfg) health.SetDNSHealth(err) if err != nil { return err @@ -1241,12 +1210,12 @@ func (e *userspaceEngine) Close() { r := bufio.NewReader(strings.NewReader("")) e.wgdev.IpcSetOperation(r) - e.resolver.Close() e.magicConn.Close() e.linkMonUnregister() if e.linkMonOwned { e.linkMon.Close() } + e.dns.Down() e.router.Close() e.wgdev.Close() e.tundev.Close()