diff --git a/net/dns/config.go b/net/dns/config.go index 7fb5e5ebd..1f9716bec 100644 --- a/net/dns/config.go +++ b/net/dns/config.go @@ -91,6 +91,30 @@ func (c Config) hasDefaultIPResolversOnly() bool { return true } +// hasHostsWithoutSplitDNSRoutes reports whether c contains any Host entries +// that aren't covered by a SplitDNS route suffix. +func (c Config) hasHostsWithoutSplitDNSRoutes() bool { + // TODO(bradfitz): this could be more efficient, but we imagine + // the number of SplitDNS routes and/or hosts will be small. + for host := range c.Hosts { + if !c.hasSplitDNSRouteForHost(host) { + return true + } + } + return false +} + +// hasSplitDNSRouteForHost reports whether c contains a SplitDNS route +// that contains hosts. +func (c Config) hasSplitDNSRouteForHost(host dnsname.FQDN) bool { + for route := range c.Routes { + if route.Contains(host) { + return true + } + } + return false +} + func (c Config) hasDefaultResolvers() bool { return len(c.DefaultResolvers) > 0 } diff --git a/net/dns/manager.go b/net/dns/manager.go index cace1fea5..281732e0e 100644 --- a/net/dns/manager.go +++ b/net/dns/manager.go @@ -207,9 +207,14 @@ func (m *Manager) compileConfig(cfg Config) (rcfg resolver.Config, ocfg OSConfig // case where cfg is entirely zero, in which case these // configs clear all Tailscale DNS settings. return rcfg, ocfg, nil - case cfg.hasDefaultIPResolversOnly(): - // Trivial CorpDNS configuration, just override the OS - // resolver. + case cfg.hasDefaultIPResolversOnly() && !cfg.hasHostsWithoutSplitDNSRoutes(): + // Trivial CorpDNS configuration, just override the OS resolver. + // + // If there are hosts (ExtraRecords) that are not covered by an existing + // SplitDNS route, then we don't go into this path so that we fall into + // the next case and send the extra record hosts queries through + // 100.100.100.100 instead where we can answer them. + // // TODO: for OSes that support it, pass IP:port and DoH // addresses directly to OS. // https://github.com/tailscale/tailscale/issues/1666 diff --git a/net/dns/manager_test.go b/net/dns/manager_test.go index 3ab7636be..f5893ec00 100644 --- a/net/dns/manager_test.go +++ b/net/dns/manager_test.go @@ -199,6 +199,71 @@ func TestManager(t *testing.T) { "bradfitz.ts.com.", "2.3.4.5"), }, }, + { + // If Hosts are specified (i.e. ExtraRecords) that aren't a split + // DNS route and a global resolver is specified, then make + // everything go via 100.100.100.100. + name: "hosts-with-global-dns-uses-quad100", + split: true, + in: Config{ + DefaultResolvers: mustRes("1.1.1.1", "9.9.9.9"), + Hosts: hosts( + "foo.tld.", "1.2.3.4", + "bar.tld.", "2.3.4.5"), + }, + os: OSConfig{ + Nameservers: mustIPs("100.100.100.100"), + }, + rs: resolver.Config{ + Hosts: hosts( + "foo.tld.", "1.2.3.4", + "bar.tld.", "2.3.4.5"), + Routes: upstreams(".", "1.1.1.1", "9.9.9.9"), + }, + }, + { + // This is the above hosts-with-global-dns-uses-quad100 test but + // verifying that if global DNS servers aren't set (the 1.1.1.1 and + // 9.9.9.9 above), then we don't configure 100.100.100.100 as the + // resolver. + name: "hosts-without-global-dns-not-use-quad100", + split: true, + in: Config{ + Hosts: hosts( + "foo.tld.", "1.2.3.4", + "bar.tld.", "2.3.4.5"), + }, + os: OSConfig{}, + rs: resolver.Config{ + Hosts: hosts( + "foo.tld.", "1.2.3.4", + "bar.tld.", "2.3.4.5"), + }, + }, + { + // This tests that ExtraRecords (foo.tld and bar.tld here) don't trigger forcing + // traffic through 100.100.100.100 if there's Split DNS support and the extra + // records are part of a split DNS route. + name: "hosts-with-extrarecord-hosts-with-routes-no-quad100", + split: true, + in: Config{ + Routes: upstreams( + "tld.", "4.4.4.4", + ), + Hosts: hosts( + "foo.tld.", "1.2.3.4", + "bar.tld.", "2.3.4.5"), + }, + os: OSConfig{ + Nameservers: mustIPs("4.4.4.4"), + MatchDomains: fqdns("tld."), + }, + rs: resolver.Config{ + Hosts: hosts( + "foo.tld.", "1.2.3.4", + "bar.tld.", "2.3.4.5"), + }, + }, { name: "corp", in: Config{ diff --git a/tailcfg/tailcfg.go b/tailcfg/tailcfg.go index da8dddcac..aa66ad66d 100644 --- a/tailcfg/tailcfg.go +++ b/tailcfg/tailcfg.go @@ -77,7 +77,8 @@ type CapabilityVersion int // 38: 2022-08-11: added PingRequest.URLIsNoise // 39: 2022-08-15: clients can talk Noise over arbitrary HTTPS port // 40: 2022-08-22: added Node.KeySignature, PeersChangedPatch.KeySignature -const CurrentCapabilityVersion CapabilityVersion = 40 +// 41: 2022-08-30: uses 100.100.100.100 for route-less ExtraRecords if global nameservers is set +const CurrentCapabilityVersion CapabilityVersion = 41 type StableID string