Browse Source

Port binding check.

Adam Ierymenko 5 years ago
parent
commit
64c8171e13
2 changed files with 104 additions and 75 deletions
  1. 41 69
      go/pkg/zerotier/misc.go
  2. 63 6
      go/pkg/zerotier/node.go

+ 41 - 69
go/pkg/zerotier/misc.go

@@ -43,79 +43,51 @@ func allZero(b []byte) bool {
 	return true
 	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<const struct sockaddr_in *>(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<const unsigned char *>(reinterpret_cast<const struct sockaddr_in6 *>(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
+	}
 
 
+	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 IP_SCOPE_NONE;
+	return true
 }
 }
-*/
+
+// The ipClassify code below is based on and should produce identical results to
+// InetAddress::ipScope() in the C++ code.
 
 
 const (
 const (
 	ipClassificationNone          = -1
 	ipClassificationNone          = -1

+ 63 - 6
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
 // Node is an instance of the ZeroTier core node and related C++ I/O code
 type Node struct {
 type Node struct {
-	basePath               string
-	localConfigPath        string
-	localConfig            LocalConfig
 	networks               map[NetworkID]*Network
 	networks               map[NetworkID]*Network
 	networksByMAC          map[MAC]*Network  // locked by networksLock
 	networksByMAC          map[MAC]*Network  // locked by networksLock
 	interfaceAddresses     map[string]net.IP // physical external IPs on the machine
 	interfaceAddresses     map[string]net.IP // physical external IPs on the machine
+	basePath               string
+	localConfigPath        string
+	localConfig            LocalConfig
 	localConfigLock        sync.RWMutex
 	localConfigLock        sync.RWMutex
 	networksLock           sync.RWMutex
 	networksLock           sync.RWMutex
 	interfaceAddressesLock sync.Mutex
 	interfaceAddressesLock sync.Mutex
@@ -173,15 +173,72 @@ func NewNode(basePath string) (*Node, error) {
 	}
 	}
 
 
 	n := new(Node)
 	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.basePath = basePath
 	n.localConfigPath = path.Join(basePath, "local.conf")
 	n.localConfigPath = path.Join(basePath, "local.conf")
 	err := n.localConfig.Read(n.localConfigPath, true)
 	err := n.localConfig.Read(n.localConfigPath, true)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		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)
 	cpath := C.CString(basePath)
 	n.gn = C.ZT_GoNode_new(cpath)
 	n.gn = C.ZT_GoNode_new(cpath)