summaryrefslogtreecommitdiff
path: root/cli/zerotier.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'cli/zerotier.cpp')
-rw-r--r--cli/zerotier.cpp230
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;
}