Przeglądaj źródła

More new CLI work.

Adam Ierymenko 9 lat temu
rodzic
commit
32d9850263
4 zmienionych plików z 225 dodań i 83 usunięć
  1. 179 51
      cli/zerotier.cpp
  2. 40 0
      osdep/OSUtils.cpp
  3. 5 0
      osdep/OSUtils.hpp
  4. 1 32
      service/OneService.cpp

+ 179 - 51
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;
 }

+ 40 - 0
osdep/OSUtils.cpp

@@ -37,7 +37,11 @@
 #endif
 
 #ifdef __WINDOWS__
+#include <windows.h>
 #include <wincrypt.h>
+#include <ShlObj.h>
+#include <netioapi.h>
+#include <iphlpapi.h>
 #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 };
 

+ 5 - 0
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];
 };

+ 1 - 32
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()