diff options
Diffstat (limited to 'service/OneService.cpp')
-rw-r--r-- | service/OneService.cpp | 319 |
1 files changed, 297 insertions, 22 deletions
diff --git a/service/OneService.cpp b/service/OneService.cpp index 13108aa1..3f45cd1e 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -28,6 +28,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <stdint.h> #include <string> #include <map> @@ -46,15 +47,29 @@ #include "../node/Utils.hpp" #include "../node/InetAddress.hpp" #include "../node/MAC.hpp" +#include "../node/Identity.hpp" #include "../osdep/Phy.hpp" +#include "../osdep/Thread.hpp" #include "../osdep/OSUtils.hpp" +#include "../osdep/Http.hpp" +#include "../osdep/BackgroundResolver.hpp" #include "OneService.hpp" #include "ControlPlane.hpp" +#ifdef ZT_ENABLE_NETWORK_CONTROLLER +#include "../controller/SqliteNetworkController.hpp" +#else +class SqliteNetworkController; +#endif // ZT_ENABLE_NETWORK_CONTROLLER + #ifdef __WINDOWS__ #include <ShlObj.h> +#else +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> #endif // Include the right tap device driver for this platform -- add new platforms here @@ -85,15 +100,223 @@ namespace ZeroTier { typedef BSDEthernetTap EthernetTap; } // How often to check for new multicast subscriptions on a tap device #define ZT_TAP_CHECK_MULTICAST_INTERVAL 30000 +// Path under ZT1 home for controller database if controller is enabled +#define ZT1_CONTROLLER_DB_PATH "controller.db" + +// TCP fallback relay host +#define ZT1_TCP_FALLBACK_RELAY "tcp-fallback.zerotier.com" + +// Frequency at which we re-resolve the TCP fallback relay +#define ZT1_TCP_FALLBACK_RERESOLVE_DELAY 86400000 + namespace ZeroTier { +namespace { + +#ifdef ZT_AUTO_UPDATE +#define ZT_AUTO_UPDATE_MAX_HTTP_RESPONSE_SIZE (1024 * 1024 * 64) +#define ZT_AUTO_UPDATE_CHECK_PERIOD 21600000 +class BackgroundSoftwareUpdateChecker +{ +public: + bool isValidSigningIdentity(const Identity &id) + { + return ( + /* 0005 */ (id == Identity("ba57ea350e:0:9d4be6d7f86c5660d5ee1951a3d759aa6e12a84fc0c0b74639500f1dbc1a8c566622e7d1c531967ebceb1e9d1761342f88324a8ba520c93c35f92f35080fa23f")) + /* 0006 */ ||(id == Identity("5067b21b83:0:8af477730f5055c48135b84bed6720a35bca4c0e34be4060a4c636288b1ec22217eb22709d610c66ed464c643130c51411bbb0294eef12fbe8ecc1a1e2c63a7a")) + /* 0007 */ ||(id == Identity("4f5e97a8f1:0:57880d056d7baeb04bbc057d6f16e6cb41388570e87f01492fce882485f65a798648595610a3ad49885604e7fb1db2dd3c2c534b75e42c3c0b110ad07b4bb138")) + /* 0008 */ ||(id == Identity("580bbb8e15:0:ad5ef31155bebc6bc413991992387e083fed26d699997ef76e7c947781edd47d1997161fa56ba337b1a2b44b129fd7c7197ce5185382f06011bc88d1363b4ddd")) + ); + } + + void doUpdateCheck() + { + std::string url(OneService::autoUpdateUrl()); + if ((url.length() <= 7)||(url.substr(0,7) != "http://")) + return; + + std::string httpHost; + std::string httpPath; + { + std::size_t slashIdx = url.substr(7).find_first_of('/'); + if (slashIdx == std::string::npos) { + httpHost = url.substr(7); + httpPath = "/"; + } else { + httpHost = url.substr(7,slashIdx); + httpPath = url.substr(slashIdx + 7); + } + } + if (httpHost.length() == 0) + return; + + std::vector<InetAddress> ips(OSUtils::resolve(httpHost.c_str())); + for(std::vector<InetAddress>::iterator ip(ips.begin());ip!=ips.end();++ip) { + if (!ip->port()) + ip->setPort(80); + std::string nfoPath = httpPath + "LATEST.nfo"; + std::map<std::string,std::string> requestHeaders,responseHeaders; + std::string body; + requestHeaders["Host"] = httpHost; + unsigned int scode = Http::GET(ZT_AUTO_UPDATE_MAX_HTTP_RESPONSE_SIZE,60000,reinterpret_cast<const struct sockaddr *>(&(*ip)),nfoPath.c_str(),requestHeaders,responseHeaders,body); + //fprintf(stderr,"UPDATE %s %s %u %lu\n",ip->toString().c_str(),nfoPath.c_str(),scode,body.length()); + if ((scode == 200)&&(body.length() > 0)) { + /* NFO fields: + * + * file=<filename> + * signedBy=<signing identity> + * ed25519=<ed25519 ECC signature of archive> + * vMajor=<major version> + * vMinor=<minor version> + * vRevision=<revision> */ + Dictionary nfo(body); + + unsigned int vMajor = Utils::strToUInt(nfo.get("vMajor","0").c_str()); + unsigned int vMinor = Utils::strToUInt(nfo.get("vMinor","0").c_str()); + unsigned int vRevision = Utils::strToUInt(nfo.get("vRevision","0").c_str()); + if (Utils::compareVersion(vMajor,vMinor,vRevision,ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION) <= 0) { + //fprintf(stderr,"UPDATE %u.%u.%u is not newer than our version\n",vMajor,vMinor,vRevision); + return; + } + + Identity signedBy; + if ((!signedBy.fromString(nfo.get("signedBy","")))||(!isValidSigningIdentity(signedBy))) { + //fprintf(stderr,"UPDATE invalid signedBy or not authorized signing identity.\n"); + return; + } + + std::string filePath(nfo.get("file","")); + if ((!filePath.length())||(filePath.find("..") != std::string::npos)) + return; + filePath = httpPath + filePath; + + std::string fileData; + if (Http::GET(ZT_AUTO_UPDATE_MAX_HTTP_RESPONSE_SIZE,60000,reinterpret_cast<const struct sockaddr *>(&(*ip)),filePath.c_str(),requestHeaders,responseHeaders,fileData) != 200) { + //fprintf(stderr,"UPDATE GET %s failed\n",filePath.c_str()); + return; + } + + std::string ed25519(nfo.get("ed25519","")); + if ((ed25519.length() == 0)||(!signedBy.verify(fileData.data(),(unsigned int)fileData.length(),ed25519.data(),(unsigned int)ed25519.length()))) { + //fprintf(stderr,"UPDATE %s failed signature check!\n",filePath.c_str()); + return; + } + + /* --------------------------------------------------------------- */ + /* We made it! Begin OS-specific installation code. */ + +#ifdef __APPLE__ + /* OSX version is in the form of a MacOSX .pkg file, so we will + * launch installer (normally in /usr/sbin) to install it. It will + * then turn around and shut down the service, update files, and + * relaunch. */ + { + char bashp[128],pkgp[128]; + Utils::snprintf(bashp,sizeof(bashp),"/tmp/ZeroTierOne-update-%u.%u.%u.sh",vMajor,vMinor,vRevision); + Utils::snprintf(pkgp,sizeof(pkgp),"/tmp/ZeroTierOne-update-%u.%u.%u.pkg",vMajor,vMinor,vRevision); + FILE *pkg = fopen(pkgp,"w"); + if ((!pkg)||(fwrite(fileData.data(),fileData.length(),1,pkg) != 1)) { + fclose(pkg); + unlink(bashp); + unlink(pkgp); + fprintf(stderr,"UPDATE error writing %s\n",pkgp); + return; + } + fclose(pkg); + FILE *bash = fopen(bashp,"w"); + if (!bash) { + fclose(pkg); + unlink(bashp); + unlink(pkgp); + fprintf(stderr,"UPDATE error writing %s\n",bashp); + return; + } + fprintf(bash, + "#!/bin/bash\n" + "export PATH=/bin:/usr/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/local/sbin\n" + "sleep 2\n" + "installer -pkg \"%s\" -target /\n" + "sleep 1\n" + "rm -f \"%s\" \"%s\"\n" + "exit 0\n", + pkgp, + pkgp, + bashp); + fclose(bash); + long pid = (long)vfork(); + if (pid == 0) { + execl("/bin/bash","/bin/bash",bashp,(char *)0); + exit(0); + } + } +#endif // __APPLE__ + +#ifdef __WINDOWS__ + /* Windows version comes in the form of .MSI package that + * takes care of everything. */ + { + char tempp[512],batp[512],msip[512],cmdline[512]; + if (GetTempPathA(sizeof(tempp),tempp) <= 0) + return; + CreateDirectoryA(tempp,(LPSECURITY_ATTRIBUTES)0); + Utils::snprintf(batp,sizeof(batp),"%s\\ZeroTierOne-update-%u.%u.%u.bat",tempp,vMajor,vMinor,vRevision); + Utils::snprintf(msip,sizeof(msip),"%s\\ZeroTierOne-update-%u.%u.%u.msi",tempp,vMajor,vMinor,vRevision); + FILE *msi = fopen(msip,"wb"); + if ((!msi)||(fwrite(fileData.data(),(size_t)fileData.length(),1,msi) != 1)) { + fclose(msi); + return; + } + fclose(msi); + FILE *bat = fopen(batp,"wb"); + if (!bat) + return; + fprintf(bat, + "TIMEOUT.EXE /T 1 /NOBREAK\r\n" + "NET.EXE STOP \"ZeroTierOneService\"\r\n" + "TIMEOUT.EXE /T 1 /NOBREAK\r\n" + "MSIEXEC.EXE /i \"%s\" /qn\r\n" + "TIMEOUT.EXE /T 1 /NOBREAK\r\n" + "NET.EXE START \"ZeroTierOneService\"\r\n" + "DEL \"%s\"\r\n" + "DEL \"%s\"\r\n", + msip, + msip, + batp); + fclose(bat); + STARTUPINFOA si; + PROCESS_INFORMATION pi; + memset(&si,0,sizeof(si)); + memset(&pi,0,sizeof(pi)); + Utils::snprintf(cmdline,sizeof(cmdline),"CMD.EXE /c \"%s\"",batp); + CreateProcessA(NULL,cmdline,NULL,NULL,FALSE,CREATE_NO_WINDOW|CREATE_NEW_PROCESS_GROUP,NULL,NULL,&si,&pi); + } +#endif // __WINDOWS__ + + /* --------------------------------------------------------------- */ + + return; + } // else try to fetch from next IP address + } + } + + void threadMain() + throw() + { + try { + this->doUpdateCheck(); + } catch ( ... ) {} + } +}; +static BackgroundSoftwareUpdateChecker backgroundSoftwareUpdateChecker; +#endif // ZT_AUTO_UPDATE + class OneServiceImpl; static int SnodeVirtualNetworkConfigFunction(ZT1_Node *node,void *uptr,uint64_t nwid,enum ZT1_VirtualNetworkConfigOperation op,const ZT1_VirtualNetworkConfig *nwconf); static void SnodeEventCallback(ZT1_Node *node,void *uptr,enum ZT1_Event event,const void *metaData); static long SnodeDataStoreGetFunction(ZT1_Node *node,void *uptr,const char *name,void *buf,unsigned long bufSize,unsigned long readIndex,unsigned long *totalSize); static int SnodeDataStorePutFunction(ZT1_Node *node,void *uptr,const char *name,const void *data,unsigned long len,int secure); -static int SnodeWirePacketSendFunction(ZT1_Node *node,void *uptr,const struct sockaddr_storage *addr,unsigned int desperation,const void *data,unsigned int len); +static int SnodeWirePacketSendFunction(ZT1_Node *node,void *uptr,const struct sockaddr_storage *addr,const void *data,unsigned int len); static void SnodeVirtualNetworkFrameFunction(ZT1_Node *node,void *uptr,uint64_t nwid,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len); static void StapFrameHandler(void *uptr,uint64_t nwid,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len); @@ -147,10 +370,13 @@ struct TcpConnection class OneServiceImpl : public OneService { public: - OneServiceImpl(const char *hp,unsigned int port,NetworkController *master,const char *overrideRootTopology) : + OneServiceImpl(const char *hp,unsigned int port,const char *overrideRootTopology) : _homePath((hp) ? hp : "."), + _tcpFallbackResolver(ZT1_TCP_FALLBACK_RELAY), +#ifdef ZT_ENABLE_NETWORK_CONTROLLER + _controller((_homePath + ZT_PATH_SEPARATOR_S + ZT1_CONTROLLER_DB_PATH).c_str()), +#endif _phy(this,true), - _master(master), _overrideRootTopology((overrideRootTopology) ? overrideRootTopology : ""), _node((Node *)0), _controlPlane((ControlPlane *)0), @@ -227,13 +453,16 @@ public: SnodeEventCallback, ((_overrideRootTopology.length() > 0) ? _overrideRootTopology.c_str() : (const char *)0)); - if (_master) - _node->setNetconfMaster((void *)_master); +#ifdef ZT_ENABLE_NETWORK_CONTROLLER + _node->setNetconfMaster((void *)&_controller); +#endif _controlPlane = new ControlPlane(this,_node,(_homePath + ZT_PATH_SEPARATOR_S + "ui").c_str()); _controlPlane->addAuthToken(authToken.c_str()); - if (_master) - _controlPlane->mount("controller",reinterpret_cast<SqliteNetworkController *>(_master)); + +#ifdef ZT_ENABLE_NETWORK_CONTROLLER + _controlPlane->setController(&_controller); +#endif { // Remember networks from previous session std::vector<std::string> networksDotD(OSUtils::listDirectory((_homePath + ZT_PATH_SEPARATOR_S + "networks.d").c_str())); @@ -246,6 +475,10 @@ public: _nextBackgroundTaskDeadline = 0; uint64_t lastTapMulticastGroupCheck = 0; + uint64_t lastTcpFallbackResolve = 0; +#ifdef ZT_AUTO_UPDATE + uint64_t lastSoftwareUpdateCheck = 0; +#endif // ZT_AUTO_UPDATE for(;;) { _run_m.lock(); if (!_run) { @@ -263,6 +496,18 @@ public: dl = _nextBackgroundTaskDeadline; } +#ifdef ZT_AUTO_UPDATE + if ((now - lastSoftwareUpdateCheck) >= ZT_AUTO_UPDATE_CHECK_PERIOD) { + lastSoftwareUpdateCheck = OSUtils::now(); + Thread::start(&backgroundSoftwareUpdateChecker); + } +#endif // ZT_AUTO_UPDATE + + if ((now - lastTcpFallbackResolve) >= ZT1_TCP_FALLBACK_RERESOLVE_DELAY) { + lastTcpFallbackResolve = now; + _tcpFallbackResolver.resolveNow(); + } + if ((now - lastTapMulticastGroupCheck) >= ZT_TAP_CHECK_MULTICAST_INTERVAL) { lastTapMulticastGroupCheck = now; Mutex::Lock _l(_taps_m); @@ -345,7 +590,6 @@ public: ZT1_ResultCode rc = _node->processWirePacket( OSUtils::now(), (const struct sockaddr_storage *)from, // Phy<> uses sockaddr_storage, so it'll always be that big - 0, // desperation == 0, direct UDP data, len, &_nextBackgroundTaskDeadline); @@ -364,7 +608,8 @@ public: if (!success) return; - // Outgoing connections are right now only tunnel connections + // Outgoing TCP connections are always TCP fallback tunnel connections. + TcpConnection *tc = &(_tcpConections[sock]); tc->type = TcpConnection::TCP_TUNNEL_OUTGOING; tc->shouldKeepAlive = true; // unused @@ -392,7 +637,8 @@ public: inline void phyOnTcpAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN,const struct sockaddr *from) { - // Incoming connections are TCP HTTP requests + // Incoming TCP connections are HTTP JSON API requests. + TcpConnection *tc = &(_tcpConections[sockN]); tc->type = TcpConnection::TCP_HTTP_INCOMING; tc->shouldKeepAlive = true; @@ -422,6 +668,7 @@ public: { TcpConnection *tc = reinterpret_cast<TcpConnection *>(*uptr); switch(tc->type) { + case TcpConnection::TCP_HTTP_INCOMING: case TcpConnection::TCP_HTTP_OUTGOING: http_parser_execute(&(tc->parser),&HTTP_PARSER_SETTINGS,(const char *)data,len); @@ -430,6 +677,7 @@ public: return; } break; + case TcpConnection::TCP_TUNNEL_OUTGOING: tc->body.append((const char *)data,len); if (tc->body.length() > 65535) { @@ -481,7 +729,6 @@ public: ZT1_ResultCode rc = _node->processWirePacket( OSUtils::now(), (const struct sockaddr_storage *)&from, // Phy<> uses sockaddr_storage, so it'll always be that big - 1, // desperation == 1, TCP tunnel proxy data, plen, &_nextBackgroundTaskDeadline); @@ -496,12 +743,14 @@ public: return; } } + if (tc->body.length() > (mlen + 5)) tc->body = tc->body.substr(mlen + 5); else tc->body = ""; } } break; + } } @@ -509,7 +758,7 @@ public: { TcpConnection *tc = reinterpret_cast<TcpConnection *>(*uptr); if (tc->writeBuf.length()) { - long sent = _phy.tcpSend(sock,tc->writeBuf.data(),tc->writeBuf.length(),true); + long sent = (long)_phy.tcpSend(sock,tc->writeBuf.data(),(unsigned long)tc->writeBuf.length(),true); if (sent > 0) { tc->lastActivity = OSUtils::now(); if ((unsigned long)sent == (unsigned long)tc->writeBuf.length()) { @@ -598,9 +847,6 @@ public: this->terminate(); } break; - case ZT1_EVENT_SAW_MORE_RECENT_VERSION: { - } break; - case ZT1_EVENT_TRACE: { if (metaData) { ::fprintf(stderr,"%s"ZT_EOL_S,(const char *)metaData); @@ -667,7 +913,7 @@ public: } } - inline int nodeWirePacketSendFunction(const struct sockaddr_storage *addr,unsigned int desperation,const void *data,unsigned int len) + inline int nodeWirePacketSendFunction(const struct sockaddr_storage *addr,const void *data,unsigned int len) { switch(addr->ss_family) { case AF_INET: @@ -763,8 +1009,11 @@ private: } const std::string _homePath; + BackgroundResolver _tcpFallbackResolver; +#ifdef ZT_ENABLE_NETWORK_CONTROLLER + SqliteNetworkController _controller; +#endif Phy<OneServiceImpl *> _phy; - NetworkController *_master; std::string _overrideRootTopology; Node *_node; PhySocket *_v4UdpSocket; @@ -796,8 +1045,8 @@ static long SnodeDataStoreGetFunction(ZT1_Node *node,void *uptr,const char *name { return reinterpret_cast<OneServiceImpl *>(uptr)->nodeDataStoreGetFunction(name,buf,bufSize,readIndex,totalSize); } static int SnodeDataStorePutFunction(ZT1_Node *node,void *uptr,const char *name,const void *data,unsigned long len,int secure) { return reinterpret_cast<OneServiceImpl *>(uptr)->nodeDataStorePutFunction(name,data,len,secure); } -static int SnodeWirePacketSendFunction(ZT1_Node *node,void *uptr,const struct sockaddr_storage *addr,unsigned int desperation,const void *data,unsigned int len) -{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodeWirePacketSendFunction(addr,desperation,data,len); } +static int SnodeWirePacketSendFunction(ZT1_Node *node,void *uptr,const struct sockaddr_storage *addr,const void *data,unsigned int len) +{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodeWirePacketSendFunction(addr,data,len); } static void SnodeVirtualNetworkFrameFunction(ZT1_Node *node,void *uptr,uint64_t nwid,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len) { reinterpret_cast<OneServiceImpl *>(uptr)->nodeVirtualNetworkFrameFunction(nwid,sourceMac,destMac,etherType,vlanId,data,len); } @@ -887,6 +1136,8 @@ static int ShttpOnMessageComplete(http_parser *parser) return 0; } +} // anonymous namespace + std::string OneService::platformDefaultHomePath() { #ifdef __UNIX_LIKE__ @@ -896,8 +1147,8 @@ std::string OneService::platformDefaultHomePath() return std::string("/Library/Application Support/ZeroTier/One"); #else -#ifdef __FreeBSD__ - // FreeBSD likes /var/db instead of /var/lib +#ifdef __BSD__ + // BSD likes /var/db instead of /var/lib return std::string("/var/db/zerotier-one"); #else // Use /var/lib for Linux and other *nix @@ -923,7 +1174,31 @@ std::string OneService::platformDefaultHomePath() #endif // __UNIX_LIKE__ or not... } -OneService *OneService::newInstance(const char *hp,unsigned int port,NetworkController *master,const char *overrideRootTopology) { return new OneServiceImpl(hp,port,master,overrideRootTopology); } +std::string OneService::autoUpdateUrl() +{ +#ifdef ZT_AUTO_UPDATE + +/* +#if defined(__LINUX__) && ( defined(__i386__) || defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(__i386) ) + if (sizeof(void *) == 8) + return "http://download.zerotier.com/ZeroTierOneInstaller-linux-x64-LATEST.nfo"; + else return "http://download.zerotier.com/ZeroTierOneInstaller-linux-x86-LATEST.nfo"; +#endif +*/ + +#if defined(__APPLE__) && ( defined(__i386__) || defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(__i386) ) + return "http://download.zerotier.com/update/mac_intel/"; +#endif + +#ifdef __WINDOWS__ + return "http://download.zerotier.com/update/win_intel/"; +#endif + +#endif // ZT_AUTO_UPDATE + return std::string(); +} + +OneService *OneService::newInstance(const char *hp,unsigned int port,const char *overrideRootTopology) { return new OneServiceImpl(hp,port,overrideRootTopology); } OneService::~OneService() {} } // namespace ZeroTier |