From 32d9850263ad10be75d967d495e236392fac32a1 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 23 Jun 2016 12:37:15 -0700 Subject: [PATCH] More new CLI work. --- cli/zerotier.cpp | 230 ++++++++++++++++++++++++++++++++--------- osdep/OSUtils.cpp | 40 +++++++ osdep/OSUtils.hpp | 5 + service/OneService.cpp | 33 +----- 4 files changed, 225 insertions(+), 83 deletions(-) diff --git a/cli/zerotier.cpp b/cli/zerotier.cpp index 12064d6ff..f9eb4716b 100644 --- a/cli/zerotier.cpp +++ b/cli/zerotier.cpp @@ -1,3 +1,24 @@ +/* + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2016 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +// Note: unlike the rest of ZT's code base, this requires C++11 due to +// the JSON library it uses and other things. + #include #include #include @@ -22,14 +43,46 @@ #include #include #include +#include #include using json = nlohmann::json; -using OSUtils = ZeroTier::OSUtils; +using namespace ZeroTier; + +#define ZT_CLI_FLAG_VERBOSE 'v' +#define ZT_CLI_FLAG_UNSAFE_SSL 'X' + +struct CLIState +{ + std::string atname; + std::string command; + std::vector args; + std::map opts; + json settings; +}; namespace { +static std::string trimString(const std::string &s) +{ + unsigned long end = (unsigned long)s.length(); + while (end) { + char c = s[end - 1]; + if ((c == ' ')||(c == '\r')||(c == '\n')||(!c)||(c == '\t')) + --end; + else break; + } + unsigned long start = 0; + while (start < end) { + char c = s[start]; + if ((c == ' ')||(c == '\r')||(c == '\n')||(!c)||(c == '\t')) + ++start; + else break; + } + return s.substr(start,end - start); +} + static inline std::string getSettingsFilePath() { #ifdef __WINDOWS__ @@ -41,15 +94,6 @@ static inline std::string getSettingsFilePath() #endif } -static json loadSettings() -{ - json settings; - std::string buf; - if (OSUtils::readFile(getSettingsFilePath().c_str(),buf)) - settings = json::parse(buf); - return settings; -} - static bool saveSettings(const json &settings) { std::string sfp(getSettingsFilePath().c_str()); @@ -75,13 +119,13 @@ static void dumpHelp() std::cout << " -X - Do not check SSL certs (CAUTION!)" << std::endl; std::cout << std::endl; std::cout << "CLI Configuration Commands:" << std::endl; - std::cout << " cli-set - Set a CLI config option" << std::endl; + std::cout << " cli-set - Set a CLI option ('cli-set help')" << std::endl; std::cout << " cli-ls - List configured @things" << std::endl; std::cout << " cli-rm @name - Remove a configured @thing" << std::endl; std::cout << " cli-add-zt @name - Add a ZeroTier service" << std::endl; std::cout << " cli-add-central @name - Add ZeroTier Central instance" << std::endl; std::cout << std::endl; - std::cout << "ZeroTier Service Commands:" << std::endl; + std::cout << "ZeroTier One Service Commands:" << std::endl; std::cout << " ls - List currently joined networks" << std::endl; std::cout << " join [opt=value ...] - Join a network" << std::endl; std::cout << " leave - Leave a network" << std::endl; @@ -90,7 +134,7 @@ static void dumpHelp() std::cout << std::endl; std::cout << "Network Controller Commands:" << std::endl; std::cout << " net-create - Create a new network" << std::endl; - std::cout << " net-rm - Delete a network (BE CAREFUL!)" << std::endl; + std::cout << " net-rm - Delete a network (CAUTION!)" << std::endl; std::cout << " net-ls - List administered networks" << std::endl; std::cout << " net-members - List members of a network" << std::endl; std::cout << " net-show [
] - Get network or member info" << std::endl; @@ -106,6 +150,43 @@ static void dumpHelp() std::cout << std::endl; } +static size_t _curlStringAppendCallback(void *contents,size_t size,size_t nmemb,void *stdstring) +{ + size_t totalSize = size * nmemb; + reinterpret_cast(stdstring)->append((const char *)contents,totalSize); + return totalSize; +} + +static std::tuple GET(const CLIState &state,const std::map &headers,const std::string &url) +{ + int status = -1; + std::string body; + char errbuf[CURL_ERROR_SIZE]; + + CURL *curl = curl_easy_init(); + if (!curl) { + std::cerr << "FATAL: curl_easy_init() failed" << std::endl; + exit(-1); + } + + curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,_curlStringAppendCallback); + curl_easy_setopt(curl,CURLOPT_WRITEDATA,(void *)&body); + curl_easy_setopt(curl,CURLOPT_USERAGENT,"ZeroTier-CLI"); + curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,(state.opts.count(ZT_CLI_FLAG_UNSAFE_SSL) > 0) ? 0L : 1L); + curl_easy_setopt(curl,CURLOPT_ERRORBUFFER,errbuf); + + memset(errbuf,0,sizeof(errbuf)); + CURLcode res = curl_easy_perform(curl); + errbuf[CURL_ERROR_SIZE-1] = (char)0; // sanity check + + if (res != CURLE_OK) + return std::make_tuple(-1,std::string(errbuf)); + + curl_easy_cleanup(curl); + + return std::make_tuple(0,body); +} + } // anonymous namespace ////////////////////////////////////////////////////////////////////////////// @@ -123,67 +204,114 @@ int main(int argc,char **argv) } #endif - CURL *const curl = curl_easy_init(); + curl_global_init(CURL_GLOBAL_DEFAULT); + + CLIState state; - std::string atname; - std::string command; - std::vector args; - std::map opts; for(int i=1;i 0)&&(port < 65536))&&(authToken.length() > 0)) { + state.settings["things"]["local"]["url"] = (std::string("http://127.0.0.1:") + portStr + "/"); + state.settings["things"]["local"]["auth"] = authToken; + initSuccess = true; + } + } + + if (!saveSettings(state.settings)) { + std::cerr << "FATAL: unable to write " << getSettingsFilePath() << std::endl; + exit(-1); + } + + if (initSuccess) { + std::cerr << "INFO: initialized new config at " << getSettingsFilePath() << std::endl; + } else { + std::cerr << "INFO: initialized new config at " << getSettingsFilePath() << " but could not auto-init local ZeroTier One service config from " << oneHome << " -- you will need to set local service URL and port manually if you want to control a local instance of ZeroTier One. (This happens if you are not root/administrator.)" << std::endl; + } + } + } + + if ((state.command.length() == 0)||(state.command == "help")) { dumpHelp(); return -1; - } else if (command == "cli-set") { - } else if (command == "cli-ls") { - } else if (command == "cli-rm") { - } else if (command == "cli-add-zt") { - } else if (command == "cli-add-central") { - } else if (command == "ls") { - } else if (command == "join") { - } else if (command == "leave") { - } else if (command == "peers") { - } else if (command == "show") { - } else if (command == "net-create") { - } else if (command == "net-rm") { - } else if (command == "net-ls") { - } else if (command == "net-members") { - } else if (command == "net-show") { - } else if (command == "net-auth") { - } else if (command == "net-set") { - } else if (command == "id-generate") { - } else if (command == "id-validate") { - } else if (command == "id-sign") { - } else if (command == "id-verify") { - } else if (command == "id-getpublic") { + } else if (state.command == "cli-set") { + } else if (state.command == "cli-ls") { + } else if (state.command == "cli-rm") { + } else if (state.command == "cli-add-zt") { + } else if (state.command == "cli-add-central") { + } else if (state.command == "ls") { + } else if (state.command == "join") { + } else if (state.command == "leave") { + } else if (state.command == "peers") { + } else if (state.command == "show") { + } else if (state.command == "net-create") { + } else if (state.command == "net-rm") { + } else if (state.command == "net-ls") { + } else if (state.command == "net-members") { + } else if (state.command == "net-show") { + } else if (state.command == "net-auth") { + } else if (state.command == "net-set") { + } else if (state.command == "id-generate") { + } else if (state.command == "id-validate") { + } else if (state.command == "id-sign") { + } else if (state.command == "id-verify") { + } else if (state.command == "id-getpublic") { } else { dumpHelp(); return -1; } - curl_easy_cleanup(curl); + curl_global_cleanup(); return 0; } diff --git a/osdep/OSUtils.cpp b/osdep/OSUtils.cpp index 829322a30..3a04308b0 100644 --- a/osdep/OSUtils.cpp +++ b/osdep/OSUtils.cpp @@ -37,7 +37,11 @@ #endif #ifdef __WINDOWS__ +#include #include +#include +#include +#include #endif #include "OSUtils.hpp" @@ -227,6 +231,42 @@ bool OSUtils::writeFile(const char *path,const void *buf,unsigned int len) return false; } +std::string OSUtils::platformDefaultHomePath() +{ +#ifdef __UNIX_LIKE__ + +#ifdef __APPLE__ + // /Library/... on Apple + return std::string("/Library/Application Support/ZeroTier/One"); +#else + +#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 + return std::string("/var/lib/zerotier-one"); +#endif + +#endif + +#else // not __UNIX_LIKE__ + +#ifdef __WINDOWS__ + // Look up app data folder on Windows, e.g. C:\ProgramData\... + char buf[16384]; + if (SUCCEEDED(SHGetFolderPathA(NULL,CSIDL_COMMON_APPDATA,NULL,0,buf))) + return (std::string(buf) + "\\ZeroTier\\One"); + else return std::string("C:\\ZeroTier\\One"); +#else + + return (std::string(ZT_PATH_SEPARATOR_S) + "ZeroTier" + ZT_PATH_SEPARATOR_S + "One"); // UNKNOWN PLATFORM + +#endif + +#endif // __UNIX_LIKE__ or not... +} + // Used to convert HTTP header names to ASCII lower case const unsigned char OSUtils::TOLOWER_TABLE[256] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, ' ', '!', '"', '#', '$', '%', '&', 0x27, '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff }; diff --git a/osdep/OSUtils.hpp b/osdep/OSUtils.hpp index 80f7bfe08..25bed9fe0 100644 --- a/osdep/OSUtils.hpp +++ b/osdep/OSUtils.hpp @@ -235,6 +235,11 @@ public: */ static inline char toLower(char c) throw() { return (char)OSUtils::TOLOWER_TABLE[(unsigned long)c]; } + /** + * @return Platform default ZeroTier One home path + */ + static std::string platformDefaultHomePath(); + private: static const unsigned char TOLOWER_TABLE[256]; }; diff --git a/service/OneService.cpp b/service/OneService.cpp index 534dfbf99..c1b240506 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -1916,38 +1916,7 @@ static int ShttpOnMessageComplete(http_parser *parser) std::string OneService::platformDefaultHomePath() { -#ifdef __UNIX_LIKE__ - -#ifdef __APPLE__ - // /Library/... on Apple - return std::string("/Library/Application Support/ZeroTier/One"); -#else - -#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 - return std::string("/var/lib/zerotier-one"); -#endif - -#endif - -#else // not __UNIX_LIKE__ - -#ifdef __WINDOWS__ - // Look up app data folder on Windows, e.g. C:\ProgramData\... - char buf[16384]; - if (SUCCEEDED(SHGetFolderPathA(NULL,CSIDL_COMMON_APPDATA,NULL,0,buf))) - return (std::string(buf) + "\\ZeroTier\\One"); - else return std::string("C:\\ZeroTier\\One"); -#else - - return std::string(); // UNKNOWN PLATFORM - -#endif - -#endif // __UNIX_LIKE__ or not... + return OSUtils::platformDefaultHomePath(); } std::string OneService::autoUpdateUrl()