frp.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452
  1. //
  2. // Created by xcbosa on 2023/1/31.
  3. //
  4. #pragma once
  5. #include <string>
  6. #include "utils/utils.h"
  7. #include "webuiconf.h"
  8. #include "fs.hpp"
  9. #include <signal.h>
  10. #include <strstream>
  11. #include "frp.h"
  12. #include <iomanip>
  13. using namespace std;
  14. using namespace xc;
  15. using namespace xc::utils;
  16. namespace xc::frp {
  17. set<int> profileUsingPorts(string profile) {
  18. set<int> usingPorts;
  19. INIFile ini(conf::getFrpcDir() + "/" + profile);
  20. string value = ini.getMust("common")->get("webui_allowServerPorts");
  21. auto v = split(value, ",");
  22. int low(0), len(0);
  23. if (v.size() == 2) {
  24. try {
  25. low = stoi(v[0]);
  26. len = stoi(v[1]);
  27. } catch (...) { }
  28. }
  29. for (int i = low; i < low + len; i++) {
  30. usingPorts.insert(i);
  31. }
  32. return usingPorts;
  33. }
  34. set<int> serverUsingPorts(string serverIp) {
  35. set<int> usingPorts;
  36. for (string file : fs::contentsOfDirectory(conf::getFrpcDir())) {
  37. INIFile ini(conf::getFrpcDir() + "/" + file);
  38. if (ini.getMust("common")->get("server_addr") == serverIp) {
  39. auto profilePorts = profileUsingPorts(file);
  40. for (auto port : profilePorts) {
  41. usingPorts.insert(port);
  42. }
  43. }
  44. }
  45. return usingPorts;
  46. }
  47. void addProfile(string name, string ip, string port, string token) {
  48. INI ini;
  49. auto frpCommon = ini.getMust("common");
  50. frpCommon->set("server_addr", ip);
  51. frpCommon->set("server_port", port);
  52. frpCommon->set("token", token);
  53. frpCommon->set("tls_enable", "true");
  54. auto alreadyUsingPorts = serverUsingPorts(ip);
  55. int low = 0, len = conf::allowPortCountPerProfile;
  56. for (int i = 10000; i < 60000; i += len) {
  57. bool badPage = false;
  58. for (int j = i; j < i + len; j++) {
  59. if (alreadyUsingPorts.count(j)) {
  60. badPage = true;
  61. break;
  62. }
  63. }
  64. if (!badPage) {
  65. low = i;
  66. break;
  67. }
  68. }
  69. if (low == 0) {
  70. len = 0;
  71. }
  72. ostringstream oss;
  73. oss << low;
  74. oss << ",";
  75. oss << len;
  76. frpCommon->set("webui_allowServerPorts", oss.str());
  77. saveTextFile(conf::getFrpcDir() + "/" + name, ini.getINIString());
  78. }
  79. class FrpProcessWrapper {
  80. public:
  81. FrpProcessWrapper(string fileName, string filePath) {
  82. this->fileName = fileName;
  83. this->filePath = filePath;
  84. this->updated = false;
  85. }
  86. void startAndKeepRunning() {
  87. cout << "[FrpProcessWrapper] [" << fileName << "] Start" << endl;
  88. this->doKill();
  89. this->doStart();
  90. }
  91. void update() {
  92. struct stat buf;
  93. if (stat(this->filePath.c_str(), &buf) == 0) {
  94. if (this->mutationTime != buf.st_mtime) {
  95. this->mutationTime = buf.st_mtime;
  96. this->reloadConfig();
  97. return;
  98. }
  99. }
  100. INIFile profileINI(this->filePath);
  101. bool shouldRunning = profileINI.data.size() > 1;
  102. if (getRunningPid() == 0) {
  103. if (shouldRunning) {
  104. cout << "[FrpProcessWrapper] [" << fileName << "] Exit unexpectedly, restarting..." << endl;
  105. this->doStart();
  106. }
  107. } else {
  108. if (!shouldRunning) {
  109. this->doKill();
  110. }
  111. }
  112. }
  113. int getRunningPid() {
  114. #if __APPLE__
  115. ::FILE *psStdoutFd = popen("ps -ef | grep frpc | grep -v grep", "r");
  116. #elif __linux__
  117. ::FILE *psStdoutFd = popen("ps -ef --columns 1000 | grep frpc | grep -v grep", "r");
  118. #endif
  119. ostringstream oss;
  120. char buff[1024];
  121. while (::fgets(buff, sizeof(buff), psStdoutFd)) {
  122. oss << buff;
  123. }
  124. ::pclose(psStdoutFd);
  125. string str = oss.str();
  126. auto lines = split(str, "\n");
  127. for (auto line : lines) {
  128. if (line.find("frpc -c") != line.npos && line.find(this->filePath) != line.npos) {
  129. strstream ss;
  130. ss << line;
  131. string owner;
  132. int pid;
  133. ss >> owner;
  134. ss >> pid;
  135. return pid;
  136. }
  137. }
  138. return 0;
  139. }
  140. void stop() {
  141. cout << "[FrpProcessWrapper] [" << fileName << "] Stop" << endl;
  142. this->doKill();
  143. }
  144. void reloadConfig() {
  145. cout << "[FrpProcessWrapper] [" << fileName << "] Changes Detected, Reload..." << endl;
  146. this->doKill();
  147. this->doStart();
  148. }
  149. bool updated;
  150. string fileName;
  151. string filePath;
  152. long mutationTime;
  153. private:
  154. void doStart() {
  155. struct stat buf;
  156. if (stat(this->filePath.c_str(), &buf) == 0) {
  157. this->mutationTime = buf.st_mtime;
  158. }
  159. INIFile profileINI(this->filePath);
  160. if (profileINI.data.size() < 2) {
  161. return;
  162. }
  163. ostringstream oss;
  164. oss << "frpc -c " << this->filePath << " &";
  165. string launchCmd = oss.str();
  166. ::system(launchCmd.c_str());
  167. }
  168. void doKill() {
  169. int pid = getRunningPid();
  170. if (pid > 0) {
  171. kill(pid, SIGKILL);
  172. }
  173. }
  174. };
  175. set<string> reloadConfigForFilePathRequests;
  176. mutex reloadConfigForFilePathRequestsLocker;
  177. void reloadProfileFilePath(string filePath) {
  178. reloadConfigForFilePathRequestsLocker.lock();
  179. reloadConfigForFilePathRequests.insert(filePath);
  180. reloadConfigForFilePathRequestsLocker.unlock();
  181. }
  182. void frpDaemon() {
  183. ::system("killall frpc");
  184. char readBuff[1024];
  185. vector<FrpProcessWrapper *> frpProcesses;
  186. while (true) {
  187. for (auto it : frpProcesses) {
  188. it->updated = false;
  189. }
  190. for (string file : fs::contentsOfDirectory(conf::getFrpcDir())) {
  191. string filePath = conf::getFrpcDir() + "/" + file;
  192. bool updated = false;
  193. for (auto process : frpProcesses) {
  194. if (process->filePath == filePath) {
  195. process->update();
  196. process->updated = true;
  197. updated = true;
  198. break;
  199. }
  200. }
  201. if (!updated) {
  202. auto newProcess = new FrpProcessWrapper(file, filePath);
  203. newProcess->startAndKeepRunning();
  204. newProcess->updated = true;
  205. frpProcesses.push_back(newProcess);
  206. }
  207. }
  208. for (auto it = frpProcesses.begin(); it != frpProcesses.end(); ) {
  209. FrpProcessWrapper *proc = *it;
  210. if (!proc->updated) {
  211. proc->stop();
  212. delete proc;
  213. it = frpProcesses.erase(it);
  214. } else {
  215. reloadConfigForFilePathRequestsLocker.lock();
  216. if (reloadConfigForFilePathRequests.count(proc->filePath)) {
  217. reloadConfigForFilePathRequests.erase(proc->filePath);
  218. reloadConfigForFilePathRequestsLocker.unlock();
  219. proc->reloadConfig();
  220. } else {
  221. reloadConfigForFilePathRequestsLocker.unlock();
  222. }
  223. it++;
  224. }
  225. }
  226. usleep(1000 * 1000);
  227. }
  228. }
  229. ProfilePortInfo::ProfilePortInfo() { }
  230. ProfilePortInfo::ProfilePortInfo(string localIp, int localPort, int remotePort):
  231. localIp(localIp), localPort(localPort), remotePort(remotePort), uuid(create_uuid()) { }
  232. ProfilePortInfo::ProfilePortInfo(string localIp, int localPort, int remotePort, string uuid):
  233. localIp(localIp), localPort(localPort), remotePort(remotePort), uuid(uuid) { }
  234. ProfileInfo::ProfileInfo() { }
  235. ProfileInfo::ProfileInfo(string profileName) {
  236. this->profileName = profileName;
  237. INIFile file(conf::getFrpcDir() + "/" + profileName);
  238. bool needSave = false;
  239. for (auto &it : file.data) {
  240. if (it.getTitle() == "common") {
  241. this->serverAddr = it.get("server_addr");
  242. this->serverPort = to_int(it.get("server_port"), 0);
  243. this->token = it.get("token");
  244. string users = it.get("webui_availableForUsers");
  245. for (auto it : split(users, "/")) {
  246. trim(it);
  247. if (!it.empty()) {
  248. this->users.insert(it);
  249. }
  250. }
  251. string allowPortStr = it.get("webui_allowServerPorts");
  252. auto list = split(allowPortStr, ",");
  253. if (list.size() == 2) {
  254. this->allowPortLow = to_int(list[0], 0);
  255. this->allowPortCount = to_int(list[1], 0);
  256. }
  257. } else {
  258. string type = it.get("type");
  259. string localIp = it.get("local_ip");
  260. bool success;
  261. int localPort = to_int(it.get("local_port"), success);
  262. if (!success || !is_in(localPort, 0, 65536)) { continue; }
  263. int remotePort = to_int(it.get("remote_port"), success);
  264. if (!success || !is_in(localPort, 0, 65536)) { continue; }
  265. string id = it.get("id");
  266. if (id.empty()) {
  267. id = create_uuid();
  268. needSave = true;
  269. }
  270. it.set("id", id);
  271. if (type == "tcp" || type == "udp") {
  272. bool alreadyHave = false;
  273. for (auto v : this->ports) {
  274. if (v.remotePort == remotePort) {
  275. alreadyHave = true;
  276. }
  277. }
  278. if (alreadyHave) { continue; }
  279. this->ports.push_back(ProfilePortInfo(localIp, localPort, remotePort, id));
  280. }
  281. }
  282. }
  283. if (needSave) {
  284. file.save();
  285. }
  286. }
  287. int ProfileInfo::getFirstFreeRemotePort() {
  288. set<int> usingPorts;
  289. for (auto port : this->ports) {
  290. usingPorts.insert(port.remotePort);
  291. }
  292. for (int i = this->allowPortLow; i < this->allowPortLow + this->allowPortCount; i++) {
  293. if (!usingPorts.count(i)) {
  294. return i;
  295. }
  296. }
  297. return -1;
  298. }
  299. int ProfileInfo::getFirstFreeRemotePort(int ifNoneThenReturn) {
  300. int value = getFirstFreeRemotePort();
  301. if (value == -1) return ifNoneThenReturn;
  302. return value;
  303. }
  304. vector<int> ProfileInfo::getFreeRemotePorts() {
  305. return getFreeRemotePorts(65536);
  306. }
  307. vector<int> ProfileInfo::getFreeRemotePorts(int maxCnt) {
  308. set<int> usingPorts;
  309. vector<int> res;
  310. for (auto port : this->ports) {
  311. usingPorts.insert(port.remotePort);
  312. }
  313. int cnt = 0;
  314. for (int i = this->allowPortLow; i < this->allowPortLow + this->allowPortCount; i++) {
  315. if (!usingPorts.count(i)) {
  316. res.push_back(i);
  317. cnt++;
  318. if (cnt >= maxCnt) {
  319. break;
  320. }
  321. }
  322. }
  323. return res;
  324. }
  325. vector<int> ProfileInfo::getFreeRemotePortsAndAppend(int me) {
  326. vector<int> ports = getFreeRemotePorts();
  327. ports.push_back(me);
  328. std::sort(ports.begin(), ports.end());
  329. return ports;
  330. }
  331. void ProfileInfo::addPortInfo(ProfilePortInfo portInfo) {
  332. for (auto &port : this->ports) {
  333. if (port.remotePort == portInfo.remotePort) {
  334. port.localPort = portInfo.localPort;
  335. port.localIp = portInfo.localIp;
  336. return;
  337. }
  338. }
  339. this->ports.insert(this->ports.begin(), portInfo);
  340. }
  341. void ProfileInfo::removePort(int remotePort) {
  342. for (auto ptr = this->ports.begin(); ptr != this->ports.end(); ptr++) {
  343. if (ptr->remotePort == remotePort) {
  344. this->ports.erase(ptr);
  345. return;
  346. }
  347. }
  348. }
  349. string ProfileInfo::getServerAddr() { return this->serverAddr; }
  350. int ProfileInfo::getAllowPortLow() { return this->allowPortLow; }
  351. int ProfileInfo::getAllowPortCount() { return this->allowPortCount; }
  352. void ProfileInfo::save() const {
  353. string path = conf::getFrpcDir() + "/" + this->profileName;
  354. INIFile ini(path);
  355. ini.data.clear();
  356. ini.getMust("common")->set("server_addr", this->serverAddr);
  357. ini.getMust("common")->set("server_port", to_string(this->serverPort));
  358. ini.getMust("common")->set("token", this->token);
  359. ini.getMust("common")->set("tls_enable", "true");
  360. ostringstream usersOss;
  361. for (auto user : this->users) {
  362. usersOss << user;
  363. usersOss << "/";
  364. }
  365. ini.getMust("common")->set("webui_availableForUsers", usersOss.str());
  366. ostringstream oss;
  367. oss << this->allowPortLow << "," << this->allowPortCount;
  368. ini.getMust("common")->set("webui_allowServerPorts", oss.str());
  369. for (auto port : this->ports) {
  370. for (auto type : conf::supportTypes) {
  371. ostringstream titleOss;
  372. titleOss << "frpc-webui-" << port.remotePort << "-" << type;
  373. ini.getMust(titleOss.str())->set("type", type);
  374. ini.getMust(titleOss.str())->set("local_ip", port.localIp);
  375. ini.getMust(titleOss.str())->set("local_port", to_string(port.localPort));
  376. ini.getMust(titleOss.str())->set("remote_port", to_string(port.remotePort));
  377. ini.getMust(titleOss.str())->set("use_encryption", "true");
  378. ini.getMust(titleOss.str())->set("use_compression", "true");
  379. }
  380. }
  381. ini.save();
  382. }
  383. void ProfileInfo::addUser(string name) {
  384. this->users.insert(name);
  385. }
  386. void ProfileInfo::removeUser(string name) {
  387. this->users.erase(name);
  388. }
  389. bool ProfileInfo::availableForUser(string name) {
  390. return this->users.count(name) > 0;
  391. }
  392. vector<ProfileInfo> listUserAvailableProfiles(string user) {
  393. vector<ProfileInfo> profiles;
  394. for (auto profile : fs::contentsOfDirectory(conf::getFrpcDir())) {
  395. ProfileInfo profileInfo(profile);
  396. if (profileInfo.availableForUser(user)) {
  397. profiles.push_back(profileInfo);
  398. }
  399. }
  400. return profiles;
  401. }
  402. vector<string> listingAvailableServerAndPortForUser(string username) {
  403. auto profiles = listUserAvailableProfiles(username);
  404. vector<string> items;
  405. for (auto profile : profiles) {
  406. for (auto port : profile.getFreeRemotePorts()) {
  407. items.push_back(profile.getServerAddr() + ":" + to_string(port));
  408. }
  409. }
  410. return items;
  411. }
  412. }