From d923a3d5ee9676d28dfae1408adfb4ff04080923 Mon Sep 17 00:00:00 2001 From: Dmytro Shynkevych Date: Thu, 16 Jul 2020 14:19:54 -0400 Subject: [PATCH] wgengine/router/dns: create Signed-off-by: Dmytro Shynkevych --- wgengine/router/{dns.go => dns/config.go} | 17 +++++- .../router/{dns_direct.go => dns/direct.go} | 10 ++-- wgengine/router/dns/manager.go | 56 +++++++++++++++++++ .../{dns_networkmanager.go => dns/nm.go} | 0 .../{dns_resolvconf.go => dns/resolvconf.go} | 0 .../{dns_resolved.go => dns/resolved.go} | 0 wgengine/router/router.go | 3 +- 7 files changed, 77 insertions(+), 9 deletions(-) rename wgengine/router/{dns.go => dns/config.go} (75%) rename wgengine/router/{dns_direct.go => dns/direct.go} (91%) create mode 100644 wgengine/router/dns/manager.go rename wgengine/router/{dns_networkmanager.go => dns/nm.go} (100%) rename wgengine/router/{dns_resolvconf.go => dns/resolvconf.go} (100%) rename wgengine/router/{dns_resolved.go => dns/resolved.go} (100%) diff --git a/wgengine/router/dns.go b/wgengine/router/dns/config.go similarity index 75% rename from wgengine/router/dns.go rename to wgengine/router/dns/config.go index f3a2d146b..271d4fc6d 100644 --- a/wgengine/router/dns.go +++ b/wgengine/router/dns/config.go @@ -2,23 +2,29 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package router +package dns import ( "inet.af/netaddr" ) -// DNSConfig is the subset of Config that contains DNS parameters. +// Config is the subset of router.Config that contains DNS parameters. type DNSConfig struct { // Nameservers are the IP addresses of the nameservers to use. Nameservers []netaddr.IP // Domains are the search domains to use. Domains []string + // PerDomain indicates whether it is preferred to use Nameservers + // only for queries for subdomains of Domains. + // + // Note that Nameservers may still be applied to all queries + // if the selected configuration mode does not support per-domain settings. + PerDomain bool } // EquivalentTo determines whether its argument and receiver // represent equivalent DNS configurations (then DNS reconfig is a no-op). -func (lhs DNSConfig) EquivalentTo(rhs DNSConfig) bool { +func (lhs Config) EquivalentTo(rhs Config) bool { if len(lhs.Nameservers) != len(rhs.Nameservers) { return false } @@ -27,6 +33,10 @@ func (lhs DNSConfig) EquivalentTo(rhs DNSConfig) bool { return false } + if lhs.PerDomain != rhs.PerDomain { + 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 { @@ -35,6 +45,7 @@ func (lhs DNSConfig) EquivalentTo(rhs DNSConfig) bool { } } + // The order of domains, on the other hand, is significant. for i, domain := range lhs.Domains { if rhs.Domains[i] != domain { return false diff --git a/wgengine/router/dns_direct.go b/wgengine/router/dns/direct.go similarity index 91% rename from wgengine/router/dns_direct.go rename to wgengine/router/dns/direct.go index 3418eea25..ffcc06295 100644 --- a/wgengine/router/dns_direct.go +++ b/wgengine/router/dns/direct.go @@ -25,8 +25,8 @@ const ( resolvConf = "/etc/resolv.conf" ) -// dnsWriteConfig writes DNS configuration in resolv.conf format to the given writer. -func dnsWriteConfig(w io.Writer, servers []netaddr.IP, domains []string) { +// writeResolvConf writes DNS configuration in resolv.conf format to the given writer. +func writeResolvConf(w io.Writer, servers []netaddr.IP, domains []string) { io.WriteString(w, "# resolv.conf(5) file generated by tailscale\n") io.WriteString(w, "# DO NOT EDIT THIS FILE BY HAND -- CHANGES WILL BE OVERWRITTEN\n\n") for _, ns := range servers { @@ -44,8 +44,8 @@ func dnsWriteConfig(w io.Writer, servers []netaddr.IP, domains []string) { } } -// dnsReadConfig reads DNS configuration from /etc/resolv.conf. -func dnsReadConfig() (DNSConfig, error) { +// readResolvConf reads DNS configuration from /etc/resolv.conf. +func readResolvConf() (DNSConfig, error) { var config DNSConfig f, err := os.Open("/etc/resolv.conf") @@ -89,7 +89,7 @@ func dnsReadConfig() (DNSConfig, error) { func dnsDirectUp(config DNSConfig) error { // Write the tsConf file. buf := new(bytes.Buffer) - dnsWriteConfig(buf, config.Nameservers, config.Domains) + writeResolvConf(buf, config.Nameservers, config.Domains) if err := atomicfile.WriteFile(tsConf, buf.Bytes(), 0644); err != nil { return err } diff --git a/wgengine/router/dns/manager.go b/wgengine/router/dns/manager.go new file mode 100644 index 000000000..f57494378 --- /dev/null +++ b/wgengine/router/dns/manager.go @@ -0,0 +1,56 @@ +// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package dns + +import ( + "time" + + "tailscale.com/types/logger" +) + +// setTimeout is the time interval within which Manager.Set should complete. +// +// This is particularly useful because certain conditions can cause indefinite hangs +// (such as improper dbus auth followed by contextless dbus.Object.Call). +// Such operations should be wrapped in a timeout context. +const setTimeout = time.Second + +type managerImpl interface { + Set(Config) error + Get() Config + Reset() error +} + +type Manager struct { + impl managerImpl + oldConfig Config +} + +func NewManager(logf logger.Logf, interfaceName string) *Manager { + return &Manager{ + impl: newManager(logf, interfaceName), + } +} + +func (m *Manager) Up(config Config) error { + if len(config.Nameservers) == 0 { + return m.impl.Down() + } + + if config.EquivalentTo(m.oldConfig) { + return nil + } + + err := m.impl.Up(config) + if err == nil { + m.oldConfig = config + } + + return err +} + +func (m *Manager) Down() error { + return m.impl.Down() +} diff --git a/wgengine/router/dns_networkmanager.go b/wgengine/router/dns/nm.go similarity index 100% rename from wgengine/router/dns_networkmanager.go rename to wgengine/router/dns/nm.go diff --git a/wgengine/router/dns_resolvconf.go b/wgengine/router/dns/resolvconf.go similarity index 100% rename from wgengine/router/dns_resolvconf.go rename to wgengine/router/dns/resolvconf.go diff --git a/wgengine/router/dns_resolved.go b/wgengine/router/dns/resolved.go similarity index 100% rename from wgengine/router/dns_resolved.go rename to wgengine/router/dns/resolved.go diff --git a/wgengine/router/router.go b/wgengine/router/router.go index 21926c5da..79ea365cf 100644 --- a/wgengine/router/router.go +++ b/wgengine/router/router.go @@ -11,6 +11,7 @@ import ( "github.com/tailscale/wireguard-go/tun" "inet.af/netaddr" "tailscale.com/types/logger" + "tailscale.com/wgengine/router/dns" ) // Router is responsible for managing the system network stack. @@ -72,7 +73,7 @@ type Config struct { LocalAddrs []netaddr.IPPrefix Routes []netaddr.IPPrefix // routes to point into the Tailscale interface - DNSConfig + DNS dns.Config // Linux-only things below, ignored on other platforms.