wgengine/router/dns: create

Signed-off-by: Dmytro Shynkevych <dmytro@tailscale.com>
dshynkev/dns-refactor
Dmytro Shynkevych 2020-07-16 14:19:54 -04:00
parent 1f923124bf
commit d923a3d5ee
No known key found for this signature in database
GPG Key ID: FF5E2F3DAD97EA23
7 changed files with 77 additions and 9 deletions

View File

@ -2,23 +2,29 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package router package dns
import ( import (
"inet.af/netaddr" "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 { type DNSConfig struct {
// Nameservers are the IP addresses of the nameservers to use. // Nameservers are the IP addresses of the nameservers to use.
Nameservers []netaddr.IP Nameservers []netaddr.IP
// Domains are the search domains to use. // Domains are the search domains to use.
Domains []string 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 // EquivalentTo determines whether its argument and receiver
// represent equivalent DNS configurations (then DNS reconfig is a no-op). // 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) { if len(lhs.Nameservers) != len(rhs.Nameservers) {
return false return false
} }
@ -27,6 +33,10 @@ func (lhs DNSConfig) EquivalentTo(rhs DNSConfig) bool {
return false return false
} }
if lhs.PerDomain != rhs.PerDomain {
return false
}
// With how we perform resolution order shouldn't matter, // With how we perform resolution order shouldn't matter,
// but it is unlikely that we will encounter different orders. // but it is unlikely that we will encounter different orders.
for i, server := range lhs.Nameservers { 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 { for i, domain := range lhs.Domains {
if rhs.Domains[i] != domain { if rhs.Domains[i] != domain {
return false return false

View File

@ -25,8 +25,8 @@ const (
resolvConf = "/etc/resolv.conf" resolvConf = "/etc/resolv.conf"
) )
// dnsWriteConfig writes DNS configuration in resolv.conf format to the given writer. // writeResolvConf writes DNS configuration in resolv.conf format to the given writer.
func dnsWriteConfig(w io.Writer, servers []netaddr.IP, domains []string) { func writeResolvConf(w io.Writer, servers []netaddr.IP, domains []string) {
io.WriteString(w, "# resolv.conf(5) file generated by tailscale\n") 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") io.WriteString(w, "# DO NOT EDIT THIS FILE BY HAND -- CHANGES WILL BE OVERWRITTEN\n\n")
for _, ns := range servers { 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. // readResolvConf reads DNS configuration from /etc/resolv.conf.
func dnsReadConfig() (DNSConfig, error) { func readResolvConf() (DNSConfig, error) {
var config DNSConfig var config DNSConfig
f, err := os.Open("/etc/resolv.conf") f, err := os.Open("/etc/resolv.conf")
@ -89,7 +89,7 @@ func dnsReadConfig() (DNSConfig, error) {
func dnsDirectUp(config DNSConfig) error { func dnsDirectUp(config DNSConfig) error {
// Write the tsConf file. // Write the tsConf file.
buf := new(bytes.Buffer) 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 { if err := atomicfile.WriteFile(tsConf, buf.Bytes(), 0644); err != nil {
return err return err
} }

View File

@ -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()
}

View File

@ -11,6 +11,7 @@ import (
"github.com/tailscale/wireguard-go/tun" "github.com/tailscale/wireguard-go/tun"
"inet.af/netaddr" "inet.af/netaddr"
"tailscale.com/types/logger" "tailscale.com/types/logger"
"tailscale.com/wgengine/router/dns"
) )
// Router is responsible for managing the system network stack. // Router is responsible for managing the system network stack.
@ -72,7 +73,7 @@ type Config struct {
LocalAddrs []netaddr.IPPrefix LocalAddrs []netaddr.IPPrefix
Routes []netaddr.IPPrefix // routes to point into the Tailscale interface Routes []netaddr.IPPrefix // routes to point into the Tailscale interface
DNSConfig DNS dns.Config
// Linux-only things below, ignored on other platforms. // Linux-only things below, ignored on other platforms.