diff options
Diffstat (limited to 'one.cpp')
-rw-r--r-- | one.cpp | 865 |
1 files changed, 594 insertions, 271 deletions
@@ -1,6 +1,6 @@ /* * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/ + * Copyright (C) 2011-2018 ZeroTier, Inc. https://www.zerotier.com/ * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -14,6 +14,14 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. */ #include <stdio.h> @@ -43,31 +51,41 @@ #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> +#include <sys/uio.h> +#include <dirent.h> #include <signal.h> +#ifdef __LINUX__ +#include <sys/prctl.h> +#include <sys/syscall.h> +#include <sys/wait.h> +#include <linux/capability.h> +#include <linux/securebits.h> +#endif #endif #include <string> #include <stdexcept> +#include <iostream> +#include <sstream> #include "version.h" #include "include/ZeroTierOne.h" -#ifdef ZT_USE_SYSTEM_JSON_PARSER -#include <json-parser/json.h> -#else -#include "ext/json-parser/json.h" -#endif - #include "node/Identity.hpp" #include "node/CertificateOfMembership.hpp" #include "node/Utils.hpp" #include "node/NetworkController.hpp" +#include "node/Buffer.hpp" +#include "node/World.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; @@ -75,7 +93,7 @@ using namespace ZeroTier; static OneService *volatile zt1Service = (OneService *)0; #define PROGRAM_NAME "ZeroTier One" -#define COPYRIGHT_NOTICE "Copyright © 2011–2016 ZeroTier, Inc." +#define COPYRIGHT_NOTICE "Copyright (c) 2011-2018 ZeroTier, Inc." #define LICENSE_GRANT \ "This is free software: you may copy, modify, and/or distribute this" ZT_EOL_S \ "work under the terms of the GNU General Public License, version 3 or" ZT_EOL_S \ @@ -91,9 +109,10 @@ static OneService *volatile zt1Service = (OneService *)0; static void cliPrintHelp(const char *pn,FILE *out) { fprintf(out, - "%s version %d.%d.%d" ZT_EOL_S, + "%s version %d.%d.%d build %d (platform %d arch %d)" ZT_EOL_S, PROGRAM_NAME, - ZEROTIER_ONE_VERSION_MAJOR, ZEROTIER_ONE_VERSION_MINOR, ZEROTIER_ONE_VERSION_REVISION); + ZEROTIER_ONE_VERSION_MAJOR, ZEROTIER_ONE_VERSION_MINOR, ZEROTIER_ONE_VERSION_REVISION, ZEROTIER_ONE_VERSION_BUILD, + ZT_BUILD_PLATFORM, ZT_BUILD_ARCHITECTURE); fprintf(out, COPYRIGHT_NOTICE ZT_EOL_S LICENSE_GRANT ZT_EOL_S); @@ -112,6 +131,10 @@ static void cliPrintHelp(const char *pn,FILE *out) fprintf(out," join <network> - Join a network" ZT_EOL_S); fprintf(out," leave <network> - Leave a network" ZT_EOL_S); fprintf(out," set <network> <setting> - Set a network setting" ZT_EOL_S); + fprintf(out," get <network> <setting> - Get a network setting" ZT_EOL_S); + fprintf(out," listmoons - List moons (federated root sets)" ZT_EOL_S); + fprintf(out," orbit <world ID> <seed> - Join a moon via any member root" ZT_EOL_S); + fprintf(out," deorbit <world ID> - Leave a moon" ZT_EOL_S); } static std::string cliFixJsonCRs(const std::string &s) @@ -238,9 +261,9 @@ static int cli(int argc,char **argv) if (hd) { char p[4096]; #ifdef __APPLE__ - Utils::snprintf(p,sizeof(p),"%s/Library/Application Support/ZeroTier/One/authtoken.secret",hd); + OSUtils::ztsnprintf(p,sizeof(p),"%s/Library/Application Support/ZeroTier/One/authtoken.secret",hd); #else - Utils::snprintf(p,sizeof(p),"%s/.zeroTierOneAuthToken",hd); + OSUtils::ztsnprintf(p,sizeof(p),"%s/.zeroTierOneAuthToken",hd); #endif OSUtils::readFile(p,authToken); } @@ -256,7 +279,7 @@ static int cli(int argc,char **argv) InetAddress addr; { char addrtmp[256]; - Utils::snprintf(addrtmp,sizeof(addrtmp),"%s/%u",ip.c_str(),port); + OSUtils::ztsnprintf(addrtmp,sizeof(addrtmp),"%s/%u",ip.c_str(),port); addr = InetAddress(addrtmp); } @@ -276,235 +299,174 @@ static int cli(int argc,char **argv) responseHeaders, responseBody); if (scode == 200) { - printf("%s",cliFixJsonCRs(responseBody).c_str()); + 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")) { - unsigned int scode = Http::GET( - 1024 * 1024 * 16, - 60000, - (const struct sockaddr *)&addr, - "/status", - requestHeaders, - responseHeaders, - responseBody); + 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",cliFixJsonCRs(responseBody).c_str()); - return 0; + printf("%s" ZT_EOL_S,OSUtils::jsonDump(j).c_str()); } else { - json_value *j = json_parse(responseBody.c_str(),responseBody.length()); - bool good = false; - if (j) { - if (j->type == json_object) { - const char *address = (const char *)0; - bool online = false; - const char *version = (const char *)0; - for(unsigned int k=0;k<j->u.object.length;++k) { - if ((!strcmp(j->u.object.values[k].name,"address"))&&(j->u.object.values[k].value->type == json_string)) - address = j->u.object.values[k].value->u.string.ptr; - else if ((!strcmp(j->u.object.values[k].name,"version"))&&(j->u.object.values[k].value->type == json_string)) - version = j->u.object.values[k].value->u.string.ptr; - else if ((!strcmp(j->u.object.values[k].name,"online"))&&(j->u.object.values[k].value->type == json_boolean)) - online = (j->u.object.values[k].value->u.boolean != 0); - } - if ((address)&&(version)) { - printf("200 info %s %s %s" ZT_EOL_S,address,(online ? "ONLINE" : "OFFLINE"),version); - good = true; - } - } - json_value_free(j); - } - if (good) { - return 0; - } else { - printf("%u %s invalid JSON response" ZT_EOL_S,scode,command.c_str()); - return 1; + 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") { - unsigned int scode = Http::GET( - 1024 * 1024 * 16, - 60000, - (const struct sockaddr *)&addr, - "/peer", - requestHeaders, - responseHeaders, - responseBody); + 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",cliFixJsonCRs(responseBody).c_str()); - return 0; + printf("%s" ZT_EOL_S,OSUtils::jsonDump(j).c_str()); } else { - printf("200 listpeers <ztaddr> <paths> <latency> <version> <role>" ZT_EOL_S); - json_value *j = json_parse(responseBody.c_str(),responseBody.length()); - if (j) { - if (j->type == json_array) { - for(unsigned int p=0;p<j->u.array.length;++p) { - json_value *jp = j->u.array.values[p]; - if (jp->type == json_object) { - const char *address = (const char *)0; - std::string paths; - int64_t latency = 0; - int64_t versionMajor = -1,versionMinor = -1,versionRev = -1; - const char *role = (const char *)0; - for(unsigned int k=0;k<jp->u.object.length;++k) { - if ((!strcmp(jp->u.object.values[k].name,"address"))&&(jp->u.object.values[k].value->type == json_string)) - address = jp->u.object.values[k].value->u.string.ptr; - else if ((!strcmp(jp->u.object.values[k].name,"versionMajor"))&&(jp->u.object.values[k].value->type == json_integer)) - versionMajor = jp->u.object.values[k].value->u.integer; - else if ((!strcmp(jp->u.object.values[k].name,"versionMinor"))&&(jp->u.object.values[k].value->type == json_integer)) - versionMinor = jp->u.object.values[k].value->u.integer; - else if ((!strcmp(jp->u.object.values[k].name,"versionRev"))&&(jp->u.object.values[k].value->type == json_integer)) - versionRev = jp->u.object.values[k].value->u.integer; - else if ((!strcmp(jp->u.object.values[k].name,"role"))&&(jp->u.object.values[k].value->type == json_string)) - role = jp->u.object.values[k].value->u.string.ptr; - else if ((!strcmp(jp->u.object.values[k].name,"latency"))&&(jp->u.object.values[k].value->type == json_integer)) - latency = jp->u.object.values[k].value->u.integer; - else if ((!strcmp(jp->u.object.values[k].name,"paths"))&&(jp->u.object.values[k].value->type == json_array)) { - for(unsigned int pp=0;pp<jp->u.object.values[k].value->u.array.length;++pp) { - json_value *jpath = jp->u.object.values[k].value->u.array.values[pp]; - if (jpath->type == json_object) { - const char *paddr = (const char *)0; - int64_t lastSend = 0; - int64_t lastReceive = 0; - bool preferred = false; - bool active = false; - for(unsigned int kk=0;kk<jpath->u.object.length;++kk) { - if ((!strcmp(jpath->u.object.values[kk].name,"address"))&&(jpath->u.object.values[kk].value->type == json_string)) - paddr = jpath->u.object.values[kk].value->u.string.ptr; - else if ((!strcmp(jpath->u.object.values[kk].name,"lastSend"))&&(jpath->u.object.values[kk].value->type == json_integer)) - lastSend = jpath->u.object.values[kk].value->u.integer; - else if ((!strcmp(jpath->u.object.values[kk].name,"lastReceive"))&&(jpath->u.object.values[kk].value->type == json_integer)) - lastReceive = jpath->u.object.values[kk].value->u.integer; - else if ((!strcmp(jpath->u.object.values[kk].name,"preferred"))&&(jpath->u.object.values[kk].value->type == json_boolean)) - preferred = (jpath->u.object.values[kk].value->u.boolean != 0); - else if ((!strcmp(jpath->u.object.values[kk].name,"active"))&&(jpath->u.object.values[kk].value->type == json_boolean)) - active = (jpath->u.object.values[kk].value->u.boolean != 0); - } - if ((paddr)&&(active)) { - int64_t now = (int64_t)OSUtils::now(); - if (lastSend > 0) - lastSend = now - lastSend; - if (lastReceive > 0) - lastReceive = now - lastReceive; - char pathtmp[256]; - Utils::snprintf(pathtmp,sizeof(pathtmp),"%s;%lld;%lld;%s", - paddr, - lastSend, - lastReceive, - (preferred ? "preferred" : "active")); - if (paths.length()) - paths.push_back(','); - paths.append(pathtmp); - } - } - } - } - } - if ((address)&&(role)) { - char verstr[64]; - if ((versionMajor >= 0)&&(versionMinor >= 0)&&(versionRev >= 0)) - Utils::snprintf(verstr,sizeof(verstr),"%lld.%lld.%lld",versionMajor,versionMinor,versionRev); - else { - verstr[0] = '-'; - verstr[1] = (char)0; - } - printf("200 listpeers %s %s %lld %s %s" ZT_EOL_S,address,(paths.length()) ? paths.c_str() : "-",(long long)latency,verstr,role); + printf("200 listpeers <ztaddr> <path> <latency> <version> <role>" ZT_EOL_S); + if (j.is_array()) { + for(unsigned long k=0;k<j.size();++k) { + nlohmann::json &p = j[k]; + std::string bestPath; + nlohmann::json &paths = p["paths"]; + if (paths.is_array()) { + for(unsigned long i=0;i<paths.size();++i) { + nlohmann::json &path = paths[i]; + if (path["preferred"]) { + char tmp[256]; + std::string addr = path["address"]; + const int64_t now = OSUtils::now(); + OSUtils::ztsnprintf(tmp,sizeof(tmp),"%s;%lld;%lld",addr.c_str(),now - (int64_t)path["lastSend"],now - (int64_t)path["lastReceive"]); + bestPath = tmp; + break; } } } + if (bestPath.length() == 0) bestPath = "-"; + char ver[128]; + int64_t vmaj = p["versionMajor"]; + int64_t vmin = p["versionMinor"]; + int64_t vrev = p["versionRev"]; + if (vmaj >= 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()); } - json_value_free(j); } - return 0; } + return 0; } else { printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str()); return 1; } } else if (command == "listnetworks") { - unsigned int scode = Http::GET( - 1024 * 1024 * 16, - 60000, - (const struct sockaddr *)&addr, - "/network", - requestHeaders, - responseHeaders, - responseBody); + 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",cliFixJsonCRs(responseBody).c_str()); - return 0; + printf("%s" ZT_EOL_S,OSUtils::jsonDump(j).c_str()); } else { printf("200 listnetworks <nwid> <name> <mac> <status> <type> <dev> <ZT assigned ips>" ZT_EOL_S); - json_value *j = json_parse(responseBody.c_str(),responseBody.length()); - if (j) { - if (j->type == json_array) { - for(unsigned int p=0;p<j->u.array.length;++p) { - json_value *jn = j->u.array.values[p]; - if (jn->type == json_object) { - const char *nwid = (const char *)0; - const char *name = ""; - const char *mac = (const char *)0; - const char *status = (const char *)0; - const char *type = (const char *)0; - const char *portDeviceName = ""; - std::string ips; - for(unsigned int k=0;k<jn->u.object.length;++k) { - if ((!strcmp(jn->u.object.values[k].name,"nwid"))&&(jn->u.object.values[k].value->type == json_string)) - nwid = jn->u.object.values[k].value->u.string.ptr; - else if ((!strcmp(jn->u.object.values[k].name,"name"))&&(jn->u.object.values[k].value->type == json_string)) - name = jn->u.object.values[k].value->u.string.ptr; - else if ((!strcmp(jn->u.object.values[k].name,"mac"))&&(jn->u.object.values[k].value->type == json_string)) - mac = jn->u.object.values[k].value->u.string.ptr; - else if ((!strcmp(jn->u.object.values[k].name,"status"))&&(jn->u.object.values[k].value->type == json_string)) - status = jn->u.object.values[k].value->u.string.ptr; - else if ((!strcmp(jn->u.object.values[k].name,"type"))&&(jn->u.object.values[k].value->type == json_string)) - type = jn->u.object.values[k].value->u.string.ptr; - else if ((!strcmp(jn->u.object.values[k].name,"portDeviceName"))&&(jn->u.object.values[k].value->type == json_string)) - portDeviceName = jn->u.object.values[k].value->u.string.ptr; - else if ((!strcmp(jn->u.object.values[k].name,"assignedAddresses"))&&(jn->u.object.values[k].value->type == json_array)) { - for(unsigned int a=0;a<jn->u.object.values[k].value->u.array.length;++a) { - json_value *aa = jn->u.object.values[k].value->u.array.values[a]; - if (aa->type == json_string) { - if (ips.length()) - ips.push_back(','); - ips.append(aa->u.string.ptr); - } - } + if (j.is_array()) { + for(unsigned long i=0;i<j.size();++i) { + nlohmann::json &n = j[i]; + if (n.is_object()) { + std::string aa; + nlohmann::json &assignedAddresses = n["assignedAddresses"]; + if (assignedAddresses.is_array()) { + for(unsigned long j=0;j<assignedAddresses.size();++j) { + nlohmann::json &addr = assignedAddresses[j]; + if (addr.is_string()) { + if (aa.length() > 0) aa.push_back(','); + aa.append(addr.get<std::string>()); } } - if ((nwid)&&(mac)&&(status)&&(type)) { - printf("200 listnetworks %s %s %s %s %s %s %s" ZT_EOL_S, - nwid, - (((name)&&(name[0])) ? name : "-"), - mac, - status, - type, - (((portDeviceName)&&(portDeviceName[0])) ? portDeviceName : "-"), - ((ips.length() > 0) ? ips.c_str() : "-")); - } } + 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()); } } - json_value_free(j); } } + 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) { - cliPrintHelp(argv[0],stderr); + printf("invalid network id" ZT_EOL_S); return 2; } requestHeaders["Content-Type"] = "application/json"; @@ -532,7 +494,7 @@ static int cli(int argc,char **argv) } } else if (command == "leave") { if (arg1.length() != 16) { - cliPrintHelp(argv[0],stderr); + printf("invalid network id" ZT_EOL_S); return 2; } unsigned int scode = Http::DEL( @@ -554,20 +516,94 @@ static int cli(int argc,char **argv) printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str()); return 1; } + } else if (command == "listmoons") { + const unsigned int scode = Http::GET(1024 * 1024 * 16,60000,(const struct sockaddr *)&addr,"/moon",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) { + printf("%s" ZT_EOL_S,OSUtils::jsonDump(j).c_str()); + return 0; + } else { + printf("%u %s %s" ZT_EOL_S,scode,command.c_str(),responseBody.c_str()); + return 1; + } + } else if (command == "orbit") { + const uint64_t worldId = Utils::hexStrToU64(arg1.c_str()); + const uint64_t seed = Utils::hexStrToU64(arg2.c_str()); + if ((worldId)&&(seed)) { + char jsons[1024]; + OSUtils::ztsnprintf(jsons,sizeof(jsons),"{\"seed\":\"%s\"}",arg2.c_str()); + 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("/moon/") + arg1).c_str(), + requestHeaders, + jsons, + (unsigned long)strlen(jsons), + responseHeaders, + responseBody); + if (scode == 200) { + printf("200 orbit 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 == "deorbit") { + unsigned int scode = Http::DEL( + 1024 * 1024 * 16, + 60000, + (const struct sockaddr *)&addr, + (std::string("/moon/") + arg1).c_str(), + requestHeaders, + responseHeaders, + responseBody); + if (scode == 200) { + if (json) { + printf("%s",cliFixJsonCRs(responseBody).c_str()); + } else { + printf("200 deorbit 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) { - cliPrintHelp(argv[0],stderr); + printf("invalid network id" ZT_EOL_S); 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]; - Utils::snprintf(jsons,sizeof(jsons),"{\"%s\":%s}", + 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]; - Utils::snprintf(cl,sizeof(cl),"%u",(unsigned int)strlen(jsons)); + OSUtils::ztsnprintf(cl,sizeof(cl),"%u",(unsigned int)strlen(jsons)); requestHeaders["Content-Type"] = "application/json"; requestHeaders["Content-Length"] = cl; unsigned int scode = Http::POST( @@ -577,7 +613,7 @@ static int cli(int argc,char **argv) (std::string("/network/") + arg1).c_str(), requestHeaders, jsons, - strlen(jsons), + (unsigned long)strlen(jsons), responseHeaders, responseBody); if (scode == 200) { @@ -592,6 +628,48 @@ static int cli(int argc,char **argv) cliPrintHelp(argv[0],stderr); return 2; } + } else if (command == "get") { + if (arg1.length() != 16) { + cliPrintHelp(argv[0],stderr); + return 2; + } + char jsons[1024], cl[128]; + OSUtils::ztsnprintf(cl,sizeof(cl),"%u",(unsigned int)strlen(jsons)); + requestHeaders["Content-Type"] = "application/json"; + requestHeaders["Content-Length"] = cl; + 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 (j.is_array()) { + for(unsigned long i=0;i<j.size();++i) { + nlohmann::json &n = j[i]; + if (n.is_object()) { + if (n["id"] == arg1) { + printf("%s\n", OSUtils::jsonString(n[arg2],"-").c_str()); + } + } + } + } + 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; @@ -619,7 +697,8 @@ static void idtoolPrintHelp(FILE *out,const char *pn) fprintf(out," getpublic <identity.secret>" ZT_EOL_S); fprintf(out," sign <identity.secret> <file>" ZT_EOL_S); fprintf(out," verify <identity.secret/public> <file> <signature>" ZT_EOL_S); - fprintf(out," mkcom <identity.secret> [<id,value,maxDelta> ...] (hexadecimal integers)" ZT_EOL_S); + fprintf(out," initmoon <identity.public of first seed>" ZT_EOL_S); + fprintf(out," genmoon <moon json>" ZT_EOL_S); } static Identity getIdFromArg(char *arg) @@ -631,7 +710,7 @@ static Identity getIdFromArg(char *arg) } else { // identity is to be read from a file std::string idser; if (OSUtils::readFile(arg,idser)) { - if (id.fromString(idser)) + if (id.fromString(idser.c_str())) return id; } } @@ -654,7 +733,7 @@ static int idtool(int argc,char **argv) int vanityBits = 0; if (argc >= 5) { vanity = Utils::hexStrToU64(argv[4]) & 0xffffffffffULL; - vanityBits = 4 * strlen(argv[4]); + vanityBits = 4 * (int)strlen(argv[4]); if (vanityBits > 40) vanityBits = 40; } @@ -672,14 +751,15 @@ static int idtool(int argc,char **argv) } } - std::string idser = id.toString(true); + 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); + idser = id.toString(false,idtmp); if (!OSUtils::writeFile(argv[3],idser)) { fprintf(stderr,"Error writing to %s" ZT_EOL_S,argv[3]); return 1; @@ -714,7 +794,8 @@ static int idtool(int argc,char **argv) return 1; } - printf("%s",id.toString(false).c_str()); + char idtmp[1024]; + printf("%s",id.toString(false,idtmp)); } else if (!strcmp(argv[1],"sign")) { if (argc < 4) { idtoolPrintHelp(stdout,argv[0]); @@ -738,9 +819,10 @@ static int idtool(int argc,char **argv) return 1; } C25519::Signature signature = id.sign(inf.data(),(unsigned int)inf.length()); - printf("%s",Utils::hex(signature.data,(unsigned int)signature.size()).c_str()); + char hexbuf[1024]; + printf("%s",Utils::hex(signature.data,ZT_C25519_SIGNATURE_LEN,hexbuf)); } else if (!strcmp(argv[1],"verify")) { - if (argc < 4) { + if (argc < 5) { idtoolPrintHelp(stdout,argv[0]); return 1; } @@ -757,41 +839,113 @@ static int idtool(int argc,char **argv) return 1; } - std::string signature(Utils::unhex(argv[4])); + 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 { - fprintf(stderr,"%s signature check FAILED" ZT_EOL_S,argv[3]); - return 1; + 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 if (!strcmp(argv[1],"mkcom")) { + } else if (!strcmp(argv[1],"initmoon")) { if (argc < 3) { idtoolPrintHelp(stdout,argv[0]); - return 1; - } + } else { + const Identity id = getIdFromArg(argv[2]); + if (!id) { + fprintf(stderr,"%s is not a valid identity" ZT_EOL_S,argv[2]); + return 1; + } - Identity id = getIdFromArg(argv[2]); - if ((!id)||(!id.hasPrivate())) { - fprintf(stderr,"Identity argument invalid, does not include private key, or file unreadable: %s" ZT_EOL_S,argv[2]); - return 1; + C25519::Pair kp(C25519::generate()); + + char idtmp[4096]; + nlohmann::json mj; + mj["objtype"] = "world"; + mj["worldType"] = "moon"; + mj["updatesMustBeSignedBy"] = mj["signingKey"] = Utils::hex(kp.pub.data,ZT_C25519_PUBLIC_KEY_LEN,idtmp); + mj["signingKey_SECRET"] = Utils::hex(kp.priv.data,ZT_C25519_PRIVATE_KEY_LEN,idtmp); + mj["id"] = id.address().toString(idtmp); + nlohmann::json seedj; + seedj["identity"] = id.toString(false,idtmp); + seedj["stableEndpoints"] = nlohmann::json::array(); + (mj["roots"] = nlohmann::json::array()).push_back(seedj); + std::string mjd(OSUtils::jsonDump(mj)); + + printf("%s" ZT_EOL_S,mjd.c_str()); } + } else if (!strcmp(argv[1],"genmoon")) { + if (argc < 3) { + idtoolPrintHelp(stdout,argv[0]); + } else { + std::string buf; + if (!OSUtils::readFile(argv[2],buf)) { + fprintf(stderr,"cannot read %s" ZT_EOL_S,argv[2]); + return 1; + } + nlohmann::json mj(OSUtils::jsonParse(buf)); - CertificateOfMembership com; - for(int a=3;a<argc;++a) { - std::vector<std::string> params(Utils::split(argv[a],",","","")); - if (params.size() == 3) { - uint64_t qId = Utils::hexStrToU64(params[0].c_str()); - uint64_t qValue = Utils::hexStrToU64(params[1].c_str()); - uint64_t qMaxDelta = Utils::hexStrToU64(params[2].c_str()); - com.setQualifier(qId,qValue,qMaxDelta); + const uint64_t id = Utils::hexStrToU64(OSUtils::jsonString(mj["id"],"0").c_str()); + if (!id) { + fprintf(stderr,"ID in %s is invalid" ZT_EOL_S,argv[2]); + return 1; + } + + World::Type t; + if (mj["worldType"] == "moon") { + t = World::TYPE_MOON; + } else if (mj["worldType"] == "planet") { + t = World::TYPE_PLANET; + } else { + fprintf(stderr,"invalid worldType" ZT_EOL_S); + return 1; } - } - if (!com.sign(id)) { - fprintf(stderr,"Signature of certificate of membership failed." ZT_EOL_S); - return 1; - } - printf("%s",com.toString().c_str()); + C25519::Pair signingKey; + C25519::Public updatesMustBeSignedBy; + Utils::unhex(OSUtils::jsonString(mj["signingKey"],"").c_str(),signingKey.pub.data,ZT_C25519_PUBLIC_KEY_LEN); + Utils::unhex(OSUtils::jsonString(mj["signingKey_SECRET"],"").c_str(),signingKey.priv.data,ZT_C25519_PRIVATE_KEY_LEN); + Utils::unhex(OSUtils::jsonString(mj["updatesMustBeSignedBy"],"").c_str(),updatesMustBeSignedBy.data,ZT_C25519_PUBLIC_KEY_LEN); + + std::vector<World::Root> roots; + nlohmann::json &rootsj = mj["roots"]; + if (rootsj.is_array()) { + for(unsigned long i=0;i<(unsigned long)rootsj.size();++i) { + nlohmann::json &r = rootsj[i]; + if (r.is_object()) { + roots.push_back(World::Root()); + roots.back().identity = Identity(OSUtils::jsonString(r["identity"],"").c_str()); + nlohmann::json &stableEndpointsj = r["stableEndpoints"]; + if (stableEndpointsj.is_array()) { + for(unsigned long k=0;k<(unsigned long)stableEndpointsj.size();++k) + roots.back().stableEndpoints.push_back(InetAddress(OSUtils::jsonString(stableEndpointsj[k],"").c_str())); + std::sort(roots.back().stableEndpoints.begin(),roots.back().stableEndpoints.end()); + } + } + } + } + std::sort(roots.begin(),roots.end()); + + const int64_t now = OSUtils::now(); + World w(World::make(t,id,now,updatesMustBeSignedBy,roots,signingKey)); + Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> wbuf; + w.serialize(wbuf); + char fn[128]; + OSUtils::ztsnprintf(fn,sizeof(fn),"%.16llx.moon",w.id()); + OSUtils::writeFile(fn,wbuf.data(),wbuf.size()); + printf("wrote %s (signed world with timestamp %llu)" ZT_EOL_S,fn,(unsigned long long)now); + } } else { idtoolPrintHelp(stdout,argv[0]); return 1; @@ -817,6 +971,147 @@ static void _sighandlerQuit(int sig) } #endif +// Drop privileges on Linux, if supported by libc etc. and "zerotier-one" user exists on system +#ifdef __LINUX__ +#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 */ /****************************************************************************/ @@ -979,14 +1274,11 @@ static void printHelp(const char *cn,FILE *out) fprintf(out, COPYRIGHT_NOTICE ZT_EOL_S LICENSE_GRANT ZT_EOL_S); - std::string updateUrl(OneService::autoUpdateUrl()); - if (updateUrl.length()) - fprintf(out,"Automatic updates enabled:" ZT_EOL_S" %s" ZT_EOL_S" (all updates are securely authenticated by 256-bit ECDSA signature)" ZT_EOL_S"" ZT_EOL_S,updateUrl.c_str()); 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 - Run as unprivileged user (skip privilege check)" ZT_EOL_S); + fprintf(out," -U - Skip privilege check and do not attempt to drop privileges" ZT_EOL_S); fprintf(out," -p<port> - Port for UDP and TCP/HTTP (default: 9993, 0 for random)" ZT_EOL_S); #ifdef __UNIX_LIKE__ @@ -1004,8 +1296,54 @@ static void printHelp(const char *cn,FILE *out) 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 _tmain(int argc, _TCHAR* argv[]) +int __cdecl _tmain(int argc, _TCHAR* argv[]) #else int main(int argc,char **argv) #endif @@ -1086,7 +1424,7 @@ int main(int argc,char **argv) if (argv[i][2]) { printHelp(argv[0],stdout); return 0; - } else return idtool(argc,argv); + } else return idtool(argc-1,argv+1); case 'q': // Invoke cli personality if (argv[i][2]) { @@ -1157,7 +1495,7 @@ int main(int argc,char **argv) fprintf(stderr,"%s: no home path specified and no platform default available" ZT_EOL_S,argv[0]); return 1; } else { - std::vector<std::string> hpsp(Utils::split(homeDir.c_str(),ZT_PATH_SEPARATOR_S,"","")); + std::vector<std::string> 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); @@ -1172,6 +1510,12 @@ int main(int argc,char **argv) } } + // 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)) { @@ -1210,8 +1554,8 @@ int main(int argc,char **argv) } else { // Running from service manager _winPokeAHole(); - ZeroTierOneService zt1Service; - if (CServiceBase::Run(zt1Service) == TRUE) { + 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]); @@ -1221,6 +1565,11 @@ int main(int argc,char **argv) #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 @@ -1232,39 +1581,13 @@ int main(int argc,char **argv) } #endif // __UNIX_LIKE__ - unsigned int returnValue = 0; - - 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,argv[0],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; + _OneServiceRunner thr(argv[0],homeDir,port); + thr.threadMain(); + //Thread::join(Thread::start(&thr)); #ifdef __UNIX_LIKE__ OSUtils::rm(pidPath.c_str()); #endif - return returnValue; + return thr.returnValue; } |