net/dns: replace resolver IPs with type for DoH

We currently plumb full URLs for DNS resolvers from the control server
down to the client. But when we pass the values into the net/dns
package, we throw away any URL that isn't a bare IP. This commit
continues the plumbing, and gets the URL all the way to the built in
forwarder. (It stops before plumbing URLs into the OS configurations
that can handle them.)

For #2596

Signed-off-by: David Crawshaw <crawshaw@tailscale.com>
This commit is contained in:
David Crawshaw
2021-08-03 06:56:31 -07:00
committed by David Crawshaw
parent 7bfd4f521d
commit 9502b515f1
11 changed files with 287 additions and 147 deletions

View File

@@ -11,6 +11,7 @@ import (
"inet.af/netaddr"
"tailscale.com/net/dns/resolver"
"tailscale.com/types/dnstype"
"tailscale.com/util/dnsname"
)
@@ -20,14 +21,14 @@ type Config struct {
// which aren't covered by more specific per-domain routes below.
// If empty, the OS's default resolvers (the ones that predate
// Tailscale altering the configuration) are used.
DefaultResolvers []netaddr.IPPort
DefaultResolvers []dnstype.Resolver
// Routes maps a DNS suffix to the resolvers that should be used
// 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
Routes map[dnsname.FQDN][]dnstype.Resolver
// SearchDomains are DNS suffixes to try when expanding
// single-label queries.
SearchDomains []dnsname.FQDN
@@ -45,7 +46,7 @@ type Config struct {
// spammy stuff like *.arpa entries and replacing it with a total count.
func (c *Config) WriteToBufioWriter(w *bufio.Writer) {
w.WriteString("{DefaultResolvers:")
resolver.WriteIPPorts(w, c.DefaultResolvers)
resolver.WriteDNSResolvers(w, c.DefaultResolvers)
w.WriteString(" Routes:")
resolver.WriteRoutes(w, c.Routes)
@@ -65,10 +66,21 @@ func (c Config) hasRoutes() bool {
return len(c.Routes) > 0
}
// hasDefaultResolversOnly reports whether the only resolvers in c are
// DefaultResolvers.
func (c Config) hasDefaultResolversOnly() bool {
return c.hasDefaultResolvers() && !c.hasRoutes()
// hasDefaultIPResolversOnly reports whether the only resolvers in c are
// DefaultResolvers, and that those resolvers are simple IP addresses.
func (c Config) hasDefaultIPResolversOnly() bool {
if !c.hasDefaultResolvers() || c.hasRoutes() {
return false
}
for _, r := range c.DefaultResolvers {
if ipp, err := netaddr.ParseIPPort(r.Addr); err == nil && ipp.Port() == 53 {
continue
}
if _, err := netaddr.ParseIP(r.Addr); err != nil {
return false
}
}
return true
}
func (c Config) hasDefaultResolvers() bool {
@@ -78,9 +90,9 @@ func (c Config) hasDefaultResolvers() bool {
// singleResolverSet returns the resolvers used by c.Routes if all
// routes use the same resolvers, or nil if multiple sets of resolvers
// are specified.
func (c Config) singleResolverSet() []netaddr.IPPort {
func (c Config) singleResolverSet() []dnstype.Resolver {
var (
prev []netaddr.IPPort
prev []dnstype.Resolver
prevInitialized bool
)
for _, resolvers := range c.Routes {
@@ -89,7 +101,7 @@ func (c Config) singleResolverSet() []netaddr.IPPort {
prevInitialized = true
continue
}
if !sameIPPorts(prev, resolvers) {
if !sameResolverNames(prev, resolvers) {
return nil
}
}
@@ -108,16 +120,29 @@ func (c Config) matchDomains() []dnsname.FQDN {
return ret
}
func sameIPPorts(a, b []netaddr.IPPort) bool {
func sameResolverNames(a, b []dnstype.Resolver) bool {
if len(a) != len(b) {
return false
}
for i := range a {
if a[i].Addr != b[i].Addr {
return false
}
if !sameIPs(a[i].BootstrapResolution, b[i].BootstrapResolution) {
return false
}
}
return true
}
func sameIPs(a, b []netaddr.IP) bool {
if len(a) != len(b) {
return false
}
for i := range a {
if a[i] != b[i] {
return false
}
}
return true
}