2
0

zerotier.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. /*
  2. * ZeroTier One - Network Virtualization Everywhere
  3. * Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. // Note: unlike the rest of ZT's code base, this requires C++11 due to
  19. // the JSON library it uses and other things.
  20. #include <stdio.h>
  21. #include <stdlib.h>
  22. #include <stdint.h>
  23. #include <string.h>
  24. #include "../node/Constants.hpp"
  25. #include "../version.h"
  26. #include "../osdep/OSUtils.hpp"
  27. #include "../ext/json/json.hpp"
  28. #ifdef __WINDOWS__
  29. #include <WinSock2.h>
  30. #include <windows.h>
  31. #include <tchar.h>
  32. #include <wchar.h>
  33. #else
  34. #include <ctype.h>
  35. #include <unistd.h>
  36. #endif
  37. #include <iostream>
  38. #include <string>
  39. #include <map>
  40. #include <vector>
  41. #include <tuple>
  42. #include <curl/curl.h>
  43. using json = nlohmann::json;
  44. using namespace ZeroTier;
  45. #define ZT_CLI_FLAG_VERBOSE 'v'
  46. #define ZT_CLI_FLAG_UNSAFE_SSL 'X'
  47. struct CLIState
  48. {
  49. std::string atname;
  50. std::string command;
  51. std::vector<std::string> args;
  52. std::map<char,std::string> opts;
  53. json settings;
  54. };
  55. namespace {
  56. static std::string trimString(const std::string &s)
  57. {
  58. unsigned long end = (unsigned long)s.length();
  59. while (end) {
  60. char c = s[end - 1];
  61. if ((c == ' ')||(c == '\r')||(c == '\n')||(!c)||(c == '\t'))
  62. --end;
  63. else break;
  64. }
  65. unsigned long start = 0;
  66. while (start < end) {
  67. char c = s[start];
  68. if ((c == ' ')||(c == '\r')||(c == '\n')||(!c)||(c == '\t'))
  69. ++start;
  70. else break;
  71. }
  72. return s.substr(start,end - start);
  73. }
  74. static inline std::string getSettingsFilePath()
  75. {
  76. #ifdef __WINDOWS__
  77. #else
  78. const char *home = getenv("HOME");
  79. if (!home)
  80. home = "/";
  81. return (std::string(home) + "/.zerotierCliSettings");
  82. #endif
  83. }
  84. static bool saveSettings(const json &settings)
  85. {
  86. std::string sfp(getSettingsFilePath().c_str());
  87. std::string buf(settings.dump(2));
  88. if (OSUtils::writeFile(sfp.c_str(),buf)) {
  89. OSUtils::lockDownFile(sfp.c_str(),false);
  90. return true;
  91. }
  92. return false;
  93. }
  94. static void dumpHelp()
  95. {
  96. std::cout << "ZeroTier Newer-Spiffier CLI " << ZEROTIER_ONE_VERSION_MAJOR << "." << ZEROTIER_ONE_VERSION_MINOR << "." << ZEROTIER_ONE_VERSION_REVISION << std::endl;
  97. std::cout << "(c)2016 ZeroTier, Inc. / Licensed under the GNU GPL v3" << std::endl;
  98. std::cout << std::endl;
  99. std::cout << "Configuration path: " << getSettingsFilePath() << std::endl;
  100. std::cout << std::endl;
  101. std::cout << "Usage: zerotier [-option] [@name] <command> [<command options>]" << std::endl;
  102. std::cout << std::endl;
  103. std::cout << "Options:" << std::endl;
  104. std::cout << " -v - Verbose JSON output" << std::endl;
  105. std::cout << " -X - Do not check SSL certs (CAUTION!)" << std::endl;
  106. std::cout << std::endl;
  107. std::cout << "CLI Configuration Commands:" << std::endl;
  108. std::cout << " cli-set <setting> <value> - Set a CLI option ('cli-set help')" << std::endl;
  109. std::cout << " cli-ls - List configured @things" << std::endl;
  110. std::cout << " cli-rm @name - Remove a configured @thing" << std::endl;
  111. std::cout << " cli-add-zt @name <url> <auth> - Add a ZeroTier service" << std::endl;
  112. std::cout << " cli-add-central @name <url> <auth> - Add ZeroTier Central instance" << std::endl;
  113. std::cout << std::endl;
  114. std::cout << "ZeroTier One Service Commands:" << std::endl;
  115. std::cout << " ls - List currently joined networks" << std::endl;
  116. std::cout << " join <network> [opt=value ...] - Join a network" << std::endl;
  117. std::cout << " leave <network> - Leave a network" << std::endl;
  118. std::cout << " peers - List ZeroTier VL1 peers" << std::endl;
  119. std::cout << " show [<network/peer address>] - Get info about self or object" << std::endl;
  120. std::cout << std::endl;
  121. std::cout << "Network Controller Commands:" << std::endl;
  122. std::cout << " net-create - Create a new network" << std::endl;
  123. std::cout << " net-rm <network> - Delete a network (CAUTION!)" << std::endl;
  124. std::cout << " net-ls - List administered networks" << std::endl;
  125. std::cout << " net-members <network> - List members of a network" << std::endl;
  126. std::cout << " net-show <network> [<address>] - Get network or member info" << std::endl;
  127. std::cout << " net-auth <network> <address> - Authorize a member" << std::endl;
  128. std::cout << " net-set <path> <value> - See 'net-set help'" << std::endl;
  129. std::cout << std::endl;
  130. std::cout << "Identity Commands:" << std::endl;
  131. std::cout << " id-generate [<vanity prefix>] - Generate a ZeroTier identity" << std::endl;
  132. std::cout << " id-validate <identity> - Locally validate an identity" << std::endl;
  133. std::cout << " id-sign <identity> <file> - Sign a file" << std::endl;
  134. std::cout << " id-verify <secret> <file> <sig> - Verify a file's signature" << std::endl;
  135. std::cout << " id-getpublic <secret> - Get full identity's public portion" << std::endl;
  136. std::cout << std::endl;
  137. }
  138. static size_t _curlStringAppendCallback(void *contents,size_t size,size_t nmemb,void *stdstring)
  139. {
  140. size_t totalSize = size * nmemb;
  141. reinterpret_cast<std::string *>(stdstring)->append((const char *)contents,totalSize);
  142. return totalSize;
  143. }
  144. static std::tuple<int,std::string> GET(const CLIState &state,const std::map<std::string,std::string> &headers,const std::string &url)
  145. {
  146. std::string body;
  147. char errbuf[CURL_ERROR_SIZE];
  148. char urlbuf[4096];
  149. CURL *curl = curl_easy_init();
  150. if (!curl) {
  151. std::cerr << "FATAL: curl_easy_init() failed" << std::endl;
  152. exit(-1);
  153. }
  154. curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,_curlStringAppendCallback);
  155. curl_easy_setopt(curl,CURLOPT_WRITEDATA,(void *)&body);
  156. curl_easy_setopt(curl,CURLOPT_USERAGENT,"ZeroTier-CLI");
  157. curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,(state.opts.count(ZT_CLI_FLAG_UNSAFE_SSL) > 0) ? 0L : 1L);
  158. curl_easy_setopt(curl,CURLOPT_ERRORBUFFER,errbuf);
  159. curl_easy_setopt(curl,CURLOPT_FOLLOWLOCATION,0L);
  160. Utils::scopy(urlbuf,sizeof(urlbuf),url.c_str());
  161. curl_easy_setopt(curl,CURLOPT_URL,urlbuf);
  162. struct curl_slist *hdrs = (struct curl_slist *)0;
  163. for(std::map<std::string,std::string>::const_iterator i(headers.begin());i!=headers.end();++i) {
  164. std::string htmp(i->first);
  165. htmp.append(": ");
  166. htmp.append(i->second);
  167. hdrs = curl_slist_append(hdrs,htmp.c_str());
  168. }
  169. if (hdrs)
  170. curl_easy_setopt(curl,CURLOPT_HTTPHEADER,hdrs);
  171. memset(errbuf,0,sizeof(errbuf));
  172. CURLcode res = curl_easy_perform(curl);
  173. errbuf[CURL_ERROR_SIZE-1] = (char)0; // sanity check
  174. if (res != CURLE_OK)
  175. return std::make_tuple(-1,std::string(errbuf));
  176. int rc = (int)curl_easy_getinfo(curl,CURLINFO_RESPONSE_CODE);
  177. curl_easy_cleanup(curl);
  178. if (hdrs)
  179. curl_slist_free_all(hdrs);
  180. return std::make_tuple(rc,body);
  181. }
  182. } // anonymous namespace
  183. //////////////////////////////////////////////////////////////////////////////
  184. #ifdef __WINDOWS__
  185. int _tmain(int argc, _TCHAR* argv[])
  186. #else
  187. int main(int argc,char **argv)
  188. #endif
  189. {
  190. #ifdef __WINDOWS__
  191. {
  192. WSADATA wsaData;
  193. WSAStartup(MAKEWORD(2,2),&wsaData);
  194. }
  195. #endif
  196. curl_global_init(CURL_GLOBAL_DEFAULT);
  197. CLIState state;
  198. for(int i=1;i<argc;++i) {
  199. if ((i == 1)&&(argv[i][0] == '@')) {
  200. state.atname = argv[i];
  201. } else if (state.command.length() == 0) {
  202. if (argv[i][0] == '-') {
  203. if (!argv[i][1]) {
  204. dumpHelp();
  205. return -1;
  206. } else if (argv[i][2]) {
  207. state.opts[argv[i][1]] = argv[i] + 2;
  208. } else {
  209. state.opts[argv[i][1]] = "";
  210. }
  211. } else {
  212. state.command = argv[i];
  213. }
  214. } else {
  215. state.args.push_back(std::string(argv[i]));
  216. }
  217. }
  218. {
  219. std::string buf;
  220. if (OSUtils::readFile(getSettingsFilePath().c_str(),buf))
  221. state.settings = json::parse(buf);
  222. if (state.settings.empty()) {
  223. // Default settings
  224. state.settings = {
  225. { "configVersion", 1 },
  226. { "things", {
  227. { "my.zerotier.com", {
  228. { "type", "central" },
  229. { "url", "https://my.zerotier.com/" },
  230. { "auth", "" }
  231. }},
  232. { "local", {
  233. { "type", "one" },
  234. { "url", "" },
  235. { "auth", "" }
  236. }}
  237. }},
  238. { "defaultController", "@my.zerotier.com" },
  239. { "defaultOne", "@local" }
  240. };
  241. std::string oneHome(OSUtils::platformDefaultHomePath());
  242. std::string authToken,portStr;
  243. bool initSuccess = false;
  244. 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)) {
  245. portStr = trimString(portStr);
  246. authToken = trimString(authToken);
  247. int port = Utils::strToInt(portStr.c_str());
  248. if (((port > 0)&&(port < 65536))&&(authToken.length() > 0)) {
  249. state.settings["things"]["local"]["url"] = (std::string("http://127.0.0.1:") + portStr + "/");
  250. state.settings["things"]["local"]["auth"] = authToken;
  251. initSuccess = true;
  252. }
  253. }
  254. if (!saveSettings(state.settings)) {
  255. std::cerr << "FATAL: unable to write " << getSettingsFilePath() << std::endl;
  256. exit(-1);
  257. }
  258. if (initSuccess) {
  259. std::cerr << "INFO: initialized new config at " << getSettingsFilePath() << std::endl;
  260. } else {
  261. 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;
  262. }
  263. }
  264. }
  265. if ((state.command.length() == 0)||(state.command == "help")) {
  266. dumpHelp();
  267. return -1;
  268. } else if (state.command == "cli-set") {
  269. } else if (state.command == "cli-ls") {
  270. } else if (state.command == "cli-rm") {
  271. } else if (state.command == "cli-add-zt") {
  272. } else if (state.command == "cli-add-central") {
  273. } else if (state.command == "ls") {
  274. } else if (state.command == "join") {
  275. } else if (state.command == "leave") {
  276. } else if (state.command == "peers") {
  277. } else if (state.command == "show") {
  278. } else if (state.command == "net-create") {
  279. } else if (state.command == "net-rm") {
  280. } else if (state.command == "net-ls") {
  281. } else if (state.command == "net-members") {
  282. } else if (state.command == "net-show") {
  283. } else if (state.command == "net-auth") {
  284. } else if (state.command == "net-set") {
  285. } else if (state.command == "id-generate") {
  286. } else if (state.command == "id-validate") {
  287. } else if (state.command == "id-sign") {
  288. } else if (state.command == "id-verify") {
  289. } else if (state.command == "id-getpublic") {
  290. } else {
  291. dumpHelp();
  292. return -1;
  293. }
  294. curl_global_cleanup();
  295. return 0;
  296. }