diff options
Diffstat (limited to 'service/OneService.cpp')
-rw-r--r-- | service/OneService.cpp | 715 |
1 files changed, 546 insertions, 169 deletions
diff --git a/service/OneService.cpp b/service/OneService.cpp index 389cdc91..40ffce2f 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -1,6 +1,6 @@ /* * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/ + * Copyright (C) 2011-2019 ZeroTier, Inc. https://www.zerotier.com/ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -13,7 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. + * along with this program. If not, see <http://www.gnu.org/licenses/>. * * -- * @@ -34,6 +34,9 @@ #include <vector> #include <algorithm> #include <list> +#include <thread> +#include <mutex> +#include <condition_variable> #include "../version.h" #include "../include/ZeroTierOne.h" @@ -57,6 +60,7 @@ #include "../osdep/PortMapper.hpp" #include "../osdep/Binder.hpp" #include "../osdep/ManagedRoute.hpp" +#include "../osdep/BlockingQueue.hpp" #include "OneService.hpp" #include "SoftwareUpdater.hpp" @@ -67,9 +71,12 @@ #include <ShlObj.h> #include <netioapi.h> #include <iphlpapi.h> +//#include <unistd.h> +#define stat _stat #else #include <sys/types.h> #include <sys/socket.h> +#include <sys/stat.h> #include <sys/wait.h> #include <unistd.h> #include <ifaddrs.h> @@ -81,11 +88,18 @@ #include "../ext/http-parser/http_parser.h" #endif +#if ZT_VAULT_SUPPORT +extern "C" { +#include <curl/curl.h> +} +#endif + #include "../ext/json/json.hpp" using json = nlohmann::json; #include "../controller/EmbeddedNetworkController.hpp" +#include "../controller/RabbitMQ.hpp" #ifdef ZT_USE_TEST_TAP @@ -99,14 +113,14 @@ namespace ZeroTier { typedef TestEthernetTap EthernetTap; } #include "../controller/EmbeddedNetworkController.hpp" #include "../node/Node.hpp" // Use the virtual netcon endpoint instead of a tun/tap port driver -#include "../include/VirtualTap.h" +#include "../include/VirtualTap.hpp" namespace ZeroTier { typedef VirtualTap EthernetTap; } #else #ifdef __APPLE__ -#include "../osdep/OSXEthernetTap.hpp" -namespace ZeroTier { typedef OSXEthernetTap EthernetTap; } +#include "../osdep/MacEthernetTap.hpp" +namespace ZeroTier { typedef MacEthernetTap EthernetTap; } #endif // __APPLE__ #ifdef __LINUX__ #include "../osdep/LinuxEthernetTap.hpp" @@ -129,10 +143,14 @@ namespace ZeroTier { typedef NetBSDEthernetTap EthernetTap; } namespace ZeroTier { typedef BSDEthernetTap EthernetTap; } #endif // __OpenBSD__ -#endif // ZT_SERVICE_NETCON +#endif // ZT_SDK #endif // ZT_USE_TEST_TAP +#ifndef ZT_SOFTWARE_UPDATE_DEFAULT +#define ZT_SOFTWARE_UPDATE_DEFAULT "disable" +#endif + // Sanity limits for HTTP #define ZT_MAX_HTTP_MESSAGE_SIZE (1024 * 1024 * 64) #define ZT_MAX_HTTP_CONNECTIONS 65536 @@ -145,7 +163,9 @@ namespace ZeroTier { typedef BSDEthernetTap EthernetTap; } #define ZT_TAP_CHECK_MULTICAST_INTERVAL 5000 // TCP fallback relay (run by ZeroTier, Inc. -- this will eventually go away) +#ifndef ZT_SDK #define ZT_TCP_FALLBACK_RELAY "204.80.128.1/443" +#endif // Frequency at which we re-resolve the TCP fallback relay #define ZT_TCP_FALLBACK_RERESOLVE_DELAY 86400000 @@ -162,10 +182,20 @@ namespace ZeroTier { typedef BSDEthernetTap EthernetTap; } // TCP activity timeout #define ZT_TCP_ACTIVITY_TIMEOUT 60000 +#if ZT_VAULT_SUPPORT +size_t curlResponseWrite(void *ptr, size_t size, size_t nmemb, std::string *data) +{ + data->append((char*)ptr, size * nmemb); + return size * nmemb; +} +#endif + namespace ZeroTier { namespace { +static const InetAddress NULL_INET_ADDR; + // Fake TLS hello for TCP tunnel outgoing connections (TUNNELED mode) static const char ZT_TCP_TUNNEL_HELLO[9] = { 0x17,0x03,0x03,0x00,0x04,(char)ZEROTIER_ONE_VERSION_MAJOR,(char)ZEROTIER_ONE_VERSION_MINOR,(char)((ZEROTIER_ONE_VERSION_REVISION >> 8) & 0xff),(char)(ZEROTIER_ONE_VERSION_REVISION & 0xff) }; @@ -283,6 +313,39 @@ static void _peerToJson(nlohmann::json &pj,const ZT_Peer *peer) pj["paths"] = pa; } +static void _peerAggregateLinkToJson(nlohmann::json &pj,const ZT_Peer *peer) +{ + char tmp[256]; + OSUtils::ztsnprintf(tmp,sizeof(tmp),"%.10llx",peer->address); + pj["aggregateLinkLatency"] = peer->latency; + + nlohmann::json pa = nlohmann::json::array(); + for(unsigned int i=0;i<peer->pathCount;++i) { + int64_t lastSend = peer->paths[i].lastSend; + int64_t lastReceive = peer->paths[i].lastReceive; + nlohmann::json j; + j["address"] = reinterpret_cast<const InetAddress *>(&(peer->paths[i].address))->toString(tmp); + j["lastSend"] = (lastSend < 0) ? 0 : lastSend; + j["lastReceive"] = (lastReceive < 0) ? 0 : lastReceive; + //j["trustedPathId"] = peer->paths[i].trustedPathId; + //j["active"] = (bool)(peer->paths[i].expired == 0); + //j["expired"] = (bool)(peer->paths[i].expired != 0); + //j["preferred"] = (bool)(peer->paths[i].preferred != 0); + j["latency"] = peer->paths[i].latency; + j["pdv"] = peer->paths[i].packetDelayVariance; + //j["throughputDisturbCoeff"] = peer->paths[i].throughputDisturbCoeff; + //j["packetErrorRatio"] = peer->paths[i].packetErrorRatio; + //j["packetLossRatio"] = peer->paths[i].packetLossRatio; + j["stability"] = peer->paths[i].stability; + j["throughput"] = peer->paths[i].throughput; + //j["maxThroughput"] = peer->paths[i].maxThroughput; + j["allocation"] = peer->paths[i].allocation; + j["ifname"] = peer->paths[i].ifname; + pa.push_back(j); + } + pj["paths"] = pa; +} + static void _moonToJson(nlohmann::json &mj,const World &world) { char tmp[4096]; @@ -384,6 +447,15 @@ struct TcpConnection Mutex writeq_m; }; +struct OneServiceIncomingPacket +{ + uint64_t now; + int64_t sock; + struct sockaddr_storage from; + unsigned int size; + uint8_t data[ZT_MAX_MTU]; +}; + class OneServiceImpl : public OneService { public: @@ -403,7 +475,11 @@ public: PhySocket *_localControlSocket6; bool _updateAutoApply; bool _allowTcpFallbackRelay; + bool _allowSecondaryPort; + unsigned int _multipathMode; unsigned int _primaryPort; + unsigned int _secondaryPort; + unsigned int _tertiaryPort; volatile unsigned int _udpPortPickerCounter; // Local configuration and memo-ized information from it @@ -418,11 +494,13 @@ public: std::vector< std::string > _interfacePrefixBlacklist; Mutex _localConfig_m; + std::vector<InetAddress> explicitBind; + /* * To attempt to handle NAT/gateway craziness we use three local UDP ports: * * [0] is the normal/default port, usually 9993 - * [1] is a port dervied from our ZeroTier address + * [1] is a port derived from our ZeroTier address * [2] is a port computed from the normal/default for use with uPnP/NAT-PMP mappings * * [2] exists because on some gateways trying to do regular NAT-t interferes @@ -481,10 +559,20 @@ public: PortMapper *_portMapper; #endif + // HashiCorp Vault Settings +#if ZT_VAULT_SUPPORT + bool _vaultEnabled; + std::string _vaultURL; + std::string _vaultToken; + std::string _vaultPath; // defaults to cubbyhole/zerotier/identity.secret for per-access key storage +#endif + // Set to false to force service to stop volatile bool _run; Mutex _run_m; + MQConfig *_mqc; + // end member variables ---------------------------------------------------- OneServiceImpl(const char *hp,unsigned int port) : @@ -513,11 +601,22 @@ public: #ifdef ZT_USE_MINIUPNPC ,_portMapper((PortMapper *)0) #endif +#ifdef ZT_VAULT_SUPPORT + ,_vaultEnabled(false) + ,_vaultURL() + ,_vaultToken() + ,_vaultPath("cubbyhole/zerotier") +#endif ,_run(true) + ,_mqc(NULL) { _ports[0] = 0; _ports[1] = 0; _ports[2] = 0; + +#if ZT_VAULT_SUPPORT + curl_global_init(CURL_GLOBAL_DEFAULT); +#endif } virtual ~OneServiceImpl() @@ -525,10 +624,16 @@ public: _binder.closeAll(_phy); _phy.close(_localControlSocket4); _phy.close(_localControlSocket6); + +#if ZT_VAULT_SUPPORT + curl_global_cleanup(); +#endif + #ifdef ZT_USE_MINIUPNPC delete _portMapper; #endif delete _controller; + delete _mqc; } virtual ReasonForTermination run() @@ -568,98 +673,8 @@ public: _node = new Node(this,(void *)0,&cb,OSUtils::now()); } - // Read local configuration - std::vector<InetAddress> explicitBind; - { - std::map<InetAddress,ZT_PhysicalPathConfiguration> ppc; - - // LEGACY: support old "trustedpaths" flat file - FILE *trustpaths = fopen((_homePath + ZT_PATH_SEPARATOR_S "trustedpaths").c_str(),"r"); - if (trustpaths) { - fprintf(stderr,"WARNING: 'trustedpaths' flat file format is deprecated in favor of path definitions in local.conf" ZT_EOL_S); - char buf[1024]; - while (fgets(buf,sizeof(buf),trustpaths)) { - int fno = 0; - char *saveptr = (char *)0; - uint64_t trustedPathId = 0; - InetAddress trustedPathNetwork; - for(char *f=Utils::stok(buf,"=\r\n \t",&saveptr);(f);f=Utils::stok((char *)0,"=\r\n \t",&saveptr)) { - if (fno == 0) { - trustedPathId = Utils::hexStrToU64(f); - } else if (fno == 1) { - trustedPathNetwork = InetAddress(f); - } else break; - ++fno; - } - if ( (trustedPathId != 0) && ((trustedPathNetwork.ss_family == AF_INET)||(trustedPathNetwork.ss_family == AF_INET6)) && (trustedPathNetwork.netmaskBits() > 0) ) { - ppc[trustedPathNetwork].trustedPathId = trustedPathId; - ppc[trustedPathNetwork].mtu = 0; // use default - } - } - fclose(trustpaths); - } - - // Read local config file - Mutex::Lock _l2(_localConfig_m); - std::string lcbuf; - if (OSUtils::readFile((_homePath + ZT_PATH_SEPARATOR_S "local.conf").c_str(),lcbuf)) { - try { - _localConfig = OSUtils::jsonParse(lcbuf); - if (!_localConfig.is_object()) { - fprintf(stderr,"WARNING: unable to parse local.conf (root element is not a JSON object)" ZT_EOL_S); - } - } catch ( ... ) { - fprintf(stderr,"WARNING: unable to parse local.conf (invalid JSON)" ZT_EOL_S); - } - } - - // Get any trusted paths in local.conf (we'll parse the rest of physical[] elsewhere) - json &physical = _localConfig["physical"]; - if (physical.is_object()) { - for(json::iterator phy(physical.begin());phy!=physical.end();++phy) { - InetAddress net(OSUtils::jsonString(phy.key(),"").c_str()); - if (net) { - if (phy.value().is_object()) { - uint64_t tpid; - if ((tpid = OSUtils::jsonInt(phy.value()["trustedPathId"],0ULL)) != 0ULL) { - if ((net.ss_family == AF_INET)||(net.ss_family == AF_INET6)) - ppc[net].trustedPathId = tpid; - } - ppc[net].mtu = (int)OSUtils::jsonInt(phy.value()["mtu"],0ULL); // 0 means use default - } - } - } - } - - json &settings = _localConfig["settings"]; - if (settings.is_object()) { - // Allow controller DB path to be put somewhere else - const std::string cdbp(OSUtils::jsonString(settings["controllerDbPath"],"")); - if (cdbp.length() > 0) - _controllerDbPath = cdbp; - - // Bind to wildcard instead of to specific interfaces (disables full tunnel capability) - json &bind = settings["bind"]; - if (bind.is_array()) { - for(unsigned long i=0;i<bind.size();++i) { - const std::string ips(OSUtils::jsonString(bind[i],"")); - if (ips.length() > 0) { - InetAddress ip(ips.c_str()); - if ((ip.ss_family == AF_INET)||(ip.ss_family == AF_INET6)) - explicitBind.push_back(ip); - } - } - } - } - - // Set trusted paths if there are any - if (ppc.size() > 0) { - for(std::map<InetAddress,ZT_PhysicalPathConfiguration>::iterator i(ppc.begin());i!=ppc.end();++i) - _node->setPhysicalPathConfiguration(reinterpret_cast<const struct sockaddr_storage *>(&(i->first)),&(i->second)); - } - } - - // Apply other runtime configuration from local.conf + // local.conf + readLocalSettings(); applyLocalConfig(); // Make sure we can use the primary port, and hunt for one if configured to do so @@ -708,16 +723,18 @@ public: // 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. Buggy NATs are a running theme. - _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 (_allowSecondaryPort) { + _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 (_trialBind(_ports[1])) - break; } #ifdef ZT_USE_MINIUPNPC @@ -726,7 +743,7 @@ public: // 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]; + _ports[2] = (_tertiaryPort == 0) ? _ports[1] : _tertiaryPort; for(int i=0;;++i) { if (i > 1000) { _ports[2] = 0; @@ -750,7 +767,7 @@ public: OSUtils::rmDashRf((_homePath + ZT_PATH_SEPARATOR_S "iddb.d").c_str()); // Network controller is now enabled by default for desktop and server - _controller = new EmbeddedNetworkController(_node,_controllerDbPath.c_str()); + _controller = new EmbeddedNetworkController(_node,_controllerDbPath.c_str(),_ports[0], _mqc); _node->setNetconfMaster((void *)_controller); // Join existing networks in networks.d @@ -780,8 +797,10 @@ public: int64_t lastTapMulticastGroupCheck = 0; int64_t lastBindRefresh = 0; int64_t lastUpdateCheck = clockShouldBe; + int64_t lastMultipathModeUpdate = 0; int64_t lastCleanedPeersDb = 0; int64_t lastLocalInterfaceAddressCheck = (clockShouldBe - ZT_LOCAL_INTERFACE_CHECK_INTERVAL) + 15000; // do this in 15s to give portmapper time to configure and other things time to settle + int64_t lastLocalConfFileCheck = OSUtils::now(); for(;;) { _run_m.lock(); if (!_run) { @@ -810,8 +829,21 @@ public: _updater->apply(); } + // Reload local.conf if anything changed recently + if ((now - lastLocalConfFileCheck) >= ZT_LOCAL_CONF_FILE_CHECK_INTERVAL) { + lastLocalConfFileCheck = now; + struct stat result; + if(stat((_homePath + ZT_PATH_SEPARATOR_S "local.conf").c_str(), &result)==0) { + int64_t mod_time = result.st_mtime * 1000; + if ((now - mod_time) <= ZT_LOCAL_CONF_FILE_CHECK_INTERVAL) { + readLocalSettings(); + applyLocalConfig(); + } + } + } + // Refresh bindings in case device's interfaces have changed, and also sync routes to update any shadow routes (e.g. shadow default) - if (((now - lastBindRefresh) >= ZT_BINDER_REFRESH_PERIOD)||(restarted)) { + if (((now - lastBindRefresh) >= (_multipathMode ? ZT_BINDER_REFRESH_PERIOD / 8 : ZT_BINDER_REFRESH_PERIOD))||(restarted)) { lastBindRefresh = now; unsigned int p[3]; unsigned int pc = 0; @@ -828,6 +860,11 @@ public: } } } + // Update multipath mode (if needed) + if (((now - lastMultipathModeUpdate) >= ZT_BINDER_REFRESH_PERIOD / 8)||(restarted)) { + lastMultipathModeUpdate = now; + _node->setMultipathMode(_multipathMode); + } // Run background task processor in core if it's time to do so int64_t dl = _nextBackgroundTaskDeadline; @@ -863,7 +900,7 @@ public: } // Sync information about physical network interfaces - if ((now - lastLocalInterfaceAddressCheck) >= ZT_LOCAL_INTERFACE_CHECK_INTERVAL) { + if ((now - lastLocalInterfaceAddressCheck) >= (_multipathMode ? ZT_LOCAL_INTERFACE_CHECK_INTERVAL / 8 : ZT_LOCAL_INTERFACE_CHECK_INTERVAL)) { lastLocalInterfaceAddressCheck = now; _node->clearLocalInterfaceAddresses(); @@ -922,6 +959,109 @@ public: return _termReason; } + void readLocalSettings() + { + // Read local configuration + std::map<InetAddress,ZT_PhysicalPathConfiguration> ppc; + + // LEGACY: support old "trustedpaths" flat file + FILE *trustpaths = fopen((_homePath + ZT_PATH_SEPARATOR_S "trustedpaths").c_str(),"r"); + if (trustpaths) { + fprintf(stderr,"WARNING: 'trustedpaths' flat file format is deprecated in favor of path definitions in local.conf" ZT_EOL_S); + char buf[1024]; + while (fgets(buf,sizeof(buf),trustpaths)) { + int fno = 0; + char *saveptr = (char *)0; + uint64_t trustedPathId = 0; + InetAddress trustedPathNetwork; + for(char *f=Utils::stok(buf,"=\r\n \t",&saveptr);(f);f=Utils::stok((char *)0,"=\r\n \t",&saveptr)) { + if (fno == 0) { + trustedPathId = Utils::hexStrToU64(f); + } else if (fno == 1) { + trustedPathNetwork = InetAddress(f); + } else break; + ++fno; + } + if ( (trustedPathId != 0) && ((trustedPathNetwork.ss_family == AF_INET)||(trustedPathNetwork.ss_family == AF_INET6)) && (trustedPathNetwork.netmaskBits() > 0) ) { + ppc[trustedPathNetwork].trustedPathId = trustedPathId; + ppc[trustedPathNetwork].mtu = 0; // use default + } + } + fclose(trustpaths); + } + + // Read local config file + Mutex::Lock _l2(_localConfig_m); + std::string lcbuf; + if (OSUtils::readFile((_homePath + ZT_PATH_SEPARATOR_S "local.conf").c_str(),lcbuf)) { + try { + _localConfig = OSUtils::jsonParse(lcbuf); + if (!_localConfig.is_object()) { + fprintf(stderr,"ERROR: unable to parse local.conf (root element is not a JSON object)" ZT_EOL_S); + exit(1); + } + } catch ( ... ) { + fprintf(stderr,"ERROR: unable to parse local.conf (invalid JSON)" ZT_EOL_S); + exit(1); + } + } + + // Get any trusted paths in local.conf (we'll parse the rest of physical[] elsewhere) + json &physical = _localConfig["physical"]; + if (physical.is_object()) { + for(json::iterator phy(physical.begin());phy!=physical.end();++phy) { + InetAddress net(OSUtils::jsonString(phy.key(),"").c_str()); + if (net) { + if (phy.value().is_object()) { + uint64_t tpid; + if ((tpid = OSUtils::jsonInt(phy.value()["trustedPathId"],0ULL)) != 0ULL) { + if ((net.ss_family == AF_INET)||(net.ss_family == AF_INET6)) + ppc[net].trustedPathId = tpid; + } + ppc[net].mtu = (int)OSUtils::jsonInt(phy.value()["mtu"],0ULL); // 0 means use default + } + } + } + } + + json &settings = _localConfig["settings"]; + if (settings.is_object()) { + // Allow controller DB path to be put somewhere else + const std::string cdbp(OSUtils::jsonString(settings["controllerDbPath"],"")); + if (cdbp.length() > 0) + _controllerDbPath = cdbp; + + json &rmq = settings["rabbitmq"]; + if (rmq.is_object() && _mqc == NULL) { + fprintf(stderr, "Reading RabbitMQ Config\n"); + _mqc = new MQConfig; + _mqc->port = rmq["port"]; + _mqc->host = OSUtils::jsonString(rmq["host"], "").c_str(); + _mqc->username = OSUtils::jsonString(rmq["username"], "").c_str(); + _mqc->password = OSUtils::jsonString(rmq["password"], "").c_str(); + } + + // Bind to wildcard instead of to specific interfaces (disables full tunnel capability) + json &bind = settings["bind"]; + if (bind.is_array()) { + for(unsigned long i=0;i<bind.size();++i) { + const std::string ips(OSUtils::jsonString(bind[i],"")); + if (ips.length() > 0) { + InetAddress ip(ips.c_str()); + if ((ip.ss_family == AF_INET)||(ip.ss_family == AF_INET6)) + explicitBind.push_back(ip); + } + } + } + } + + // Set trusted paths if there are any + if (ppc.size() > 0) { + for(std::map<InetAddress,ZT_PhysicalPathConfiguration>::iterator i(ppc.begin());i!=ppc.end();++i) + _node->setPhysicalPathConfiguration(reinterpret_cast<const struct sockaddr_storage *>(&(i->first)),&(i->second)); + } + } + virtual ReasonForTermination reasonForTermination() const { Mutex::Lock _l(_termReason_m); @@ -944,44 +1084,26 @@ public: } #ifdef ZT_SDK - virtual void leave(const uint64_t hp) - { - _node->leave(hp, NULL, NULL); - } - - virtual void join(const uint64_t hp) - { - _node->join(hp, NULL, NULL); - } - virtual std::string givenHomePath() { return _homePath; } - std::vector<ZT_VirtualNetworkRoute> *getRoutes(uint64_t nwid) + void getRoutes(uint64_t nwid, void *routeArray, unsigned int *numRoutes) { Mutex::Lock _l(_nets_m); NetworkState &n = _nets[nwid]; - std::vector<ZT_VirtualNetworkRoute> *routes = new std::vector<ZT_VirtualNetworkRoute>(); - for(int i=0; i<ZT_MAX_NETWORK_ROUTES; i++) { - routes->push_back(n.config.routes[i]); + *numRoutes = *numRoutes < n.config.routeCount ? *numRoutes : n.config.routeCount; + for(unsigned int i=0; i<*numRoutes; i++) { + ZT_VirtualNetworkRoute *vnr = (ZT_VirtualNetworkRoute*)routeArray; + memcpy(&vnr[i], &(n.config.routes[i]), sizeof(ZT_VirtualNetworkRoute)); } - return routes; } virtual Node *getNode() { return _node; } - - virtual void removeNets() - { - Mutex::Lock _l(_nets_m); - std::map<uint64_t,NetworkState>::iterator i; - for(i = _nets.begin(); i != _nets.end(); i++) - delete i->second.tap; - } #endif // ZT_SDK virtual void terminate() @@ -1082,16 +1204,6 @@ public: #ifdef __SYNOLOGY__ // Authenticate via Synology's built-in cgi script if (!isAuth) { - /* - fprintf(stderr, "path = %s\n", path.c_str()); - fprintf(stderr, "headers.size=%d\n", headers.size()); - std::map<std::string, std::string>::const_iterator it(headers.begin()); - while(it != headers.end()) { - fprintf(stderr,"header[%s] = %s\n", (it->first).c_str(), (it->second).c_str()); - it++; - } - */ - // parse out url args int synotoken_pos = path.find("SynoToken"); int argpos = path.find("?"); if(synotoken_pos != std::string::npos && argpos != std::string::npos) { @@ -1104,10 +1216,6 @@ public: setenv("HTTP_COOKIE", cookie_val.c_str(), true); setenv("HTTP_X_SYNO_TOKEN", synotoken_val.c_str(), true); setenv("REMOTE_ADDR", ah2->second.c_str(),true); - //fprintf(stderr, "HTTP_COOKIE: %s\n",std::getenv ("HTTP_COOKIE")); - //fprintf(stderr, "HTTP_X_SYNO_TOKEN: %s\n",std::getenv ("HTTP_X_SYNO_TOKEN")); - //fprintf(stderr, "REMOTE_ADDR: %s\n",std::getenv ("REMOTE_ADDR")); - // check synology web auth char user[256], buf[1024]; FILE *fp = NULL; bzero(user, 256); @@ -1153,6 +1261,23 @@ public: json &settings = res["config"]["settings"]; settings["primaryPort"] = OSUtils::jsonInt(settings["primaryPort"],(uint64_t)_primaryPort) & 0xffff; settings["allowTcpFallbackRelay"] = OSUtils::jsonBool(settings["allowTcpFallbackRelay"],_allowTcpFallbackRelay); + + if (_multipathMode) { + json &multipathConfig = res["multipath"]; + ZT_PeerList *pl = _node->peers(); + char peerAddrStr[256]; + if (pl) { + for(unsigned long i=0;i<pl->peerCount;++i) { + if (pl->peers[i].hadAggregateLink) { + nlohmann::json pj; + _peerAggregateLinkToJson(pj,&(pl->peers[i])); + OSUtils::ztsnprintf(peerAddrStr,sizeof(peerAddrStr),"%.10llx",pl->peers[i].address); + multipathConfig[peerAddrStr] = (pj); + } + } + } + } + #ifdef ZT_USE_MINIUPNPC settings["portMappingEnabled"] = OSUtils::jsonBool(settings["portMappingEnabled"],true); #else @@ -1481,6 +1606,17 @@ public: _primaryPort = (unsigned int)OSUtils::jsonInt(settings["primaryPort"],(uint64_t)_primaryPort) & 0xffff; _allowTcpFallbackRelay = OSUtils::jsonBool(settings["allowTcpFallbackRelay"],true); + _allowSecondaryPort = OSUtils::jsonBool(settings["allowSecondaryPort"],true); + _secondaryPort = (unsigned int)OSUtils::jsonInt(settings["secondaryPort"],0); + _tertiaryPort = (unsigned int)OSUtils::jsonInt(settings["tertiaryPort"],0); + if (_secondaryPort != 0 || _tertiaryPort != 0) { + fprintf(stderr,"WARNING: using manually-specified ports. This can cause NAT issues." ZT_EOL_S); + } + _multipathMode = (unsigned int)OSUtils::jsonInt(settings["multipathMode"],0); + if (_multipathMode != 0 && _allowTcpFallbackRelay) { + fprintf(stderr,"WARNING: multipathMode cannot be used with allowTcpFallbackRelay. Disabling allowTcpFallbackRelay" ZT_EOL_S); + _allowTcpFallbackRelay = false; + } _portMappingEnabled = OSUtils::jsonBool(settings["portMappingEnabled"],true); #ifndef ZT_SDK @@ -1518,6 +1654,47 @@ public: } } +#if ZT_VAULT_SUPPORT + json &vault = settings["vault"]; + if (vault.is_object()) { + const std::string url(OSUtils::jsonString(vault["vaultURL"], "").c_str()); + if (!url.empty()) { + _vaultURL = url; + } + + const std::string token(OSUtils::jsonString(vault["vaultToken"], "").c_str()); + if (!token.empty()) { + _vaultToken = token; + } + + const std::string path(OSUtils::jsonString(vault["vaultPath"], "").c_str()); + if (!path.empty()) { + _vaultPath = path; + } + } + + // also check environment variables for values. Environment variables + // will override local.conf variables + const std::string envURL(getenv("VAULT_ADDR")); + if (!envURL.empty()) { + _vaultURL = envURL; + } + + const std::string envToken(getenv("VAULT_TOKEN")); + if (!envToken.empty()) { + _vaultToken = envToken; + } + + const std::string envPath(getenv("VAULT_PATH")); + if (!envPath.empty()) { + _vaultPath = envPath; + } + + if (!_vaultURL.empty() && !_vaultToken.empty()) { + _vaultEnabled = true; + } +#endif + // Checks if a managed IP or route target is allowed bool checkIfManagedIsAllowed(const NetworkState &n,const InetAddress &target) { @@ -1632,21 +1809,37 @@ public: const InetAddress *const target = reinterpret_cast<const InetAddress *>(&(n.config.routes[i].target)); const InetAddress *const via = reinterpret_cast<const InetAddress *>(&(n.config.routes[i].via)); + const InetAddress *src = NULL; + for (unsigned int j=0; j<n.config.assignedAddressCount; ++j) { + const InetAddress *const tmp = reinterpret_cast<const InetAddress *>(&(n.config.assignedAddresses[j])); + if (target->isV4() && tmp->isV4()) { + src = reinterpret_cast<InetAddress *>(&(n.config.assignedAddresses[j])); + break; + } else if (target->isV6() && tmp->isV6()) { + src = reinterpret_cast<InetAddress *>(&(n.config.assignedAddresses[j])); + break; + } + } + if (!src) + src = &NULL_INET_ADDR; + if ( (!checkIfManagedIsAllowed(n,*target)) || ((via->ss_family == target->ss_family)&&(matchIpOnly(myIps,*via))) ) continue; bool haveRoute = false; // Ignore routes implied by local managed IPs since adding the IP adds the route +#ifndef __APPLE__ for(std::vector<InetAddress>::iterator ip(n.managedIps.begin());ip!=n.managedIps.end();++ip) { if ((target->netmaskBits() == ip->netmaskBits())&&(target->containsAddress(*ip))) { haveRoute = true; break; } } +#endif if (haveRoute) continue; - +#ifndef ZT_SDK // If we've already applied this route, just sync it and continue for(std::list< SharedPtr<ManagedRoute> >::iterator mr(n.managedRoutes.begin());mr!=n.managedRoutes.end();++mr) { if ( ((*mr)->target() == *target) && ( ((via->ss_family == target->ss_family)&&((*mr)->via().ipsEqual(*via))) || (tapdev == (*mr)->device()) ) ) { @@ -1659,9 +1852,10 @@ public: continue; // Add and apply new routes - n.managedRoutes.push_back(SharedPtr<ManagedRoute>(new ManagedRoute(*target,*via,tapdev))); + n.managedRoutes.push_back(SharedPtr<ManagedRoute>(new ManagedRoute(*target,*via,*src,tapdev))); if (!n.managedRoutes.back()->sync()) n.managedRoutes.pop_back(); +#endif } } } @@ -1672,16 +1866,10 @@ public: inline void phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *localAddr,const struct sockaddr *from,void *data,unsigned long len) { + const uint64_t now = OSUtils::now(); if ((len >= 16)&&(reinterpret_cast<const InetAddress *>(from)->ipScope() == InetAddress::IP_SCOPE_GLOBAL)) - _lastDirectReceiveFromGlobal = OSUtils::now(); - const ZT_ResultCode rc = _node->processWirePacket( - (void *)0, - OSUtils::now(), - reinterpret_cast<int64_t>(sock), - reinterpret_cast<const struct sockaddr_storage *>(from), // Phy<> uses sockaddr_storage, so it'll always be that big - data, - len, - &_nextBackgroundTaskDeadline); + _lastDirectReceiveFromGlobal = now; + const ZT_ResultCode rc = _node->processWirePacket(nullptr,now,reinterpret_cast<int64_t>(sock),reinterpret_cast<const struct sockaddr_storage *>(from),data,len,&_nextBackgroundTaskDeadline); if (ZT_ResultCode_isFatal(rc)) { char tmp[256]; OSUtils::ztsnprintf(tmp,sizeof(tmp),"fatal error code from processWirePacket: %d",(int)rc); @@ -1722,6 +1910,13 @@ public: _phy.close(sockN,false); return; } else { +#ifdef ZT_SDK + // Immediately close new local connections. The intention is to prevent the backplane from being accessed when operating as libzt + if (!allowHttpBackplaneManagement && ((InetAddress*)from)->ipScope() == InetAddress::IP_SCOPE_LOOPBACK) { + _phy.close(sockN,false); + return; + } +#endif TcpConnection *tc = new TcpConnection(); { Mutex::Lock _l(_tcpConnections_m); @@ -1921,7 +2116,7 @@ public: inline void phyOnUnixAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN) {} inline void phyOnUnixClose(PhySocket *sock,void **uptr) {} inline void phyOnUnixData(PhySocket *sock,void **uptr,void *data,unsigned long len) {} - inline void phyOnUnixWritable(PhySocket *sock,void **uptr,bool lwip_invoked) {} + inline void phyOnUnixWritable(PhySocket *sock,void **uptr) {} inline int nodeVirtualNetworkConfigFunction(uint64_t nwid,void **nuptr,enum ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nwc) { @@ -1999,7 +2194,7 @@ public: // After setting up tap, fall through to CONFIG_UPDATE since we also want to do this... case ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE: - ZT_FAST_MEMCPY(&(n.config),nwc,sizeof(ZT_VirtualNetworkConfig)); + memcpy(&(n.config),nwc,sizeof(ZT_VirtualNetworkConfig)); if (n.tap) { // sanity check #if defined(__WINDOWS__) && !defined(ZT_SDK) // wait for up to 5 seconds for the WindowsEthernetTap to actually be initialized @@ -2081,8 +2276,88 @@ public: } } +#if ZT_VAULT_SUPPORT + inline bool nodeVaultPutIdentity(enum ZT_StateObjectType type, const void *data, int len) + { + bool retval = false; + if (type != ZT_STATE_OBJECT_IDENTITY_PUBLIC && type != ZT_STATE_OBJECT_IDENTITY_SECRET) { + return retval; + } + + CURL *curl = curl_easy_init(); + if (curl) { + char token[512] = { 0 }; + snprintf(token, sizeof(token), "X-Vault-Token: %s", _vaultToken.c_str()); + + struct curl_slist *chunk = NULL; + chunk = curl_slist_append(chunk, token); + + + char content_type[512] = { 0 }; + snprintf(content_type, sizeof(content_type), "Content-Type: application/json"); + + chunk = curl_slist_append(chunk, content_type); + + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk); + + char url[2048] = { 0 }; + snprintf(url, sizeof(url), "%s/v1/%s", _vaultURL.c_str(), _vaultPath.c_str()); + + curl_easy_setopt(curl, CURLOPT_URL, url); + + json d = json::object(); + if (type == ZT_STATE_OBJECT_IDENTITY_PUBLIC) { + std::string key((const char*)data, len); + d["public"] = key; + } + else if (type == ZT_STATE_OBJECT_IDENTITY_SECRET) { + std::string key((const char*)data, len); + d["secret"] = key; + } + + if (!d.empty()) { + std::string post = d.dump(); + + if (!post.empty()) { + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post.c_str()); + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, post.length()); + +#ifndef NDEBUG + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); +#endif + + CURLcode res = curl_easy_perform(curl); + if (res == CURLE_OK) { + long response_code = 0; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code); + if (response_code == 200 || response_code == 204) { + retval = true; + } + } + } + } + + curl_easy_cleanup(curl); + curl = NULL; + curl_slist_free_all(chunk); + chunk = NULL; + } + + return retval; + } +#endif + inline void nodeStatePutFunction(enum ZT_StateObjectType type,const uint64_t id[2],const void *data,int len) { +#if ZT_VAULT_SUPPORT + if (_vaultEnabled && (type == ZT_STATE_OBJECT_IDENTITY_SECRET || type == ZT_STATE_OBJECT_IDENTITY_PUBLIC)) { + if (nodeVaultPutIdentity(type, data, len)) { + // value successfully written to Vault + return; + } + // else fallback to disk + } +#endif char p[1024]; FILE *f; bool secure = false; @@ -2117,17 +2392,22 @@ public: return; } - if (len >= 0) { + if ((len >= 0)&&(data)) { // Check to see if we've already written this first. This reduces // redundant writes and I/O overhead on most platforms and has // little effect on others. f = fopen(p,"rb"); if (f) { - char buf[65535]; - long l = (long)fread(buf,1,sizeof(buf),f); - fclose(f); - if ((l == (long)len)&&(memcmp(data,buf,l) == 0)) - return; + char *const buf = (char *)malloc(len*4); + if (buf) { + long l = (long)fread(buf,1,(size_t)(len*4),f); + fclose(f); + if ((l == (long)len)&&(memcmp(data,buf,l) == 0)) { + free(buf); + return; + } + free(buf); + } } f = fopen(p,"wb"); @@ -2149,8 +2429,93 @@ public: } } +#if ZT_VAULT_SUPPORT + inline int nodeVaultGetIdentity(enum ZT_StateObjectType type, void *data, unsigned int maxlen) + { + if (type != ZT_STATE_OBJECT_IDENTITY_SECRET && type != ZT_STATE_OBJECT_IDENTITY_PUBLIC) { + return -1; + } + + int ret = -1; + CURL *curl = curl_easy_init(); + if (curl) { + char token[512] = { 0 }; + snprintf(token, sizeof(token), "X-Vault-Token: %s", _vaultToken.c_str()); + + struct curl_slist *chunk = NULL; + chunk = curl_slist_append(chunk, token); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk); + + char url[2048] = { 0 }; + snprintf(url, sizeof(url), "%s/v1/%s", _vaultURL.c_str(), _vaultPath.c_str()); + + curl_easy_setopt(curl, CURLOPT_URL, url); + + std::string response; + std::string res_headers; + + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curlResponseWrite); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response); + curl_easy_setopt(curl, CURLOPT_HEADERDATA, &res_headers); + +#ifndef NDEBUG + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); +#endif + + CURLcode res = curl_easy_perform(curl); + + if (res == CURLE_OK) { + long response_code = 0; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code); + if (response_code == 200) { + try { + json payload = json::parse(response); + if (!payload["data"].is_null()) { + json &d = payload["data"]; + if (type == ZT_STATE_OBJECT_IDENTITY_SECRET) { + std::string secret = OSUtils::jsonString(d["secret"],""); + + if (!secret.empty()) { + ret = (int)secret.length(); + memcpy(data, secret.c_str(), ret); + } + } + else if (type == ZT_STATE_OBJECT_IDENTITY_PUBLIC) { + std::string pub = OSUtils::jsonString(d["public"],""); + + if (!pub.empty()) { + ret = (int)pub.length(); + memcpy(data, pub.c_str(), ret); + } + } + } + } + catch (...) { + ret = -1; + } + } + } + + curl_easy_cleanup(curl); + curl = NULL; + curl_slist_free_all(chunk); + chunk = NULL; + } + return ret; + } +#endif + inline int nodeStateGetFunction(enum ZT_StateObjectType type,const uint64_t id[2],void *data,unsigned int maxlen) { +#if ZT_VAULT_SUPPORT + if (_vaultEnabled && (type == ZT_STATE_OBJECT_IDENTITY_SECRET || type == ZT_STATE_OBJECT_IDENTITY_PUBLIC) ) { + int retval = nodeVaultGetIdentity(type, data, maxlen); + if (retval >= 0) + return retval; + + // else continue file based lookup + } +#endif char p[4096]; switch(type) { case ZT_STATE_OBJECT_IDENTITY_PUBLIC: @@ -2178,6 +2543,17 @@ public: if (f) { int n = (int)fread(data,1,maxlen,f); fclose(f); +#if ZT_VAULT_SUPPORT + if (_vaultEnabled && (type == ZT_STATE_OBJECT_IDENTITY_SECRET || type == ZT_STATE_OBJECT_IDENTITY_PUBLIC)) { + // If we've gotten here while Vault is enabled, Vault does not know the key and it's been + // read from disk instead. + // + // We should put the value in Vault and remove the local file. + if (nodeVaultPutIdentity(type, data, n)) { + unlink(p); + } + } +#endif if (n >= 0) return n; } @@ -2330,7 +2706,7 @@ public: else return 0; const std::vector<InetAddress> *l = lh->get(ztaddr); if ((l)&&(l->size() > 0)) { - ZT_FAST_MEMCPY(result,&((*l)[(unsigned long)_node->prng() % l->size()]),sizeof(struct sockaddr_storage)); + memcpy(result,&((*l)[(unsigned long)_node->prng() % l->size()]),sizeof(struct sockaddr_storage)); return 1; } else return 0; } @@ -2403,6 +2779,7 @@ public: #endif #ifdef __APPLE__ + if ((ifname[0] == 'f')&&(ifname[1] == 'e')&&(ifname[2] == 't')&&(ifname[3] == 'h')) return false; // ... as is feth# if ((ifname[0] == 'l')&&(ifname[1] == 'o')) return false; // loopback if ((ifname[0] == 'z')&&(ifname[1] == 't')) return false; // sanity check: zt# if ((ifname[0] == 't')&&(ifname[1] == 'u')&&(ifname[2] == 'n')) return false; // tun# is probably an OpenVPN tunnel or similar |