@ -5,6 +5,7 @@
package dns
import (
"strings"
"time"
"inet.af/netaddr"
@ -50,47 +51,149 @@ func NewManager(logf logger.Logf, oscfg OSConfigurator, linkMon *monitor.Mon) *M
return m
}
// forceSplitDNSForTesting alters cfg to be a split DNS configuration
// that only captures search paths. It's intended for testing split
// DNS until the functionality is linked up in the admin panel.
func forceSplitDNSForTesting ( cfg * Config ) {
if len ( cfg . DefaultResolvers ) == 0 {
return
}
if cfg . Routes == nil {
cfg . Routes = map [ string ] [ ] netaddr . IPPort { }
}
for _ , search := range cfg . SearchDomains {
cfg . Routes [ search ] = cfg . DefaultResolvers
}
cfg . DefaultResolvers = 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 { }
if false {
// Temporary, for danderson to test things.
forceSplitDNSForTesting ( & cfg )
}
resolverCfg := resolver . Config {
Hosts : cfg . Hosts ,
LocalDomains : cfg . AuthoritativeSuffixes ,
Routes : map [ string ] [ ] netaddr . IPPort { } ,
rcfg , ocfg := m . compileConfig ( cfg )
m . logf ( "Resolvercfg: %+v" , rcfg )
m . logf ( "OScfg: %+v" , ocfg )
if err := m . resolver . SetConfig ( rcfg ) ; err != nil {
return err
}
osCfg := OSConfig {
SearchDomains : cfg . SearchDomains ,
if err := m . os . SetDNS ( ocfg ) ; err != nil {
return err
}
// 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
return nil
}
// compileConfig converts cfg into a quad-100 resolver configuration
// and an OS-level configuration.
func ( m * Manager ) compileConfig ( cfg Config ) ( resolver . Config , OSConfig ) {
// Deal with trivial configs first.
switch {
case ! cfg . needsOSResolver ( ) :
// Set search domains, but nothing else. This also covers the
// case where cfg is entirely zero, in which case these
// configs clear all Tailscale DNS settings.
return resolver . Config { } , OSConfig {
SearchDomains : cfg . SearchDomains ,
}
case cfg . hasDefaultResolversOnly ( ) :
// Trivial CorpDNS configuration, just override the OS
// resolver.
return resolver . Config { } , OSConfig {
Nameservers : toIPsOnly ( cfg . DefaultResolvers ) ,
SearchDomains : cfg . SearchDomains ,
}
case cfg . hasDefaultResolvers ( ) :
// Default resolvers plus other stuff always ends up proxying
// through quad-100.
rcfg := resolver . Config {
Routes : map [ string ] [ ] netaddr . IPPort {
"." : cfg . DefaultResolvers ,
} ,
Hosts : cfg . Hosts ,
LocalDomains : addFQDNDots ( cfg . AuthoritativeSuffixes ) ,
}
for suffix , resolvers := range cfg . Routes {
resolverCfg . Routes [ suffix ] = resolvers
r cfg. Routes [ suffix + "." ] = resolvers
}
} else {
for _ , resolver := range cfg . DefaultResolvers {
osCfg . Nameservers = append ( osCfg . Nameservers , resolver . IP )
ocfg := OSConfig {
Nameservers : [ ] netaddr . IP { tsaddr . TailscaleServiceIP ( ) } ,
SearchDomains: cfg . SearchDomains ,
}
return rcfg , ocfg
}
if err := m . resolver . SetConfig ( resolverCfg ) ; err != nil {
return err
// From this point on, we're figuring out split DNS
// configurations. The possible cases don't return directly any
// more, because as a final step we have to handle the case where
// the OS can't do split DNS.
var rcfg resolver . Config
var ocfg OSConfig
if ! cfg . hasHosts ( ) && cfg . singleResolverSet ( ) != nil && m . os . SupportsSplitDNS ( ) {
// Split DNS configuration requested, where all split domains
// go to the same resolvers. We can let the OS do it.
return resolver . Config { } , OSConfig {
Nameservers : toIPsOnly ( cfg . singleResolverSet ( ) ) ,
SearchDomains : cfg . SearchDomains ,
MatchDomains : cfg . matchDomains ( ) ,
}
}
if err := m . os . SetDNS ( osCfg ) ; err != nil {
return err
// Split DNS configuration with either multiple upstream routes,
// or routes + MagicDNS, or just MagicDNS, or on an OS that cannot
// split-DNS. Install a split config pointing at quad-100.
rcfg = resolver . Config {
Routes : map [ string ] [ ] netaddr . IPPort { } ,
Hosts : cfg . Hosts ,
LocalDomains : addFQDNDots ( cfg . AuthoritativeSuffixes ) ,
}
for suffix , resolvers := range cfg . Routes {
rcfg . Routes [ suffix + "." ] = resolvers
}
ocfg = OSConfig {
Nameservers : [ ] netaddr . IP { tsaddr . TailscaleServiceIP ( ) } ,
SearchDomains : cfg . SearchDomains ,
}
return nil
// If the OS can't do native split-dns, read out the underlying
// resolver config and blend it into our config.
// TODO: for now, use quad-8 as the upstream until more plumbing
// is done.
if m . os . SupportsSplitDNS ( ) {
ocfg . MatchDomains = cfg . matchDomains ( )
} else {
rcfg . Routes [ "." ] = [ ] netaddr . IPPort { netaddr . MustParseIPPort ( "8.8.8.8:53" ) }
}
return rcfg , ocfg
}
func addFQDNDots ( domains [ ] string ) [ ] string {
ret := make ( [ ] string , 0 , len ( domains ) )
for _ , dom := range domains {
ret = append ( ret , strings . TrimSuffix ( dom , "." ) + "." )
}
return ret
}
// toIPsOnly returns only the IP portion of ipps.
// TODO: this discards port information on the assumption that we're
// always pointing at port 53.
// https://github.com/tailscale/tailscale/issues/1666 tracks making
// that not true, if we ever want to.
func toIPsOnly ( ipps [ ] netaddr . IPPort ) ( ret [ ] netaddr . IP ) {
for _ , ipp := range ipps {
ret = append ( ret , ipp . IP )
}
return ret
}
func ( m * Manager ) EnqueueRequest ( bs [ ] byte , from netaddr . IPPort ) error {