diff --git a/include/ZeroTierSockets.h b/include/ZeroTierSockets.h index 712ef54..0710896 100644 --- a/include/ZeroTierSockets.h +++ b/include/ZeroTierSockets.h @@ -1058,9 +1058,7 @@ int zts_py_getblocking(int fd); // Central API // //----------------------------------------------------------------------------// -#ifdef ZTS_ENABLE_PYTHON #define ZTS_DISABLE_CENTRAL_API 1 -#endif #ifndef ZTS_DISABLE_CENTRAL_API @@ -1332,6 +1330,40 @@ ZTS_API int ZTCALL zts_init_set_roots(const void* roots_data, unsigned int len); */ ZTS_API int ZTCALL zts_init_set_port(unsigned short port); +/** + * @brief Set range that random ports will be selected from. This is an initialization function that can + * only be called before `zts_node_start()`. + * + * @param start_port Start of port range + * @param end_port End of port range + * @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node + * experiences a problem, `ZTS_ERR_ARG` if invalid argument. + */ +ZTS_API int ZTCALL zts_init_set_random_port_range(unsigned short start_port, unsigned short end_port); + +/** + * @brief Allow or disallow ZeroTier from automatically selecting a backup port to help get through + * buggy NAT. This is enabled by default. This port is randomly chosen and should be disabled if you + * want to control exactly which ports ZeroTier talks on and (iff) you know with absolute certainty + * that traffic on your chosen primary port is allowed. This is an initialization function that can + * only be called before `zts_node_start()`. + * + * @param port Port number + * @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node + * experiences a problem, `ZTS_ERR_ARG` if invalid argument. + */ +ZTS_API int ZTCALL zts_init_allow_secondary_port(unsigned int allowed); + +/** + * @brief Allow or disallow the use of port-mapping. This is enabled by default. This is an + * initialization function that can only be called before `zts_node_start()`. + * + * @param port Port number + * @return `ZTS_ERR_OK` if successful, `ZTS_ERR_SERVICE` if the node + * experiences a problem, `ZTS_ERR_ARG` if invalid argument. + */ +ZTS_API int ZTCALL zts_init_allow_port_mapping(unsigned int allowed); + /** * @brief Enable or disable whether the node will cache network details * (enabled by default when `zts_init_from_storage()` is used.) Must be called before diff --git a/src/Controls.cpp b/src/Controls.cpp index dae2a67..bea618d 100644 --- a/src/Controls.cpp +++ b/src/Controls.cpp @@ -70,7 +70,7 @@ int init_subsystems() #endif // ZTS_ENABLE_CUSTOM_SIGNAL_HANDLERS if (! zts_service) { #if defined(__WINDOWS__) - WSAStartup(MAKEWORD(2, 2), &wsaData); + WSAStartup(MAKEWORD(2, 2), &wsaData); #endif zts_service = new NodeService(); zts_service->setUserEventSystem(zts_events); @@ -130,7 +130,7 @@ int zts_init_blacklist_if(const char* prefix, unsigned int len) int zts_init_set_roots(const void* roots_data, unsigned int len) { ACQUIRE_SERVICE_OFFLINE(); - return zts_service->setWorld(roots_data, len); + return zts_service->setRoots(roots_data, len); } int zts_init_set_port(unsigned short port) @@ -140,6 +140,25 @@ int zts_init_set_port(unsigned short port) return ZTS_ERR_OK; } +int zts_init_set_random_port_range(unsigned short start_port, unsigned short end_port) +{ + ACQUIRE_SERVICE_OFFLINE(); + zts_service->setRandomPortRange(start_port, end_port); + return ZTS_ERR_OK; +} + +int zts_init_allow_secondary_port(unsigned int allowed) +{ + ACQUIRE_SERVICE_OFFLINE(); + return zts_service->allowSecondaryPort(allowed); +} + +int zts_init_allow_port_mapping(unsigned int allowed) +{ + ACQUIRE_SERVICE_OFFLINE(); + return zts_service->allowPortMapping(allowed); +} + int zts_init_allow_peer_cache(unsigned int allowed) { ACQUIRE_SERVICE_OFFLINE(); @@ -155,7 +174,7 @@ int zts_init_allow_net_cache(unsigned int allowed) int zts_init_allow_roots_cache(unsigned int allowed) { ACQUIRE_SERVICE_OFFLINE(); - return zts_service->allowWorldCaching(allowed); + return zts_service->allowRootSetCaching(allowed); } int zts_init_allow_id_cache(unsigned int allowed) diff --git a/src/NodeService.cpp b/src/NodeService.cpp index e4bb131..4c0a04c 100644 --- a/src/NodeService.cpp +++ b/src/NodeService.cpp @@ -167,20 +167,25 @@ NodeService::NodeService() , _node((Node*)0) , _nodeId(0x0) , _primaryPort() + , _secondaryPort(0) + , _tertiaryPort(0) + , _randomPortRangeStart(0) + , _randomPortRangeEnd(0) , _udpPortPickerCounter(0) , _lastDirectReceiveFromGlobal(0) , _lastRestart(0) , _nextBackgroundTaskDeadline(0) , _run(false) , _termReason(ONE_STILL_RUNNING) - , _portMappingEnabled(true) + , _allowPortMapping(true) #ifdef ZT_USE_MINIUPNPC , _portMapper((PortMapper*)0) #endif + , _allowSecondaryPort(true) , _allowNetworkCaching(true) , _allowPeerCaching(true) , _allowIdentityCaching(true) - , _allowWorldCaching(true) + , _allowRootSetCaching(true) , _userDefinedWorld(false) , _nodeIsOnline(false) , _eventsEnabled(false) @@ -240,6 +245,9 @@ NodeService::ReasonForTermination NodeService::run() _node = new Node(this, (void*)0, &cb, OSUtils::now()); } + unsigned int minPort = (_randomPortRangeStart ? _randomPortRangeStart : 20000); + unsigned int maxPort = (_randomPortRangeEnd ? _randomPortRangeEnd : 45500); + // Make sure we can use the primary port, and hunt for one if // configured to do so const int portTrials = (_primaryPort == 0) ? 256 : 1; // if port is 0, pick random @@ -247,10 +255,11 @@ NodeService::ReasonForTermination NodeService::run() if (_primaryPort == 0) { unsigned int randp = 0; Utils::getSecureRandom(&randp, sizeof(randp)); - _primaryPort = 20000 + (randp % 45500); + _primaryPort = (randp % (maxPort - minPort + 1)) + minPort; } if (_trialBind(_primaryPort)) { _ports[0] = _primaryPort; + break; } else { _primaryPort = 0; @@ -268,25 +277,32 @@ NodeService::ReasonForTermination NodeService::run() // fail if more than one device behind the same NAT tries to use the // same internal private address port number. Buggy NATs are a // running theme. - _ports[1] = (_secondaryPort == 0) ? 20000 + ((unsigned int)_node->address() % 45500) : _secondaryPort; - for (int i = 0;; ++i) { - if (i > 1000) { - _ports[1] = 0; - break; - } - else if (++_ports[1] >= 65536) { - _ports[1] = 20000; - } - if (_trialBind(_ports[1])) { - break; + if (_allowSecondaryPort) { + //_ports[1] = (_secondaryPort == 0) ? minPort + ((unsigned int)_node->address() % maxPort) : _secondaryPort; + _ports[1] = (_secondaryPort == 0) ? (((unsigned int)_node->address() % (maxPort - minPort + 1)) + minPort) + : _secondaryPort; + for (int i = 0;; ++i) { + if (i > 1000) { + _ports[1] = 0; + break; + } + else if (++_ports[1] >= maxPort) { + _ports[1] = minPort; + } + if (_trialBind(_ports[1])) { + _secondaryPort = _ports[1]; + break; + } } } #ifdef ZT_USE_MINIUPNPC - if (_portMappingEnabled) { + if (_allowPortMapping) { // If we're running uPnP/NAT-PMP, bind a *third* port for that. // We can't use the other two ports for that because some NATs // do really funky stuff with ports that are explicitly mapped // that breaks things. + maxPort = (_randomPortRangeEnd ? _randomPortRangeEnd : 65536); + if (_ports[1]) { _ports[2] = (_tertiaryPort == 0) ? _ports[1] : _tertiaryPort; for (int i = 0;; ++i) { @@ -294,10 +310,11 @@ NodeService::ReasonForTermination NodeService::run() _ports[2] = 0; break; } - else if (++_ports[2] >= 65536) { - _ports[2] = 20000; + else if (++_ports[2] >= maxPort) { + _ports[2] = minPort; } if (_trialBind(_ports[2])) { + _tertiaryPort = _ports[2]; break; } } @@ -511,7 +528,7 @@ void NodeService::terminate() _allowNetworkCaching = true; _allowPeerCaching = true; _allowIdentityCaching = true; - _allowWorldCaching = true; + _allowRootSetCaching = true; memset(_publicIdStr, 0, ZT_IDENTITY_STRING_BUFFER_LENGTH); memset(_secretIdStr, 0, ZT_IDENTITY_STRING_BUFFER_LENGTH); _interfacePrefixBlacklist.clear(); @@ -1270,7 +1287,7 @@ void NodeService::nodeStatePutFunction( case ZT_STATE_OBJECT_PLANET: sendEventToUser(ZTS_EVENT_STORE_PLANET, data, len); memcpy(_rootsData, data, len); - if (_homePath.length() > 0 && _allowWorldCaching) { + if (_homePath.length() > 0 && _allowRootSetCaching) { OSUtils::ztsnprintf(p, sizeof(p), "%s" ZT_PATH_SEPARATOR_S "roots", _homePath.c_str()); } else { @@ -1711,11 +1728,42 @@ int NodeService::setPrimaryPort(unsigned short primaryPort) return ZTS_ERR_OK; } +int NodeService::setRandomPortRange(unsigned short startPort, unsigned short endPort) +{ + Mutex::Lock _lr(_run_m); + if (_run) { + return ZTS_ERR_SERVICE; + } + _randomPortRangeStart = startPort; + _randomPortRangeEnd = endPort; + return ZTS_ERR_OK; +} + unsigned short NodeService::getPrimaryPort() const { return _primaryPort; } +int NodeService::allowPortMapping(unsigned int allowed) +{ + Mutex::Lock _lr(_run_m); + if (_run) { + return ZTS_ERR_SERVICE; + } + _allowPortMapping = allowed; + return ZTS_ERR_OK; +} + +int NodeService::allowSecondaryPort(unsigned int allowed) +{ + Mutex::Lock _lr(_run_m); + if (_run) { + return ZTS_ERR_SERVICE; + } + _allowSecondaryPort = allowed; + return ZTS_ERR_OK; +} + int NodeService::setUserEventSystem(Events* events) { Mutex::Lock _lr(_run_m); @@ -1735,7 +1783,7 @@ void NodeService::enableEvents() _events->enable(); } -int NodeService::setWorld(const void* rootsData, unsigned int len) +int NodeService::setRoots(const void* rootsData, unsigned int len) { if (! rootsData || len <= 0 || len > ZTS_STORE_DATA_LEN) { return ZTS_ERR_ARG; @@ -1834,13 +1882,13 @@ int NodeService::allowIdentityCaching(unsigned int allowed) return ZTS_ERR_OK; } -int NodeService::allowWorldCaching(unsigned int allowed) +int NodeService::allowRootSetCaching(unsigned int allowed) { Mutex::Lock _lr(_run_m); if (_run) { return ZTS_ERR_SERVICE; } - _allowWorldCaching = allowed; + _allowRootSetCaching = allowed; return ZTS_ERR_OK; } int NodeService::getNetworkBroadcast(uint64_t net_id) diff --git a/src/NodeService.hpp b/src/NodeService.hpp index 5dab7ec..6e704dc 100644 --- a/src/NodeService.hpp +++ b/src/NodeService.hpp @@ -117,9 +117,13 @@ class NodeService { Node* _node; uint64_t _nodeId; - unsigned int _primaryPort = 0; - unsigned int _secondaryPort = 0; - unsigned int _tertiaryPort = 0; + unsigned int _primaryPort; + unsigned int _secondaryPort; + unsigned int _tertiaryPort; + + unsigned int _randomPortRangeStart; + unsigned int _randomPortRangeEnd; + volatile unsigned int _udpPortPickerCounter; std::map peerCache; @@ -197,15 +201,16 @@ class NodeService { std::string _fatalErrorMessage; // uPnP/NAT-PMP port mapper if enabled - bool _portMappingEnabled; // local.conf settings + bool _allowPortMapping; #ifdef ZT_USE_MINIUPNPC PortMapper* _portMapper; #endif + bool _allowSecondaryPort; uint8_t _allowNetworkCaching; uint8_t _allowPeerCaching; uint8_t _allowIdentityCaching; - uint8_t _allowWorldCaching; + uint8_t _allowRootSetCaching; char _publicIdStr[ZT_IDENTITY_STRING_BUFFER_LENGTH] = { 0 }; char _secretIdStr[ZT_IDENTITY_STRING_BUFFER_LENGTH] = { 0 }; @@ -368,19 +373,28 @@ class NodeService { /** Instruct the NodeService on where to look for identity files and caches */ int setHomePath(const char* homePath); - /** Set the NodeService's primary port */ + /** Set the primary port */ int setPrimaryPort(unsigned short primaryPort); - /** Get the NodeService's primary port */ + /** Set random range to select backup ports from */ + int setRandomPortRange(unsigned short startPort, unsigned short endPort); + + /** Get the primary port */ unsigned short getPrimaryPort() const; + /** Allow or disallow port-mapping */ + int allowPortMapping(unsigned int allowed); + + /** Allow or disallow backup port */ + int allowSecondaryPort(unsigned int allowed); + /** Set the event system instance used to convey messages to the user */ int setUserEventSystem(Events* events); void enableEvents(); /** Set the roots definition */ - int setWorld(const void* data, unsigned int len); + int setRoots(const void* data, unsigned int len); /** Add Interface prefix to blacklist (prevents ZeroTier from using that interface) */ int addInterfacePrefixToBlacklist(const char* prefix, unsigned int len); @@ -401,7 +415,7 @@ class NodeService { int allowIdentityCaching(unsigned int allowed); /** Allow ZeroTier to cache root definitions to storage */ - int allowWorldCaching(unsigned int allowed); + int allowRootSetCaching(unsigned int allowed); /** Return whether broadcast is enabled on the given network */ int getNetworkBroadcast(uint64_t net_id); diff --git a/src/bindings/csharp/CSharpSockets.cxx b/src/bindings/csharp/CSharpSockets.cxx index f01fb60..5ba3765 100644 --- a/src/bindings/csharp/CSharpSockets.cxx +++ b/src/bindings/csharp/CSharpSockets.cxx @@ -688,15 +688,24 @@ SWIGEXPORT int SWIGSTDCALL CSharp_zts_init_set_roots(void* jarg1, int jarg2) return jresult; } -SWIGEXPORT int SWIGSTDCALL CSharp_zts_init_set_port(unsigned short jarg1) +SWIGEXPORT int SWIGSTDCALL CSharp_zts_init_set_port(unsigned short port) { - int jresult; - unsigned short arg1; - int result; - arg1 = (unsigned short)jarg1; - result = (int)zts_init_set_port(arg1); - jresult = result; - return jresult; + return zts_init_set_port(port); +} + +SWIGEXPORT int SWIGSTDCALL CSharp_zts_init_set_random_port_range(unsigned short start_port, unsigned short end_port) +{ + return zts_init_set_random_port_range(start_port, end_port); +} + +SWIGEXPORT int SWIGSTDCALL CSharp_zts_init_allow_secondary_port(int allowed) +{ + return zts_init_allow_secondary_port(allowed); +} + +SWIGEXPORT int SWIGSTDCALL CSharp_zts_init_allow_port_mapping(int allowed) +{ + return zts_init_allow_port_mapping(allowed); } SWIGEXPORT int SWIGSTDCALL CSharp_zts_init_allow_net_cache(int jarg1) diff --git a/src/bindings/csharp/Node.cs b/src/bindings/csharp/Node.cs index f08f19c..a631a54 100644 --- a/src/bindings/csharp/Node.cs +++ b/src/bindings/csharp/Node.cs @@ -30,7 +30,6 @@ namespace ZeroTier.Core public class Node { static ulong _id = 0x0; - static ushort _primaryPort; static ushort _secondaryPort; static ushort _tertiaryPort; static int _versionMajor; @@ -39,7 +38,7 @@ namespace ZeroTier.Core static bool _isOnline = false; static bool _hasBeenFreed = false; string _configFilePath; - ushort _servicePort; + ushort _primaryPort; static ZeroTierManagedEventCallback _managedCallback; CSharpCallbackWithStruct _unmanagedCallback; @@ -64,7 +63,6 @@ namespace ZeroTier.Core _versionRev = 0; _isOnline = false; _configFilePath = string.Empty; - _servicePort = 0; _networks.Clear(); _peers.Clear(); } @@ -99,11 +97,16 @@ namespace ZeroTier.Core { int res = Constants.ERR_OK; if ((res = zts_init_set_port(port)) == Constants.ERR_OK) { - _servicePort = port; + _primaryPort = port; } return res; } + public int InitSetRandomPortRange(UInt16 startPort, UInt16 endPort) + { + return zts_init_set_random_port_range(startPort, endPort); + } + public int InitSetRoots(byte[] roots_data, int len) { IntPtr unmanagedPointer = Marshal.AllocHGlobal(roots_data.Length); @@ -118,6 +121,16 @@ namespace ZeroTier.Core return zts_init_allow_net_cache(Convert.ToByte(allowed)); } + public int InitAllowSecondaryPort(bool allowed) + { + return zts_init_allow_secondary_port(Convert.ToByte(allowed)); + } + + public int InitAllowPortMapping(bool allowed) + { + return zts_init_allow_port_mapping(Convert.ToByte(allowed)); + } + public int InitAllowPeerCaching(bool allowed) { return zts_init_allow_peer_cache(Convert.ToByte(allowed)); @@ -338,7 +351,7 @@ namespace ZeroTier.Core (zts_route_info_t)Marshal.PtrToStructure(msg.route, typeof(zts_route_info_t)); newEvent = new ZeroTier.Core.Event(); newEvent.Code = msg.event_code; - newEvent.RouteInfo = default; // new RouteInfo(); + // newEvent.RouteInfo = default; // new RouteInfo(); if (msg.event_code == Constants.EVENT_ROUTE_ADDED) { newEvent.Name = "EVENT_ROUTE_ADDED"; @@ -354,10 +367,10 @@ namespace ZeroTier.Core zts_peer_info_t peer_info = (zts_peer_info_t)Marshal.PtrToStructure(msg.peer, typeof(zts_peer_info_t)); newEvent = new ZeroTier.Core.Event(); newEvent.Code = msg.event_code; - newEvent.PeerInfo = default; // new PeerInfo(); + // newEvent.PeerInfo = default; // new PeerInfo(); if (peer_info.role == Constants.PEER_ROLE_PLANET) { - Console.WriteLine("ROOT"); + newEvent.Name = "PEER_ROLE_PLANET"; } if (msg.event_code == Constants.EVENT_PEER_DIRECT) { newEvent.Name = "EVENT_PEER_DIRECT"; @@ -381,7 +394,7 @@ namespace ZeroTier.Core (zts_addr_info_t)Marshal.PtrToStructure(msg.addr, typeof(zts_addr_info_t)); newEvent = new ZeroTier.Core.Event(); newEvent.Code = msg.event_code; - newEvent.AddressInfo = default; // new AddressInfo(); + // newEvent.AddressInfo = default; // new AddressInfo(); if (msg.event_code == Constants.EVENT_ADDR_ADDED_IP4) { newEvent.Name = "EVENT_ADDR_ADDED_IP4"; @@ -402,7 +415,7 @@ namespace ZeroTier.Core if (msg.cache != IntPtr.Zero) { newEvent = new ZeroTier.Core.Event(); newEvent.Code = msg.event_code; - newEvent.AddressInfo = default; // new AddressInfo(); + // newEvent.AddressInfo = default; // new AddressInfo(); if (msg.event_code == Constants.EVENT_STORE_IDENTITY_SECRET) { newEvent.Name = "EVENT_STORE_IDENTITY_SECRET"; @@ -580,12 +593,22 @@ namespace ZeroTier.Core } } + // id + [DllImport("libzt", CharSet = CharSet.Ansi, EntryPoint = "CSharp_zts_id_new")] static extern int zts_id_new(string arg1, IntPtr arg2); [DllImport("libzt", CharSet = CharSet.Ansi, EntryPoint = "CSharp_zts_id_pair_is_valid")] static extern int zts_id_pair_is_valid(string arg1, int arg2); + // init + + [DllImport("libzt", EntryPoint = "CSharp_zts_init_allow_net_cache")] + static extern int zts_init_allow_net_cache(int arg1); + + [DllImport("libzt", EntryPoint = "CSharp_zts_init_allow_peer_cache")] + static extern int zts_init_allow_peer_cache(int arg1); + [DllImport("libzt", CharSet = CharSet.Ansi, EntryPoint = "CSharp_zts_init_from_storage")] static extern int zts_init_from_storage(string arg1); @@ -604,6 +627,15 @@ namespace ZeroTier.Core [DllImport("libzt", EntryPoint = "CSharp_zts_init_set_port")] static extern int zts_init_set_port(ushort arg1); + [DllImport("libzt", EntryPoint = "CSharp_zts_init_set_random_port_range")] + static extern int zts_init_set_random_port_range(ushort arg1, ushort arg2); + + [DllImport("libzt", EntryPoint = "CSharp_zts_init_allow_secondary_port")] + static extern int zts_init_allow_secondary_port(int arg1); + + [DllImport("libzt", EntryPoint = "CSharp_zts_init_allow_port_mapping")] + static extern int zts_init_allow_port_mapping(int arg1); + // Core query API [DllImport("libzt", EntryPoint = "CSharp_zts_core_lock_obtain")] @@ -643,17 +675,6 @@ namespace ZeroTier.Core [DllImport("libzt", EntryPoint = "CSharp_zts_core_query_mc")] static extern int zts_core_query_mc(ulong net_id, int idx, ref ulong mac, ref uint adi); - // init - - [DllImport("libzt", EntryPoint = "CSharp_zts_init_allow_net_cache")] - static extern int zts_init_allow_net_cache(int arg1); - - [DllImport("libzt", EntryPoint = "CSharp_zts_init_allow_peer_cache")] - static extern int zts_init_allow_peer_cache(int arg1); - - [DllImport("libzt", EntryPoint = "CSharp_zts_init_clear")] - static extern int zts_init_clear(); - // addr [DllImport("libzt", EntryPoint = "CSharp_zts_addr_is_assigned")] diff --git a/test/selftest.cs b/test/selftest.cs index 7722aaf..a24cbc2 100644 --- a/test/selftest.cs +++ b/test/selftest.cs @@ -26,13 +26,16 @@ public class ExampleApp { // node.InitAllowIdentityCaching(true); // node.InitAllowWorldCaching(false); node.InitSetEventHandler(OnZeroTierEvent); - node.InitSetPort(0); // Will randomly attempt ports if set to 0 + //node.InitSetPort(0); // Will randomly attempt ports if not specified or is set to 0 + node.InitSetRandomPortRange(40000, 50000); + // node.InitAllowSecondaryPort(false); // (OPTIONAL) Set custom signed roots // In this case we only allow ZeroTier to contact our Amsterdam root server // To see examples of how to generate and sign roots definitions see docs.zerotier.com + /* var rootsData = new byte[] { 0x01, 0x00, 0x00, 0x00, 0x00, 0x08, 0xea, 0xc9, 0x0a, 0x00, 0x00, 0x01, 0x6c, 0xe3, 0xe2, 0x39, 0x55, 0x74, 0xeb, 0x27, 0x9d, 0xc9, 0xe7, 0x5a, 0x52, 0xbb, 0x91, 0x8f, 0xf7, 0x43, 0x3c, 0xbf, 0x77, 0x5a, 0x4b, 0x57, @@ -52,6 +55,7 @@ public class ExampleApp { 0x00, 0x00, 0x00, 0x00, 0x27, 0x09 }; node.InitSetRoots(rootsData, rootsData.Length); + */ node.Start(); // Network activity only begins after calling Start() while (! node.Online) {