diff options
Diffstat (limited to 'cli/zerotier.cpp')
-rw-r--r-- | cli/zerotier.cpp | 230 |
1 files changed, 179 insertions, 51 deletions
diff --git a/cli/zerotier.cpp b/cli/zerotier.cpp index 12064d6f..f9eb4716 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 <http://www.gnu.org/licenses/>. + */ + +// Note: unlike the rest of ZT's code base, this requires C++11 due to +// the JSON library it uses and other things. + #include <stdio.h> #include <stdlib.h> #include <stdint.h> @@ -22,14 +43,46 @@ #include <string> #include <map> #include <vector> +#include <tuple> #include <curl/curl.h> 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<std::string> args; + std::map<char,std::string> 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 <setting> <value> - Set a CLI config option" << std::endl; + std::cout << " cli-set <setting> <value> - 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 <url> <auth> - Add a ZeroTier service" << std::endl; std::cout << " cli-add-central @name <url> <auth> - 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 <network> [opt=value ...] - Join a network" << std::endl; std::cout << " leave <network> - 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 <network> - Delete a network (BE CAREFUL!)" << std::endl; + std::cout << " net-rm <network> - Delete a network (CAUTION!)" << std::endl; std::cout << " net-ls - List administered networks" << std::endl; std::cout << " net-members <network> - List members of a network" << std::endl; std::cout << " net-show <network> [<address>] - 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<std::string *>(stdstring)->append((const char *)contents,totalSize); + return totalSize; +} + +static std::tuple<int,std::string> GET(const CLIState &state,const std::map<std::string,std::string> &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<std::string> args; - std::map<char,std::string> opts; for(int i=1;i<argc;++i) { if ((i == 1)&&(argv[i][0] == '@')) { - atname = argv[i]; - } else if (nextIsOptValue) { - opts[nextIsOptValue] = argv[i]; - nextIsOptValue = 0; - } else if (command.length() == 0) { + state.atname = argv[i]; + } else if (state.command.length() == 0) { if (argv[i][0] == '-') { if (!argv[i][1]) { dumpHelp(); return -1; } else if (argv[i][2]) { - opts[argv[i][1]] = argv[i] + 2; + state.opts[argv[i][1]] = argv[i] + 2; } else { - opts[argv[i][1]] = ""; + state.opts[argv[i][1]] = ""; } } else { - command = argv[i]; + state.command = argv[i]; } } else { - args.push_back(std::string(argv[i])); + state.args.push_back(std::string(argv[i])); + } + } + + { + std::string buf; + if (OSUtils::readFile(getSettingsFilePath().c_str(),buf)) + state.settings = json::parse(buf); + + if (state.settings.empty()) { + // Default settings + state.settings = { + { "configVersion", 1 }, + { "things", { + { "my.zerotier.com", { + { "type", "central" }, + { "url", "https://my.zerotier.com/" }, + { "auth", "" } + }}, + { "local", { + { "type", "one" }, + { "url", "" }, + { "auth", "" } + }} + }}, + { "defaultController", "@my.zerotier.com" }, + { "defaultOne", "@local" } + }; + + std::string oneHome(OSUtils::platformDefaultHomePath()); + std::string authToken,portStr; + bool initSuccess = false; + if (OSUtils::readFile((oneHome + ZT_PATH_SEPARATOR_S + "authtoken.secret").c_str(),authToken)&&OSUtils::readFile((oneHome + ZT_PATH_SEPARATOR_S + "zerotier-one.port").c_str(),portStr)) { + portStr = trimString(portStr); + authToken = trimString(authToken); + int port = Utils::strToInt(portStr.c_str()); + if (((port > 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 ((command.length() == 0)||(command == "help")) { + 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; } |