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

@@ -6,12 +6,14 @@ package dns
import (
"runtime"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"inet.af/netaddr"
"tailscale.com/net/dns/resolver"
"tailscale.com/types/dnstype"
"tailscale.com/util/dnsname"
)
@@ -93,7 +95,7 @@ func TestManager(t *testing.T) {
{
name: "corp",
in: Config{
DefaultResolvers: mustIPPs("1.1.1.1:53", "9.9.9.9:53"),
DefaultResolvers: mustRes("1.1.1.1:53", "9.9.9.9:53"),
SearchDomains: fqdns("tailscale.com", "universe.tf"),
},
os: OSConfig{
@@ -104,7 +106,7 @@ func TestManager(t *testing.T) {
{
name: "corp-split",
in: Config{
DefaultResolvers: mustIPPs("1.1.1.1:53", "9.9.9.9:53"),
DefaultResolvers: mustRes("1.1.1.1:53", "9.9.9.9:53"),
SearchDomains: fqdns("tailscale.com", "universe.tf"),
},
split: true,
@@ -116,7 +118,7 @@ func TestManager(t *testing.T) {
{
name: "corp-magic",
in: Config{
DefaultResolvers: mustIPPs("1.1.1.1:53", "9.9.9.9:53"),
DefaultResolvers: mustRes("1.1.1.1:53", "9.9.9.9:53"),
SearchDomains: fqdns("tailscale.com", "universe.tf"),
Routes: upstreams("ts.com", ""),
Hosts: hosts(
@@ -138,7 +140,7 @@ func TestManager(t *testing.T) {
{
name: "corp-magic-split",
in: Config{
DefaultResolvers: mustIPPs("1.1.1.1:53", "9.9.9.9:53"),
DefaultResolvers: mustRes("1.1.1.1:53", "9.9.9.9:53"),
SearchDomains: fqdns("tailscale.com", "universe.tf"),
Routes: upstreams("ts.com", ""),
Hosts: hosts(
@@ -161,7 +163,7 @@ func TestManager(t *testing.T) {
{
name: "corp-routes",
in: Config{
DefaultResolvers: mustIPPs("1.1.1.1:53", "9.9.9.9:53"),
DefaultResolvers: mustRes("1.1.1.1:53", "9.9.9.9:53"),
Routes: upstreams("corp.com", "2.2.2.2:53"),
SearchDomains: fqdns("tailscale.com", "universe.tf"),
},
@@ -178,7 +180,7 @@ func TestManager(t *testing.T) {
{
name: "corp-routes-split",
in: Config{
DefaultResolvers: mustIPPs("1.1.1.1:53", "9.9.9.9:53"),
DefaultResolvers: mustRes("1.1.1.1:53", "9.9.9.9:53"),
Routes: upstreams("corp.com", "2.2.2.2:53"),
SearchDomains: fqdns("tailscale.com", "universe.tf"),
},
@@ -368,6 +370,26 @@ func TestManager(t *testing.T) {
LocalDomains: fqdns("ts.com."),
},
},
{
name: "exit-node-forward",
in: Config{
DefaultResolvers: mustRes("http://[fd7a:115c:a1e0:ab12:4843:cd96:6245:7a66]:2982/doh"),
Hosts: hosts(
"dave.ts.com.", "1.2.3.4",
"bradfitz.ts.com.", "2.3.4.5"),
SearchDomains: fqdns("tailscale.com", "universe.tf"),
},
os: OSConfig{
Nameservers: mustIPs("100.100.100.100"),
SearchDomains: fqdns("tailscale.com", "universe.tf"),
},
rs: resolver.Config{
Routes: upstreams(".", "http://[fd7a:115c:a1e0:ab12:4843:cd96:6245:7a66]:2982/doh"),
Hosts: hosts(
"dave.ts.com.", "1.2.3.4",
"bradfitz.ts.com.", "2.3.4.5"),
},
},
}
for _, test := range tests {
@@ -408,6 +430,13 @@ func mustIPPs(strs ...string) (ret []netaddr.IPPort) {
return ret
}
func mustRes(strs ...string) (ret []dnstype.Resolver) {
for _, s := range strs {
ret = append(ret, dnstype.Resolver{Addr: s})
}
return ret
}
func fqdns(strs ...string) (ret []dnsname.FQDN) {
for _, s := range strs {
fqdn, err := dnsname.ToFQDN(s)
@@ -439,20 +468,42 @@ func hosts(strs ...string) (ret map[dnsname.FQDN][]netaddr.IP) {
return ret
}
func upstreams(strs ...string) (ret map[dnsname.FQDN][]netaddr.IPPort) {
func hostsR(strs ...string) (ret map[dnsname.FQDN][]dnstype.Resolver) {
var key dnsname.FQDN
ret = map[dnsname.FQDN][]netaddr.IPPort{}
ret = map[dnsname.FQDN][]dnstype.Resolver{}
for _, s := range strs {
if s == "" {
if ip, err := netaddr.ParseIP(s); err == nil {
if key == "" {
panic("IPPort provided before suffix")
panic("IP provided before name")
}
ret[key] = nil
} else if ipp, err := netaddr.ParseIPPort(s); err == nil {
if key == "" {
panic("IPPort provided before suffix")
}
ret[key] = append(ret[key], ipp)
ret[key] = append(ret[key], dnstype.Resolver{Addr: ip.String()})
} else {
fqdn, err := dnsname.ToFQDN(s)
if err != nil {
panic(err)
}
key = fqdn
}
}
return ret
}
func upstreams(strs ...string) (ret map[dnsname.FQDN][]dnstype.Resolver) {
var key dnsname.FQDN
ret = map[dnsname.FQDN][]dnstype.Resolver{}
for _, s := range strs {
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")
}
ret[key] = append(ret[key], dnstype.Resolver{Addr: ipp.String()})
} else if strings.HasPrefix(s, "http") {
ret[key] = append(ret[key], dnstype.Resolver{Addr: s})
} else {
fqdn, err := dnsname.ToFQDN(s)
if err != nil {