diff --git a/ipn/ipnlocal/local.go b/ipn/ipnlocal/local.go index 12a66d3b0..c0a107a16 100644 --- a/ipn/ipnlocal/local.go +++ b/ipn/ipnlocal/local.go @@ -1725,7 +1725,6 @@ func (b *LocalBackend) authReconfig() { } dcfg.Hosts[fqdn] = ips } - dcfg.AuthoritativeSuffixes = magicDNSRootDomains(nm) dcfg.Hosts = map[dnsname.FQDN][]netaddr.IP{} set(nm.Name, nm.Addresses) for _, peer := range nm.Peers { @@ -1770,8 +1769,8 @@ func (b *LocalBackend) authReconfig() { dcfg.SearchDomains = append(dcfg.SearchDomains, fqdn) } if nm.DNS.Proxied { // actually means "enable MagicDNS" - for _, dom := range dcfg.AuthoritativeSuffixes { - dcfg.Routes[dom] = []netaddr.IPPort{netaddr.IPPortFrom(tsaddr.TailscaleServiceIP(), 53)} + for _, dom := range magicDNSRootDomains(nm) { + dcfg.Routes[dom] = nil // resolve internally with dcfg.Hosts } } @@ -1795,7 +1794,7 @@ func (b *LocalBackend) authReconfig() { // // https://github.com/tailscale/tailscale/issues/1713 addDefault(nm.DNS.FallbackResolvers) - case len(dcfg.Routes) == 0 && len(dcfg.Hosts) == 0 && len(dcfg.AuthoritativeSuffixes) == 0: + case len(dcfg.Routes) == 0: // No settings requiring split DNS, no problem. case version.OS() == "android": // We don't support split DNS at all on Android yet. diff --git a/net/dns/config.go b/net/dns/config.go index 04158807d..dc0b73440 100644 --- a/net/dns/config.go +++ b/net/dns/config.go @@ -22,6 +22,8 @@ type Config struct { // for queries that fall within that suffix. // If a query doesn't match any entry in Routes, the // DefaultResolvers are used. + // A Routes entry with no resolvers means the route should be + // authoritatively answered using the contents of Hosts. Routes map[dnsname.FQDN][]netaddr.IPPort // SearchDomains are DNS suffixes to try when expanding // single-label queries. @@ -34,12 +36,6 @@ type Config struct { // it to resolve, you also need to add appropriate routes to // Routes. Hosts map[dnsname.FQDN][]netaddr.IP - // AuthoritativeSuffixes is a list of fully-qualified DNS suffixes - // for which the in-process Tailscale resolver is authoritative. - // Queries for names within AuthoritativeSuffixes can only be - // fulfilled by entries in Hosts. Queries with no match in Hosts - // return NXDOMAIN. - AuthoritativeSuffixes []dnsname.FQDN } // needsAnyResolvers reports whether c requires a resolver to be set @@ -66,17 +62,21 @@ func (c Config) hasDefaultResolvers() bool { // routes use the same resolvers, or nil if multiple sets of resolvers // are specified. func (c Config) singleResolverSet() []netaddr.IPPort { - var first []netaddr.IPPort + var ( + prev []netaddr.IPPort + prevInitialized bool + ) for _, resolvers := range c.Routes { - if first == nil { - first = resolvers + if !prevInitialized { + prev = resolvers + prevInitialized = true continue } - if !sameIPPorts(first, resolvers) { + if !sameIPPorts(prev, resolvers) { return nil } } - return first + return prev } // matchDomains returns the list of match suffixes needed by Routes. diff --git a/net/dns/manager.go b/net/dns/manager.go index eae919df5..db34475c7 100644 --- a/net/dns/manager.go +++ b/net/dns/manager.go @@ -75,27 +75,18 @@ func (m *Manager) Set(cfg Config) error { // compileConfig converts cfg into a quad-100 resolver configuration // and an OS-level configuration. func (m *Manager) compileConfig(cfg Config) (rcfg resolver.Config, ocfg OSConfig, err error) { - authDomains := make(map[dnsname.FQDN]bool, len(cfg.AuthoritativeSuffixes)) - for _, dom := range cfg.AuthoritativeSuffixes { - authDomains[dom] = true - } - addRoutes := func() { - for suffix, resolvers := range cfg.Routes { - // Don't add resolver routes for authoritative domains, - // since they're meant to be authoritatively handled - // internally. - if authDomains[suffix] { - continue - } - rcfg.Routes[suffix] = resolvers - } - } - // The internal resolver always gets MagicDNS hosts and // authoritative suffixes, even if we don't propagate MagicDNS to // the OS. rcfg.Hosts = cfg.Hosts - rcfg.LocalDomains = cfg.AuthoritativeSuffixes + routes := map[dnsname.FQDN][]netaddr.IPPort{} // assigned conditionally to rcfg.Routes below. + for suffix, resolvers := range cfg.Routes { + if len(resolvers) == 0 { + rcfg.LocalDomains = append(rcfg.LocalDomains, suffix) + } else { + routes[suffix] = resolvers + } + } // Similarly, the OS always gets search paths. ocfg.SearchDomains = cfg.SearchDomains @@ -114,10 +105,8 @@ func (m *Manager) compileConfig(cfg Config) (rcfg resolver.Config, ocfg OSConfig case cfg.hasDefaultResolvers(): // Default resolvers plus other stuff always ends up proxying // through quad-100. - rcfg.Routes = map[dnsname.FQDN][]netaddr.IPPort{ - ".": cfg.DefaultResolvers, - } - addRoutes() + rcfg.Routes = routes + rcfg.Routes["."] = cfg.DefaultResolvers ocfg.Nameservers = []netaddr.IP{tsaddr.TailscaleServiceIP()} return rcfg, ocfg, nil } @@ -154,8 +143,7 @@ func (m *Manager) compileConfig(cfg Config) (rcfg resolver.Config, ocfg OSConfig // Split DNS configuration with either multiple upstream routes, // or routes + MagicDNS, or just MagicDNS, or on an OS that cannot // split-DNS. Install a split config pointing at quad-100. - rcfg.Routes = map[dnsname.FQDN][]netaddr.IPPort{} - addRoutes() + rcfg.Routes = routes ocfg.Nameservers = []netaddr.IP{tsaddr.TailscaleServiceIP()} // If the OS can't do native split-dns, read out the underlying diff --git a/net/dns/manager_test.go b/net/dns/manager_test.go index 618a6c142..8835f2aec 100644 --- a/net/dns/manager_test.go +++ b/net/dns/manager_test.go @@ -83,13 +83,11 @@ func TestManager(t *testing.T) { Hosts: hosts( "dave.ts.com.", "1.2.3.4", "bradfitz.ts.com.", "2.3.4.5"), - AuthoritativeSuffixes: fqdns("ts.com"), }, rs: resolver.Config{ Hosts: hosts( "dave.ts.com.", "1.2.3.4", "bradfitz.ts.com.", "2.3.4.5"), - LocalDomains: fqdns("ts.com"), }, }, { @@ -120,11 +118,10 @@ func TestManager(t *testing.T) { in: Config{ DefaultResolvers: mustIPPs("1.1.1.1:53", "9.9.9.9:53"), SearchDomains: fqdns("tailscale.com", "universe.tf"), - Routes: upstreams("ts.com", "100.100.100.100:53"), + Routes: upstreams("ts.com", ""), Hosts: hosts( "dave.ts.com.", "1.2.3.4", "bradfitz.ts.com.", "2.3.4.5"), - AuthoritativeSuffixes: fqdns("ts.com"), }, os: OSConfig{ Nameservers: mustIPs("100.100.100.100"), @@ -143,11 +140,10 @@ func TestManager(t *testing.T) { in: Config{ DefaultResolvers: mustIPPs("1.1.1.1:53", "9.9.9.9:53"), SearchDomains: fqdns("tailscale.com", "universe.tf"), - Routes: upstreams("ts.com", "100.100.100.100:53"), + Routes: upstreams("ts.com", ""), Hosts: hosts( "dave.ts.com.", "1.2.3.4", "bradfitz.ts.com.", "2.3.4.5"), - AuthoritativeSuffixes: fqdns("ts.com"), }, split: true, os: OSConfig{ @@ -279,9 +275,8 @@ func TestManager(t *testing.T) { Hosts: hosts( "dave.ts.com.", "1.2.3.4", "bradfitz.ts.com.", "2.3.4.5"), - Routes: upstreams("ts.com", "100.100.100.100:53"), - AuthoritativeSuffixes: fqdns("ts.com"), - SearchDomains: fqdns("tailscale.com", "universe.tf"), + Routes: upstreams("ts.com", ""), + SearchDomains: fqdns("tailscale.com", "universe.tf"), }, bs: OSConfig{ Nameservers: mustIPs("8.8.8.8"), @@ -305,9 +300,8 @@ func TestManager(t *testing.T) { Hosts: hosts( "dave.ts.com.", "1.2.3.4", "bradfitz.ts.com.", "2.3.4.5"), - Routes: upstreams("ts.com", "100.100.100.100:53"), - AuthoritativeSuffixes: fqdns("ts.com"), - SearchDomains: fqdns("tailscale.com", "universe.tf"), + Routes: upstreams("ts.com", ""), + SearchDomains: fqdns("tailscale.com", "universe.tf"), }, split: true, os: OSConfig{ @@ -325,12 +319,11 @@ func TestManager(t *testing.T) { { name: "routes-magic", in: Config{ - Routes: upstreams("corp.com", "2.2.2.2:53"), + Routes: upstreams("corp.com", "2.2.2.2:53", "ts.com", ""), Hosts: hosts( "dave.ts.com.", "1.2.3.4", "bradfitz.ts.com.", "2.3.4.5"), - AuthoritativeSuffixes: fqdns("ts.com"), - SearchDomains: fqdns("tailscale.com", "universe.tf"), + SearchDomains: fqdns("tailscale.com", "universe.tf"), }, bs: OSConfig{ Nameservers: mustIPs("8.8.8.8"), @@ -355,12 +348,11 @@ func TestManager(t *testing.T) { in: Config{ Routes: upstreams( "corp.com", "2.2.2.2:53", - "ts.com", "100.100.100.100:53"), + "ts.com", ""), Hosts: hosts( "dave.ts.com.", "1.2.3.4", "bradfitz.ts.com.", "2.3.4.5"), - AuthoritativeSuffixes: fqdns("ts.com"), - SearchDomains: fqdns("tailscale.com", "universe.tf"), + SearchDomains: fqdns("tailscale.com", "universe.tf"), }, split: true, os: OSConfig{ @@ -451,7 +443,12 @@ func upstreams(strs ...string) (ret map[dnsname.FQDN][]netaddr.IPPort) { var key dnsname.FQDN ret = map[dnsname.FQDN][]netaddr.IPPort{} for _, s := range strs { - if ipp, err := netaddr.ParseIPPort(s); err == nil { + if s == "" { + if key == "" { + panic("IPPort provided before suffix") + } + ret[key] = nil + } else if ipp, err := netaddr.ParseIPPort(s); err == nil { if key == "" { panic("IPPort provided before suffix") }