mirror of
				https://github.com/tailscale/tailscale.git
				synced 2025-10-31 08:11:32 +01:00 
			
		
		
		
	This gets close to all of the remaining ones. Updates #12912 Change-Id: I9c672bbed2654a6c5cab31e0cbece6c107d8c6fa Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
		
			
				
	
	
		
			121 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			121 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright (c) Tailscale Inc & AUTHORS
 | |
| // SPDX-License-Identifier: BSD-3-Clause
 | |
| 
 | |
| package tsdial
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"net"
 | |
| 	"net/netip"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| 
 | |
| 	"tailscale.com/types/netmap"
 | |
| 	"tailscale.com/util/dnsname"
 | |
| )
 | |
| 
 | |
| // dnsMap maps MagicDNS names (both base + FQDN) to their first IP.
 | |
| // It must not be mutated once created.
 | |
| //
 | |
| // Example keys are "foo.domain.tld.beta.tailscale.net" and "foo",
 | |
| // both without trailing dots, and both always lowercase.
 | |
| type dnsMap map[string]netip.Addr
 | |
| 
 | |
| // canonMapKey canonicalizes its input s to be a dnsMap map key.
 | |
| func canonMapKey(s string) string {
 | |
| 	return strings.ToLower(strings.TrimSuffix(s, "."))
 | |
| }
 | |
| 
 | |
| func dnsMapFromNetworkMap(nm *netmap.NetworkMap) dnsMap {
 | |
| 	if nm == nil {
 | |
| 		return nil
 | |
| 	}
 | |
| 	ret := make(dnsMap)
 | |
| 	suffix := nm.MagicDNSSuffix()
 | |
| 	have4 := false
 | |
| 	addrs := nm.GetAddresses()
 | |
| 	if nm.Name != "" && addrs.Len() > 0 {
 | |
| 		ip := addrs.At(0).Addr()
 | |
| 		ret[canonMapKey(nm.Name)] = ip
 | |
| 		if dnsname.HasSuffix(nm.Name, suffix) {
 | |
| 			ret[canonMapKey(dnsname.TrimSuffix(nm.Name, suffix))] = ip
 | |
| 		}
 | |
| 		for _, p := range addrs.All() {
 | |
| 			if p.Addr().Is4() {
 | |
| 				have4 = true
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	for _, p := range nm.Peers {
 | |
| 		if p.Name() == "" {
 | |
| 			continue
 | |
| 		}
 | |
| 		for _, pfx := range p.Addresses().All() {
 | |
| 			ip := pfx.Addr()
 | |
| 			if ip.Is4() && !have4 {
 | |
| 				continue
 | |
| 			}
 | |
| 			ret[canonMapKey(p.Name())] = ip
 | |
| 			if dnsname.HasSuffix(p.Name(), suffix) {
 | |
| 				ret[canonMapKey(dnsname.TrimSuffix(p.Name(), suffix))] = ip
 | |
| 			}
 | |
| 			break
 | |
| 		}
 | |
| 	}
 | |
| 	for _, rec := range nm.DNS.ExtraRecords {
 | |
| 		if rec.Type != "" {
 | |
| 			continue
 | |
| 		}
 | |
| 		ip, err := netip.ParseAddr(rec.Value)
 | |
| 		if err != nil {
 | |
| 			continue
 | |
| 		}
 | |
| 		ret[canonMapKey(rec.Name)] = ip
 | |
| 	}
 | |
| 	return ret
 | |
| }
 | |
| 
 | |
| // errUnresolved is a sentinel error returned by dnsMap.resolveMemory.
 | |
| var errUnresolved = errors.New("address well formed but not resolved")
 | |
| 
 | |
| func splitHostPort(addr string) (host string, port uint16, err error) {
 | |
| 	host, portStr, err := net.SplitHostPort(addr)
 | |
| 	if err != nil {
 | |
| 		return "", 0, err
 | |
| 	}
 | |
| 	port16, err := strconv.ParseUint(portStr, 10, 16)
 | |
| 	if err != nil {
 | |
| 		return "", 0, fmt.Errorf("invalid port in address %q", addr)
 | |
| 	}
 | |
| 	return host, uint16(port16), nil
 | |
| }
 | |
| 
 | |
| // Resolve resolves addr into an IP:port using first the MagicDNS contents
 | |
| // of m, else using the system resolver.
 | |
| //
 | |
| // The error is [exactly] errUnresolved if the addr is a name that isn't known
 | |
| // in the map.
 | |
| func (m dnsMap) resolveMemory(ctx context.Context, network, addr string) (_ netip.AddrPort, err error) {
 | |
| 	host, port, err := splitHostPort(addr)
 | |
| 	if err != nil {
 | |
| 		// addr malformed or invalid port.
 | |
| 		return netip.AddrPort{}, err
 | |
| 	}
 | |
| 	if ip, err := netip.ParseAddr(host); err == nil {
 | |
| 		// addr was literal ip:port.
 | |
| 		return netip.AddrPortFrom(ip, port), nil
 | |
| 	}
 | |
| 
 | |
| 	// Host is not an IP, so assume it's a DNS name.
 | |
| 
 | |
| 	// Try MagicDNS first, otherwise a real DNS lookup.
 | |
| 	ip := m[canonMapKey(host)]
 | |
| 	if ip.IsValid() {
 | |
| 		return netip.AddrPortFrom(ip, port), nil
 | |
| 	}
 | |
| 
 | |
| 	return netip.AddrPort{}, errUnresolved
 | |
| }
 |