diff --git a/attic/Binder.hpp b/attic/Binder.hpp deleted file mode 100644 index 67debc80b..000000000 --- a/attic/Binder.hpp +++ /dev/null @@ -1,461 +0,0 @@ -/* - * Copyright (c)2019 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2023-01-01 - * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. - */ -/****/ - -#ifndef ZT_BINDER_HPP -#define ZT_BINDER_HPP - -#include "../node/Constants.hpp" - -#include -#include -#include -#include - -#ifdef __WINDOWS__ -#include -#include -#include -#include -#include -#else -#include -#include -#include -#include -#include -#ifdef __LINUX__ -#include -#include -#endif -#endif - -#include -#include -#include -#include -#include -#include -#include - -#include "../node/InetAddress.hpp" -#include "../node/Mutex.hpp" -#include "../node/Utils.hpp" - -#include "Phy.hpp" -#include "OSUtils.hpp" - -#if (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__)) -#define ZT_UDP_DESIRED_BUF_SIZE 1048576 -#else -#define ZT_UDP_DESIRED_BUF_SIZE 131072 -#endif - -// Period between refreshes of bindings -#define ZT_BINDER_REFRESH_PERIOD 30000 - -// Max number of bindings -#define ZT_BINDER_MAX_BINDINGS 256 - -namespace ZeroTier { - -/** - * Enumerates local devices and binds to all potential ZeroTier path endpoints - * - * This replaces binding to wildcard (0.0.0.0 and ::0) with explicit binding - * as part of the path to default gateway support. Under the hood it uses - * different queries on different OSes to enumerate devices, and also exposes - * device enumeration and endpoint IP data for use elsewhere. - * - * On OSes that do not support local port enumeration or where this is not - * meaningful, this degrades to binding to wildcard. - */ -class Binder -{ -private: - struct _Binding - { - _Binding() : udpSock((PhySocket *)0),tcpListenSock((PhySocket *)0) {} - PhySocket *udpSock; - PhySocket *tcpListenSock; - InetAddress address; - }; - -public: - Binder() : _bindingCount(0) {} - - /** - * Close all bound ports, should be called on shutdown - * - * @param phy Physical interface - */ - template - void closeAll(Phy &phy) - { - Mutex::Lock _l(_lock); - for(unsigned int b=0,c=_bindingCount;b template - * @tparam INTERFACE_CHECKER Type for class containing shouldBindInterface() method - */ - template - void refresh(Phy &phy,unsigned int *ports,unsigned int portCount,const std::vector explicitBind,INTERFACE_CHECKER &ifChecker) - { - std::map localIfAddrs; - PhySocket *udps,*tcps; - Mutex::Lock _l(_lock); - bool interfacesEnumerated = true; - - if (explicitBind.empty()) { -#ifdef __WINDOWS__ - - char aabuf[32768]; - ULONG aalen = sizeof(aabuf); - if (GetAdaptersAddresses(AF_UNSPEC,GAA_FLAG_SKIP_ANYCAST|GAA_FLAG_SKIP_MULTICAST|GAA_FLAG_SKIP_DNS_SERVER,(void *)0,reinterpret_cast(aabuf),&aalen) == NO_ERROR) { - PIP_ADAPTER_ADDRESSES a = reinterpret_cast(aabuf); - while (a) { - PIP_ADAPTER_UNICAST_ADDRESS ua = a->FirstUnicastAddress; - while (ua) { - InetAddress ip(ua->Address.lpSockaddr); - if (ifChecker.shouldBindInterface("",ip)) { - switch(ip.ipScope()) { - default: break; - case InetAddress::IP_SCOPE_PSEUDOPRIVATE: - case InetAddress::IP_SCOPE_GLOBAL: - case InetAddress::IP_SCOPE_SHARED: - case InetAddress::IP_SCOPE_PRIVATE: - for(int x=0;x<(int)portCount;++x) { - ip.setPort(ports[x]); - localIfAddrs.insert(std::pair(ip,std::string())); - } - break; - } - } - ua = ua->Next; - } - a = a->Next; - } - } - else { - interfacesEnumerated = false; - } - -#else // not __WINDOWS__ - - /* On Linux we use an alternative method if available since getifaddrs() - * gets very slow when there are lots of network namespaces. This won't - * work unless /proc/PID/net/if_inet6 exists and it may not on some - * embedded systems, so revert to getifaddrs() there. */ - -#ifdef __LINUX__ - char fn[256],tmp[256]; - std::set ifnames; - const unsigned long pid = (unsigned long)getpid(); - - // Get all device names - OSUtils::ztsnprintf(fn,sizeof(fn),"/proc/%lu/net/dev",pid); - FILE *procf = fopen(fn,"r"); - if (procf) { - while (fgets(tmp,sizeof(tmp),procf)) { - tmp[255] = 0; - char *saveptr = (char *)0; - for(char *f=Utils::stok(tmp," \t\r\n:|",&saveptr);(f);f=Utils::stok((char *)0," \t\r\n:|",&saveptr)) { - if ((strcmp(f,"Inter-") != 0)&&(strcmp(f,"face") != 0)&&(f[0] != 0)) - ifnames.insert(f); - break; // we only want the first field - } - } - fclose(procf); - } - else { - interfacesEnumerated = false; - } - - // Get IPv6 addresses (and any device names we don't already know) - OSUtils::ztsnprintf(fn,sizeof(fn),"/proc/%lu/net/if_inet6",pid); - procf = fopen(fn,"r"); - if (procf) { - while (fgets(tmp,sizeof(tmp),procf)) { - tmp[255] = 0; - char *saveptr = (char *)0; - unsigned char ipbits[16]; - memset(ipbits,0,sizeof(ipbits)); - char *devname = (char *)0; - int n = 0; - for(char *f=Utils::stok(tmp," \t\r\n",&saveptr);(f);f=Utils::stok((char *)0," \t\r\n",&saveptr)) { - switch(n++) { - case 0: // IP in hex - Utils::unhex(f,32,ipbits,16); - break; - case 5: // device name - devname = f; - break; - } - } - if (devname) { - ifnames.insert(devname); - InetAddress ip(ipbits,16,0); - if (ifChecker.shouldBindInterface(devname,ip)) { - switch(ip.ipScope()) { - default: break; - case InetAddress::IP_SCOPE_PSEUDOPRIVATE: - case InetAddress::IP_SCOPE_GLOBAL: - case InetAddress::IP_SCOPE_SHARED: - case InetAddress::IP_SCOPE_PRIVATE: - for(int x=0;x<(int)portCount;++x) { - ip.setPort(ports[x]); - localIfAddrs.insert(std::pair(ip,std::string(devname))); - } - break; - } - } - } - } - fclose(procf); - } - - // Get IPv4 addresses for each device - if (ifnames.size() > 0) { - const int controlfd = (int)socket(AF_INET,SOCK_DGRAM,0); - struct ifconf configuration; - configuration.ifc_len = 0; - configuration.ifc_buf = nullptr; - - if (controlfd < 0) goto ip4_address_error; - if (ioctl(controlfd, SIOCGIFCONF, &configuration) < 0) goto ip4_address_error; - configuration.ifc_buf = (char*)malloc(configuration.ifc_len); - if (ioctl(controlfd, SIOCGIFCONF, &configuration) < 0) goto ip4_address_error; - - for (int i=0; i < (int)(configuration.ifc_len / sizeof(ifreq)); i ++) { - struct ifreq& request = configuration.ifc_req[i]; - struct sockaddr* addr = &request.ifr_ifru.ifru_addr; - if (addr->sa_family != AF_INET) continue; - std::string ifname = request.ifr_ifrn.ifrn_name; - // name can either be just interface name or interface name followed by ':' and arbitrary label - if (ifname.find(':') != std::string::npos) - ifname = ifname.substr(0, ifname.find(':')); - - InetAddress ip(&(((struct sockaddr_in *)addr)->sin_addr),4,0); - if (ifChecker.shouldBindInterface(ifname.c_str(), ip)) { - switch(ip.ipScope()) { - default: break; - case InetAddress::IP_SCOPE_PSEUDOPRIVATE: - case InetAddress::IP_SCOPE_GLOBAL: - case InetAddress::IP_SCOPE_SHARED: - case InetAddress::IP_SCOPE_PRIVATE: - for(int x=0;x<(int)portCount;++x) { - ip.setPort(ports[x]); - localIfAddrs.insert(std::pair(ip,ifname)); - } - break; - } - } - } - - ip4_address_error: - free(configuration.ifc_buf); - if (controlfd > 0) close(controlfd); - } - - const bool gotViaProc = (localIfAddrs.size() > 0); -#else - const bool gotViaProc = false; -#endif -#if !defined(ZT_SDK) || !defined(__ANDROID__) // getifaddrs() freeifaddrs() not available on Android - if (!gotViaProc) { - struct ifaddrs *ifatbl = (struct ifaddrs *)0; - struct ifaddrs *ifa; - if ((getifaddrs(&ifatbl) == 0)&&(ifatbl)) { - ifa = ifatbl; - while (ifa) { - if ((ifa->ifa_name)&&(ifa->ifa_addr)) { - InetAddress ip = *(ifa->ifa_addr); - if (ifChecker.shouldBindInterface(ifa->ifa_name,ip)) { - switch(ip.ipScope()) { - default: break; - case InetAddress::IP_SCOPE_PSEUDOPRIVATE: - case InetAddress::IP_SCOPE_GLOBAL: - case InetAddress::IP_SCOPE_SHARED: - case InetAddress::IP_SCOPE_PRIVATE: - for(int x=0;x<(int)portCount;++x) { - ip.setPort(ports[x]); - localIfAddrs.insert(std::pair(ip,std::string(ifa->ifa_name))); - } - break; - } - } - } - ifa = ifa->ifa_next; - } - freeifaddrs(ifatbl); - } - else { - interfacesEnumerated = false; - } - } -#endif - -#endif - } else { - for(std::vector::const_iterator i(explicitBind.begin());i!=explicitBind.end();++i) - localIfAddrs.insert(std::pair(*i,std::string())); - } - - // Default to binding to wildcard if we can't enumerate addresses - if (!interfacesEnumerated && localIfAddrs.empty()) { - for(int x=0;x<(int)portCount;++x) { - localIfAddrs.insert(std::pair(InetAddress((uint32_t)0,ports[x]),std::string())); - localIfAddrs.insert(std::pair(InetAddress((const void *)"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",16,ports[x]),std::string())); - } - } - - const unsigned int oldBindingCount = _bindingCount; - _bindingCount = 0; - - // Save bindings that are still valid, close those that are not - for(unsigned int b=0;b::const_iterator ii(localIfAddrs.begin());ii!=localIfAddrs.end();++ii) { - unsigned int bi = 0; - while (bi != _bindingCount) { - if (_bindings[bi].address == ii->first) - break; - ++bi; - } - if (bi == _bindingCount) { - udps = phy.udpBind(reinterpret_cast(&(ii->first)),(void *)0,ZT_UDP_DESIRED_BUF_SIZE); - tcps = phy.tcpListen(reinterpret_cast(&(ii->first)),(void *)0); - if ((udps)&&(tcps)) { -#ifdef __LINUX__ - // Bind Linux sockets to their device so routes that we manage do not override physical routes (wish all platforms had this!) - if (ii->second.length() > 0) { - char tmp[256]; - Utils::scopy(tmp,sizeof(tmp),ii->second.c_str()); - int fd = (int)Phy::getDescriptor(udps); - if (fd >= 0) - setsockopt(fd,SOL_SOCKET,SO_BINDTODEVICE,tmp,strlen(tmp)); - fd = (int)Phy::getDescriptor(tcps); - if (fd >= 0) - setsockopt(fd,SOL_SOCKET,SO_BINDTODEVICE,tmp,strlen(tmp)); - } -#endif // __LINUX__ - if (_bindingCount < ZT_BINDER_MAX_BINDINGS) { - _bindings[_bindingCount].udpSock = udps; - _bindings[_bindingCount].tcpListenSock = tcps; - _bindings[_bindingCount].address = ii->first; - phy.setIfName(udps,(char*)ii->second.c_str(),(int)ii->second.length()); - ++_bindingCount; - } - } else { - phy.close(udps,false); - phy.close(tcps,false); - } - } - } - } - - /** - * @return All currently bound local interface addresses - */ - inline std::vector allBoundLocalInterfaceAddresses() const - { - std::vector aa; - Mutex::Lock _l(_lock); - for(unsigned int b=0,c=_bindingCount;b - inline bool udpSendAll(Phy &phy,const struct sockaddr_storage *addr,const void *data,unsigned int len,unsigned int ttl) - { - bool r = false; - Mutex::Lock _l(_lock); - for(unsigned int b=0,c=_bindingCount;b _bindingCount; - Mutex _lock; -}; - -} // namespace ZeroTier - -#endif diff --git a/attic/Http.cpp b/attic/Http.cpp deleted file mode 100644 index 9ff1a0689..000000000 --- a/attic/Http.cpp +++ /dev/null @@ -1,287 +0,0 @@ -/* - * Copyright (c)2019 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2023-01-01 - * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. - */ -/****/ - -#include -#include -#include - -#include "Http.hpp" -#include "Phy.hpp" -#include "OSUtils.hpp" -#include "../node/Constants.hpp" -#include "../node/Utils.hpp" - -#ifdef ZT_USE_SYSTEM_HTTP_PARSER -#include -#else -#include "../ext/http-parser/http_parser.h" -#endif - -namespace ZeroTier { - -namespace { - -static int ShttpOnMessageBegin(http_parser *parser); -static int ShttpOnUrl(http_parser *parser,const char *ptr,size_t length); -#if (HTTP_PARSER_VERSION_MAJOR >= 2) && (HTTP_PARSER_VERSION_MINOR >= 2) -static int ShttpOnStatus(http_parser *parser,const char *ptr,size_t length); -#else -static int ShttpOnStatus(http_parser *parser); -#endif -static int ShttpOnHeaderField(http_parser *parser,const char *ptr,size_t length); -static int ShttpOnValue(http_parser *parser,const char *ptr,size_t length); -static int ShttpOnHeadersComplete(http_parser *parser); -static int ShttpOnBody(http_parser *parser,const char *ptr,size_t length); -static int ShttpOnMessageComplete(http_parser *parser); - -#if (HTTP_PARSER_VERSION_MAJOR >= 2) && (HTTP_PARSER_VERSION_MINOR >= 1) -static const struct http_parser_settings HTTP_PARSER_SETTINGS = { - ShttpOnMessageBegin, - ShttpOnUrl, - ShttpOnStatus, - ShttpOnHeaderField, - ShttpOnValue, - ShttpOnHeadersComplete, - ShttpOnBody, - ShttpOnMessageComplete -}; -#else -static const struct http_parser_settings HTTP_PARSER_SETTINGS = { - ShttpOnMessageBegin, - ShttpOnUrl, - ShttpOnHeaderField, - ShttpOnValue, - ShttpOnHeadersComplete, - ShttpOnBody, - ShttpOnMessageComplete -}; -#endif - -struct HttpPhyHandler -{ - // not used - inline void phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *localAddr,const struct sockaddr *from,void *data,unsigned long len) {} - inline void phyOnTcpAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN,const struct sockaddr *from) {} - - inline void phyOnTcpConnect(PhySocket *sock,void **uptr,bool success) - { - if (success) { - phy->setNotifyWritable(sock,true); - } else { - *responseBody = "connection failed"; - error = true; - done = true; - } - } - - inline void phyOnTcpClose(PhySocket *sock,void **uptr) - { - done = true; - } - - inline void phyOnTcpData(PhySocket *sock,void **uptr,void *data,unsigned long len) - { - lastActivity = OSUtils::now(); - http_parser_execute(&parser,&HTTP_PARSER_SETTINGS,(const char *)data,len); - if ((parser.upgrade)||(parser.http_errno != HPE_OK)) - phy->close(sock); - } - - inline void phyOnTcpWritable(PhySocket *sock,void **uptr) - { - if (writePtr < (unsigned long)writeBuf.length()) { - long n = phy->streamSend(sock,writeBuf.data() + writePtr,(unsigned long)writeBuf.length() - writePtr,true); - if (n > 0) - writePtr += n; - } - if (writePtr >= (unsigned long)writeBuf.length()) - phy->setNotifyWritable(sock,false); - } - - inline void phyOnFileDescriptorActivity(PhySocket *sock,void **uptr,bool readable,bool writable) {} -#ifdef __UNIX_LIKE__ - 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) {} -#endif // __UNIX_LIKE__ - - http_parser parser; - std::string currentHeaderField; - std::string currentHeaderValue; - unsigned long messageSize; - unsigned long writePtr; - uint64_t lastActivity; - std::string writeBuf; - - unsigned long maxResponseSize; - std::map *responseHeaders; - std::string *responseBody; - bool error; - bool done; - - Phy *phy; - PhySocket *sock; -}; - -static int ShttpOnMessageBegin(http_parser *parser) -{ - return 0; -} -static int ShttpOnUrl(http_parser *parser,const char *ptr,size_t length) -{ - return 0; -} -#if (HTTP_PARSER_VERSION_MAJOR >= 2) && (HTTP_PARSER_VERSION_MINOR >= 2) -static int ShttpOnStatus(http_parser *parser,const char *ptr,size_t length) -#else -static int ShttpOnStatus(http_parser *parser) -#endif -{ - /* - HttpPhyHandler *hh = reinterpret_cast(parser->data); - hh->messageSize += (unsigned long)length; - if (hh->messageSize > hh->maxResponseSize) - return -1; - */ - return 0; -} -static int ShttpOnHeaderField(http_parser *parser,const char *ptr,size_t length) -{ - HttpPhyHandler *hh = reinterpret_cast(parser->data); - hh->messageSize += (unsigned long)length; - if (hh->messageSize > hh->maxResponseSize) - return -1; - if ((hh->currentHeaderField.length())&&(hh->currentHeaderValue.length())) { - (*hh->responseHeaders)[hh->currentHeaderField] = hh->currentHeaderValue; - hh->currentHeaderField = ""; - hh->currentHeaderValue = ""; - } - for(size_t i=0;icurrentHeaderField.push_back(OSUtils::toLower(ptr[i])); - return 0; -} -static int ShttpOnValue(http_parser *parser,const char *ptr,size_t length) -{ - HttpPhyHandler *hh = reinterpret_cast(parser->data); - hh->messageSize += (unsigned long)length; - if (hh->messageSize > hh->maxResponseSize) - return -1; - hh->currentHeaderValue.append(ptr,length); - return 0; -} -static int ShttpOnHeadersComplete(http_parser *parser) -{ - HttpPhyHandler *hh = reinterpret_cast(parser->data); - if ((hh->currentHeaderField.length())&&(hh->currentHeaderValue.length())) - (*hh->responseHeaders)[hh->currentHeaderField] = hh->currentHeaderValue; - return 0; -} -static int ShttpOnBody(http_parser *parser,const char *ptr,size_t length) -{ - HttpPhyHandler *hh = reinterpret_cast(parser->data); - hh->messageSize += (unsigned long)length; - if (hh->messageSize > hh->maxResponseSize) - return -1; - hh->responseBody->append(ptr,length); - return 0; -} -static int ShttpOnMessageComplete(http_parser *parser) -{ - HttpPhyHandler *hh = reinterpret_cast(parser->data); - hh->phy->close(hh->sock); - return 0; -} - -} // anonymous namespace - -unsigned int Http::_do( - const char *method, - unsigned long maxResponseSize, - unsigned long timeout, - const struct sockaddr *remoteAddress, - const char *path, - const std::map &requestHeaders, - const void *requestBody, - unsigned long requestBodyLength, - std::map &responseHeaders, - std::string &responseBody) -{ - try { - responseHeaders.clear(); - responseBody = ""; - - HttpPhyHandler handler; - - http_parser_init(&(handler.parser),HTTP_RESPONSE); - handler.parser.data = (void *)&handler; - handler.messageSize = 0; - handler.writePtr = 0; - handler.lastActivity = OSUtils::now(); - - try { - char tmp[1024]; - OSUtils::ztsnprintf(tmp,sizeof(tmp),"%s %s HTTP/1.1\r\n",method,path); - handler.writeBuf.append(tmp); - for(std::map::const_iterator h(requestHeaders.begin());h!=requestHeaders.end();++h) { - OSUtils::ztsnprintf(tmp,sizeof(tmp),"%s: %s\r\n",h->first.c_str(),h->second.c_str()); - handler.writeBuf.append(tmp); - } - handler.writeBuf.append("\r\n"); - if ((requestBody)&&(requestBodyLength)) - handler.writeBuf.append((const char *)requestBody,requestBodyLength); - } catch ( ... ) { - responseBody = "request too large"; - return 0; - } - - if (maxResponseSize) { - handler.maxResponseSize = maxResponseSize; - } else { - handler.maxResponseSize = 2147483647; - } - handler.responseHeaders = &responseHeaders; - handler.responseBody = &responseBody; - handler.error = false; - handler.done = false; - - Phy phy(&handler,true,true); - - bool instantConnect = false; - handler.phy = &phy; - handler.sock = phy.tcpConnect((const struct sockaddr *)remoteAddress,instantConnect,(void *)0,true); - if (!handler.sock) { - responseBody = "connection failed (2)"; - return 0; - } - - while (!handler.done) { - phy.poll(timeout / 2); - if ((timeout)&&((unsigned long)(OSUtils::now() - handler.lastActivity) > timeout)) { - phy.close(handler.sock); - responseBody = "timed out"; - return 0; - } - } - - return ((handler.error) ? 0 : ((handler.parser.http_errno != HPE_OK) ? 0 : handler.parser.status_code)); - } catch (std::exception &exc) { - responseBody = exc.what(); - return 0; - } catch ( ... ) { - responseBody = "unknown exception"; - return 0; - } -} - -} // namespace ZeroTier diff --git a/attic/Http.hpp b/attic/Http.hpp deleted file mode 100644 index d9cad4dc1..000000000 --- a/attic/Http.hpp +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (c)2019 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2023-01-01 - * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. - */ -/****/ - -#ifndef ZT_HTTP_HPP -#define ZT_HTTP_HPP - -#include -#include -#include - -#if defined(_WIN32) || defined(_WIN64) -#include -#include -#include -#else -#include -#include -#include -#include -#include -#include -#endif - -namespace ZeroTier { - -/** - * Simple synchronous HTTP client used for updater and cli - */ -class Http -{ -public: - /** - * Make HTTP GET request - * - * The caller must set all headers, including Host. - * - * @return HTTP status code or 0 on error (responseBody will contain error message) - */ - static inline unsigned int GET( - unsigned long maxResponseSize, - unsigned long timeout, - const struct sockaddr *remoteAddress, - const char *path, - const std::map &requestHeaders, - std::map &responseHeaders, - std::string &responseBody) - { - return _do( - "GET", - maxResponseSize, - timeout, - remoteAddress, - path, - requestHeaders, - (const void *)0, - 0, - responseHeaders, - responseBody); - } - - /** - * Make HTTP DELETE request - * - * The caller must set all headers, including Host. - * - * @return HTTP status code or 0 on error (responseBody will contain error message) - */ - static inline unsigned int DEL( - unsigned long maxResponseSize, - unsigned long timeout, - const struct sockaddr *remoteAddress, - const char *path, - const std::map &requestHeaders, - std::map &responseHeaders, - std::string &responseBody) - { - return _do( - "DELETE", - maxResponseSize, - timeout, - remoteAddress, - path, - requestHeaders, - (const void *)0, - 0, - responseHeaders, - responseBody); - } - - /** - * Make HTTP POST request - * - * It is the responsibility of the caller to set all headers. With POST, the - * Content-Length and Content-Type headers must be set or the POST will not - * work. - * - * @return HTTP status code or 0 on error (responseBody will contain error message) - */ - static inline unsigned int POST( - unsigned long maxResponseSize, - unsigned long timeout, - const struct sockaddr *remoteAddress, - const char *path, - const std::map &requestHeaders, - const void *postData, - unsigned long postDataLength, - std::map &responseHeaders, - std::string &responseBody) - { - return _do( - "POST", - maxResponseSize, - timeout, - remoteAddress, - path, - requestHeaders, - postData, - postDataLength, - responseHeaders, - responseBody); - } - - /** - * Make HTTP PUT request - * - * It is the responsibility of the caller to set all headers. With PUT, the - * Content-Length and Content-Type headers must be set or the PUT will not - * work. - * - * @return HTTP status code or 0 on error (responseBody will contain error message) - */ - static inline unsigned int PUT( - unsigned long maxResponseSize, - unsigned long timeout, - const struct sockaddr *remoteAddress, - const char *path, - const std::map &requestHeaders, - const void *postData, - unsigned long postDataLength, - std::map &responseHeaders, - std::string &responseBody) - { - return _do( - "PUT", - maxResponseSize, - timeout, - remoteAddress, - path, - requestHeaders, - postData, - postDataLength, - responseHeaders, - responseBody); - } - -private: - static unsigned int _do( - const char *method, - unsigned long maxResponseSize, - unsigned long timeout, - const struct sockaddr *remoteAddress, - const char *path, - const std::map &requestHeaders, - const void *requestBody, - unsigned long requestBodyLength, - std::map &responseHeaders, - std::string &responseBody); -}; - -} // namespace ZeroTier - -#endif diff --git a/attic/Phy.hpp b/attic/Phy.hpp deleted file mode 100644 index d4934edf3..000000000 --- a/attic/Phy.hpp +++ /dev/null @@ -1,1177 +0,0 @@ -/* - * Copyright (c)2019 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2023-01-01 - * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. - */ -/****/ - -#ifndef ZT_PHY_HPP -#define ZT_PHY_HPP - -#include -#include -#include - -#include -#include - -#if defined(_WIN32) || defined(_WIN64) - -#include -#include -#include - -#define ZT_PHY_SOCKFD_TYPE SOCKET -#define ZT_PHY_SOCKFD_NULL (INVALID_SOCKET) -#define ZT_PHY_SOCKFD_VALID(s) ((s) != INVALID_SOCKET) -#define ZT_PHY_CLOSE_SOCKET(s) ::closesocket(s) -#define ZT_PHY_MAX_SOCKETS (FD_SETSIZE) -#define ZT_PHY_MAX_INTERCEPTS ZT_PHY_MAX_SOCKETS -#define ZT_PHY_SOCKADDR_STORAGE_TYPE struct sockaddr_storage - -#else // not Windows - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(__linux__) || defined(linux) || defined(__LINUX__) || defined(__linux) -#ifndef IPV6_DONTFRAG -#define IPV6_DONTFRAG 62 -#endif -#endif - -#define ZT_PHY_SOCKFD_TYPE int -#define ZT_PHY_SOCKFD_NULL (-1) -#define ZT_PHY_SOCKFD_VALID(s) ((s) > -1) -#define ZT_PHY_CLOSE_SOCKET(s) ::close(s) -#define ZT_PHY_MAX_SOCKETS (FD_SETSIZE) -#define ZT_PHY_MAX_INTERCEPTS ZT_PHY_MAX_SOCKETS -#define ZT_PHY_SOCKADDR_STORAGE_TYPE struct sockaddr_storage - -#endif // Windows or not - -namespace ZeroTier { - -/** - * Opaque socket type - */ -typedef void PhySocket; - -/** - * Simple templated non-blocking sockets implementation - * - * Yes there is boost::asio and libuv, but I like small binaries and I hate - * build dependencies. Both drag in a whole bunch of pasta with them. - * - * This class is templated on a pointer to a handler class which must - * implement the following functions: - * - * For all platforms: - * - * phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *localAddr,const struct sockaddr *from,void *data,unsigned long len) - * phyOnTcpConnect(PhySocket *sock,void **uptr,bool success) - * phyOnTcpAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN,const struct sockaddr *from) - * phyOnTcpClose(PhySocket *sock,void **uptr) - * phyOnTcpData(PhySocket *sock,void **uptr,void *data,unsigned long len) - * phyOnTcpWritable(PhySocket *sock,void **uptr) - * phyOnFileDescriptorActivity(PhySocket *sock,void **uptr,bool readable,bool writable) - * - * On Linux/OSX/Unix only (not required/used on Windows or elsewhere): - * - * phyOnUnixAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN) - * phyOnUnixClose(PhySocket *sock,void **uptr) - * phyOnUnixData(PhySocket *sock,void **uptr,void *data,unsigned long len) - * phyOnUnixWritable(PhySocket *sock,void **uptr) - * - * These templates typically refer to function objects. Templates are used to - * avoid the call overhead of indirection, which is surprisingly high for high - * bandwidth applications pushing a lot of packets. - * - * The 'sock' pointer above is an opaque pointer to a socket. Each socket - * has a 'uptr' user-settable/modifiable pointer associated with it, which - * can be set on bind/connect calls and is passed as a void ** to permit - * resetting at any time. The ACCEPT handler takes two sets of sock and - * uptr: sockL and uptrL for the listen socket, and sockN and uptrN for - * the new TCP connection socket that has just been created. - * - * Handlers are always called. On outgoing TCP connection, CONNECT is always - * called on either success or failure followed by DATA and/or WRITABLE as - * indicated. On socket close, handlers are called unless close() is told - * explicitly not to call handlers. It is safe to close a socket within a - * handler, and in that case close() can be told not to call handlers to - * prevent recursion. - * - * This isn't thread-safe with the exception of whack(), which is safe to - * call from another thread to abort poll(). - */ -template -class Phy -{ -private: - HANDLER_PTR_TYPE _handler; - - enum PhySocketType - { - ZT_PHY_SOCKET_CLOSED = 0x00, // socket is closed, will be removed on next poll() - ZT_PHY_SOCKET_TCP_OUT_PENDING = 0x01, - ZT_PHY_SOCKET_TCP_OUT_CONNECTED = 0x02, - ZT_PHY_SOCKET_TCP_IN = 0x03, - ZT_PHY_SOCKET_TCP_LISTEN = 0x04, - ZT_PHY_SOCKET_UDP = 0x05, - ZT_PHY_SOCKET_FD = 0x06, - ZT_PHY_SOCKET_UNIX_IN = 0x07, - ZT_PHY_SOCKET_UNIX_LISTEN = 0x08 - }; - - struct PhySocketImpl { - PhySocketImpl() { memset(ifname, 0, sizeof(ifname)); } - PhySocketType type; - ZT_PHY_SOCKFD_TYPE sock; - void *uptr; // user-settable pointer - ZT_PHY_SOCKADDR_STORAGE_TYPE saddr; // remote for TCP_OUT and TCP_IN, local for TCP_LISTEN, RAW, and UDP - char ifname[16]; - }; - - std::list _socks; - fd_set _readfds; - fd_set _writefds; -#if defined(_WIN32) || defined(_WIN64) - fd_set _exceptfds; -#endif - long _nfds; - - ZT_PHY_SOCKFD_TYPE _whackReceiveSocket; - ZT_PHY_SOCKFD_TYPE _whackSendSocket; - - bool _noDelay; - bool _noCheck; - -public: - /** - * @param handler Pointer of type HANDLER_PTR_TYPE to handler - * @param noDelay If true, disable TCP NAGLE algorithm on TCP sockets - * @param noCheck If true, attempt to set UDP SO_NO_CHECK option to disable sending checksums - */ - Phy(HANDLER_PTR_TYPE handler,bool noDelay,bool noCheck) : - _handler(handler) - { - FD_ZERO(&_readfds); - FD_ZERO(&_writefds); - -#if defined(_WIN32) || defined(_WIN64) - FD_ZERO(&_exceptfds); - - SOCKET pipes[2]; - { // hack copied from StackOverflow, behaves a bit like pipe() on *nix systems - struct sockaddr_in inaddr; - struct sockaddr addr; - SOCKET lst=::socket(AF_INET, SOCK_STREAM,IPPROTO_TCP); - if (lst == INVALID_SOCKET) - throw std::runtime_error("unable to create pipes for select() abort"); - memset(&inaddr, 0, sizeof(inaddr)); - memset(&addr, 0, sizeof(addr)); - inaddr.sin_family = AF_INET; - inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - inaddr.sin_port = 0; - int yes=1; - setsockopt(lst,SOL_SOCKET,SO_REUSEADDR,(char*)&yes,sizeof(yes)); - bind(lst,(struct sockaddr *)&inaddr,sizeof(inaddr)); - listen(lst,1); - int len=sizeof(inaddr); - getsockname(lst, &addr,&len); - pipes[0]=::socket(AF_INET, SOCK_STREAM,0); - if (pipes[0] == INVALID_SOCKET) - throw std::runtime_error("unable to create pipes for select() abort"); - connect(pipes[0],&addr,len); - pipes[1]=accept(lst,0,0); - closesocket(lst); - } -#else // not Windows - int pipes[2]; - if (::pipe(pipes)) - throw std::runtime_error("unable to create pipes for select() abort"); -#endif // Windows or not - - _nfds = (pipes[0] > pipes[1]) ? (long)pipes[0] : (long)pipes[1]; - _whackReceiveSocket = pipes[0]; - _whackSendSocket = pipes[1]; - _noDelay = noDelay; - _noCheck = noCheck; - } - - ~Phy() - { - for(typename std::list::const_iterator s(_socks.begin());s!=_socks.end();++s) { - if (s->type != ZT_PHY_SOCKET_CLOSED) - this->close((PhySocket *)&(*s),true); - } - ZT_PHY_CLOSE_SOCKET(_whackReceiveSocket); - ZT_PHY_CLOSE_SOCKET(_whackSendSocket); - } - - /** - * @param s Socket object - * @return Underlying OS-type (usually int or long) file descriptor associated with object - */ - static inline ZT_PHY_SOCKFD_TYPE getDescriptor(PhySocket *s) throw() { return reinterpret_cast(s)->sock; } - - /** - * @param s Socket object - * @return Pointer to user object - */ - static inline void** getuptr(PhySocket *s) throw() { return &(reinterpret_cast(s)->uptr); } - - /** - * @param s Socket object - * @param nameBuf Buffer to store name of interface which this Socket object is bound to - * @param buflen Length of buffer to copy name into - */ - static inline void getIfName(PhySocket *s, char *nameBuf, int buflen) - { - if (s) { - memcpy(nameBuf, reinterpret_cast(s)->ifname, buflen); - } - } - - /** - * @param s Socket object - * @param ifname Buffer containing name of interface that this Socket object is bound to - * @param len Length of name of interface - */ - static inline void setIfName(PhySocket *s, char *ifname, int len) - { - if (s) { - memcpy(&(reinterpret_cast(s)->ifname), ifname, len); - } - } - - /** - * Whether or not the socket object is in a closed state - * - * @param s Socket object - * @return true if socket is closed, false if otherwise - */ - inline bool isClosed(PhySocket *s) - { - PhySocketImpl *sws = (reinterpret_cast(s)); - return sws->type == ZT_PHY_SOCKET_CLOSED; - } - - /** - * Get state of socket object - * - * @param s Socket object - * @return State of socket - */ - inline int getState(PhySocket *s) - { - PhySocketImpl *sws = (reinterpret_cast(s)); - return sws->type; - } - - /** - * In the event that this socket is erased, we need a way to convey to the multipath logic - * that this path is no longer valid. - * - * @param s Socket object - * @return Whether the state of this socket is within an acceptable range of values - */ - inline bool isValidState(PhySocket *s) - { - if (s) { - PhySocketImpl *sws = (reinterpret_cast(s)); - return sws->type >= ZT_PHY_SOCKET_CLOSED && sws->type <= ZT_PHY_SOCKET_UNIX_LISTEN; - } - return false; - } - - /** - * Cause poll() to stop waiting immediately - * - * This can be used to reset the polling loop after changes that require - * attention, or to shut down a background thread that is waiting, etc. - */ - inline void whack() - { -#if defined(_WIN32) || defined(_WIN64) - ::send(_whackSendSocket,(const char *)this,1,0); -#else - (void)(::write(_whackSendSocket,(PhySocket *)this,1)); -#endif - } - - /** - * @return Number of open sockets - */ - inline unsigned long count() const throw() { return _socks.size(); } - - /** - * @return Maximum number of sockets allowed - */ - inline unsigned long maxCount() const throw() { return ZT_PHY_MAX_SOCKETS; } - - /** - * Wrap a raw file descriptor in a PhySocket structure - * - * This can be used to select/poll on a raw file descriptor as part of this - * class's I/O loop. By default the fd is set for read notification but - * this can be controlled with setNotifyReadable(). When any detected - * condition is present, the phyOnFileDescriptorActivity() callback is - * called with one or both of its arguments 'true'. - * - * The Phy<>::close() method *must* be called when you're done with this - * file descriptor to remove it from the select/poll set, but unlike other - * types of sockets Phy<> does not actually close the underlying fd or - * otherwise manage its life cycle. There is also no close notification - * callback for this fd, since Phy<> doesn't actually perform reading or - * writing or detect error conditions. This is only useful for adding a - * file descriptor to Phy<> to select/poll on it. - * - * @param fd Raw file descriptor - * @param uptr User pointer to supply to callbacks - * @return PhySocket wrapping fd or NULL on failure (out of memory or too many sockets) - */ - inline PhySocket *wrapSocket(ZT_PHY_SOCKFD_TYPE fd,void *uptr = (void *)0) - { - if (_socks.size() >= ZT_PHY_MAX_SOCKETS) - return (PhySocket *)0; - try { - _socks.push_back(PhySocketImpl()); - } catch ( ... ) { - return (PhySocket *)0; - } - PhySocketImpl &sws = _socks.back(); - if ((long)fd > _nfds) - _nfds = (long)fd; - FD_SET(fd,&_readfds); - sws.type = ZT_PHY_SOCKET_UNIX_IN; /* TODO: Type was changed to allow for CBs with new RPC model */ - sws.sock = fd; - sws.uptr = uptr; - memset(&(sws.saddr),0,sizeof(struct sockaddr_storage)); - // no sockaddr for this socket type, leave saddr null - return (PhySocket *)&sws; - } - - /** - * Bind a UDP socket - * - * @param localAddress Local endpoint address and port - * @param uptr Initial value of user pointer associated with this socket (default: NULL) - * @param bufferSize Desired socket receive/send buffer size -- will set as close to this as possible (default: 0, leave alone) - * @return Socket or NULL on failure to bind - */ - inline PhySocket *udpBind(const struct sockaddr *localAddress,void *uptr = (void *)0,int bufferSize = 0) - { - if (_socks.size() >= ZT_PHY_MAX_SOCKETS) - return (PhySocket *)0; - - ZT_PHY_SOCKFD_TYPE s = ::socket(localAddress->sa_family,SOCK_DGRAM,0); - if (!ZT_PHY_SOCKFD_VALID(s)) - return (PhySocket *)0; - - if (bufferSize > 0) { - int bs = bufferSize; - while (bs >= 65536) { - int tmpbs = bs; - if (setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char *)&tmpbs,sizeof(tmpbs)) == 0) - break; - bs -= 16384; - } - bs = bufferSize; - while (bs >= 65536) { - int tmpbs = bs; - if (setsockopt(s,SOL_SOCKET,SO_SNDBUF,(const char *)&tmpbs,sizeof(tmpbs)) == 0) - break; - bs -= 16384; - } - } - -#if defined(_WIN32) || defined(_WIN64) - { - BOOL f; - if (localAddress->sa_family == AF_INET6) { - f = TRUE; setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,(const char *)&f,sizeof(f)); - f = FALSE; setsockopt(s,IPPROTO_IPV6,IPV6_DONTFRAG,(const char *)&f,sizeof(f)); - } - f = FALSE; setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(const char *)&f,sizeof(f)); - f = TRUE; setsockopt(s,SOL_SOCKET,SO_BROADCAST,(const char *)&f,sizeof(f)); - } -#else // not Windows - { - int f; - if (localAddress->sa_family == AF_INET6) { - f = 1; setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,(void *)&f,sizeof(f)); -#ifdef IPV6_MTU_DISCOVER - f = 0; setsockopt(s,IPPROTO_IPV6,IPV6_MTU_DISCOVER,&f,sizeof(f)); -#endif -#ifdef IPV6_DONTFRAG - f = 0; setsockopt(s,IPPROTO_IPV6,IPV6_DONTFRAG,&f,sizeof(f)); -#endif - } - f = 0; setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(void *)&f,sizeof(f)); - f = 1; setsockopt(s,SOL_SOCKET,SO_BROADCAST,(void *)&f,sizeof(f)); -#ifdef IP_DONTFRAG - f = 0; setsockopt(s,IPPROTO_IP,IP_DONTFRAG,&f,sizeof(f)); -#endif -#ifdef IP_MTU_DISCOVER - f = 0; setsockopt(s,IPPROTO_IP,IP_MTU_DISCOVER,&f,sizeof(f)); -#endif -#ifdef SO_NO_CHECK - // For now at least we only set SO_NO_CHECK on IPv4 sockets since some - // IPv6 stacks incorrectly discard zero checksum packets. May remove - // this restriction later once broken stuff dies more. - if ((localAddress->sa_family == AF_INET)&&(_noCheck)) { - f = 1; setsockopt(s,SOL_SOCKET,SO_NO_CHECK,(void *)&f,sizeof(f)); - } -#endif - } -#endif // Windows or not - - if (::bind(s,localAddress,(localAddress->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in))) { - ZT_PHY_CLOSE_SOCKET(s); - return (PhySocket *)0; - } - -#if defined(_WIN32) || defined(_WIN64) - { u_long iMode=1; ioctlsocket(s,FIONBIO,&iMode); } -#else - fcntl(s,F_SETFL,O_NONBLOCK); -#endif - - try { - _socks.push_back(PhySocketImpl()); - } catch ( ... ) { - ZT_PHY_CLOSE_SOCKET(s); - return (PhySocket *)0; - } - PhySocketImpl &sws = _socks.back(); - - if ((long)s > _nfds) - _nfds = (long)s; - FD_SET(s,&_readfds); - sws.type = ZT_PHY_SOCKET_UDP; - sws.sock = s; - sws.uptr = uptr; - memset(&(sws.saddr),0,sizeof(struct sockaddr_storage)); - memcpy(&(sws.saddr),localAddress,(localAddress->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)); - - return (PhySocket *)&sws; - } - - /** - * Set the IP TTL for the next outgoing packet (for IPv4 UDP sockets only) - * - * @param ttl New TTL (0 or >255 will set it to 255) - * @return True on success - */ - inline bool setIp4UdpTtl(PhySocket *sock,unsigned int ttl) - { - PhySocketImpl &sws = *(reinterpret_cast(sock)); -#if defined(_WIN32) || defined(_WIN64) - DWORD tmp = ((ttl == 0)||(ttl > 255)) ? 255 : (DWORD)ttl; - return (::setsockopt(sws.sock,IPPROTO_IP,IP_TTL,(const char *)&tmp,sizeof(tmp)) == 0); -#else - int tmp = ((ttl == 0)||(ttl > 255)) ? 255 : (int)ttl; - return (::setsockopt(sws.sock,IPPROTO_IP,IP_TTL,(void *)&tmp,sizeof(tmp)) == 0); -#endif - } - - /** - * Send a UDP packet - * - * @param sock UDP socket - * @param remoteAddress Destination address (must be correct type for socket) - * @param data Data to send - * @param len Length of packet - * @return True if packet appears to have been sent successfully - */ - inline bool udpSend(PhySocket *sock,const struct sockaddr *remoteAddress,const void *data,unsigned long len) - { - PhySocketImpl &sws = *(reinterpret_cast(sock)); -#if defined(_WIN32) || defined(_WIN64) - return ((long)::sendto(sws.sock,reinterpret_cast(data),len,0,remoteAddress,(remoteAddress->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) == (long)len); -#else - return ((long)::sendto(sws.sock,data,len,0,remoteAddress,(remoteAddress->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) == (long)len); -#endif - } - -#ifdef __UNIX_LIKE__ - /** - * Listen for connections on a Unix domain socket - * - * @param path Path to Unix domain socket - * @param uptr Arbitrary pointer to associate - * @return PhySocket or NULL if cannot bind - */ - inline PhySocket *unixListen(const char *path,void *uptr = (void *)0) - { - struct sockaddr_un sun; - - if (_socks.size() >= ZT_PHY_MAX_SOCKETS) - return (PhySocket *)0; - - memset(&sun,0,sizeof(sun)); - sun.sun_family = AF_UNIX; - if (strlen(path) >= sizeof(sun.sun_path)) - return (PhySocket *)0; - strcpy(sun.sun_path,path); - - ZT_PHY_SOCKFD_TYPE s = ::socket(PF_UNIX,SOCK_STREAM,0); - if (!ZT_PHY_SOCKFD_VALID(s)) - return (PhySocket *)0; - - ::fcntl(s,F_SETFL,O_NONBLOCK); - - ::unlink(path); - if (::bind(s,(struct sockaddr *)&sun,sizeof(struct sockaddr_un)) != 0) { - ZT_PHY_CLOSE_SOCKET(s); - return (PhySocket *)0; - } - if (::listen(s,128) != 0) { - ZT_PHY_CLOSE_SOCKET(s); - return (PhySocket *)0; - } - - try { - _socks.push_back(PhySocketImpl()); - } catch ( ... ) { - ZT_PHY_CLOSE_SOCKET(s); - return (PhySocket *)0; - } - PhySocketImpl &sws = _socks.back(); - - if ((long)s > _nfds) - _nfds = (long)s; - FD_SET(s,&_readfds); - sws.type = ZT_PHY_SOCKET_UNIX_LISTEN; - sws.sock = s; - sws.uptr = uptr; - memset(&(sws.saddr),0,sizeof(struct sockaddr_storage)); - memcpy(&(sws.saddr),&sun,sizeof(struct sockaddr_un)); - - return (PhySocket *)&sws; - } -#endif // __UNIX_LIKE__ - - /** - * Bind a local listen socket to listen for new TCP connections - * - * @param localAddress Local address and port - * @param uptr Initial value of uptr for new socket (default: NULL) - * @return Socket or NULL on failure to bind - */ - inline PhySocket *tcpListen(const struct sockaddr *localAddress,void *uptr = (void *)0) - { - if (_socks.size() >= ZT_PHY_MAX_SOCKETS) - return (PhySocket *)0; - - ZT_PHY_SOCKFD_TYPE s = ::socket(localAddress->sa_family,SOCK_STREAM,0); - if (!ZT_PHY_SOCKFD_VALID(s)) - return (PhySocket *)0; - -#if defined(_WIN32) || defined(_WIN64) - { - BOOL f; - f = TRUE; ::setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,(const char *)&f,sizeof(f)); - f = TRUE; ::setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(const char *)&f,sizeof(f)); - f = (_noDelay ? TRUE : FALSE); setsockopt(s,IPPROTO_TCP,TCP_NODELAY,(char *)&f,sizeof(f)); - u_long iMode=1; - ioctlsocket(s,FIONBIO,&iMode); - } -#else - { - int f; - f = 1; ::setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,(void *)&f,sizeof(f)); - f = 1; ::setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(void *)&f,sizeof(f)); - f = (_noDelay ? 1 : 0); setsockopt(s,IPPROTO_TCP,TCP_NODELAY,(char *)&f,sizeof(f)); - fcntl(s,F_SETFL,O_NONBLOCK); - } -#endif - - if (::bind(s,localAddress,(localAddress->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in))) { - ZT_PHY_CLOSE_SOCKET(s); - return (PhySocket *)0; - } - - if (::listen(s,1024)) { - ZT_PHY_CLOSE_SOCKET(s); - return (PhySocket *)0; - } - - try { - _socks.push_back(PhySocketImpl()); - } catch ( ... ) { - ZT_PHY_CLOSE_SOCKET(s); - return (PhySocket *)0; - } - PhySocketImpl &sws = _socks.back(); - - if ((long)s > _nfds) - _nfds = (long)s; - FD_SET(s,&_readfds); - sws.type = ZT_PHY_SOCKET_TCP_LISTEN; - sws.sock = s; - sws.uptr = uptr; - memset(&(sws.saddr),0,sizeof(struct sockaddr_storage)); - memcpy(&(sws.saddr),localAddress,(localAddress->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)); - - return (PhySocket *)&sws; - } - - /** - * Start a non-blocking connect; CONNECT handler is called on success or failure - * - * A return value of NULL indicates a synchronous failure such as a - * failure to open a socket. The TCP connection handler is not called - * in this case. - * - * It is possible on some platforms for an "instant connect" to occur, - * such as when connecting to a loopback address. In this case, the - * 'connected' result parameter will be set to 'true' and if the - * 'callConnectHandler' flag is true (the default) the TCP connect - * handler will be called before the function returns. - * - * These semantics can be a bit confusing, but they're less so than - * the underlying semantics of asynchronous TCP connect. - * - * @param remoteAddress Remote address - * @param connected Result parameter: set to whether an "instant connect" has occurred (true if yes) - * @param uptr Initial value of uptr for new socket (default: NULL) - * @param callConnectHandler If true, call TCP connect handler even if result is known before function exit (default: true) - * @return New socket or NULL on failure - */ - inline PhySocket *tcpConnect(const struct sockaddr *remoteAddress,bool &connected,void *uptr = (void *)0,bool callConnectHandler = true) - { - if (_socks.size() >= ZT_PHY_MAX_SOCKETS) - return (PhySocket *)0; - - ZT_PHY_SOCKFD_TYPE s = ::socket(remoteAddress->sa_family,SOCK_STREAM,0); - if (!ZT_PHY_SOCKFD_VALID(s)) { - connected = false; - return (PhySocket *)0; - } - -#if defined(_WIN32) || defined(_WIN64) - { - BOOL f; - if (remoteAddress->sa_family == AF_INET6) { f = TRUE; ::setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,(const char *)&f,sizeof(f)); } - f = TRUE; ::setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(const char *)&f,sizeof(f)); - f = (_noDelay ? TRUE : FALSE); setsockopt(s,IPPROTO_TCP,TCP_NODELAY,(char *)&f,sizeof(f)); - u_long iMode=1; - ioctlsocket(s,FIONBIO,&iMode); - } -#else - { - int f; - if (remoteAddress->sa_family == AF_INET6) { f = 1; ::setsockopt(s,IPPROTO_IPV6,IPV6_V6ONLY,(void *)&f,sizeof(f)); } - f = 1; ::setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(void *)&f,sizeof(f)); - f = (_noDelay ? 1 : 0); setsockopt(s,IPPROTO_TCP,TCP_NODELAY,(char *)&f,sizeof(f)); - fcntl(s,F_SETFL,O_NONBLOCK); - } -#endif - - connected = true; - if (::connect(s,remoteAddress,(remoteAddress->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in))) { - connected = false; -#if defined(_WIN32) || defined(_WIN64) - if (WSAGetLastError() != WSAEWOULDBLOCK) { -#else - if (errno != EINPROGRESS) { -#endif - ZT_PHY_CLOSE_SOCKET(s); - return (PhySocket *)0; - } // else connection is proceeding asynchronously... - } - - try { - _socks.push_back(PhySocketImpl()); - } catch ( ... ) { - ZT_PHY_CLOSE_SOCKET(s); - return (PhySocket *)0; - } - PhySocketImpl &sws = _socks.back(); - - if ((long)s > _nfds) - _nfds = (long)s; - if (connected) { - FD_SET(s,&_readfds); - sws.type = ZT_PHY_SOCKET_TCP_OUT_CONNECTED; - } else { - FD_SET(s,&_writefds); -#if defined(_WIN32) || defined(_WIN64) - FD_SET(s,&_exceptfds); -#endif - sws.type = ZT_PHY_SOCKET_TCP_OUT_PENDING; - } - sws.sock = s; - sws.uptr = uptr; - memset(&(sws.saddr),0,sizeof(struct sockaddr_storage)); - memcpy(&(sws.saddr),remoteAddress,(remoteAddress->sa_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)); - - if ((callConnectHandler)&&(connected)) { - try { - _handler->phyOnTcpConnect((PhySocket *)&sws,&(sws.uptr),true); - } catch ( ... ) {} - } - - return (PhySocket *)&sws; - } - - /** - * Try to set buffer sizes as close to the given value as possible - * - * This will try the specified value and then lower values in 16K increments - * until one works. - * - * @param sock Socket - * @param receiveBufferSize Desired size of receive buffer - * @param sendBufferSize Desired size of send buffer - */ - inline void setBufferSizes(const PhySocket *sock,int receiveBufferSize,int sendBufferSize) - { - PhySocketImpl &sws = *(reinterpret_cast(sock)); - if (receiveBufferSize > 0) { - while (receiveBufferSize > 0) { - int tmpbs = receiveBufferSize; - if (::setsockopt(sws.sock,SOL_SOCKET,SO_RCVBUF,(const char *)&tmpbs,sizeof(tmpbs)) == 0) - break; - receiveBufferSize -= 16384; - } - } - if (sendBufferSize > 0) { - while (sendBufferSize > 0) { - int tmpbs = sendBufferSize; - if (::setsockopt(sws.sock,SOL_SOCKET,SO_SNDBUF,(const char *)&tmpbs,sizeof(tmpbs)) == 0) - break; - sendBufferSize -= 16384; - } - } - } - - /** - * Attempt to send data to a stream socket (non-blocking) - * - * If -1 is returned, the socket should no longer be used as it is now - * destroyed. If callCloseHandler is true, the close handler will be - * called before the function returns. - * - * This can be used with TCP, Unix, or socket pair sockets. - * - * @param sock An open stream socket (other socket types will fail) - * @param data Data to send - * @param len Length of data - * @param callCloseHandler If true, call close handler on socket closing failure condition (default: true) - * @return Number of bytes actually sent or -1 on fatal error (socket closure) - */ - inline long streamSend(PhySocket *sock,const void *data,unsigned long len,bool callCloseHandler = true) - { - PhySocketImpl &sws = *(reinterpret_cast(sock)); -#if defined(_WIN32) || defined(_WIN64) - long n = (long)::send(sws.sock,reinterpret_cast(data),len,0); - if (n == SOCKET_ERROR) { - switch(WSAGetLastError()) { - case WSAEINTR: - case WSAEWOULDBLOCK: - return 0; - default: - this->close(sock,callCloseHandler); - return -1; - } - } -#else // not Windows - long n = (long)::send(sws.sock,data,len,0); - if (n < 0) { - switch(errno) { -#ifdef EAGAIN - case EAGAIN: -#endif -#if defined(EWOULDBLOCK) && ( !defined(EAGAIN) || (EWOULDBLOCK != EAGAIN) ) - case EWOULDBLOCK: -#endif -#ifdef EINTR - case EINTR: -#endif - return 0; - default: - this->close(sock,callCloseHandler); - return -1; - } - } -#endif // Windows or not - return n; - } - -#ifdef __UNIX_LIKE__ - /** - * Attempt to send data to a Unix domain socket connection (non-blocking) - * - * If -1 is returned, the socket should no longer be used as it is now - * destroyed. If callCloseHandler is true, the close handler will be - * called before the function returns. - * - * @param sock An open Unix socket (other socket types will fail) - * @param data Data to send - * @param len Length of data - * @param callCloseHandler If true, call close handler on socket closing failure condition (default: true) - * @return Number of bytes actually sent or -1 on fatal error (socket closure) - */ - inline long unixSend(PhySocket *sock,const void *data,unsigned long len,bool callCloseHandler = true) - { - PhySocketImpl &sws = *(reinterpret_cast(sock)); - long n = (long)::write(sws.sock,data,len); - if (n < 0) { - switch(errno) { -#ifdef EAGAIN - case EAGAIN: -#endif -#if defined(EWOULDBLOCK) && ( !defined(EAGAIN) || (EWOULDBLOCK != EAGAIN) ) - case EWOULDBLOCK: -#endif -#ifdef EINTR - case EINTR: -#endif - return 0; - default: - this->close(sock,callCloseHandler); - return -1; - } - } - return n; - } -#endif // __UNIX_LIKE__ - - /** - * For streams, sets whether we want to be notified that the socket is writable - * - * This can be used with TCP, Unix, or socket pair sockets. - * - * Call whack() if this is being done from another thread and you want - * it to take effect immediately. Otherwise it is only guaranteed to - * take effect on the next poll(). - * - * @param sock Stream connection socket - * @param notifyWritable Want writable notifications? - */ - inline void setNotifyWritable(PhySocket *sock,bool notifyWritable) - { - PhySocketImpl &sws = *(reinterpret_cast(sock)); - if (notifyWritable) { - FD_SET(sws.sock,&_writefds); - } else { - FD_CLR(sws.sock,&_writefds); - } - } - - /** - * Set whether we want to be notified that a socket is readable - * - * This is primarily for raw sockets added with wrapSocket(). It could be - * used with others, but doing so would essentially lock them and prevent - * data from being read from them until this is set to 'true' again. - * - * @param sock Socket to modify - * @param notifyReadable True if socket should be monitored for readability - */ - inline void setNotifyReadable(PhySocket *sock,bool notifyReadable) - { - PhySocketImpl &sws = *(reinterpret_cast(sock)); - if (notifyReadable) { - FD_SET(sws.sock,&_readfds); - } else { - FD_CLR(sws.sock,&_readfds); - } - } - - /** - * Wait for activity and handle one or more events - * - * Note that this is not guaranteed to wait up to 'timeout' even - * if nothing happens, as whack() or other events such as signals - * may cause premature termination. - * - * @param timeout Timeout in milliseconds or 0 for none (forever) - */ - inline void poll(unsigned long timeout) - { - char buf[131072]; - struct sockaddr_storage ss; - struct timeval tv; - fd_set rfds,wfds,efds; - - memcpy(&rfds,&_readfds,sizeof(rfds)); - memcpy(&wfds,&_writefds,sizeof(wfds)); -#if defined(_WIN32) || defined(_WIN64) - memcpy(&efds,&_exceptfds,sizeof(efds)); -#else - FD_ZERO(&efds); -#endif - - tv.tv_sec = (long)(timeout / 1000); - tv.tv_usec = (long)((timeout % 1000) * 1000); - if (::select((int)_nfds + 1,&rfds,&wfds,&efds,(timeout > 0) ? &tv : (struct timeval *)0) <= 0) - return; - - if (FD_ISSET(_whackReceiveSocket,&rfds)) { - char tmp[16]; -#if defined(_WIN32) || defined(_WIN64) - ::recv(_whackReceiveSocket,tmp,16,0); -#else - ::read(_whackReceiveSocket,tmp,16); -#endif - } - - for(typename std::list::iterator s(_socks.begin());s!=_socks.end();) { - switch (s->type) { - - case ZT_PHY_SOCKET_TCP_OUT_PENDING: -#if defined(_WIN32) || defined(_WIN64) - if (FD_ISSET(s->sock,&efds)) { - this->close((PhySocket *)&(*s),true); - } else // ... if -#endif - if (FD_ISSET(s->sock,&wfds)) { - socklen_t slen = sizeof(ss); - if (::getpeername(s->sock,(struct sockaddr *)&ss,&slen) != 0) { - this->close((PhySocket *)&(*s),true); - } else { - s->type = ZT_PHY_SOCKET_TCP_OUT_CONNECTED; - FD_SET(s->sock,&_readfds); - FD_CLR(s->sock,&_writefds); -#if defined(_WIN32) || defined(_WIN64) - FD_CLR(s->sock,&_exceptfds); -#endif - try { - _handler->phyOnTcpConnect((PhySocket *)&(*s),&(s->uptr),true); - } catch ( ... ) {} - } - } - break; - - case ZT_PHY_SOCKET_TCP_OUT_CONNECTED: - case ZT_PHY_SOCKET_TCP_IN: { - ZT_PHY_SOCKFD_TYPE sock = s->sock; // if closed, s->sock becomes invalid as s is no longer dereferencable - if (FD_ISSET(sock,&rfds)) { - long n = (long)::recv(sock,buf,sizeof(buf),0); - if (n <= 0) { - this->close((PhySocket *)&(*s),true); - } else { - try { - _handler->phyOnTcpData((PhySocket *)&(*s),&(s->uptr),(void *)buf,(unsigned long)n); - } catch ( ... ) {} - } - } - if ((FD_ISSET(sock,&wfds))&&(FD_ISSET(sock,&_writefds))) { - try { - _handler->phyOnTcpWritable((PhySocket *)&(*s),&(s->uptr)); - } catch ( ... ) {} - } - } break; - - case ZT_PHY_SOCKET_TCP_LISTEN: - if (FD_ISSET(s->sock,&rfds)) { - memset(&ss,0,sizeof(ss)); - socklen_t slen = sizeof(ss); - ZT_PHY_SOCKFD_TYPE newSock = ::accept(s->sock,(struct sockaddr *)&ss,&slen); - if (ZT_PHY_SOCKFD_VALID(newSock)) { - if (_socks.size() >= ZT_PHY_MAX_SOCKETS) { - ZT_PHY_CLOSE_SOCKET(newSock); - } else { -#if defined(_WIN32) || defined(_WIN64) - { BOOL f = (_noDelay ? TRUE : FALSE); setsockopt(newSock,IPPROTO_TCP,TCP_NODELAY,(char *)&f,sizeof(f)); } - { u_long iMode=1; ioctlsocket(newSock,FIONBIO,&iMode); } -#else - { int f = (_noDelay ? 1 : 0); setsockopt(newSock,IPPROTO_TCP,TCP_NODELAY,(char *)&f,sizeof(f)); } - fcntl(newSock,F_SETFL,O_NONBLOCK); -#endif - _socks.push_back(PhySocketImpl()); - PhySocketImpl &sws = _socks.back(); - FD_SET(newSock,&_readfds); - if ((long)newSock > _nfds) - _nfds = (long)newSock; - sws.type = ZT_PHY_SOCKET_TCP_IN; - sws.sock = newSock; - sws.uptr = (void *)0; - memcpy(&(sws.saddr),&ss,sizeof(struct sockaddr_storage)); - try { - _handler->phyOnTcpAccept((PhySocket *)&(*s),(PhySocket *)&(_socks.back()),&(s->uptr),&(sws.uptr),(const struct sockaddr *)&(sws.saddr)); - } catch ( ... ) {} - } - } - } - break; - - case ZT_PHY_SOCKET_UDP: - if (FD_ISSET(s->sock,&rfds)) { - for(int k=0;k<1024;++k) { - memset(&ss,0,sizeof(ss)); - socklen_t slen = sizeof(ss); - long n = (long)::recvfrom(s->sock,buf,sizeof(buf),0,(struct sockaddr *)&ss,&slen); - if (n > 0) { - try { - _handler->phyOnDatagram((PhySocket *)&(*s),&(s->uptr),(const struct sockaddr *)&(s->saddr),(const struct sockaddr *)&ss,(void *)buf,(unsigned long)n); - } catch ( ... ) {} - } else if (n < 0) - break; - } - } - break; - - case ZT_PHY_SOCKET_UNIX_IN: { -#ifdef __UNIX_LIKE__ - ZT_PHY_SOCKFD_TYPE sock = s->sock; // if closed, s->sock becomes invalid as s is no longer dereferencable - if ((FD_ISSET(sock,&wfds))&&(FD_ISSET(sock,&_writefds))) { - try { - _handler->phyOnUnixWritable((PhySocket *)&(*s),&(s->uptr)); - } catch ( ... ) {} - } - if (FD_ISSET(sock,&rfds)) { - long n = (long)::read(sock,buf,sizeof(buf)); - if (n <= 0) { - this->close((PhySocket *)&(*s),true); - } else { - try { - _handler->phyOnUnixData((PhySocket *)&(*s),&(s->uptr),(void *)buf,(unsigned long)n); - } catch ( ... ) {} - } - } -#endif // __UNIX_LIKE__ - } break; - - case ZT_PHY_SOCKET_UNIX_LISTEN: -#ifdef __UNIX_LIKE__ - if (FD_ISSET(s->sock,&rfds)) { - memset(&ss,0,sizeof(ss)); - socklen_t slen = sizeof(ss); - ZT_PHY_SOCKFD_TYPE newSock = ::accept(s->sock,(struct sockaddr *)&ss,&slen); - if (ZT_PHY_SOCKFD_VALID(newSock)) { - if (_socks.size() >= ZT_PHY_MAX_SOCKETS) { - ZT_PHY_CLOSE_SOCKET(newSock); - } else { - fcntl(newSock,F_SETFL,O_NONBLOCK); - _socks.push_back(PhySocketImpl()); - PhySocketImpl &sws = _socks.back(); - FD_SET(newSock,&_readfds); - if ((long)newSock > _nfds) - _nfds = (long)newSock; - sws.type = ZT_PHY_SOCKET_UNIX_IN; - sws.sock = newSock; - sws.uptr = (void *)0; - memcpy(&(sws.saddr),&ss,sizeof(struct sockaddr_storage)); - try { - //_handler->phyOnUnixAccept((PhySocket *)&(*s),(PhySocket *)&(_socks.back()),&(s->uptr),&(sws.uptr)); - } catch ( ... ) {} - } - } - } -#endif // __UNIX_LIKE__ - break; - - case ZT_PHY_SOCKET_FD: { - ZT_PHY_SOCKFD_TYPE sock = s->sock; - const bool readable = ((FD_ISSET(sock,&rfds))&&(FD_ISSET(sock,&_readfds))); - const bool writable = ((FD_ISSET(sock,&wfds))&&(FD_ISSET(sock,&_writefds))); - if ((readable)||(writable)) { - try { - //_handler->phyOnFileDescriptorActivity((PhySocket *)&(*s),&(s->uptr),readable,writable); - } catch ( ... ) {} - } - } break; - - default: - break; - - } - - if (s->type == ZT_PHY_SOCKET_CLOSED) - _socks.erase(s++); - else ++s; - } - } - - /** - * @param sock Socket to close - * @param callHandlers If true, call handlers for TCP connect (success: false) or close (default: true) - */ - inline void close(PhySocket *sock,bool callHandlers = true) - { - if (!sock) - return; - PhySocketImpl &sws = *(reinterpret_cast(sock)); - if (sws.type == ZT_PHY_SOCKET_CLOSED) - return; - - FD_CLR(sws.sock,&_readfds); - FD_CLR(sws.sock,&_writefds); -#if defined(_WIN32) || defined(_WIN64) - FD_CLR(sws.sock,&_exceptfds); -#endif - - if (sws.type != ZT_PHY_SOCKET_FD) - ZT_PHY_CLOSE_SOCKET(sws.sock); - -#ifdef __UNIX_LIKE__ - if (sws.type == ZT_PHY_SOCKET_UNIX_LISTEN) - ::unlink(((struct sockaddr_un *)(&(sws.saddr)))->sun_path); -#endif // __UNIX_LIKE__ - - if (callHandlers) { - switch(sws.type) { - case ZT_PHY_SOCKET_TCP_OUT_PENDING: - try { - _handler->phyOnTcpConnect(sock,&(sws.uptr),false); - } catch ( ... ) {} - break; - case ZT_PHY_SOCKET_TCP_OUT_CONNECTED: - case ZT_PHY_SOCKET_TCP_IN: - try { - _handler->phyOnTcpClose(sock,&(sws.uptr)); - } catch ( ... ) {} - break; - case ZT_PHY_SOCKET_UNIX_IN: -#ifdef __UNIX_LIKE__ - try { - _handler->phyOnUnixClose(sock,&(sws.uptr)); - } catch ( ... ) {} -#endif // __UNIX_LIKE__ - break; - default: - break; - } - } - - // Causes entry to be deleted from list in poll(), ignored elsewhere - sws.type = ZT_PHY_SOCKET_CLOSED; - - if ((long)sws.sock >= (long)_nfds) { - long nfds = (long)_whackSendSocket; - if ((long)_whackReceiveSocket > nfds) - nfds = (long)_whackReceiveSocket; - for(typename std::list::iterator s(_socks.begin());s!=_socks.end();++s) { - if ((s->type != ZT_PHY_SOCKET_CLOSED)&&((long)s->sock > nfds)) - nfds = (long)s->sock; - } - _nfds = nfds; - } - } -}; - -} // namespace ZeroTier - -#endif diff --git a/attic/PortMapper-libnatpmp.c b/attic/PortMapper-libnatpmp.c deleted file mode 100644 index 5da85cba9..000000000 --- a/attic/PortMapper-libnatpmp.c +++ /dev/null @@ -1,14 +0,0 @@ -#define ENABLE_STRNATPMPERR -#define _BSD_SOURCE -#define _DEFAULT_SOURCE -#define _XOPEN_SOURCE 600 - -#ifdef __APPLE__ -#ifndef _DARWIN_C_SOURCE -#define _DARWIN_C_SOURCE -#endif -#endif - -#include "../ext/libnatpmp/getgateway.c" -#include "../ext/libnatpmp/wingettimeofday.c" -#include "../ext/libnatpmp/natpmp.c" diff --git a/attic/PortMapper-miniupnpc.c b/attic/PortMapper-miniupnpc.c deleted file mode 100644 index 8d28da109..000000000 --- a/attic/PortMapper-miniupnpc.c +++ /dev/null @@ -1,41 +0,0 @@ -#define MINIUPNP_STATICLIB -#define MINIUPNPC_SET_SOCKET_TIMEOUT -#define MINIUPNPC_GET_SRC_ADDR -#define _BSD_SOURCE -#define _DEFAULT_SOURCE -#define _XOPEN_SOURCE 600 -#define MINIUPNPC_VERSION_STRING "2.0" -#define UPNP_VERSION_STRING "UPnP/1.1" - -#ifdef __LINUX__ -#define OS_STRING "Linux" -#endif -#ifdef __APPLE__ -#define OS_STRING "Darwin" -#endif -#ifdef __WINDOWS__ -#define OS_STRING "Windows" -#endif -#ifndef OS_STRING -#define OS_STRING "ZeroTier" -#endif - -#ifdef __APPLE__ -#ifndef _DARWIN_C_SOURCE -#define _DARWIN_C_SOURCE -#endif -#endif - -#include "../ext/miniupnpc/connecthostport.c" -#include "../ext/miniupnpc/igd_desc_parse.c" -#include "../ext/miniupnpc/minisoap.c" -#include "../ext/miniupnpc/miniupnpc.c" -#include "../ext/miniupnpc/miniwget.c" -#include "../ext/miniupnpc/minixml.c" -#include "../ext/miniupnpc/portlistingparse.c" -#include "../ext/miniupnpc/receivedata.c" -#include "../ext/miniupnpc/upnpcommands.c" -#include "../ext/miniupnpc/upnpdev.c" -#include "../ext/miniupnpc/upnperrors.c" -#include "../ext/miniupnpc/upnpreplyparse.c" -#include "../ext/miniupnpc/minissdpc.c" diff --git a/attic/PortMapper.cpp b/attic/PortMapper.cpp deleted file mode 100644 index d0ed87c37..000000000 --- a/attic/PortMapper.cpp +++ /dev/null @@ -1,334 +0,0 @@ -/* - * Copyright (c)2019 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2023-01-01 - * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. - */ -/****/ - -// Uncomment to dump debug messages -//#define ZT_PORTMAPPER_TRACE 1 - -#ifdef __ANDROID__ -#include -#define PM_TRACE(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, "PortMapper", __VA_ARGS__)) -#else -#define PM_TRACE(...) fprintf(stderr, __VA_ARGS__) -#endif - -#include -#include -#include - -#include - -#include "../node/Utils.hpp" -#include "OSUtils.hpp" -#include "PortMapper.hpp" - -// These must be defined to get rid of dynamic export stuff in libminiupnpc and libnatpmp -#ifdef __WINDOWS__ -#ifndef MINIUPNP_STATICLIB -#define MINIUPNP_STATICLIB -#endif -#ifndef STATICLIB -#define STATICLIB -#endif -#endif - -#ifdef ZT_USE_SYSTEM_MINIUPNPC -#include -#include -#else -#ifdef __ANDROID__ -#include "miniupnpc.h" -#include "upnpcommands.h" -#else -#include "../ext/miniupnpc/miniupnpc.h" -#include "../ext/miniupnpc/upnpcommands.h" -#endif -#endif - -#ifdef ZT_USE_SYSTEM_NATPMP -#include -#else -#ifdef __ANDROID__ -#include "natpmp.h" -#else -#include "../ext/libnatpmp/natpmp.h" -#endif -#endif - -namespace ZeroTier { - -class PortMapperImpl -{ -public: - PortMapperImpl(int localUdpPortToMap,const char *un) : - run(true), - localPort(localUdpPortToMap), - uniqueName(un) - { - } - - ~PortMapperImpl() {} - - void threadMain() - throw() - { - int mode = 0; // 0 == NAT-PMP, 1 == UPnP - -#ifdef ZT_PORTMAPPER_TRACE - fprintf(stderr,"PortMapper: started for UDP port %d" ZT_EOL_S,localPort); -#endif - - while (run) { - - // --------------------------------------------------------------------- - // NAT-PMP mode (preferred) - // --------------------------------------------------------------------- - if (mode == 0) { - natpmp_t natpmp; - natpmpresp_t response; - int r = 0; - - bool natPmpSuccess = false; - for(int tries=0;tries<60;++tries) { - int tryPort = (int)localPort + tries; - if (tryPort >= 65535) - tryPort = (tryPort - 65535) + 1025; - - memset(&natpmp,0,sizeof(natpmp)); - memset(&response,0,sizeof(response)); - - if (initnatpmp(&natpmp,0,0) != 0) { - mode = 1; - closenatpmp(&natpmp); -#ifdef ZT_PORTMAPPER_TRACE - PM_TRACE("PortMapper: NAT-PMP: init failed, switching to UPnP mode" ZT_EOL_S); -#endif - break; - } - - InetAddress publicAddress; - sendpublicaddressrequest(&natpmp); - int64_t myTimeout = OSUtils::now() + 5000; - do { - fd_set fds; - struct timeval timeout; - FD_ZERO(&fds); - FD_SET(natpmp.s, &fds); - getnatpmprequesttimeout(&natpmp, &timeout); - select(FD_SETSIZE, &fds, NULL, NULL, &timeout); - r = readnatpmpresponseorretry(&natpmp, &response); - if (OSUtils::now() >= myTimeout) - break; - } while (r == NATPMP_TRYAGAIN); - if (r == 0) { - publicAddress = InetAddress((uint32_t)response.pnu.publicaddress.addr.s_addr,0); - } else { -#ifdef ZT_PORTMAPPER_TRACE - PM_TRACE("PortMapper: NAT-PMP: request for external address failed, aborting..." ZT_EOL_S); -#endif - closenatpmp(&natpmp); - break; - } - - sendnewportmappingrequest(&natpmp,NATPMP_PROTOCOL_UDP,localPort,tryPort,(ZT_PORTMAPPER_REFRESH_DELAY * 2) / 1000); - myTimeout = OSUtils::now() + 10000; - do { - fd_set fds; - struct timeval timeout; - FD_ZERO(&fds); - FD_SET(natpmp.s, &fds); - getnatpmprequesttimeout(&natpmp, &timeout); - select(FD_SETSIZE, &fds, NULL, NULL, &timeout); - r = readnatpmpresponseorretry(&natpmp, &response); - if (OSUtils::now() >= myTimeout) - break; - } while (r == NATPMP_TRYAGAIN); - if (r == 0) { - publicAddress.setPort(response.pnu.newportmapping.mappedpublicport); -#ifdef ZT_PORTMAPPER_TRACE - char paddr[128]; - PM_TRACE("PortMapper: NAT-PMP: mapped %u to %s" ZT_EOL_S,(unsigned int)localPort,publicAddress.toString(paddr)); -#endif - Mutex::Lock sl(surface_l); - surface.clear(); - surface.push_back(publicAddress); - natPmpSuccess = true; - closenatpmp(&natpmp); - break; - } else { - closenatpmp(&natpmp); - // continue - } - } - - if (!natPmpSuccess) { - mode = 1; -#ifdef ZT_PORTMAPPER_TRACE - PM_TRACE("PortMapper: NAT-PMP: request failed, switching to UPnP mode" ZT_EOL_S); -#endif - } - } - // --------------------------------------------------------------------- - - // --------------------------------------------------------------------- - // UPnP mode - // --------------------------------------------------------------------- - if (mode == 1) { - char lanaddr[4096]; - char externalip[4096]; // no range checking? so make these buffers larger than any UDP packet a uPnP server could send us as a precaution :P - char inport[16]; - char outport[16]; - struct UPNPUrls urls; - struct IGDdatas data; - - int upnpError = 0; - UPNPDev *devlist = upnpDiscoverAll(5000,(const char *)0,(const char *)0,0,0,2,&upnpError); - if (devlist) { - -#ifdef ZT_PORTMAPPER_TRACE - { - UPNPDev *dev = devlist; - while (dev) { - PM_TRACE("PortMapper: found UPnP device at URL '%s': %s" ZT_EOL_S,dev->descURL,dev->st); - dev = dev->pNext; - } - } -#endif - - memset(lanaddr,0,sizeof(lanaddr)); - memset(externalip,0,sizeof(externalip)); - memset(&urls,0,sizeof(urls)); - memset(&data,0,sizeof(data)); - OSUtils::ztsnprintf(inport,sizeof(inport),"%d",localPort); - - if ((UPNP_GetValidIGD(devlist,&urls,&data,lanaddr,sizeof(lanaddr)))&&(lanaddr[0])) { -#ifdef ZT_PORTMAPPER_TRACE - PM_TRACE("PortMapper: UPnP: my LAN IP address: %s" ZT_EOL_S,lanaddr); -#endif - if ((UPNP_GetExternalIPAddress(urls.controlURL,data.first.servicetype,externalip) == UPNPCOMMAND_SUCCESS)&&(externalip[0])) { -#ifdef ZT_PORTMAPPER_TRACE - PM_TRACE("PortMapper: UPnP: my external IP address: %s" ZT_EOL_S,externalip); -#endif - - for(int tries=0;tries<60;++tries) { - int tryPort = (int)localPort + tries; - if (tryPort >= 65535) - tryPort = (tryPort - 65535) + 1025; - OSUtils::ztsnprintf(outport,sizeof(outport),"%u",tryPort); - - // First check and see if this port is already mapped to the - // same unique name. If so, keep this mapping and don't try - // to map again since this can break buggy routers. But don't - // fail if this command fails since not all routers support it. - { - char haveIntClient[128]; // 128 == big enough for all these as per miniupnpc "documentation" - char haveIntPort[128]; - char haveDesc[128]; - char haveEnabled[128]; - char haveLeaseDuration[128]; - memset(haveIntClient,0,sizeof(haveIntClient)); - memset(haveIntPort,0,sizeof(haveIntPort)); - memset(haveDesc,0,sizeof(haveDesc)); - memset(haveEnabled,0,sizeof(haveEnabled)); - memset(haveLeaseDuration,0,sizeof(haveLeaseDuration)); - if ((UPNP_GetSpecificPortMappingEntry(urls.controlURL,data.first.servicetype,outport,"UDP",(const char *)0,haveIntClient,haveIntPort,haveDesc,haveEnabled,haveLeaseDuration) == UPNPCOMMAND_SUCCESS)&&(uniqueName == haveDesc)) { -#ifdef ZT_PORTMAPPER_TRACE - PM_TRACE("PortMapper: UPnP: reusing previously reserved external port: %s" ZT_EOL_S,outport); -#endif - Mutex::Lock sl(surface_l); - surface.clear(); - InetAddress tmp(externalip); - tmp.setPort(tryPort); - surface.push_back(tmp); - break; - } - } - - // Try to map this port - int mapResult = 0; - if ((mapResult = UPNP_AddPortMapping(urls.controlURL,data.first.servicetype,outport,inport,lanaddr,uniqueName.c_str(),"UDP",(const char *)0,"0")) == UPNPCOMMAND_SUCCESS) { -#ifdef ZT_PORTMAPPER_TRACE - PM_TRACE("PortMapper: UPnP: reserved external port: %s" ZT_EOL_S,outport); -#endif - Mutex::Lock sl(surface_l); - surface.clear(); - InetAddress tmp(externalip); - tmp.setPort(tryPort); - surface.push_back(tmp); - break; - } else { -#ifdef ZT_PORTMAPPER_TRACE - PM_TRACE("PortMapper: UPnP: UPNP_AddPortMapping(%s) failed: %d" ZT_EOL_S,outport,mapResult); -#endif - Thread::sleep(1000); - } - } - - } else { - mode = 0; -#ifdef ZT_PORTMAPPER_TRACE - PM_TRACE("PortMapper: UPnP: UPNP_GetExternalIPAddress failed, returning to NAT-PMP mode" ZT_EOL_S); -#endif - } - } else { - mode = 0; -#ifdef ZT_PORTMAPPER_TRACE - PM_TRACE("PortMapper: UPnP: UPNP_GetValidIGD failed, returning to NAT-PMP mode" ZT_EOL_S); -#endif - } - - freeUPNPDevlist(devlist); - - } else { - mode = 0; -#ifdef ZT_PORTMAPPER_TRACE - PM_TRACE("PortMapper: upnpDiscover failed, returning to NAT-PMP mode: %d" ZT_EOL_S,upnpError); -#endif - } - } - // --------------------------------------------------------------------- - -#ifdef ZT_PORTMAPPER_TRACE - PM_TRACE("UPNPClient: rescanning in %d ms" ZT_EOL_S,ZT_PORTMAPPER_REFRESH_DELAY); -#endif - Thread::sleep(ZT_PORTMAPPER_REFRESH_DELAY); - } - - delete this; - } - - volatile bool run; - int localPort; - std::string uniqueName; - - Mutex surface_l; - std::vector surface; -}; - -PortMapper::PortMapper(int localUdpPortToMap,const char *uniqueName) -{ - _impl = new PortMapperImpl(localUdpPortToMap,uniqueName); - Thread::start(_impl); -} - -PortMapper::~PortMapper() -{ - _impl->run = false; -} - -std::vector PortMapper::get() const -{ - Mutex::Lock _l(_impl->surface_l); - return _impl->surface; -} - -} // namespace ZeroTier diff --git a/attic/PortMapper.hpp b/attic/PortMapper.hpp deleted file mode 100644 index 54b04de1b..000000000 --- a/attic/PortMapper.hpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c)2019 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2023-01-01 - * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. - */ -/****/ - -#ifndef ZT_PORTMAPPER_HPP -#define ZT_PORTMAPPER_HPP - -#include - -#include "../node/Constants.hpp" -#include "../node/InetAddress.hpp" -#include "../node/Mutex.hpp" -#include "Thread.hpp" - -/** - * How frequently should we refresh our UPNP/NAT-PnP/whatever state? - */ -#define ZT_PORTMAPPER_REFRESH_DELAY 120000 - -namespace ZeroTier { - -class PortMapperImpl; - -/** - * UPnP/NAT-PnP port mapping "daemon" - */ -class PortMapper -{ - friend class PortMapperImpl; - -public: - /** - * Create and start port mapper service - * - * @param localUdpPortToMap Port we want visible to the outside world - * @param name Unique name of this endpoint (based on ZeroTier address) - */ - PortMapper(int localUdpPortToMap,const char *uniqueName); - - ~PortMapper(); - - /** - * @return All current external mappings for our port - */ - std::vector get() const; - -private: - PortMapperImpl *_impl; -}; - -} // namespace ZeroTier - -#endif diff --git a/attic/Root.hpp b/attic/Root.hpp deleted file mode 100644 index c526007da..000000000 --- a/attic/Root.hpp +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (c)2019 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2023-01-01 - * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. - */ -/****/ - -#ifndef ZT_ROOT_HPP -#define ZT_ROOT_HPP - -#include "Constants.hpp" -#include "Str.hpp" -#include "ECC384.hpp" -#include "Locator.hpp" -#include "InetAddress.hpp" -#include "Utils.hpp" -#include "Identity.hpp" -#include "Mutex.hpp" - -namespace ZeroTier { - -/** - * A root entry pointing to a node capable of global identity lookup and indirect transit - * - * Root entries point to DNS records that contain TXT entries that decode to Locator objects - * pointing to actual root nodes. A default root identity and static addresses can also be - * provided as fallback if DNS is not available. - * - * Note that root identities can change if DNS returns a different result, but that DNS entries - * are authenticated using their own signature scheme. This allows a root DNS name to serve - * up different roots based on factors like location or relative load of different roots. - * - * It's also possible to create a root with no DNS and no DNS validator public key. This root - * will be a static entry pointing to a single root identity and set of physical addresses. - */ -class Root -{ -public: - ZT_ALWAYS_INLINE Root() : _dnsPublicKeySize(0) {} - - /** - * Create a new root entry - * - * @param dn DNS name - * @param dnspk DNS public key for record validation - * @param dnspksize Size of DNS public key (currently always the size of a NIST P-384 point compressed public key) - * @param dflId Default identity if DNS is not available - * @param dflAddrs Default IP addresses if DNS is not available - */ - template - ZT_ALWAYS_INLINE Root(S dn,const uint8_t *const dnspk,const unsigned int dnspksize,const Identity &dflId,const std::vector &dflAddrs) : - _defaultIdentity(dflId), - _defaultAddresses(dflAddrs), - _dnsName(dn), - _dnsPublicKeySize(dnspksize) - { - if (dnspksize != 0) { - if (dnspksize > sizeof(_dnsPublicKey)) - throw ZT_EXCEPTION_INVALID_ARGUMENT; - memcpy(_dnsPublicKey,dnspk,dnspksize); - } - } - - /** - * @return Current identity (either default or latest locator) - */ - ZT_ALWAYS_INLINE const Identity id() const - { - if (_lastFetchedLocator.id()) - return _lastFetchedLocator.id(); - return _defaultIdentity; - } - - /** - * @param id Identity to check - * @return True if identity equals this root's current identity - */ - ZT_ALWAYS_INLINE bool is(const Identity &id) const - { - return ((_lastFetchedLocator.id()) ? (id == _lastFetchedLocator.id()) : (id == _defaultIdentity)); - } - - /** - * @return Current ZeroTier address (either default or latest locator) - */ - ZT_ALWAYS_INLINE const Address address() const - { - if (_lastFetchedLocator.id()) - return _lastFetchedLocator.id().address(); - return _defaultIdentity.address(); - } - - /** - * @return DNS name for this root or empty string if static entry with no DNS - */ - ZT_ALWAYS_INLINE const Str dnsName() const { return _dnsName; } - - /** - * @return Latest locator or NIL locator object if none - */ - ZT_ALWAYS_INLINE Locator locator() const { return _lastFetchedLocator; } - - /** - * @return Timestamp of latest retrieved locator or 0 if none - */ - ZT_ALWAYS_INLINE int64_t locatorTimestamp() const { return _lastFetchedLocator.timestamp(); } - - /** - * Update locator, returning true if new locator is valid and newer than existing - */ - ZT_ALWAYS_INLINE bool updateLocator(const Locator &loc) - { - if (!loc.verify()) - return false; - if ((loc.phy().size() > 0)&&(loc.timestamp() > _lastFetchedLocator.timestamp())) { - _lastFetchedLocator = loc; - return true; - } - return false; - } - - /** - * Update this root's locator from a series of TXT records - */ - template - ZT_ALWAYS_INLINE bool updateLocatorFromTxt(I start,I end) - { - try { - if (_dnsPublicKeySize != ZT_ECC384_PUBLIC_KEY_SIZE) - return false; - Locator loc; - if (!loc.decodeTxtRecords(start,end,_dnsPublicKey)) // also does verify() - return false; - if ((loc.phy().size() > 0)&&(loc.timestamp() > _lastFetchedLocator.timestamp())) { - _lastFetchedLocator = loc; - return true; - } - return false; - } catch ( ... ) {} - return false; - } - - /** - * Pick a random physical IP for this root with the given address family - * - * @param addressFamily AF_INET or AF_INET6 - * @return Address or InetAddress::NIL if no addresses exist for the given family - */ - ZT_ALWAYS_INLINE const InetAddress &pickPhysical(const int addressFamily) const - { - std::vector pickList; - const std::vector *const av = (_lastFetchedLocator) ? &(_lastFetchedLocator.phy()) : &_defaultAddresses; - for(std::vector::const_iterator i(av->begin());i!=av->end();++i) { - if (addressFamily == (int)i->ss_family) { - pickList.push_back(&(*i)); - } - } - if (pickList.size() == 1) - return *pickList[0]; - else if (pickList.size() > 1) - return *pickList[(unsigned long)Utils::random() % (unsigned long)pickList.size()]; - return InetAddress::NIL; - } - -private: - Identity _defaultIdentity; - std::vector _defaultAddresses; - Str _dnsName; - Locator _lastFetchedLocator; - unsigned int _dnsPublicKeySize; - uint8_t _dnsPublicKey[ZT_ECC384_PUBLIC_KEY_SIZE]; -}; - -} // namespace ZeroTier - -#endif diff --git a/attic/linux-old-glibc-compat.c b/attic/linux-old-glibc-compat.c deleted file mode 100644 index 6d793a2d0..000000000 --- a/attic/linux-old-glibc-compat.c +++ /dev/null @@ -1,18 +0,0 @@ -#include -#include -#include - -__asm__(".symver memcpy,memcpy@GLIBC_2.2.5"); - -#ifdef __cplusplus -extern "C" { -#endif - -extern void *__wrap_memcpy(void *dest,const void *src,size_t n) -{ - return memcpy(dest,src,n); -} - -#ifdef __cplusplus -} -#endif diff --git a/attic/listaddrinfo.go b/attic/listaddrinfo.go deleted file mode 100644 index 3db54bf00..000000000 --- a/attic/listaddrinfo.go +++ /dev/null @@ -1,30 +0,0 @@ -package main - -import ( - "fmt" - "net" -) - -func main() { - ifs, err := net.Interfaces() - if err != nil { - fmt.Printf("Error: %s\n", err.Error()) - return - } - for _, i := range ifs { - fmt.Printf("name: %s\n", i.Name) - fmt.Printf("hwaddr: %s\n", i.HardwareAddr.String()) - fmt.Printf("index: %d\n", i.Index) - fmt.Printf("addrs:\n") - addrs, _ := i.Addrs() - for _, a := range addrs { - fmt.Printf(" %s\n", a.String()) - } - fmt.Printf("multicast:\n") - mc, _ := i.MulticastAddrs() - for _, m := range mc { - fmt.Printf(" %s\n", m.String()) - } - fmt.Printf("\n") - } -} diff --git a/attic/one.cpp b/attic/one.cpp deleted file mode 100644 index 2a3be098e..000000000 --- a/attic/one.cpp +++ /dev/null @@ -1,1542 +0,0 @@ -/* - * Copyright (c)2019 ZeroTier, Inc. - * - * Use of this software is governed by the Business Source License included - * in the LICENSE.TXT file in the project's root directory. - * - * Change Date: 2023-01-01 - * - * On the date above, in accordance with the Business Source License, use - * of this software will be governed by version 2.0 of the Apache License. - */ -/****/ - -#include -#include -#include -#include -#include -#include - -#include "node/Constants.hpp" - -#ifdef __WINDOWS__ -#include -#include -#include -#include -#include -#include -#include -#include "osdep/WindowsEthernetTap.hpp" -#include "windows/ZeroTierOne/ServiceInstaller.h" -#include "windows/ZeroTierOne/ServiceBase.h" -#include "windows/ZeroTierOne/ZeroTierOneService.h" -#else -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef __LINUX__ -#include -#include -#include -#ifndef ZT_NO_CAPABILITIES -#include -#include -#endif -#endif -#endif - -#include -#include -#include -#include -#include - -#include "version.h" -#include "include/ZeroTierOne.h" - -#include "node/Identity.hpp" -#include "node/CertificateOfMembership.hpp" -#include "node/Utils.hpp" -#include "node/NetworkController.hpp" -#include "node/Buffer.hpp" - -#include "osdep/OSUtils.hpp" -#include "osdep/Http.hpp" -#include "osdep/Thread.hpp" - -#include "service/OneService.hpp" - -#include "ext/json/json.hpp" - -#define ZT_PID_PATH "zerotier-one.pid" - -using namespace ZeroTier; - -static OneService *volatile zt1Service = (OneService *)0; - -#define PROGRAM_NAME "ZeroTier One" -#define COPYRIGHT_NOTICE "Copyright (c) 2019 ZeroTier, Inc." -#define LICENSE_GRANT "Licensed under the ZeroTier BSL 1.1 (see LICENSE.txt)" - -/****************************************************************************/ -/* zerotier-cli personality */ -/****************************************************************************/ - -// This is getting deprecated soon in favor of the stuff in cli/ - -static void cliPrintHelp(const char *pn,FILE *out) -{ - fprintf(out, - "%s version %d.%d.%d build %d" ZT_EOL_S, - PROGRAM_NAME, - ZEROTIER_ONE_VERSION_MAJOR, ZEROTIER_ONE_VERSION_MINOR, ZEROTIER_ONE_VERSION_REVISION, ZEROTIER_ONE_VERSION_BUILD); - fprintf(out, - COPYRIGHT_NOTICE ZT_EOL_S - LICENSE_GRANT ZT_EOL_S); - fprintf(out,"Usage: %s [-switches] []" ZT_EOL_S"" ZT_EOL_S,pn); - fprintf(out,"Available switches:" ZT_EOL_S); - fprintf(out," -h - Display this help" ZT_EOL_S); - fprintf(out," -v - Show version" ZT_EOL_S); - fprintf(out," -j - Display full raw JSON output" ZT_EOL_S); - fprintf(out," -D - ZeroTier home path for parameter auto-detect" ZT_EOL_S); - fprintf(out," -p - HTTP port (default: auto)" ZT_EOL_S); - fprintf(out," -T - Authentication token (default: auto)" ZT_EOL_S); - fprintf(out,ZT_EOL_S"Available commands:" ZT_EOL_S); - fprintf(out," info - Display status info" ZT_EOL_S); - fprintf(out," listpeers - List all peers" ZT_EOL_S); - fprintf(out," peers - List all peers (prettier)" ZT_EOL_S); - fprintf(out," listnetworks - List all networks" ZT_EOL_S); - fprintf(out," join - Join a network" ZT_EOL_S); - fprintf(out," leave - Leave a network" ZT_EOL_S); - fprintf(out," set - Set a network setting" ZT_EOL_S); - fprintf(out," get - Get a network setting" ZT_EOL_S); - fprintf(out,ZT_EOL_S"Available settings:" ZT_EOL_S); - fprintf(out," Settings to use with [get/set] may include property names from " ZT_EOL_S); - fprintf(out," the JSON output of \"zerotier-cli -j listnetworks\". Additionally, " ZT_EOL_S); - fprintf(out," (ip, ip4, ip6, ip6plane, and ip6prefix can be used). For instance:" ZT_EOL_S); - fprintf(out," zerotier-cli get ip6plane will return the 6PLANE address" ZT_EOL_S); - fprintf(out," assigned to this node." ZT_EOL_S); -} - -static std::string cliFixJsonCRs(const std::string &s) -{ - std::string r; - for(std::string::const_iterator c(s.begin());c!=s.end();++c) { - if (*c == '\n') - r.append(ZT_EOL_S); - else r.push_back(*c); - } - return r; -} - -#ifdef __WINDOWS__ -static int cli(int argc, _TCHAR* argv[]) -#else -static int cli(int argc,char **argv) -#endif -{ - unsigned int port = 0; - std::string homeDir,command,arg1,arg2,authToken; - std::string ip("127.0.0.1"); - bool json = false; - for(int i=1;i 0xffff)||(port == 0)) { - cliPrintHelp(argv[0],stdout); - return 1; - } - break; - - case 'D': - if (argv[i][2]) { - homeDir = argv[i] + 2; - } else { - cliPrintHelp(argv[0],stdout); - return 1; - } - break; - - case 'H': - if (argv[i][2]) { - ip = argv[i] + 2; - } else { - cliPrintHelp(argv[0],stdout); - return 1; - } - break; - - case 'T': - if (argv[i][2]) { - authToken = argv[i] + 2; - } else { - cliPrintHelp(argv[0],stdout); - return 1; - } - break; - - case 'v': - if (argv[i][2]) { - cliPrintHelp(argv[0],stdout); - return 1; - } - printf("%d.%d.%d" ZT_EOL_S,ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION); - return 0; - - case 'h': - case '?': - default: - cliPrintHelp(argv[0],stdout); - return 0; - } - } else { - if (arg1.length()) - arg2 = argv[i]; - else if (command.length()) - arg1 = argv[i]; - else command = argv[i]; - } - } - if (!homeDir.length()) - homeDir = OneService::platformDefaultHomePath(); - - if ((!port)||(!authToken.length())) { - if (!homeDir.length()) { - fprintf(stderr,"%s: missing port or authentication token and no home directory specified to auto-detect" ZT_EOL_S,argv[0]); - return 2; - } - - if (!port) { - std::string portStr; - OSUtils::readFile((homeDir + ZT_PATH_SEPARATOR_S + "zerotier-one.port").c_str(),portStr); - port = Utils::strToUInt(portStr.c_str()); - if ((port == 0)||(port > 0xffff)) { - fprintf(stderr,"%s: missing port and zerotier-one.port not found in %s" ZT_EOL_S,argv[0],homeDir.c_str()); - return 2; - } - } - - if (!authToken.length()) { - OSUtils::readFile((homeDir + ZT_PATH_SEPARATOR_S + "authtoken.secret").c_str(),authToken); -#ifdef __UNIX_LIKE__ - if (!authToken.length()) { - const char *hd = getenv("HOME"); - if (hd) { - char p[4096]; -#ifdef __APPLE__ - OSUtils::ztsnprintf(p,sizeof(p),"%s/Library/Application Support/ZeroTier/One/authtoken.secret",hd); -#else - OSUtils::ztsnprintf(p,sizeof(p),"%s/.zeroTierOneAuthToken",hd); -#endif - OSUtils::readFile(p,authToken); - } - } -#endif - if (!authToken.length()) { - fprintf(stderr,"%s: missing authentication token and authtoken.secret not found (or readable) in %s" ZT_EOL_S,argv[0],homeDir.c_str()); - return 2; - } - } - } - - InetAddress addr; - { - char addrtmp[256]; - OSUtils::ztsnprintf(addrtmp,sizeof(addrtmp),"%s/%u",ip.c_str(),port); - addr = InetAddress(addrtmp); - } - - std::map requestHeaders; - std::map responseHeaders; - std::string responseBody; - - requestHeaders["X-ZT1-Auth"] = authToken; - - if ((command.length() > 0)&&(command[0] == '/')) { - unsigned int scode = Http::GET( - 1024 * 1024 * 16, - 60000, - (const struct sockaddr *)&addr, - command.c_str(), - requestHeaders, - responseHeaders, - responseBody); - if (scode == 200) { - printf("%s", cliFixJsonCRs(responseBody).c_str()); - return 0; - } else { - printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str()); - return 1; - } - } else if ((command == "info")||(command == "status")) { - const unsigned int scode = Http::GET(1024 * 1024 * 16,60000,(const struct sockaddr *)&addr,"/status",requestHeaders,responseHeaders,responseBody); - - if (scode == 0) { - printf("Error connecting to the ZeroTier service: %s\n\nPlease check that the service is running and that TCP port 9993 can be contacted via 127.0.0.1." ZT_EOL_S, responseBody.c_str()); - return 1; - } - - nlohmann::json j; - try { - j = OSUtils::jsonParse(responseBody); - } catch (std::exception &exc) { - printf("%u %s invalid JSON response (%s)" ZT_EOL_S,scode,command.c_str(),exc.what()); - return 1; - } catch ( ... ) { - printf("%u %s invalid JSON response (unknown exception)" ZT_EOL_S,scode,command.c_str()); - return 1; - } - - if (scode == 200) { - if (json) { - printf("%s" ZT_EOL_S,OSUtils::jsonDump(j).c_str()); - } else { - if (j.is_object()) { - printf("200 info %s %s %s" ZT_EOL_S, - OSUtils::jsonString(j["address"],"-").c_str(), - OSUtils::jsonString(j["version"],"-").c_str(), - ((j["tcpFallbackActive"]) ? "TUNNELED" : ((j["online"]) ? "ONLINE" : "OFFLINE"))); - } - } - return 0; - } else { - printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str()); - return 1; - } - } else if (command == "listpeers") { - const unsigned int scode = Http::GET(1024 * 1024 * 16,60000,(const struct sockaddr *)&addr,"/peer",requestHeaders,responseHeaders,responseBody); - - if (scode == 0) { - printf("Error connecting to the ZeroTier service: %s\n\nPlease check that the service is running and that TCP port 9993 can be contacted via 127.0.0.1." ZT_EOL_S, responseBody.c_str()); - return 1; - } - - nlohmann::json j; - try { - j = OSUtils::jsonParse(responseBody); - } catch (std::exception &exc) { - printf("%u %s invalid JSON response (%s)" ZT_EOL_S,scode,command.c_str(),exc.what()); - return 1; - } catch ( ... ) { - printf("%u %s invalid JSON response (unknown exception)" ZT_EOL_S,scode,command.c_str()); - return 1; - } - - if (scode == 200) { - if (json) { - printf("%s" ZT_EOL_S,OSUtils::jsonDump(j).c_str()); - } else { - printf("200 listpeers " ZT_EOL_S); - if (j.is_array()) { - for(unsigned long k=0;k= 0) { - OSUtils::ztsnprintf(ver,sizeof(ver),"%lld.%lld.%lld",vmaj,vmin,vrev); - } else { - ver[0] = '-'; - ver[1] = (char)0; - } - printf("200 listpeers %s %s %d %s %s" ZT_EOL_S, - OSUtils::jsonString(p["address"],"-").c_str(), - bestPath.c_str(), - (int)OSUtils::jsonInt(p["latency"],0), - ver, - OSUtils::jsonString(p["role"],"-").c_str()); - } - } - } - return 0; - } else { - printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str()); - return 1; - } - } else if (command == "peers") { - const unsigned int scode = Http::GET(1024 * 1024 * 16,60000,(const struct sockaddr *)&addr,"/peer",requestHeaders,responseHeaders,responseBody); - - if (scode == 0) { - printf("Error connecting to the ZeroTier service: %s\n\nPlease check that the service is running and that TCP port 9993 can be contacted via 127.0.0.1." ZT_EOL_S, responseBody.c_str()); - return 1; - } - - nlohmann::json j; - try { - j = OSUtils::jsonParse(responseBody); - } catch (std::exception &exc) { - printf("%u %s invalid JSON response (%s)" ZT_EOL_S,scode,command.c_str(),exc.what()); - return 1; - } catch ( ... ) { - printf("%u %s invalid JSON response (unknown exception)" ZT_EOL_S,scode,command.c_str()); - return 1; - } - - if (scode == 200) { - if (json) { - printf("%s" ZT_EOL_S,OSUtils::jsonDump(j).c_str()); - } else { - printf("200 peers\n " ZT_EOL_S); - if (j.is_array()) { - for(unsigned long k=0;k= 0) { - OSUtils::ztsnprintf(ver,sizeof(ver),"%lld.%lld.%lld",vmaj,vmin,vrev); - } else { - ver[0] = '-'; - ver[1] = (char)0; - } - printf("%s %-6s %-6s %5d %s" ZT_EOL_S, - OSUtils::jsonString(p["address"],"-").c_str(), - ver, - OSUtils::jsonString(p["role"],"-").c_str(), - (int)OSUtils::jsonInt(p["latency"],0), - bestPath.c_str()); - } - } - } - return 0; - } else { - printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str()); - return 1; - } - } else if (command == "listnetworks") { - const unsigned int scode = Http::GET(1024 * 1024 * 16,60000,(const struct sockaddr *)&addr,"/network",requestHeaders,responseHeaders,responseBody); - - if (scode == 0) { - printf("Error connecting to the ZeroTier service: %s\n\nPlease check that the service is running and that TCP port 9993 can be contacted via 127.0.0.1." ZT_EOL_S, responseBody.c_str()); - return 1; - } - - nlohmann::json j; - try { - j = OSUtils::jsonParse(responseBody); - } catch (std::exception &exc) { - printf("%u %s invalid JSON response (%s)" ZT_EOL_S,scode,command.c_str(),exc.what()); - return 1; - } catch ( ... ) { - printf("%u %s invalid JSON response (unknown exception)" ZT_EOL_S,scode,command.c_str()); - return 1; - } - - if (scode == 200) { - if (json) { - printf("%s" ZT_EOL_S,OSUtils::jsonDump(j).c_str()); - } else { - printf("200 listnetworks " ZT_EOL_S); - if (j.is_array()) { - for(unsigned long i=0;i 0) aa.push_back(','); - aa.append(addr.get()); - } - } - } - if (aa.length() == 0) aa = "-"; - printf("200 listnetworks %s %s %s %s %s %s %s" ZT_EOL_S, - OSUtils::jsonString(n["nwid"],"-").c_str(), - OSUtils::jsonString(n["name"],"-").c_str(), - OSUtils::jsonString(n["mac"],"-").c_str(), - OSUtils::jsonString(n["status"],"-").c_str(), - OSUtils::jsonString(n["type"],"-").c_str(), - OSUtils::jsonString(n["portDeviceName"],"-").c_str(), - aa.c_str()); - } - } - } - } - return 0; - } else { - printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str()); - return 1; - } - } else if (command == "join") { - if (arg1.length() != 16) { - printf("invalid network id" ZT_EOL_S); - return 2; - } - requestHeaders["Content-Type"] = "application/json"; - requestHeaders["Content-Length"] = "2"; - unsigned int scode = Http::POST( - 1024 * 1024 * 16, - 60000, - (const struct sockaddr *)&addr, - (std::string("/network/") + arg1).c_str(), - requestHeaders, - "{}", - 2, - responseHeaders, - responseBody); - if (scode == 200) { - if (json) { - printf("%s",cliFixJsonCRs(responseBody).c_str()); - } else { - printf("200 join OK" ZT_EOL_S); - } - return 0; - } else { - printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str()); - return 1; - } - } else if (command == "leave") { - if (arg1.length() != 16) { - printf("invalid network id" ZT_EOL_S); - return 2; - } - unsigned int scode = Http::DEL( - 1024 * 1024 * 16, - 60000, - (const struct sockaddr *)&addr, - (std::string("/network/") + arg1).c_str(), - requestHeaders, - responseHeaders, - responseBody); - if (scode == 200) { - if (json) { - printf("%s",cliFixJsonCRs(responseBody).c_str()); - } else { - printf("200 leave OK" ZT_EOL_S); - } - return 0; - } else { - printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str()); - return 1; - } - } else if (command == "set") { - if (arg1.length() != 16) { - fprintf(stderr,"invalid format: must be a 16-digit (network) ID\n"); - return 2; - } - if (!arg2.length()) { - fprintf(stderr,"invalid format: include a property name to set\n"); - return 2; - } - std::size_t eqidx = arg2.find('='); - if (eqidx != std::string::npos) { - if ((arg2.substr(0,eqidx) == "allowManaged")||(arg2.substr(0,eqidx) == "allowGlobal")||(arg2.substr(0,eqidx) == "allowDefault")) { - char jsons[1024]; - OSUtils::ztsnprintf(jsons,sizeof(jsons),"{\"%s\":%s}", - arg2.substr(0,eqidx).c_str(), - (((arg2.substr(eqidx,2) == "=t")||(arg2.substr(eqidx,2) == "=1")) ? "true" : "false")); - char cl[128]; - OSUtils::ztsnprintf(cl,sizeof(cl),"%u",(unsigned int)strlen(jsons)); - requestHeaders["Content-Type"] = "application/json"; - requestHeaders["Content-Length"] = cl; - unsigned int scode = Http::POST( - 1024 * 1024 * 16, - 60000, - (const struct sockaddr *)&addr, - (std::string("/network/") + arg1).c_str(), - requestHeaders, - jsons, - (unsigned long)strlen(jsons), - responseHeaders, - responseBody); - if (scode == 200) { - printf("%s",cliFixJsonCRs(responseBody).c_str()); - return 0; - } else { - printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str()); - return 1; - } - } - } else { - cliPrintHelp(argv[0],stderr); - return 2; - } - } else if (command == "get") { - if (arg1.length() != 16) { - fprintf(stderr,"invalid format: must be a 16-digit (network) ID\n"); - return 2; - } - if (!arg2.length()) { - fprintf(stderr,"invalid format: include a property name to get\n"); - return 2; - } - const unsigned int scode = Http::GET(1024 * 1024 * 16,60000,(const struct sockaddr *)&addr,"/network",requestHeaders,responseHeaders,responseBody); - if (scode == 0) { - printf("Error connecting to the ZeroTier service: %s\n\nPlease check that the service is running and that TCP port 9993 can be contacted via 127.0.0.1." ZT_EOL_S, responseBody.c_str()); - return 1; - } - nlohmann::json j; - try { - j = OSUtils::jsonParse(responseBody); - } catch (std::exception &exc) { - printf("%u %s invalid JSON response (%s)" ZT_EOL_S,scode,command.c_str(),exc.what()); - return 1; - } catch ( ... ) { - printf("%u %s invalid JSON response (unknown exception)" ZT_EOL_S,scode,command.c_str()); - return 1; - } - bool bNetworkFound = false; - if (j.is_array()) { - for(unsigned long i=0;i().find(".") != std::string::npos) - || ((arg2.find("ip6") == 0) && addr.get().find(":") != std::string::npos) - || (arg2 == "ip") - ) { - matchingIdxs[addressCountOfType++] = k; - } - } - for (int k=0; k().find("fc") == 0) { - aa.append(addr.get().substr(0,addr.get().find("/"))); - if (k < addressCountOfType-1) aa.append("\n"); - } - } - if (arg2 == "ip6prefix") { - if (addr.get().find("fc") == 0) { - aa.append(addr.get().substr(0,addr.get().find("/")).substr(0,24)); - if (k < addressCountOfType-1) aa.append("\n"); - } - } - } - else { - aa.append(addr.get().substr(0,addr.get().find("/"))); - if (k < addressCountOfType-1) aa.append("\n"); - } - } - } - printf("%s\n",aa.c_str()); - } - } - } - } - if (!bNetworkFound) { - fprintf(stderr,"unknown network ID, check that you are a member of the network\n"); - } - if (scode == 200) { - return 0; - } else { - printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str()); - return 1; - } - } else { - cliPrintHelp(argv[0],stderr); - return 0; - } - - return 0; -} - -/****************************************************************************/ -/* zerotier-idtool personality */ -/****************************************************************************/ - -static void idtoolPrintHelp(FILE *out,const char *pn) -{ - fprintf(out, - "%s version %d.%d.%d" ZT_EOL_S, - PROGRAM_NAME, - ZEROTIER_ONE_VERSION_MAJOR, ZEROTIER_ONE_VERSION_MINOR, ZEROTIER_ONE_VERSION_REVISION); - fprintf(out, - COPYRIGHT_NOTICE ZT_EOL_S - LICENSE_GRANT ZT_EOL_S); - fprintf(out,"Usage: %s []" ZT_EOL_S"" ZT_EOL_S"Commands:" ZT_EOL_S,pn); - fprintf(out," generate [] [] []" ZT_EOL_S); - fprintf(out," validate " ZT_EOL_S); - fprintf(out," getpublic " ZT_EOL_S); - fprintf(out," sign " ZT_EOL_S); - fprintf(out," verify " ZT_EOL_S); -} - -static Identity getIdFromArg(char *arg) -{ - Identity id; - if ((strlen(arg) > 32)&&(arg[10] == ':')) { // identity is a literal on the command line - if (id.fromString(arg)) - return id; - } else { // identity is to be read from a file - std::string idser; - if (OSUtils::readFile(arg,idser)) { - if (id.fromString(idser.c_str())) - return id; - } - } - return Identity(); -} - -#ifdef __WINDOWS__ -static int idtool(int argc, _TCHAR* argv[]) -#else -static int idtool(int argc,char **argv) -#endif -{ - if (argc < 2) { - idtoolPrintHelp(stdout,argv[0]); - return 1; - } - - if (!strcmp(argv[1],"generate")) { - uint64_t vanity = 0; - int vanityBits = 0; - if (argc >= 5) { - vanity = Utils::hexStrToU64(argv[4]) & 0xffffffffffULL; - vanityBits = 4 * (int)strlen(argv[4]); - if (vanityBits > 40) - vanityBits = 40; - } - - Identity id; - for(;;) { - id.generate(Identity::C25519); - if ((id.address().toInt() >> (40 - vanityBits)) == vanity) { - if (vanityBits > 0) { - fprintf(stderr,"vanity address: found %.10llx !\n",(unsigned long long)id.address().toInt()); - } - break; - } else { - fprintf(stderr,"vanity address: tried %.10llx looking for first %d bits of %.10llx\n",(unsigned long long)id.address().toInt(),vanityBits,(unsigned long long)(vanity << (40 - vanityBits))); - } - } - - char idtmp[1024]; - std::string idser = id.toString(true,idtmp); - if (argc >= 3) { - if (!OSUtils::writeFile(argv[2],idser)) { - fprintf(stderr,"Error writing to %s" ZT_EOL_S,argv[2]); - return 1; - } else printf("%s written" ZT_EOL_S,argv[2]); - if (argc >= 4) { - idser = id.toString(false,idtmp); - if (!OSUtils::writeFile(argv[3],idser)) { - fprintf(stderr,"Error writing to %s" ZT_EOL_S,argv[3]); - return 1; - } else printf("%s written" ZT_EOL_S,argv[3]); - } - } else printf("%s",idser.c_str()); - } else if (!strcmp(argv[1],"validate")) { - if (argc < 3) { - idtoolPrintHelp(stdout,argv[0]); - return 1; - } - - Identity id = getIdFromArg(argv[2]); - if (!id) { - fprintf(stderr,"Identity argument invalid or file unreadable: %s" ZT_EOL_S,argv[2]); - return 1; - } - - if (!id.locallyValidate()) { - fprintf(stderr,"%s FAILED validation." ZT_EOL_S,argv[2]); - return 1; - } else printf("%s is a valid identity" ZT_EOL_S,argv[2]); - } else if (!strcmp(argv[1],"getpublic")) { - if (argc < 3) { - idtoolPrintHelp(stdout,argv[0]); - return 1; - } - - Identity id = getIdFromArg(argv[2]); - if (!id) { - fprintf(stderr,"Identity argument invalid or file unreadable: %s" ZT_EOL_S,argv[2]); - return 1; - } - - char idtmp[1024]; - printf("%s",id.toString(false,idtmp)); - } else if (!strcmp(argv[1],"sign")) { - if (argc < 4) { - idtoolPrintHelp(stdout,argv[0]); - return 1; - } - - Identity id = getIdFromArg(argv[2]); - if (!id) { - fprintf(stderr,"Identity argument invalid or file unreadable: %s" ZT_EOL_S,argv[2]); - return 1; - } - - if (!id.hasPrivate()) { - fprintf(stderr,"%s does not contain a private key (must use private to sign)" ZT_EOL_S,argv[2]); - return 1; - } - - std::string inf; - if (!OSUtils::readFile(argv[3],inf)) { - fprintf(stderr,"%s is not readable" ZT_EOL_S,argv[3]); - return 1; - } - uint8_t signature[ZT_SIGNATURE_BUFFER_SIZE]; - const unsigned int siglen = id.sign(inf.data(),(unsigned int)inf.length(),signature,sizeof(signature)); - char hexbuf[256]; - printf("%s",Utils::hex(signature,siglen,hexbuf)); - } else if (!strcmp(argv[1],"verify")) { - if (argc < 5) { - idtoolPrintHelp(stdout,argv[0]); - return 1; - } - - Identity id = getIdFromArg(argv[2]); - if (!id) { - fprintf(stderr,"Identity argument invalid or file unreadable: %s" ZT_EOL_S,argv[2]); - return 1; - } - - std::string inf; - if (!OSUtils::readFile(argv[3],inf)) { - fprintf(stderr,"%s is not readable" ZT_EOL_S,argv[3]); - return 1; - } - - char buf[4096]; - std::string signature(buf,Utils::unhex(argv[4],buf,(unsigned int)sizeof(buf))); - if ((signature.length() > ZT_ADDRESS_LENGTH)&&(id.verify(inf.data(),(unsigned int)inf.length(),signature.data(),(unsigned int)signature.length()))) { - printf("%s signature valid" ZT_EOL_S,argv[3]); - } else { - signature.clear(); - if (OSUtils::readFile(argv[4],signature)) { - signature.assign(buf,Utils::unhex(signature.c_str(),buf,(unsigned int)sizeof(buf))); - if ((signature.length() > ZT_ADDRESS_LENGTH)&&(id.verify(inf.data(),(unsigned int)inf.length(),signature.data(),(unsigned int)signature.length()))) { - printf("%s signature valid" ZT_EOL_S,argv[3]); - } else { - fprintf(stderr,"%s signature check FAILED" ZT_EOL_S,argv[3]); - return 1; - } - } else { - fprintf(stderr,"%s signature check FAILED" ZT_EOL_S,argv[3]); - return 1; - } - } - } else { - idtoolPrintHelp(stdout,argv[0]); - return 1; - } - - return 0; -} - -/****************************************************************************/ -/* Unix helper functions and signal handlers */ -/****************************************************************************/ - -#ifdef __UNIX_LIKE__ -static void _sighandlerHup(int sig) -{ -} -static void _sighandlerQuit(int sig) -{ - OneService *s = zt1Service; - if (s) - s->terminate(); - else exit(0); -} -#endif - -// Drop privileges on Linux, if supported by libc etc. and "zerotier-one" user exists on system -#if defined(__LINUX__) && !defined(ZT_NO_CAPABILITIES) -#ifndef PR_CAP_AMBIENT -#define PR_CAP_AMBIENT 47 -#define PR_CAP_AMBIENT_IS_SET 1 -#define PR_CAP_AMBIENT_RAISE 2 -#define PR_CAP_AMBIENT_LOWER 3 -#define PR_CAP_AMBIENT_CLEAR_ALL 4 -#endif -#define ZT_LINUX_USER "zerotier-one" -#define ZT_HAVE_DROP_PRIVILEGES 1 -namespace { - -// libc doesn't export capset, it is instead located in libcap -// We ignore libcap and call it manually. -struct cap_header_struct { - __u32 version; - int pid; -}; -struct cap_data_struct { - __u32 effective; - __u32 permitted; - __u32 inheritable; -}; -static inline int _zt_capset(cap_header_struct* hdrp, cap_data_struct* datap) { return syscall(SYS_capset, hdrp, datap); } - -static void _notDropping(const char *procName,const std::string &homeDir) -{ - struct stat buf; - if (lstat(homeDir.c_str(),&buf) < 0) { - if (buf.st_uid != 0 || buf.st_gid != 0) { - fprintf(stderr, "%s: FATAL: failed to drop privileges and can't run as root since privileges were previously dropped (home directory not owned by root)" ZT_EOL_S,procName); - exit(1); - } - } - fprintf(stderr, "%s: WARNING: failed to drop privileges (kernel may not support required prctl features), running as root" ZT_EOL_S,procName); -} - -static int _setCapabilities(int flags) -{ - cap_header_struct capheader = {_LINUX_CAPABILITY_VERSION_1, 0}; - cap_data_struct capdata; - capdata.inheritable = capdata.permitted = capdata.effective = flags; - return _zt_capset(&capheader, &capdata); -} - -static void _recursiveChown(const char *path,uid_t uid,gid_t gid) -{ - struct dirent de; - struct dirent *dptr; - lchown(path,uid,gid); - DIR *d = opendir(path); - if (!d) - return; - dptr = (struct dirent *)0; - for(;;) { - if (readdir_r(d,&de,&dptr) != 0) - break; - if (!dptr) - break; - if ((strcmp(dptr->d_name,".") != 0)&&(strcmp(dptr->d_name,"..") != 0)&&(strlen(dptr->d_name) > 0)) { - std::string p(path); - p.push_back(ZT_PATH_SEPARATOR); - p.append(dptr->d_name); - _recursiveChown(p.c_str(),uid,gid); // will just fail and return on regular files - } - } - closedir(d); -} - -static void dropPrivileges(const char *procName,const std::string &homeDir) -{ - if (getuid() != 0) - return; - - // dropPrivileges switches to zerotier-one user while retaining CAP_NET_ADMIN - // and CAP_NET_RAW capabilities. - struct passwd *targetUser = getpwnam(ZT_LINUX_USER); - if (!targetUser) - return; - - if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_NET_RAW, 0, 0) < 0) { - // Kernel has no support for ambient capabilities. - _notDropping(procName,homeDir); - return; - } - if (prctl(PR_SET_SECUREBITS, SECBIT_KEEP_CAPS | SECBIT_NOROOT) < 0) { - _notDropping(procName,homeDir); - return; - } - - // Change ownership of our home directory if everything looks good (does nothing if already chown'd) - _recursiveChown(homeDir.c_str(),targetUser->pw_uid,targetUser->pw_gid); - - if (_setCapabilities((1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) | (1 << CAP_SETUID) | (1 << CAP_SETGID) | (1 << CAP_NET_BIND_SERVICE)) < 0) { - _notDropping(procName,homeDir); - return; - } - - int oldDumpable = prctl(PR_GET_DUMPABLE); - if (prctl(PR_SET_DUMPABLE, 0) < 0) { - // Disable ptracing. Otherwise there is a small window when previous - // compromised ZeroTier process could ptrace us, when we still have CAP_SETUID. - // (this is mitigated anyway on most distros by ptrace_scope=1) - fprintf(stderr,"%s: FATAL: prctl(PR_SET_DUMPABLE) failed while attempting to relinquish root permissions" ZT_EOL_S,procName); - exit(1); - } - - // Relinquish root - if (setgid(targetUser->pw_gid) < 0) { - perror("setgid"); - exit(1); - } - if (setuid(targetUser->pw_uid) < 0) { - perror("setuid"); - exit(1); - } - - if (_setCapabilities((1 << CAP_NET_ADMIN) | (1 << CAP_NET_RAW) | (1 << CAP_NET_BIND_SERVICE)) < 0) { - fprintf(stderr,"%s: FATAL: unable to drop capabilities after relinquishing root" ZT_EOL_S,procName); - exit(1); - } - - if (prctl(PR_SET_DUMPABLE, oldDumpable) < 0) { - fprintf(stderr,"%s: FATAL: prctl(PR_SET_DUMPABLE) failed while attempting to relinquish root permissions" ZT_EOL_S,procName); - exit(1); - } - - if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_ADMIN, 0, 0) < 0) { - fprintf(stderr,"%s: FATAL: prctl(PR_CAP_AMBIENT,PR_CAP_AMBIENT_RAISE,CAP_NET_ADMIN) failed while attempting to relinquish root permissions" ZT_EOL_S,procName); - exit(1); - } - if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_RAW, 0, 0) < 0) { - fprintf(stderr,"%s: FATAL: prctl(PR_CAP_AMBIENT,PR_CAP_AMBIENT_RAISE,CAP_NET_RAW) failed while attempting to relinquish root permissions" ZT_EOL_S,procName); - exit(1); - } -} - -} // anonymous namespace -#endif // __LINUX__ - -/****************************************************************************/ -/* Windows helper functions and signal handlers */ -/****************************************************************************/ - -#ifdef __WINDOWS__ -// Console signal handler routine to allow CTRL+C to work, mostly for testing -static BOOL WINAPI _winConsoleCtrlHandler(DWORD dwCtrlType) -{ - switch(dwCtrlType) { - case CTRL_C_EVENT: - case CTRL_BREAK_EVENT: - case CTRL_CLOSE_EVENT: - case CTRL_SHUTDOWN_EVENT: - OneService *s = zt1Service; - if (s) - s->terminate(); - return TRUE; - } - return FALSE; -} - -static void _winPokeAHole() -{ - char myPath[MAX_PATH]; - DWORD ps = GetModuleFileNameA(NULL,myPath,sizeof(myPath)); - if ((ps > 0)&&(ps < (DWORD)sizeof(myPath))) { - STARTUPINFOA startupInfo; - PROCESS_INFORMATION processInfo; - - startupInfo.cb = sizeof(startupInfo); - memset(&startupInfo,0,sizeof(STARTUPINFOA)); - memset(&processInfo,0,sizeof(PROCESS_INFORMATION)); - if (CreateProcessA(NULL,(LPSTR)(std::string("C:\\Windows\\System32\\netsh.exe advfirewall firewall delete rule name=\"ZeroTier One\" program=\"") + myPath + "\"").c_str(),NULL,NULL,FALSE,CREATE_NO_WINDOW,NULL,NULL,&startupInfo,&processInfo)) { - WaitForSingleObject(processInfo.hProcess,INFINITE); - CloseHandle(processInfo.hProcess); - CloseHandle(processInfo.hThread); - } - - startupInfo.cb = sizeof(startupInfo); - memset(&startupInfo,0,sizeof(STARTUPINFOA)); - memset(&processInfo,0,sizeof(PROCESS_INFORMATION)); - if (CreateProcessA(NULL,(LPSTR)(std::string("C:\\Windows\\System32\\netsh.exe advfirewall firewall add rule name=\"ZeroTier One\" dir=in action=allow program=\"") + myPath + "\" enable=yes").c_str(),NULL,NULL,FALSE,CREATE_NO_WINDOW,NULL,NULL,&startupInfo,&processInfo)) { - WaitForSingleObject(processInfo.hProcess,INFINITE); - CloseHandle(processInfo.hProcess); - CloseHandle(processInfo.hThread); - } - - startupInfo.cb = sizeof(startupInfo); - memset(&startupInfo,0,sizeof(STARTUPINFOA)); - memset(&processInfo,0,sizeof(PROCESS_INFORMATION)); - if (CreateProcessA(NULL,(LPSTR)(std::string("C:\\Windows\\System32\\netsh.exe advfirewall firewall add rule name=\"ZeroTier One\" dir=out action=allow program=\"") + myPath + "\" enable=yes").c_str(),NULL,NULL,FALSE,CREATE_NO_WINDOW,NULL,NULL,&startupInfo,&processInfo)) { - WaitForSingleObject(processInfo.hProcess,INFINITE); - CloseHandle(processInfo.hProcess); - CloseHandle(processInfo.hThread); - } - } -} - -// Returns true if this is running as the local administrator -static BOOL IsCurrentUserLocalAdministrator(void) -{ - BOOL fReturn = FALSE; - DWORD dwStatus; - DWORD dwAccessMask; - DWORD dwAccessDesired; - DWORD dwACLSize; - DWORD dwStructureSize = sizeof(PRIVILEGE_SET); - PACL pACL = NULL; - PSID psidAdmin = NULL; - - HANDLE hToken = NULL; - HANDLE hImpersonationToken = NULL; - - PRIVILEGE_SET ps; - GENERIC_MAPPING GenericMapping; - - PSECURITY_DESCRIPTOR psdAdmin = NULL; - SID_IDENTIFIER_AUTHORITY SystemSidAuthority = SECURITY_NT_AUTHORITY; - - const DWORD ACCESS_READ = 1; - const DWORD ACCESS_WRITE = 2; - - __try - { - if (!OpenThreadToken(GetCurrentThread(), TOKEN_DUPLICATE|TOKEN_QUERY,TRUE,&hToken)) - { - if (GetLastError() != ERROR_NO_TOKEN) - __leave; - if (!OpenProcessToken(GetCurrentProcess(),TOKEN_DUPLICATE|TOKEN_QUERY, &hToken)) - __leave; - } - if (!DuplicateToken (hToken, SecurityImpersonation,&hImpersonationToken)) - __leave; - if (!AllocateAndInitializeSid(&SystemSidAuthority, 2, - SECURITY_BUILTIN_DOMAIN_RID, - DOMAIN_ALIAS_RID_ADMINS, - 0, 0, 0, 0, 0, 0, &psidAdmin)) - __leave; - psdAdmin = LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH); - if (psdAdmin == NULL) - __leave; - if (!InitializeSecurityDescriptor(psdAdmin,SECURITY_DESCRIPTOR_REVISION)) - __leave; - dwACLSize = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(psidAdmin) - sizeof(DWORD); - pACL = (PACL)LocalAlloc(LPTR, dwACLSize); - if (pACL == NULL) - __leave; - if (!InitializeAcl(pACL, dwACLSize, ACL_REVISION2)) - __leave; - dwAccessMask= ACCESS_READ | ACCESS_WRITE; - if (!AddAccessAllowedAce(pACL, ACL_REVISION2, dwAccessMask, psidAdmin)) - __leave; - if (!SetSecurityDescriptorDacl(psdAdmin, TRUE, pACL, FALSE)) - __leave; - - SetSecurityDescriptorGroup(psdAdmin, psidAdmin, FALSE); - SetSecurityDescriptorOwner(psdAdmin, psidAdmin, FALSE); - - if (!IsValidSecurityDescriptor(psdAdmin)) - __leave; - dwAccessDesired = ACCESS_READ; - - GenericMapping.GenericRead = ACCESS_READ; - GenericMapping.GenericWrite = ACCESS_WRITE; - GenericMapping.GenericExecute = 0; - GenericMapping.GenericAll = ACCESS_READ | ACCESS_WRITE; - - if (!AccessCheck(psdAdmin, hImpersonationToken, dwAccessDesired, - &GenericMapping, &ps, &dwStructureSize, &dwStatus, - &fReturn)) - { - fReturn = FALSE; - __leave; - } - } - __finally - { - // Clean up. - if (pACL) LocalFree(pACL); - if (psdAdmin) LocalFree(psdAdmin); - if (psidAdmin) FreeSid(psidAdmin); - if (hImpersonationToken) CloseHandle (hImpersonationToken); - if (hToken) CloseHandle (hToken); - } - - return fReturn; -} -#endif // __WINDOWS__ - -/****************************************************************************/ -/* main() and friends */ -/****************************************************************************/ - -static void printHelp(const char *cn,FILE *out) -{ - fprintf(out, - "%s version %d.%d.%d" ZT_EOL_S, - PROGRAM_NAME, - ZEROTIER_ONE_VERSION_MAJOR, ZEROTIER_ONE_VERSION_MINOR, ZEROTIER_ONE_VERSION_REVISION); - fprintf(out, - COPYRIGHT_NOTICE ZT_EOL_S - LICENSE_GRANT ZT_EOL_S); - fprintf(out,"Usage: %s [-switches] [home directory]" ZT_EOL_S"" ZT_EOL_S,cn); - fprintf(out,"Available switches:" ZT_EOL_S); - fprintf(out," -h - Display this help" ZT_EOL_S); - fprintf(out," -v - Show version" ZT_EOL_S); - fprintf(out," -U - Skip privilege check and do not attempt to drop privileges" ZT_EOL_S); - fprintf(out," -p - Port for UDP and TCP/HTTP (default: 9993, 0 for random)" ZT_EOL_S); - -#ifdef __UNIX_LIKE__ - fprintf(out," -d - Fork and run as daemon (Unix-ish OSes)" ZT_EOL_S); -#endif // __UNIX_LIKE__ - -#ifdef __WINDOWS__ - fprintf(out," -C - Run from command line instead of as service (Windows)" ZT_EOL_S); - fprintf(out," -I - Install Windows service (Windows)" ZT_EOL_S); - fprintf(out," -R - Uninstall Windows service (Windows)" ZT_EOL_S); - fprintf(out," -D - Remove all instances of Windows tap device (Windows)" ZT_EOL_S); -#endif // __WINDOWS__ - - fprintf(out," -i - Generate and manage identities (zerotier-idtool)" ZT_EOL_S); - fprintf(out," -q - Query API (zerotier-cli)" ZT_EOL_S); -} - -class _OneServiceRunner -{ -public: - _OneServiceRunner(const char *pn,const std::string &hd,unsigned int p) : progname(pn),returnValue(0),port(p),homeDir(hd) {} - void threadMain() - throw() - { - try { - for(;;) { - zt1Service = OneService::newInstance(homeDir.c_str(),port); - switch(zt1Service->run()) { - case OneService::ONE_STILL_RUNNING: // shouldn't happen, run() won't return until done - case OneService::ONE_NORMAL_TERMINATION: - break; - case OneService::ONE_UNRECOVERABLE_ERROR: - fprintf(stderr,"%s: fatal error: %s" ZT_EOL_S,progname,zt1Service->fatalErrorMessage().c_str()); - returnValue = 1; - break; - case OneService::ONE_IDENTITY_COLLISION: { - delete zt1Service; - zt1Service = (OneService *)0; - std::string oldid; - OSUtils::readFile((homeDir + ZT_PATH_SEPARATOR_S + "identity.secret").c_str(),oldid); - if (oldid.length()) { - OSUtils::writeFile((homeDir + ZT_PATH_SEPARATOR_S + "identity.secret.saved_after_collision").c_str(),oldid); - OSUtils::rm((homeDir + ZT_PATH_SEPARATOR_S + "identity.secret").c_str()); - OSUtils::rm((homeDir + ZT_PATH_SEPARATOR_S + "identity.public").c_str()); - } - } continue; // restart! - } - break; // terminate loop -- normally we don't keep restarting - } - - delete zt1Service; - zt1Service = (OneService *)0; - } catch ( ... ) { - fprintf(stderr,"%s: unexpected exception starting main OneService instance" ZT_EOL_S,progname); - returnValue = 1; - } - } - const char *progname; - unsigned int returnValue; - unsigned int port; - const std::string &homeDir; -}; - -#ifdef __WINDOWS__ -int __cdecl _tmain(int argc, _TCHAR* argv[]) -#else -int main(int argc,char **argv) -#endif -{ -#ifdef __UNIX_LIKE__ - signal(SIGHUP,&_sighandlerHup); - signal(SIGPIPE,SIG_IGN); - signal(SIGIO,SIG_IGN); - signal(SIGUSR1,SIG_IGN); - signal(SIGUSR2,SIG_IGN); - signal(SIGALRM,SIG_IGN); - signal(SIGINT,&_sighandlerQuit); - signal(SIGTERM,&_sighandlerQuit); - signal(SIGQUIT,&_sighandlerQuit); - signal(SIGINT,&_sighandlerQuit); - - /* Ensure that there are no inherited file descriptors open from a previous - * incarnation. This is a hack to ensure that GitHub issue #61 or variants - * of it do not return, and should not do anything otherwise bad. */ - { - int mfd = STDIN_FILENO; - if (STDOUT_FILENO > mfd) mfd = STDOUT_FILENO; - if (STDERR_FILENO > mfd) mfd = STDERR_FILENO; - for(int f=mfd+1;f<1024;++f) - ::close(f); - } - - bool runAsDaemon = false; -#endif // __UNIX_LIKE__ - -#ifdef __WINDOWS__ - { - WSADATA wsaData; - WSAStartup(MAKEWORD(2,2),&wsaData); - } - -#ifdef ZT_WIN_RUN_IN_CONSOLE - bool winRunFromCommandLine = true; -#else - bool winRunFromCommandLine = false; -#endif -#endif // __WINDOWS__ - - if ((strstr(argv[0],"zerotier-idtool"))||(strstr(argv[0],"ZEROTIER-IDTOOL"))) - return idtool(argc,argv); - if ((strstr(argv[0],"zerotier-cli"))||(strstr(argv[0],"ZEROTIER-CLI"))) - return cli(argc,argv); - - std::string homeDir; - unsigned int port = ZT_DEFAULT_PORT; - bool skipRootCheck = false; - - for(int i=1;i 0xffff) { - printHelp(argv[0],stdout); - return 1; - } - break; - -#ifdef __UNIX_LIKE__ - case 'd': // Run in background as daemon - runAsDaemon = true; - break; -#endif // __UNIX_LIKE__ - - case 'U': - skipRootCheck = true; - break; - - case 'v': // Display version - printf("%d.%d.%d" ZT_EOL_S,ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION); - return 0; - - case 'i': // Invoke idtool personality - if (argv[i][2]) { - printHelp(argv[0],stdout); - return 0; - } else return idtool(argc-1,argv+1); - - case 'q': // Invoke cli personality - if (argv[i][2]) { - printHelp(argv[0],stdout); - return 0; - } else return cli(argc,argv); - -#ifdef __WINDOWS__ - case 'C': // Run from command line instead of as Windows service - winRunFromCommandLine = true; - break; - - case 'I': { // Install this binary as a Windows service - if (IsCurrentUserLocalAdministrator() != TRUE) { - fprintf(stderr,"%s: must be run as a local administrator." ZT_EOL_S,argv[0]); - return 1; - } - std::string ret(InstallService(ZT_SERVICE_NAME,ZT_SERVICE_DISPLAY_NAME,ZT_SERVICE_START_TYPE,ZT_SERVICE_DEPENDENCIES,ZT_SERVICE_ACCOUNT,ZT_SERVICE_PASSWORD)); - if (ret.length()) { - fprintf(stderr,"%s: unable to install service: %s" ZT_EOL_S,argv[0],ret.c_str()); - return 3; - } - return 0; - } break; - - case 'R': { // Uninstall this binary as Windows service - if (IsCurrentUserLocalAdministrator() != TRUE) { - fprintf(stderr,"%s: must be run as a local administrator." ZT_EOL_S,argv[0]); - return 1; - } - std::string ret(UninstallService(ZT_SERVICE_NAME)); - if (ret.length()) { - fprintf(stderr,"%s: unable to uninstall service: %s" ZT_EOL_S,argv[0],ret.c_str()); - return 3; - } - return 0; - } break; - - case 'D': { - std::string err = WindowsEthernetTap::destroyAllPersistentTapDevices(); - if (err.length() > 0) { - fprintf(stderr,"%s: unable to uninstall one or more persistent tap devices: %s" ZT_EOL_S,argv[0],err.c_str()); - return 3; - } - return 0; - } break; -#endif // __WINDOWS__ - - case 'h': - case '?': - default: - printHelp(argv[0],stdout); - return 0; - } - } else { - if (homeDir.length()) { - printHelp(argv[0],stdout); - return 0; - } else { - homeDir = argv[i]; - } - } - } - - if (!homeDir.length()) - homeDir = OneService::platformDefaultHomePath(); - if (!homeDir.length()) { - fprintf(stderr,"%s: no home path specified and no platform default available" ZT_EOL_S,argv[0]); - return 1; - } else { - std::vector hpsp(OSUtils::split(homeDir.c_str(),ZT_PATH_SEPARATOR_S,"","")); - std::string ptmp; - if (homeDir[0] == ZT_PATH_SEPARATOR) - ptmp.push_back(ZT_PATH_SEPARATOR); - for(std::vector::iterator pi(hpsp.begin());pi!=hpsp.end();++pi) { - if (ptmp.length() > 0) - ptmp.push_back(ZT_PATH_SEPARATOR); - ptmp.append(*pi); - if ((*pi != ".")&&(*pi != "..")) { - if (!OSUtils::mkdir(ptmp)) - throw std::runtime_error("home path does not exist, and could not create"); - } - } - } - - // This can be removed once the new controller code has been around for many versions - if (OSUtils::fileExists((homeDir + ZT_PATH_SEPARATOR_S + "controller.db").c_str(),true)) { - fprintf(stderr,"%s: FATAL: an old controller.db exists in %s -- see instructions in controller/README.md for how to migrate!" ZT_EOL_S,argv[0],homeDir.c_str()); - return 1; - } - -#ifdef __UNIX_LIKE__ -#ifndef ZT_ONE_NO_ROOT_CHECK - if ((!skipRootCheck)&&(getuid() != 0)) { - fprintf(stderr,"%s: must be run as root (uid 0)" ZT_EOL_S,argv[0]); - return 1; - } -#endif // !ZT_ONE_NO_ROOT_CHECK - if (runAsDaemon) { - long p = (long)fork(); - if (p < 0) { - fprintf(stderr,"%s: could not fork" ZT_EOL_S,argv[0]); - return 1; - } else if (p > 0) - return 0; // forked - // else p == 0, so we are daemonized - } -#endif // __UNIX_LIKE__ - -#ifdef __WINDOWS__ - // Uninstall legacy tap devices. New devices will automatically be installed and configured - // when tap instances are created. - WindowsEthernetTap::destroyAllLegacyPersistentTapDevices(); - - if (winRunFromCommandLine) { - // Running in "interactive" mode (mostly for debugging) - if (IsCurrentUserLocalAdministrator() != TRUE) { - if (!skipRootCheck) { - fprintf(stderr,"%s: must be run as a local administrator." ZT_EOL_S,argv[0]); - return 1; - } - } else { - _winPokeAHole(); - } - SetConsoleCtrlHandler(&_winConsoleCtrlHandler,TRUE); - // continues on to ordinary command line execution code below... - } else { - // Running from service manager - _winPokeAHole(); - ZeroTierOneService zt1WindowsService; - if (CServiceBase::Run(zt1WindowsService) == TRUE) { - return 0; - } else { - fprintf(stderr,"%s: unable to start service (try -h for help)" ZT_EOL_S,argv[0]); - return 1; - } - } -#endif // __WINDOWS__ - -#ifdef __UNIX_LIKE__ -#ifdef ZT_HAVE_DROP_PRIVILEGES - if (!skipRootCheck) - dropPrivileges(argv[0],homeDir); -#endif - - std::string pidPath(homeDir + ZT_PATH_SEPARATOR_S + ZT_PID_PATH); - { - // Write .pid file to home folder - FILE *pf = fopen(pidPath.c_str(),"w"); - if (pf) { - fprintf(pf,"%ld",(long)getpid()); - fclose(pf); - } - } -#endif // __UNIX_LIKE__ - - _OneServiceRunner thr(argv[0],homeDir,port); - thr.threadMain(); - //Thread::join(Thread::start(&thr)); - -#ifdef __UNIX_LIKE__ - OSUtils::rm(pidPath.c_str()); -#endif - - return thr.returnValue; -}