net/dns: on windows, skip site-local v6 resolvers.

Further refinement for tailscale/corp#1662.

Signed-off-by: David Anderson <danderson@tailscale.com>
This commit is contained in:
David Anderson 2021-04-27 18:24:03 -07:00
parent 8554694616
commit 44c2b7dc79

View File

@ -8,6 +8,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"os/exec" "os/exec"
"sort"
"strings" "strings"
"syscall" "syscall"
"time" "time"
@ -343,10 +344,11 @@ func (m windowsManager) getBasePrimaryResolver() (resolvers []netaddr.IP, err er
return nil, err return nil, err
} }
var ( type candidate struct {
primary winipcfg.LUID id winipcfg.LUID
best = ^uint32(0) metric uint32
) }
var candidates []candidate
for _, row := range ifrows { for _, row := range ifrows {
if !row.Connected { if !row.Connected {
continue continue
@ -354,29 +356,57 @@ func (m windowsManager) getBasePrimaryResolver() (resolvers []netaddr.IP, err er
if row.InterfaceLUID == tsLUID { if row.InterfaceLUID == tsLUID {
continue continue
} }
if row.Metric < best { candidates = append(candidates, candidate{row.InterfaceLUID, row.Metric})
primary = row.InterfaceLUID
best = row.Metric
} }
} if len(candidates) == 0 {
if primary == 0 {
// No resolvers set outside of Tailscale. // No resolvers set outside of Tailscale.
return nil, nil return nil, nil
} }
ips, err := primary.DNS() sort.Slice(candidates, func(i, j int) bool { return candidates[i].metric < candidates[j].metric })
for _, candidate := range candidates {
ips, err := candidate.id.DNS()
if err != nil { if err != nil {
return nil, err return nil, err
} }
ipLoop:
for _, stdip := range ips { for _, stdip := range ips {
if ip, ok := netaddr.FromStdIP(stdip); ok { ip, ok := netaddr.FromStdIP(stdip)
if !ok {
continue
}
// Skip IPv6 site-local resolvers. These are an ancient
// and obsolete IPv6 RFC, which Windows still faithfully
// implements. The net result is that some low-metric
// interfaces can "have" DNS resolvers, but they're just
// site-local resolver IPs that don't go anywhere. So, we
// skip the site-local resolvers in order to find the
// first interface that has real DNS servers configured.
for _, sl := range siteLocalResolvers {
if ip.WithZone("") == sl {
continue ipLoop
}
}
resolvers = append(resolvers, ip) resolvers = append(resolvers, ip)
} }
if len(resolvers) > 0 {
// Found some resolvers, we're done.
break
}
} }
return resolvers, nil return resolvers, nil
} }
var siteLocalResolvers = []netaddr.IP{
netaddr.MustParseIP("fec0:0:0:ffff::1"),
netaddr.MustParseIP("fec0:0:0:ffff::2"),
netaddr.MustParseIP("fec0:0:0:ffff::3"),
}
func isWindows7() bool { func isWindows7() bool {
key, err := registry.OpenKey(registry.LOCAL_MACHINE, versionKey, registry.READ) key, err := registry.OpenKey(registry.LOCAL_MACHINE, versionKey, registry.READ)
if err != nil { if err != nil {