OSUtils.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. /*
  2. * Copyright (c)2013-2020 ZeroTier, Inc.
  3. *
  4. * Use of this software is governed by the Business Source License included
  5. * in the LICENSE.TXT file in the project's root directory.
  6. *
  7. * Change Date: 2024-01-01
  8. *
  9. * On the date above, in accordance with the Business Source License, use
  10. * of this software will be governed by version 2.0 of the Apache License.
  11. */
  12. /****/
  13. #include "../node/Constants.hpp"
  14. #include "../node/Utils.hpp"
  15. #include "OSUtils.hpp"
  16. #ifdef __WINDOWS__
  17. #include <WinSock2.h>
  18. #include <Windows.h>
  19. #include <Shlwapi.h>
  20. #else
  21. #include <dirent.h>
  22. #include <fcntl.h>
  23. #endif
  24. #ifdef __GCC__
  25. #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
  26. #endif
  27. namespace ZeroTier {
  28. unsigned int OSUtils::ztsnprintf(char *buf,unsigned int len,const char *fmt,...)
  29. {
  30. va_list ap;
  31. va_start(ap,fmt);
  32. int n = (int)vsnprintf(buf,len,fmt,ap);
  33. va_end(ap);
  34. if ((n >= (int)len)||(n < 0)) {
  35. if (len)
  36. buf[len - 1] = (char)0;
  37. throw std::length_error("buf[] overflow");
  38. }
  39. return (unsigned int)n;
  40. }
  41. #ifdef __UNIX_LIKE__
  42. bool OSUtils::redirectUnixOutputs(const char *stdoutPath,const char *stderrPath)
  43. {
  44. int fdout = open(stdoutPath,O_WRONLY|O_CREAT,0600);
  45. if (fdout > 0) {
  46. int fderr;
  47. if (stderrPath) {
  48. fderr = open(stderrPath,O_WRONLY|O_CREAT,0600);
  49. if (fderr <= 0) {
  50. ::close(fdout);
  51. return false;
  52. }
  53. } else fderr = fdout;
  54. ::close(STDOUT_FILENO);
  55. ::close(STDERR_FILENO);
  56. ::dup2(fdout,STDOUT_FILENO);
  57. ::dup2(fderr,STDERR_FILENO);
  58. return true;
  59. }
  60. return false;
  61. }
  62. #endif // __UNIX_LIKE__
  63. std::vector<std::string> OSUtils::listDirectory(const char *path,bool includeDirectories)
  64. {
  65. std::vector<std::string> r;
  66. #ifdef __WINDOWS__
  67. HANDLE hFind;
  68. WIN32_FIND_DATAA ffd;
  69. if ((hFind = FindFirstFileA((std::string(path) + "\\*").c_str(),&ffd)) != INVALID_HANDLE_VALUE) {
  70. do {
  71. if ( (strcmp(ffd.cFileName,".")) && (strcmp(ffd.cFileName,"..")) && (((ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)||(((ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)&&(includeDirectories))) )
  72. r.push_back(std::string(ffd.cFileName));
  73. } while (FindNextFileA(hFind,&ffd));
  74. FindClose(hFind);
  75. }
  76. #else
  77. dirent de;
  78. dirent *dptr;
  79. DIR *d = opendir(path);
  80. if (!d)
  81. return r;
  82. dptr = (struct dirent *)0;
  83. for(;;) {
  84. if (readdir_r(d,&de,&dptr))
  85. break;
  86. if (dptr) {
  87. if ((strcmp(dptr->d_name,".") != 0)&&(strcmp(dptr->d_name,"..") != 0)&&((dptr->d_type != DT_DIR)||(includeDirectories)))
  88. r.push_back(std::string(dptr->d_name));
  89. } else break;
  90. }
  91. closedir(d);
  92. #endif
  93. return r;
  94. }
  95. bool OSUtils::rmDashRf(const char *path)
  96. {
  97. #ifdef __WINDOWS__
  98. HANDLE hFind;
  99. WIN32_FIND_DATAA ffd;
  100. if ((hFind = FindFirstFileA((std::string(path) + "\\*").c_str(),&ffd)) != INVALID_HANDLE_VALUE) {
  101. do {
  102. if ((strcmp(ffd.cFileName,".") != 0)&&(strcmp(ffd.cFileName,"..") != 0)) {
  103. if ((ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) {
  104. if (DeleteFileA((std::string(path) + ZT_PATH_SEPARATOR_S + ffd.cFileName).c_str()) == FALSE)
  105. return false;
  106. } else {
  107. if (!rmDashRf((std::string(path) + ZT_PATH_SEPARATOR_S + ffd.cFileName).c_str()))
  108. return false;
  109. }
  110. }
  111. } while (FindNextFileA(hFind,&ffd));
  112. FindClose(hFind);
  113. }
  114. return (RemoveDirectoryA(path) != FALSE);
  115. #else
  116. dirent de;
  117. dirent *dptr;
  118. DIR *d = opendir(path);
  119. if (!d)
  120. return true;
  121. dptr = (struct dirent *)0;
  122. for(;;) {
  123. if (readdir_r(d,&de,&dptr) != 0)
  124. break;
  125. if (!dptr)
  126. break;
  127. if ((strcmp(dptr->d_name,".") != 0)&&(strcmp(dptr->d_name,"..") != 0)&&(strlen(dptr->d_name) > 0)) {
  128. std::string p(path);
  129. p.push_back(ZT_PATH_SEPARATOR);
  130. p.append(dptr->d_name);
  131. if (unlink(p.c_str()) != 0) { // unlink first will remove symlinks instead of recursing them
  132. if (!rmDashRf(p.c_str()))
  133. return false;
  134. }
  135. }
  136. }
  137. closedir(d);
  138. return (rmdir(path) == 0);
  139. #endif
  140. }
  141. void OSUtils::lockDownFile(const char *path,bool isDir)
  142. {
  143. #ifdef __UNIX_LIKE__
  144. chmod(path,isDir ? 0700 : 0600);
  145. #else
  146. #ifdef __WINDOWS__
  147. {
  148. STARTUPINFOA startupInfo;
  149. PROCESS_INFORMATION processInfo;
  150. startupInfo.cb = sizeof(startupInfo);
  151. memset(&startupInfo,0,sizeof(STARTUPINFOA));
  152. memset(&processInfo,0,sizeof(PROCESS_INFORMATION));
  153. if (CreateProcessA(NULL,(LPSTR)(std::string("C:\\Windows\\System32\\icacls.exe \"") + path + "\" /inheritance:d /Q").c_str(),NULL,NULL,FALSE,CREATE_NO_WINDOW,NULL,NULL,&startupInfo,&processInfo)) {
  154. WaitForSingleObject(processInfo.hProcess,INFINITE);
  155. CloseHandle(processInfo.hProcess);
  156. CloseHandle(processInfo.hThread);
  157. }
  158. startupInfo.cb = sizeof(startupInfo);
  159. memset(&startupInfo,0,sizeof(STARTUPINFOA));
  160. memset(&processInfo,0,sizeof(PROCESS_INFORMATION));
  161. if (CreateProcessA(NULL,(LPSTR)(std::string("C:\\Windows\\System32\\icacls.exe \"") + path + "\" /remove *S-1-5-32-545 /Q").c_str(),NULL,NULL,FALSE,CREATE_NO_WINDOW,NULL,NULL,&startupInfo,&processInfo)) {
  162. WaitForSingleObject(processInfo.hProcess,INFINITE);
  163. CloseHandle(processInfo.hProcess);
  164. CloseHandle(processInfo.hThread);
  165. }
  166. }
  167. #endif
  168. #endif
  169. }
  170. bool OSUtils::fileExists(const char *path,bool followLinks)
  171. {
  172. struct stat s;
  173. #ifdef __UNIX_LIKE__
  174. if (!followLinks)
  175. return (lstat(path,&s) == 0);
  176. #endif
  177. return (stat(path,&s) == 0);
  178. }
  179. bool OSUtils::readFile(const char *path,std::string &buf)
  180. {
  181. char tmp[16384];
  182. FILE *f = fopen(path,"rb");
  183. if (f) {
  184. for(;;) {
  185. long n = (long)fread(tmp,1,sizeof(tmp),f);
  186. if (n > 0)
  187. buf.append(tmp,n);
  188. else break;
  189. }
  190. fclose(f);
  191. return true;
  192. }
  193. return false;
  194. }
  195. bool OSUtils::writeFile(const char *path,const void *buf,unsigned int len)
  196. {
  197. FILE *f = fopen(path,"wb");
  198. if (f) {
  199. if ((long)fwrite(buf,1,len,f) != (long)len) {
  200. fclose(f);
  201. return false;
  202. } else {
  203. fclose(f);
  204. return true;
  205. }
  206. }
  207. return false;
  208. }
  209. std::vector<std::string> OSUtils::split(const char *s,const char *const sep,const char *esc,const char *quot)
  210. {
  211. std::vector<std::string> fields;
  212. std::string buf;
  213. if (!esc)
  214. esc = "";
  215. if (!quot)
  216. quot = "";
  217. bool escapeState = false;
  218. char quoteState = 0;
  219. while (*s) {
  220. if (escapeState) {
  221. escapeState = false;
  222. buf.push_back(*s);
  223. } else if (quoteState) {
  224. if (*s == quoteState) {
  225. quoteState = 0;
  226. fields.push_back(buf);
  227. buf.clear();
  228. } else buf.push_back(*s);
  229. } else {
  230. const char *quotTmp;
  231. if (strchr(esc,*s))
  232. escapeState = true;
  233. else if ((buf.size() <= 0)&&((quotTmp = strchr(quot,*s))))
  234. quoteState = *quotTmp;
  235. else if (strchr(sep,*s)) {
  236. if (buf.size() > 0) {
  237. fields.push_back(buf);
  238. buf.clear();
  239. } // else skip runs of separators
  240. } else buf.push_back(*s);
  241. }
  242. ++s;
  243. }
  244. if (buf.size())
  245. fields.push_back(buf);
  246. return fields;
  247. }
  248. std::string OSUtils::platformDefaultHomePath()
  249. {
  250. #ifdef __QNAP__
  251. char *cmd = "/sbin/getcfg zerotier Install_Path -f /etc/config/qpkg.conf";
  252. char buf[128];
  253. FILE *fp;
  254. if ((fp = popen(cmd, "r")) == NULL) {
  255. printf("Error opening pipe!\n");
  256. return NULL;
  257. }
  258. while (fgets(buf, 128, fp) != NULL) { }
  259. if(pclose(fp)) {
  260. printf("Command not found or exited with error status\n");
  261. return NULL;
  262. }
  263. std::string homeDir = std::string(buf);
  264. homeDir.erase(std::remove(homeDir.begin(), homeDir.end(), '\n'), homeDir.end());
  265. return homeDir;
  266. #endif
  267. // Check for user-defined environment variable before using defaults
  268. #ifdef __WINDOWS__
  269. DWORD bufferSize = 65535;
  270. std::string userDefinedPath;
  271. bufferSize = GetEnvironmentVariable("ZEROTIER_HOME", &userDefinedPath[0], bufferSize);
  272. if (bufferSize) {
  273. return userDefinedPath;
  274. }
  275. #else
  276. if(const char* userDefinedPath = getenv("ZEROTIER_HOME")) {
  277. return std::string(userDefinedPath);
  278. }
  279. #endif
  280. // Finally, resort to using default paths if no user-defined path was provided
  281. #ifdef __UNIX_LIKE__
  282. #ifdef __APPLE__
  283. // /Library/... on Apple
  284. return std::string("/Library/Application Support/ZeroTier");
  285. #else
  286. #ifdef __BSD__
  287. // BSD likes /var/db instead of /var/lib
  288. return std::string("/var/db/zerotier");
  289. #else
  290. // Use /var/lib for Linux and other *nix
  291. return std::string("/var/lib/zerotier");
  292. #endif
  293. #endif
  294. #else // not __UNIX_LIKE__
  295. #ifdef __WINDOWS__
  296. // Look up app data folder on Windows, e.g. C:\ProgramData\...
  297. char buf[16384];
  298. if (SUCCEEDED(SHGetFolderPathA(NULL,CSIDL_COMMON_APPDATA,NULL,0,buf)))
  299. return (std::string(buf) + "\\ZeroTier");
  300. else return std::string("C:\\ZeroTier");
  301. #else
  302. return (std::string(ZT_PATH_SEPARATOR_S) + "ZeroTier"); // UNKNOWN PLATFORM
  303. #endif
  304. #endif // __UNIX_LIKE__ or not...
  305. }
  306. #ifndef OMIT_JSON_SUPPORT
  307. // Inline these massive JSON operations in one place only to reduce binary footprint and compile time
  308. nlohmann::json OSUtils::jsonParse(const std::string &buf) { return nlohmann::json::parse(buf.c_str()); }
  309. std::string OSUtils::jsonDump(const nlohmann::json &j,int indentation) { return j.dump(indentation); }
  310. uint64_t OSUtils::jsonInt(const nlohmann::json &jv,const uint64_t dfl)
  311. {
  312. try {
  313. if (jv.is_number()) {
  314. return (uint64_t)jv;
  315. } else if (jv.is_string()) {
  316. std::string s = jv;
  317. return Utils::strToU64(s.c_str());
  318. } else if (jv.is_boolean()) {
  319. return ((bool)jv ? 1ULL : 0ULL);
  320. }
  321. } catch ( ... ) {}
  322. return dfl;
  323. }
  324. uint64_t OSUtils::jsonIntHex(const nlohmann::json &jv,const uint64_t dfl)
  325. {
  326. try {
  327. if (jv.is_number()) {
  328. return (uint64_t)jv;
  329. } else if (jv.is_string()) {
  330. std::string s = jv;
  331. return Utils::hexStrToU64(s.c_str());
  332. } else if (jv.is_boolean()) {
  333. return ((bool)jv ? 1ULL : 0ULL);
  334. }
  335. } catch ( ... ) {}
  336. return dfl;
  337. }
  338. bool OSUtils::jsonBool(const nlohmann::json &jv,const bool dfl)
  339. {
  340. try {
  341. if (jv.is_boolean()) {
  342. return (bool)jv;
  343. } else if (jv.is_number()) {
  344. return ((uint64_t)jv > 0ULL);
  345. } else if (jv.is_string()) {
  346. std::string s = jv;
  347. if (s.length() > 0) {
  348. switch(s[0]) {
  349. case 't':
  350. case 'T':
  351. case '1':
  352. return true;
  353. }
  354. }
  355. return false;
  356. }
  357. } catch ( ... ) {}
  358. return dfl;
  359. }
  360. std::string OSUtils::jsonString(const nlohmann::json &jv,const char *dfl)
  361. {
  362. try {
  363. if (jv.is_string()) {
  364. return jv;
  365. } else if (jv.is_number()) {
  366. char tmp[64];
  367. ztsnprintf(tmp,sizeof(tmp),"%llu",(uint64_t)jv);
  368. return tmp;
  369. } else if (jv.is_boolean()) {
  370. return ((bool)jv ? std::string("1") : std::string("0"));
  371. }
  372. } catch ( ... ) {}
  373. return std::string((dfl) ? dfl : "");
  374. }
  375. #endif // OMIT_JSON_SUPPORT
  376. } // namespace ZeroTier