net/dns: unify the OS manager and internal resolver.

Signed-off-by: David Anderson <danderson@tailscale.com>
crawshaw/localapi404
David Anderson 4 years ago committed by Dave Anderson
parent 1bf91c8123
commit 7d84ee6c98

@ -92,7 +92,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
tailscale.com/logtail/filch from tailscale.com/logpolicy
tailscale.com/metrics from tailscale.com/derp
tailscale.com/net/dns from tailscale.com/ipn/ipnlocal+
tailscale.com/net/dns/resolver from tailscale.com/wgengine
tailscale.com/net/dns/resolver from tailscale.com/wgengine+
tailscale.com/net/dnscache from tailscale.com/control/controlclient+
tailscale.com/net/dnsfallback from tailscale.com/control/controlclient
tailscale.com/net/flowtrack from tailscale.com/wgengine/filter+

@ -43,32 +43,3 @@ type OSConfig struct {
// Domains are the search domains to use.
Domains []string
}
// Equal determines whether its argument and receiver
// represent equivalent DNS configurations (then DNS reconfig is a no-op).
func (lhs OSConfig) Equal(rhs OSConfig) bool {
if len(lhs.Nameservers) != len(rhs.Nameservers) {
return false
}
if len(lhs.Domains) != len(rhs.Domains) {
return false
}
// With how we perform resolution order shouldn't matter,
// but it is unlikely that we will encounter different orders.
for i, server := range lhs.Nameservers {
if rhs.Nameservers[i] != server {
return false
}
}
// The order of domains, on the other hand, is significant.
for i, domain := range lhs.Domains {
if rhs.Domains[i] != domain {
return false
}
}
return true
}

@ -7,7 +7,11 @@ package dns
import (
"time"
"inet.af/netaddr"
"tailscale.com/net/dns/resolver"
"tailscale.com/net/tsaddr"
"tailscale.com/types/logger"
"tailscale.com/wgengine/monitor"
)
// We use file-ignore below instead of ignore because on some platforms,
@ -27,16 +31,18 @@ const reconfigTimeout = time.Second
type Manager struct {
logf logger.Logf
resolver *resolver.Resolver
os OSConfigurator
config OSConfig
config Config
}
// NewManagers created a new manager from the given config.
func NewManager(logf logger.Logf, oscfg OSConfigurator) *Manager {
func NewManager(logf logger.Logf, oscfg OSConfigurator, linkMon *monitor.Mon) *Manager {
logf = logger.WithPrefix(logf, "dns: ")
m := &Manager{
logf: logf,
resolver: resolver.New(logf, linkMon),
os: oscfg,
}
@ -44,37 +50,63 @@ func NewManager(logf logger.Logf, oscfg OSConfigurator) *Manager {
return m
}
func (m *Manager) Set(config OSConfig) error {
if config.Equal(m.config) {
return nil
func (m *Manager) Set(cfg Config) error {
m.logf("Set: %+v", cfg)
if len(cfg.DefaultResolvers) == 0 {
// TODO: make other settings work even if you didn't set a
// default resolver. For now, no default resolvers == no
// managed DNS config.
cfg = Config{}
}
m.logf("Set: %+v", config)
resolverCfg := resolver.Config{
Hosts: cfg.Hosts,
LocalDomains: cfg.AuthoritativeSuffixes,
Routes: map[string][]netaddr.IPPort{},
}
osCfg := OSConfig{
Domains: cfg.SearchDomains,
}
// We must proxy through quad-100 if MagicDNS hosts are in
// use, or there are any per-domain routes.
mustProxy := len(cfg.Hosts) > 0 || len(cfg.Routes) > 0
if mustProxy {
osCfg.Nameservers = []netaddr.IP{tsaddr.TailscaleServiceIP()}
resolverCfg.Routes["."] = cfg.DefaultResolvers
for suffix, resolvers := range cfg.Routes {
resolverCfg.Routes[suffix] = resolvers
}
} else {
for _, resolver := range cfg.DefaultResolvers {
osCfg.Nameservers = append(osCfg.Nameservers, resolver.IP)
}
}
if len(config.Nameservers) == 0 {
err := m.os.Set(OSConfig{})
// If we save the config, we will not retry next time. Only do this on success.
if err == nil {
m.config = config
if err := m.resolver.SetConfig(resolverCfg); err != nil {
return err
}
if err := m.os.Set(osCfg); err != nil {
return err
}
err := m.os.Set(config)
// If we save the config, we will not retry next time. Only do this on success.
if err == nil {
m.config = config
}
return nil
}
return err
func (m *Manager) EnqueueRequest(bs []byte, from netaddr.IPPort) error {
return m.resolver.EnqueueRequest(bs, from)
}
func (m *Manager) Up() error {
return m.os.Set(m.config)
func (m *Manager) NextResponse() ([]byte, netaddr.IPPort, error) {
return m.resolver.NextResponse()
}
func (m *Manager) Down() error {
return m.os.Close()
if err := m.os.Close(); err != nil {
return err
}
m.resolver.Close()
return nil
}
// Cleanup restores the system DNS configuration to its original state
@ -82,7 +114,7 @@ func (m *Manager) Down() error {
// No other state needs to be instantiated before this runs.
func Cleanup(logf logger.Logf, interfaceName string) {
oscfg := NewOSConfigurator(logf, interfaceName)
dns := NewManager(logf, oscfg)
dns := NewManager(logf, oscfg, nil)
if err := dns.Down(); err != nil {
logf("dns down: %v", err)
}

@ -85,7 +85,6 @@ type userspaceEngine struct {
wgdev *device.Device
router router.Router
dns *dns.Manager
resolver *resolver.Resolver
magicConn *magicsock.Conn
linkMon *monitor.Mon
linkMonOwned bool // whether we created linkMon (and thus need to close it)
@ -213,7 +212,6 @@ func NewUserspaceEngine(logf logger.Logf, conf Config) (_ Engine, reterr error)
waitCh: make(chan struct{}),
tundev: tsTUNDev,
router: conf.Router,
dns: dns.NewManager(logf, conf.DNS),
pingers: make(map[wgkey.Key]*pinger),
}
e.isLocalAddr.Store(genLocalAddrFunc(nil))
@ -230,7 +228,7 @@ func NewUserspaceEngine(logf logger.Logf, conf Config) (_ Engine, reterr error)
e.linkMonOwned = true
}
e.resolver = resolver.New(logf, e.linkMon)
e.dns = dns.NewManager(logf, conf.DNS, e.linkMon)
logf("link state: %+v", e.linkMon.InterfaceState())
@ -443,7 +441,7 @@ func (e *userspaceEngine) handleLocalPackets(p *packet.Parsed, t *tstun.Wrapper)
// handleDNS is an outbound pre-filter resolving Tailscale domains.
func (e *userspaceEngine) handleDNS(p *packet.Parsed, t *tstun.Wrapper) filter.Response {
if p.Dst.IP == magicDNSIP && p.Dst.Port == magicDNSPort && p.IPProto == ipproto.UDP {
err := e.resolver.EnqueueRequest(append([]byte(nil), p.Payload()...), p.Src)
err := e.dns.EnqueueRequest(append([]byte(nil), p.Payload()...), p.Src)
if err != nil {
e.logf("dns: enqueue: %v", err)
}
@ -455,7 +453,7 @@ func (e *userspaceEngine) handleDNS(p *packet.Parsed, t *tstun.Wrapper) filter.R
// pollResolver reads responses from the DNS resolver and injects them inbound.
func (e *userspaceEngine) pollResolver() {
for {
bs, to, err := e.resolver.NextResponse()
bs, to, err := e.dns.NextResponse()
if err == resolver.ErrClosed {
return
}
@ -992,37 +990,8 @@ func (e *userspaceEngine) Reconfig(cfg *wgcfg.Config, routerCfg *router.Config,
}
if routerChanged {
resolverCfg := resolver.Config{
Hosts: dnsCfg.Hosts,
LocalDomains: dnsCfg.AuthoritativeSuffixes,
Routes: map[string][]netaddr.IPPort{},
}
osCfg := dns.OSConfig{
Domains: dnsCfg.SearchDomains,
}
// We must proxy through quad-100 if MagicDNS hosts are in
// use, or there are any per-domain routes.
mustProxy := len(dnsCfg.Hosts) > 0 || len(dnsCfg.Routes) > 0
if mustProxy {
osCfg.Nameservers = []netaddr.IP{tsaddr.TailscaleServiceIP()}
resolverCfg.Routes["."] = dnsCfg.DefaultResolvers
for suffix, resolvers := range dnsCfg.Routes {
resolverCfg.Routes[suffix] = resolvers
}
} else {
for _, resolver := range dnsCfg.DefaultResolvers {
osCfg.Nameservers = append(osCfg.Nameservers, resolver.IP)
}
}
osCfg.Domains = dnsCfg.SearchDomains
e.logf("wgengine: Reconfig: configuring DNS")
err := e.resolver.SetConfig(resolverCfg)
health.SetDNSHealth(err)
if err != nil {
return err
}
err = e.dns.Set(osCfg)
err := e.dns.Set(*dnsCfg)
health.SetDNSHealth(err)
if err != nil {
return err
@ -1241,12 +1210,12 @@ func (e *userspaceEngine) Close() {
r := bufio.NewReader(strings.NewReader(""))
e.wgdev.IpcSetOperation(r)
e.resolver.Close()
e.magicConn.Close()
e.linkMonUnregister()
if e.linkMonOwned {
e.linkMon.Close()
}
e.dns.Down()
e.router.Close()
e.wgdev.Close()
e.tundev.Close()

Loading…
Cancel
Save