diff --git a/go/pkg/zerotier/misc.go b/go/pkg/zerotier/misc.go index 4a2861ff7..4a30c8ded 100644 --- a/go/pkg/zerotier/misc.go +++ b/go/pkg/zerotier/misc.go @@ -43,79 +43,51 @@ func allZero(b []byte) bool { return true } -// The ipClassify code below is based on and should produce identical results to -// InetAddress::ipScope() in the C++ code. -/* -InetAddress::IpScope InetAddress::ipScope() const -{ - switch(ss_family) { - - case AF_INET: { - const uint32_t ip = Utils::ntoh((uint32_t)reinterpret_cast(this)->sin_addr.s_addr); - switch(ip >> 24) { - case 0x00: return IP_SCOPE_NONE; // 0.0.0.0/8 (reserved, never used) - case 0x06: return IP_SCOPE_PSEUDOPRIVATE; // 6.0.0.0/8 (US Army) - case 0x0a: return IP_SCOPE_PRIVATE; // 10.0.0.0/8 - case 0x0b: return IP_SCOPE_PSEUDOPRIVATE; // 11.0.0.0/8 (US DoD) - case 0x15: return IP_SCOPE_PSEUDOPRIVATE; // 21.0.0.0/8 (US DDN-RVN) - case 0x16: return IP_SCOPE_PSEUDOPRIVATE; // 22.0.0.0/8 (US DISA) - case 0x19: return IP_SCOPE_PSEUDOPRIVATE; // 25.0.0.0/8 (UK Ministry of Defense) - case 0x1a: return IP_SCOPE_PSEUDOPRIVATE; // 26.0.0.0/8 (US DISA) - case 0x1c: return IP_SCOPE_PSEUDOPRIVATE; // 28.0.0.0/8 (US DSI-North) - case 0x1d: return IP_SCOPE_PSEUDOPRIVATE; // 29.0.0.0/8 (US DISA) - case 0x1e: return IP_SCOPE_PSEUDOPRIVATE; // 30.0.0.0/8 (US DISA) - case 0x33: return IP_SCOPE_PSEUDOPRIVATE; // 51.0.0.0/8 (UK Department of Social Security) - case 0x37: return IP_SCOPE_PSEUDOPRIVATE; // 55.0.0.0/8 (US DoD) - case 0x38: return IP_SCOPE_PSEUDOPRIVATE; // 56.0.0.0/8 (US Postal Service) - case 0x64: - if ((ip & 0xffc00000) == 0x64400000) return IP_SCOPE_PRIVATE; // 100.64.0.0/10 - break; - case 0x7f: return IP_SCOPE_LOOPBACK; // 127.0.0.0/8 - case 0xa9: - if ((ip & 0xffff0000) == 0xa9fe0000) return IP_SCOPE_LINK_LOCAL; // 169.254.0.0/16 - break; - case 0xac: - if ((ip & 0xfff00000) == 0xac100000) return IP_SCOPE_PRIVATE; // 172.16.0.0/12 - break; - case 0xc0: - if ((ip & 0xffff0000) == 0xc0a80000) return IP_SCOPE_PRIVATE; // 192.168.0.0/16 - break; - case 0xff: return IP_SCOPE_NONE; // 255.0.0.0/8 (broadcast, or unused/unusable) - } - switch(ip >> 28) { - case 0xe: return IP_SCOPE_MULTICAST; // 224.0.0.0/4 - case 0xf: return IP_SCOPE_PSEUDOPRIVATE; // 240.0.0.0/4 ("reserved," usually unusable) - } - return IP_SCOPE_GLOBAL; - } break; - - case AF_INET6: { - const unsigned char *ip = reinterpret_cast(reinterpret_cast(this)->sin6_addr.s6_addr); - if ((ip[0] & 0xf0) == 0xf0) { - if (ip[0] == 0xff) return IP_SCOPE_MULTICAST; // ff00::/8 - if ((ip[0] == 0xfe)&&((ip[1] & 0xc0) == 0x80)) { - unsigned int k = 2; - while ((!ip[k])&&(k < 15)) ++k; - if ((k == 15)&&(ip[15] == 0x01)) - return IP_SCOPE_LOOPBACK; // fe80::1/128 - else return IP_SCOPE_LINK_LOCAL; // fe80::/10 - } - if ((ip[0] & 0xfe) == 0xfc) return IP_SCOPE_PRIVATE; // fc00::/7 - } - unsigned int k = 0; - while ((!ip[k])&&(k < 15)) ++k; - if (k == 15) { // all 0's except last byte - if (ip[15] == 0x01) return IP_SCOPE_LOOPBACK; // ::1/128 - if (ip[15] == 0x00) return IP_SCOPE_NONE; // ::/128 - } - return IP_SCOPE_GLOBAL; - } break; - +// checkPort does trial binding to a port using both UDP and TCP and returns false if any bindings fail. +func checkPort(port int) bool { + var ua net.UDPAddr + ua.IP = net.IPv6zero + ua.Port = port + uc, err := net.ListenUDP("udp6", &ua) + if uc != nil { + uc.Close() + } + if err != nil { + return false + } + ua.IP = net.IPv4zero + uc, err = net.ListenUDP("udp4", &ua) + if uc != nil { + uc.Close() + } + if err != nil { + return false } - return IP_SCOPE_NONE; + var ta net.TCPAddr + ta.IP = net.IPv6zero + ta.Port = port + tc, err := net.ListenTCP("tcp6", &ta) + if tc != nil { + tc.Close() + } + if err != nil { + return false + } + ta.IP = net.IPv4zero + tc, err = net.ListenTCP("tcp4", &ta) + if tc != nil { + tc.Close() + } + if err != nil { + return false + } + + return true } -*/ + +// The ipClassify code below is based on and should produce identical results to +// InetAddress::ipScope() in the C++ code. const ( ipClassificationNone = -1 diff --git a/go/pkg/zerotier/node.go b/go/pkg/zerotier/node.go index 56020d2ab..e31c3da29 100644 --- a/go/pkg/zerotier/node.go +++ b/go/pkg/zerotier/node.go @@ -147,12 +147,12 @@ func makeSockaddrStorage(ip net.IP, port int, ss *C.struct_sockaddr_storage) boo // Node is an instance of the ZeroTier core node and related C++ I/O code type Node struct { - basePath string - localConfigPath string - localConfig LocalConfig networks map[NetworkID]*Network networksByMAC map[MAC]*Network // locked by networksLock interfaceAddresses map[string]net.IP // physical external IPs on the machine + basePath string + localConfigPath string + localConfig LocalConfig localConfigLock sync.RWMutex networksLock sync.RWMutex interfaceAddressesLock sync.Mutex @@ -173,15 +173,72 @@ func NewNode(basePath string) (*Node, error) { } n := new(Node) + n.networks = make(map[NetworkID]*Network) + n.networksByMAC = make(map[MAC]*Network) + n.interfaceAddresses = make(map[string]net.IP) + n.basePath = basePath n.localConfigPath = path.Join(basePath, "local.conf") err := n.localConfig.Read(n.localConfigPath, true) if err != nil { return nil, err } - n.networks = make(map[NetworkID]*Network) - n.networksByMAC = make(map[MAC]*Network) - n.interfaceAddresses = make(map[string]net.IP) + + if n.localConfig.Settings.PortAutoSearch { + portsChanged := false + + portCheckCount := 0 + for portCheckCount < 2048 { + portCheckCount++ + if checkPort(n.localConfig.Settings.PrimaryPort) { + break + } + n.localConfig.Settings.PrimaryPort++ + n.localConfig.Settings.PrimaryPort &= 0xffff + portsChanged = true + } + if portCheckCount == 2048 { + return nil, errors.New("unable to bind to primary port, tried 2048 later ports") + } + + if n.localConfig.Settings.SecondaryPort > 0 { + portCheckCount = 0 + for portCheckCount < 2048 { + portCheckCount++ + if checkPort(n.localConfig.Settings.SecondaryPort) { + break + } + n.localConfig.Settings.SecondaryPort++ + n.localConfig.Settings.SecondaryPort &= 0xffff + portsChanged = true + } + if portCheckCount == 2048 { + n.localConfig.Settings.SecondaryPort = 0 + } + } + + if n.localConfig.Settings.TertiaryPort > 0 { + portCheckCount = 0 + for portCheckCount < 2048 { + portCheckCount++ + if checkPort(n.localConfig.Settings.TertiaryPort) { + break + } + n.localConfig.Settings.TertiaryPort++ + n.localConfig.Settings.TertiaryPort &= 0xffff + portsChanged = true + } + if portCheckCount == 2048 { + n.localConfig.Settings.TertiaryPort = 0 + } + } + + if portsChanged { + n.localConfig.Write(n.localConfigPath) + } + } else if !checkPort(n.localConfig.Settings.PrimaryPort) { + return nil, errors.New("unable to bind to primary port") + } cpath := C.CString(basePath) n.gn = C.ZT_GoNode_new(cpath)