diff --git a/one.cpp b/one.cpp index 3dad2ed9b..c4fc30e22 100644 --- a/one.cpp +++ b/one.cpp @@ -973,7 +973,6 @@ int main(int argc,char **argv) std::string homeDir; unsigned int port = ZT_DEFAULT_PORT; bool skipRootCheck = false; - const char *allowManagementFrom = (const char *)0; for(int i=1;irun()) { case OneService::ONE_STILL_RUNNING: // shouldn't happen, run() won't return until done case OneService::ONE_NORMAL_TERMINATION: diff --git a/service/OneService.cpp b/service/OneService.cpp index b925fc1b9..2e2563074 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -514,10 +514,10 @@ public: const std::string _homePath; BackgroundResolver _tcpFallbackResolver; - InetAddress _allowManagementFrom; EmbeddedNetworkController *_controller; Phy _phy; Node *_node; + unsigned int _primaryPort; // Local configuration and memo-ized static path definitions json _localConfig; @@ -527,6 +527,7 @@ public: Hashtable< uint64_t,std::vector > _v6Blacklists; std::vector< InetAddress > _globalV4Blacklist; std::vector< InetAddress > _globalV6Blacklist; + std::vector< InetAddress > _allowManagementFrom; std::vector< std::string > _interfacePrefixBlacklist; Mutex _localConfig_m; @@ -612,12 +613,13 @@ public: // end member variables ---------------------------------------------------- - OneServiceImpl(const char *hp,unsigned int port,const char *allowManagementFrom) : + OneServiceImpl(const char *hp,unsigned int port) : _homePath((hp) ? hp : ".") ,_tcpFallbackResolver(ZT_TCP_FALLBACK_RELAY) ,_controller((EmbeddedNetworkController *)0) ,_phy(this,false,true) ,_node((Node *)0) + ,_primaryPort(port) ,_controlPlane((ControlPlane *)0) ,_lastDirectReceiveFromGlobal(0) #ifdef ZT_TCP_FALLBACK_RELAY @@ -637,63 +639,9 @@ public: #endif ,_run(true) { - if (allowManagementFrom) - _allowManagementFrom.fromString(allowManagementFrom); - _ports[0] = 0; _ports[1] = 0; _ports[2] = 0; - - // The control socket is bound to the default/static port on localhost. If we - // can do this, we have successfully allocated a port. The binders will take - // care of binding non-local addresses for ZeroTier traffic. - const int portTrials = (port == 0) ? 256 : 1; // if port is 0, pick random - for(int k=0;kaddress() % 45500); - 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; - } - -#ifdef ZT_USE_MINIUPNPC - // 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. - if (_ports[1]) { - _ports[2] = _ports[1]; - for(int i=0;;++i) { - if (i > 1000) { - _ports[2] = 0; - break; - } else if (++_ports[2] >= 65536) { - _ports[2] = 20000; - } - if (_trialBind(_ports[2])) - break; - } - if (_ports[2]) { - char uniqueName[64]; - Utils::snprintf(uniqueName,sizeof(uniqueName),"ZeroTier/%.10llx@%u",_node->address(),_ports[2]); - _portMapper = new PortMapper(_ports[2],uniqueName); - } - } -#endif - - for(int i=0;i<3;++i) - _portsBE[i] = Utils::hton((uint16_t)_ports[i]); - + // Read local configuration { uint64_t trustedPathIds[ZT_MAX_TRUSTED_PATHS]; InetAddress trustedPathNetworks[ZT_MAX_TRUSTED_PATHS]; @@ -872,6 +778,103 @@ public: } applyLocalConfig(); + // Bind TCP control socket + const int portTrials = (_primaryPort == 0) ? 256 : 1; // if port is 0, pick random + for(int k=0;k 0) ? 0 : 0x7f000001)); // right now we just listen for TCP @127.0.0.1 + in4.sin_port = Utils::hton((uint16_t)_primaryPort); + _v4TcpControlSocket = _phy.tcpListen((const struct sockaddr *)&in4,this); + + struct sockaddr_in6 in6; + memset((void *)&in6,0,sizeof(in6)); + in6.sin6_family = AF_INET6; + in6.sin6_port = in4.sin_port; + if (_allowManagementFrom.size() == 0) + in6.sin6_addr.s6_addr[15] = 1; // IPv6 localhost == ::1 + _v6TcpControlSocket = _phy.tcpListen((const struct sockaddr *)&in6,this); + + // We must bind one of IPv4 or IPv6 -- support either failing to support hosts that + // have only IPv4 or only IPv6 stacks. + if ((_v4TcpControlSocket)||(_v6TcpControlSocket)) { + _ports[0] = _primaryPort; + break; + } else { + if (_v4TcpControlSocket) + _phy.close(_v4TcpControlSocket,false); + if (_v6TcpControlSocket) + _phy.close(_v6TcpControlSocket,false); + _primaryPort = 0; + } + } else { + _primaryPort = 0; + } + } + if (_ports[0] == 0) { + Mutex::Lock _l(_termReason_m); + _termReason = ONE_UNRECOVERABLE_ERROR; + _fatalErrorMessage = "cannot bind to local control interface port"; + return _termReason; + } + + // Write file containing primary port to be read by CLIs, etc. + char portstr[64]; + Utils::snprintf(portstr,sizeof(portstr),"%u",_ports[0]); + OSUtils::writeFile((_homePath + ZT_PATH_SEPARATOR_S + "zerotier-one.port").c_str(),std::string(portstr)); + + // Attempt to bind to a secondary port chosen from our ZeroTier address. + // This exists because there are buggy NATs out there that fail if more + // than one device behind the same NAT tries to use the same internal + // private address port number. + _ports[1] = 20000 + ((unsigned int)_node->address() % 45500); + 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; + } + +#ifdef ZT_USE_MINIUPNPC + // 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. + if (_ports[1]) { + _ports[2] = _ports[1]; + for(int i=0;;++i) { + if (i > 1000) { + _ports[2] = 0; + break; + } else if (++_ports[2] >= 65536) { + _ports[2] = 20000; + } + if (_trialBind(_ports[2])) + break; + } + if (_ports[2]) { + char uniqueName[64]; + Utils::snprintf(uniqueName,sizeof(uniqueName),"ZeroTier/%.10llx@%u",_node->address(),_ports[2]); + _portMapper = new PortMapper(_ports[2],uniqueName); + } + } +#endif + + // Populate ports in big-endian format for quick compare + for(int i=0;i<3;++i) + _portsBE[i] = Utils::hton((uint16_t)_ports[i]); + _controller = new EmbeddedNetworkController(_node,(_homePath + ZT_PATH_SEPARATOR_S + ZT_CONTROLLER_DB_PATH).c_str()); _node->setNetconfMaster((void *)_controller); @@ -1218,6 +1221,7 @@ public: } } + _allowManagementFrom.clear(); _interfacePrefixBlacklist.clear(); json &settings = _localConfig["settings"]; if (settings.is_object()) { @@ -1236,6 +1240,15 @@ public: _interfacePrefixBlacklist.push_back(tmp); } } + + json &amf = settings["allowManagementFrom"]; + if (amf.is_array()) { + for(unsigned long i=0;ifrom.ipScope() == InetAddress::IP_SCOPE_LOOPBACK)) || (_allowManagementFrom.containsAddress(tc->from)) ) { + bool allow; + { + Mutex::Lock _l(_localConfig_m); + if (_allowManagementFrom.size() == 0) { + allow = (tc->from.ipScope() == InetAddress::IP_SCOPE_LOOPBACK); + } else { + allow = false; + for(std::vector::const_iterator i(_allowManagementFrom.begin());i!=_allowManagementFrom.end();++i) { + if (i->containsAddress(tc->from)) { + allow = true; + break; + } + } + } + } + + if (allow) { try { if (_controlPlane) scode = _controlPlane->handleRequest(tc->from,tc->parser.method,tc->url,tc->headers,tc->body,data,contentType); @@ -2230,7 +2259,7 @@ std::string OneService::autoUpdateUrl() return std::string(); } -OneService *OneService::newInstance(const char *hp,unsigned int port,const char *allowManagementFrom) { return new OneServiceImpl(hp,port,allowManagementFrom); } +OneService *OneService::newInstance(const char *hp,unsigned int port) { return new OneServiceImpl(hp,port); } OneService::~OneService() {} } // namespace ZeroTier diff --git a/service/OneService.hpp b/service/OneService.hpp index 553bfd5e1..aebc051bc 100644 --- a/service/OneService.hpp +++ b/service/OneService.hpp @@ -98,12 +98,8 @@ public: * * @param hp Home path * @param port TCP and UDP port for packets and HTTP control (if 0, pick random port) - * @param allowManagementFrom If non-NULL, allow control from supplied IP/netmask */ - static OneService *newInstance( - const char *hp, - unsigned int port, - const char *allowManagementFrom = (const char *)0); + static OneService *newInstance(const char *hp,unsigned int port); virtual ~OneService();