2020-07-29 01:47:23 +00:00
|
|
|
// 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 tsdns
|
|
|
|
|
|
|
|
import (
|
|
|
|
"sort"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"inet.af/netaddr"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Map is all the data Resolver needs to resolve DNS queries within the Tailscale network.
|
|
|
|
type Map struct {
|
|
|
|
// nameToIP is a mapping of Tailscale domain names to their IP addresses.
|
|
|
|
// For example, monitoring.tailscale.us -> 100.64.0.1.
|
|
|
|
nameToIP map[string]netaddr.IP
|
2020-08-06 18:25:28 +00:00
|
|
|
// ipToName is the inverse of nameToIP.
|
|
|
|
ipToName map[netaddr.IP]string
|
2020-07-29 01:47:23 +00:00
|
|
|
// names are the keys of nameToIP in sorted order.
|
|
|
|
names []string
|
2020-08-24 21:27:21 +00:00
|
|
|
// rootDomains are the domains whose subdomains should always
|
|
|
|
// be resolved locally to prevent leakage of sensitive names.
|
|
|
|
rootDomains []string // e.g. "user.provider.beta.tailscale.net."
|
2020-07-29 01:47:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewMap returns a new Map with name to address mapping given by nameToIP.
|
2021-01-05 18:37:15 +00:00
|
|
|
//
|
|
|
|
// rootDomains are the domains whose subdomains should always be
|
|
|
|
// resolved locally to prevent leakage of sensitive names. They should
|
|
|
|
// end in a period ("user-foo.tailscale.net.").
|
2020-08-24 21:27:21 +00:00
|
|
|
func NewMap(initNameToIP map[string]netaddr.IP, rootDomains []string) *Map {
|
2020-08-06 18:25:28 +00:00
|
|
|
// TODO(dmytro): we have to allocate names and ipToName, but nameToIP can be avoided.
|
|
|
|
// It is here because control sends us names not in canonical form. Change this.
|
|
|
|
names := make([]string, 0, len(initNameToIP))
|
|
|
|
nameToIP := make(map[string]netaddr.IP, len(initNameToIP))
|
|
|
|
ipToName := make(map[netaddr.IP]string, len(initNameToIP))
|
|
|
|
|
|
|
|
for name, ip := range initNameToIP {
|
|
|
|
if len(name) == 0 {
|
|
|
|
// Nothing useful can be done with empty names.
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if name[len(name)-1] != '.' {
|
|
|
|
name += "."
|
|
|
|
}
|
2020-07-29 01:47:23 +00:00
|
|
|
names = append(names, name)
|
2020-08-06 18:25:28 +00:00
|
|
|
nameToIP[name] = ip
|
|
|
|
ipToName[ip] = name
|
2020-07-29 01:47:23 +00:00
|
|
|
}
|
|
|
|
sort.Strings(names)
|
|
|
|
|
|
|
|
return &Map{
|
|
|
|
nameToIP: nameToIP,
|
2020-08-06 18:25:28 +00:00
|
|
|
ipToName: ipToName,
|
2020-07-29 01:47:23 +00:00
|
|
|
names: names,
|
2020-08-24 21:27:21 +00:00
|
|
|
|
|
|
|
rootDomains: rootDomains,
|
2020-07-29 01:47:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func printSingleNameIP(buf *strings.Builder, name string, ip netaddr.IP) {
|
2020-08-24 21:27:21 +00:00
|
|
|
buf.WriteString(name)
|
|
|
|
buf.WriteByte('\t')
|
|
|
|
buf.WriteString(ip.String())
|
|
|
|
buf.WriteByte('\n')
|
2020-07-29 01:47:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Map) Pretty() string {
|
|
|
|
buf := new(strings.Builder)
|
|
|
|
for _, name := range m.names {
|
|
|
|
printSingleNameIP(buf, name, m.nameToIP[name])
|
|
|
|
}
|
|
|
|
return buf.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Map) PrettyDiffFrom(old *Map) string {
|
|
|
|
var (
|
|
|
|
oldNameToIP map[string]netaddr.IP
|
|
|
|
newNameToIP map[string]netaddr.IP
|
|
|
|
oldNames []string
|
|
|
|
newNames []string
|
|
|
|
)
|
|
|
|
if old != nil {
|
|
|
|
oldNameToIP = old.nameToIP
|
|
|
|
oldNames = old.names
|
|
|
|
}
|
|
|
|
if m != nil {
|
|
|
|
newNameToIP = m.nameToIP
|
|
|
|
newNames = m.names
|
|
|
|
}
|
|
|
|
|
|
|
|
buf := new(strings.Builder)
|
|
|
|
|
|
|
|
for len(oldNames) > 0 && len(newNames) > 0 {
|
|
|
|
var name string
|
|
|
|
|
|
|
|
newName, oldName := newNames[0], oldNames[0]
|
|
|
|
switch {
|
|
|
|
case oldName < newName:
|
|
|
|
name = oldName
|
|
|
|
oldNames = oldNames[1:]
|
|
|
|
case oldName > newName:
|
|
|
|
name = newName
|
|
|
|
newNames = newNames[1:]
|
|
|
|
case oldNames[0] == newNames[0]:
|
|
|
|
name = oldNames[0]
|
|
|
|
oldNames = oldNames[1:]
|
|
|
|
newNames = newNames[1:]
|
|
|
|
}
|
|
|
|
|
|
|
|
ipOld, inOld := oldNameToIP[name]
|
|
|
|
ipNew, inNew := newNameToIP[name]
|
|
|
|
switch {
|
|
|
|
case !inOld:
|
|
|
|
buf.WriteByte('+')
|
|
|
|
printSingleNameIP(buf, name, ipNew)
|
|
|
|
case !inNew:
|
|
|
|
buf.WriteByte('-')
|
|
|
|
printSingleNameIP(buf, name, ipOld)
|
|
|
|
case ipOld != ipNew:
|
|
|
|
buf.WriteByte('-')
|
|
|
|
printSingleNameIP(buf, name, ipOld)
|
|
|
|
buf.WriteByte('+')
|
|
|
|
printSingleNameIP(buf, name, ipNew)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, name := range oldNames {
|
|
|
|
if _, ok := newNameToIP[name]; !ok {
|
|
|
|
buf.WriteByte('-')
|
|
|
|
printSingleNameIP(buf, name, oldNameToIP[name])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, name := range newNames {
|
|
|
|
if _, ok := oldNameToIP[name]; !ok {
|
|
|
|
buf.WriteByte('+')
|
|
|
|
printSingleNameIP(buf, name, newNameToIP[name])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return buf.String()
|
|
|
|
}
|