Browse Source

Pick random port on -p0

Adam Ierymenko 10 years ago
parent
commit
80dc7fb675
3 changed files with 56 additions and 27 deletions
  1. 2 2
      one.cpp
  2. 49 24
      service/OneService.cpp
  3. 5 1
      service/OneService.hpp

+ 2 - 2
one.cpp

@@ -910,7 +910,7 @@ static void printHelp(const char *cn,FILE *out)
 	fprintf(out,"  -h                - Display this help"ZT_EOL_S);
 	fprintf(out,"  -h                - Display this help"ZT_EOL_S);
 	fprintf(out,"  -v                - Show version"ZT_EOL_S);
 	fprintf(out,"  -v                - Show version"ZT_EOL_S);
 	fprintf(out,"  -U                - Run as unprivileged user (skip privilege check)"ZT_EOL_S);
 	fprintf(out,"  -U                - Run as unprivileged user (skip privilege check)"ZT_EOL_S);
-	fprintf(out,"  -p<port>          - Port for UDP and TCP/HTTP (default: 9993)"ZT_EOL_S);
+	fprintf(out,"  -p<port>          - Port for UDP and TCP/HTTP (default: 9993, 0 for random)"ZT_EOL_S);
 	//fprintf(out,"  -T<path>          - Override root topology, do not authenticate or update"ZT_EOL_S);
 	//fprintf(out,"  -T<path>          - Override root topology, do not authenticate or update"ZT_EOL_S);
 
 
 #ifdef __UNIX_LIKE__
 #ifdef __UNIX_LIKE__
@@ -985,7 +985,7 @@ int main(int argc,char **argv)
 
 
 				case 'p': // port -- for both UDP and TCP, packets and control plane
 				case 'p': // port -- for both UDP and TCP, packets and control plane
 					port = Utils::strToUInt(argv[i] + 2);
 					port = Utils::strToUInt(argv[i] + 2);
-					if ((port > 0xffff)||(port == 0)) {
+					if (port > 0xffff) {
 						printHelp(argv[0],stdout);
 						printHelp(argv[0],stdout);
 						return 1;
 						return 1;
 					}
 					}

+ 49 - 24
service/OneService.cpp

@@ -415,38 +415,58 @@ public:
 		_nextBackgroundTaskDeadline(0),
 		_nextBackgroundTaskDeadline(0),
 		_tcpFallbackTunnel((TcpConnection *)0),
 		_tcpFallbackTunnel((TcpConnection *)0),
 		_termReason(ONE_STILL_RUNNING),
 		_termReason(ONE_STILL_RUNNING),
-		_port(port),
+		_port(0),
 #ifdef ZT_USE_MINIUPNPC
 #ifdef ZT_USE_MINIUPNPC
-		_upnpClient((int)port),
+		_upnpClient((UPNPClient *)0),
 #endif
 #endif
 		_run(true)
 		_run(true)
 	{
 	{
 		struct sockaddr_in in4;
 		struct sockaddr_in in4;
 		struct sockaddr_in6 in6;
 		struct sockaddr_in6 in6;
 
 
-		::memset((void *)&in4,0,sizeof(in4));
-		in4.sin_family = AF_INET;
-		in4.sin_port = Utils::hton((uint16_t)port);
-		_v4UdpSocket = _phy.udpBind((const struct sockaddr *)&in4,this,131072);
-		if (!_v4UdpSocket)
-			throw std::runtime_error("cannot bind to port (UDP/IPv4)");
-		in4.sin_addr.s_addr = Utils::hton((uint32_t)0x7f000001); // right now we just listen for TCP @localhost
-		_v4TcpListenSocket = _phy.tcpListen((const struct sockaddr *)&in4,this);
-		if (!_v4TcpListenSocket) {
-			_phy.close(_v4UdpSocket);
-			throw std::runtime_error("cannot bind to port (TCP/IPv4)");
+		const int portTrials = (port == 0) ? 256 : 1; // if port is 0, pick random
+		for(int k=0;k<portTrials;++k) {
+			if (port == 0) {
+				unsigned int randp = 0;
+				Utils::getSecureRandom(&randp,sizeof(randp));
+				port = 40000 + (randp % 25500);
+			}
+
+			::memset((void *)&in4,0,sizeof(in4));
+			in4.sin_family = AF_INET;
+			in4.sin_port = Utils::hton((uint16_t)port);
+			_v4UdpSocket = _phy.udpBind((const struct sockaddr *)&in4,this,131072);
+			if (_v4UdpSocket) {
+				in4.sin_addr.s_addr = Utils::hton((uint32_t)0x7f000001); // right now we just listen for TCP @localhost
+				_v4TcpListenSocket = _phy.tcpListen((const struct sockaddr *)&in4,this);
+				if (_v4TcpListenSocket) {
+					::memset((void *)&in6,0,sizeof(in6));
+					in6.sin6_family = AF_INET6;
+					in6.sin6_port = in4.sin_port;
+					_v6UdpSocket = _phy.udpBind((const struct sockaddr *)&in6,this,131072);
+					in6.sin6_addr.s6_addr[15] = 1; // listen for TCP only at localhost
+					_v6TcpListenSocket = _phy.tcpListen((const struct sockaddr *)&in6,this);
+
+					_port = port;
+					break; // success!
+				} else {
+					_phy.close(_v4UdpSocket,false);
+				}
+			}
+
+			port = 0;
 		}
 		}
 
 
-		::memset((void *)&in6,0,sizeof(in6));
-		in6.sin6_family = AF_INET6;
-		in6.sin6_port = in4.sin_port;
-		_v6UdpSocket = _phy.udpBind((const struct sockaddr *)&in6,this,131072);
-		in6.sin6_addr.s6_addr[15] = 1; // listen for TCP only at localhost
-		_v6TcpListenSocket = _phy.tcpListen((const struct sockaddr *)&in6,this);
+		if (_port == 0)
+			throw std::runtime_error("cannot bind to port");
 
 
 		char portstr[64];
 		char portstr[64];
-		Utils::snprintf(portstr,sizeof(portstr),"%u",port);
+		Utils::snprintf(portstr,sizeof(portstr),"%u",_port);
 		OSUtils::writeFile((_homePath + ZT_PATH_SEPARATOR_S + "zerotier-one.port").c_str(),std::string(portstr));
 		OSUtils::writeFile((_homePath + ZT_PATH_SEPARATOR_S + "zerotier-one.port").c_str(),std::string(portstr));
+
+#ifdef ZT_USE_MINIUPNPC
+		_upnpClient = new UPNPClient(_port);
+#endif
 	}
 	}
 
 
 	virtual ~OneServiceImpl()
 	virtual ~OneServiceImpl()
@@ -455,6 +475,9 @@ public:
 		_phy.close(_v6UdpSocket);
 		_phy.close(_v6UdpSocket);
 		_phy.close(_v4TcpListenSocket);
 		_phy.close(_v4TcpListenSocket);
 		_phy.close(_v6TcpListenSocket);
 		_phy.close(_v6TcpListenSocket);
+#ifdef ZT_USE_MINIUPNPC
+		delete _upnpClient;
+#endif
 	}
 	}
 
 
 	virtual ReasonForTermination run()
 	virtual ReasonForTermination run()
@@ -583,7 +606,7 @@ public:
 					_node->clearLocalInterfaceAddresses();
 					_node->clearLocalInterfaceAddresses();
 
 
 #ifdef ZT_USE_MINIUPNPC
 #ifdef ZT_USE_MINIUPNPC
-					std::vector<InetAddress> upnpAddresses(_upnpClient.get());
+					std::vector<InetAddress> upnpAddresses(_upnpClient->get());
 					for(std::vector<InetAddress>::const_iterator ext(upnpAddresses.begin());ext!=upnpAddresses.end();++ext)
 					for(std::vector<InetAddress>::const_iterator ext(upnpAddresses.begin());ext!=upnpAddresses.end();++ext)
 						_node->addLocalInterfaceAddress(reinterpret_cast<const struct sockaddr_storage *>(&(*ext)),0,ZT1_LOCAL_INTERFACE_ADDRESS_TRUST_NORMAL);
 						_node->addLocalInterfaceAddress(reinterpret_cast<const struct sockaddr_storage *>(&(*ext)),0,ZT1_LOCAL_INTERFACE_ADDRESS_TRUST_NORMAL);
 #endif
 #endif
@@ -1087,7 +1110,7 @@ public:
 		}
 		}
 	}
 	}
 
 
-	inline int nodeWirePacketSendFunction(const struct sockaddr_storage *addr,const void *data,unsigned int len)
+	inline int nodeWirePacketSendFunction(int localInterfaceId,const struct sockaddr_storage *addr,const void *data,unsigned int len)
 	{
 	{
 		int result = -1;
 		int result = -1;
 		switch(addr->ss_family) {
 		switch(addr->ss_family) {
@@ -1144,6 +1167,7 @@ public:
 #endif // ZT1_TCP_FALLBACK_RELAY
 #endif // ZT1_TCP_FALLBACK_RELAY
 
 
 				break;
 				break;
+
 			case AF_INET6:
 			case AF_INET6:
 #ifdef ZT_BREAK_UDP
 #ifdef ZT_BREAK_UDP
 				if (!OSUtils::fileExists("/tmp/ZT_BREAK_UDP")) {
 				if (!OSUtils::fileExists("/tmp/ZT_BREAK_UDP")) {
@@ -1154,6 +1178,7 @@ public:
 				}
 				}
 #endif
 #endif
 				break;
 				break;
+
 			default:
 			default:
 				return -1;
 				return -1;
 		}
 		}
@@ -1275,7 +1300,7 @@ private:
 	unsigned int _port;
 	unsigned int _port;
 
 
 #ifdef ZT_USE_MINIUPNPC
 #ifdef ZT_USE_MINIUPNPC
-	UPNPClient _upnpClient;
+	UPNPClient *_upnpClient;
 #endif
 #endif
 
 
 	bool _run;
 	bool _run;
@@ -1291,7 +1316,7 @@ static long SnodeDataStoreGetFunction(ZT1_Node *node,void *uptr,const char *name
 static int SnodeDataStorePutFunction(ZT1_Node *node,void *uptr,const char *name,const void *data,unsigned long len,int secure)
 static int SnodeDataStorePutFunction(ZT1_Node *node,void *uptr,const char *name,const void *data,unsigned long len,int secure)
 { return reinterpret_cast<OneServiceImpl *>(uptr)->nodeDataStorePutFunction(name,data,len,secure); }
 { return reinterpret_cast<OneServiceImpl *>(uptr)->nodeDataStorePutFunction(name,data,len,secure); }
 static int SnodeWirePacketSendFunction(ZT1_Node *node,void *uptr,int localInterfaceId,const struct sockaddr_storage *addr,const void *data,unsigned int len)
 static int SnodeWirePacketSendFunction(ZT1_Node *node,void *uptr,int localInterfaceId,const struct sockaddr_storage *addr,const void *data,unsigned int len)
-{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodeWirePacketSendFunction(addr,data,len); }
+{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodeWirePacketSendFunction(localInterfaceId,addr,data,len); }
 static void SnodeVirtualNetworkFrameFunction(ZT1_Node *node,void *uptr,uint64_t nwid,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
 static void SnodeVirtualNetworkFrameFunction(ZT1_Node *node,void *uptr,uint64_t nwid,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
 { reinterpret_cast<OneServiceImpl *>(uptr)->nodeVirtualNetworkFrameFunction(nwid,sourceMac,destMac,etherType,vlanId,data,len); }
 { reinterpret_cast<OneServiceImpl *>(uptr)->nodeVirtualNetworkFrameFunction(nwid,sourceMac,destMac,etherType,vlanId,data,len); }
 
 

+ 5 - 1
service/OneService.hpp

@@ -89,8 +89,12 @@ public:
 	 * Once created, you must call the run() method to actually start
 	 * Once created, you must call the run() method to actually start
 	 * processing.
 	 * processing.
 	 *
 	 *
+	 * The port is saved to a file in the home path called zerotier-one.port,
+	 * which is used by the CLI and can be used to see which port was chosen if
+	 * 0 (random port) is picked.
+	 *
 	 * @param hp Home path
 	 * @param hp Home path
-	 * @param port TCP and UDP port for packets and HTTP control
+	 * @param port TCP and UDP port for packets and HTTP control (if 0, pick random port)
 	 * @param overrideRootTopology String-serialized root topology (for testing, default: NULL)
 	 * @param overrideRootTopology String-serialized root topology (for testing, default: NULL)
 	 */
 	 */
 	static OneService *newInstance(
 	static OneService *newInstance(