upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/tor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/tor.cpp')
-rw-r--r--src/tor.cpp881
1 files changed, 881 insertions, 0 deletions
diff --git a/src/tor.cpp b/src/tor.cpp
new file mode 100644
index 0000000..20b4577
--- /dev/null
+++ b/src/tor.cpp
@@ -0,0 +1,881 @@
1// Copyright (c) 2026 Bitflash developers
2// Distributed under the MIT/X11 software license.
3
4#include "headers_core.h"
5#include "tor.h"
6#include "nostr.h"
7#include "proxy.h"
8
9#include <errno.h>
10
11#ifndef _WIN32
12#include <signal.h>
13#include <sys/wait.h>
14#endif
15
16static CCriticalSection cs_managedTor;
17static bool g_fManagedTor = false;
18static bool g_fManagedTorStopping = false;
19static unsigned short g_managedTorSocksPort = 0;
20static unsigned short g_managedTorControlPort = 0;
21static unsigned short g_managedTorP2PPort = 0;
22static std::string g_managedTorPath;
23static std::string g_managedTorDataDir;
24static std::string g_managedTorHiddenServiceDir;
25static std::string g_managedTorOnion;
26static std::string g_managedTorStatus;
27#ifdef _WIN32
28static PROCESS_INFORMATION g_managedTorProcess;
29#else
30static pid_t g_managedTorPid = -1;
31#endif
32
33static std::string PathJoin(const std::string& a, const std::string& b)
34{
35 if (a.empty())
36 return b;
37 char last = a[a.size() - 1];
38 if (last == '/' || last == '\\')
39 return a + b;
40 return a + "/" + b;
41}
42
43static std::string NormalizeTorrcPath(std::string path)
44{
45 for (size_t i = 0; i < path.size(); i++)
46 if (path[i] == '\\')
47 path[i] = '/';
48 return path;
49}
50
51static std::string QuoteTorrcPath(const std::string& path)
52{
53 std::string out = "\"";
54 for (size_t i = 0; i < path.size(); i++)
55 {
56 if (path[i] == '"' || path[i] == '\\')
57 out += '\\';
58 out += path[i];
59 }
60 out += "\"";
61 return out;
62}
63
64static std::string QuoteCommandArg(const std::string& arg)
65{
66 std::string out = "\"";
67 for (size_t i = 0; i < arg.size(); i++)
68 {
69 if (arg[i] == '"')
70 out += '\\';
71 out += arg[i];
72 }
73 out += "\"";
74 return out;
75}
76
77static bool EnsureDir(const std::string& path, std::string& errOut)
78{
79 if (path.empty())
80 {
81 errOut = "directory path is empty";
82 return false;
83 }
84#ifdef _WIN32
85 DWORD attr = GetFileAttributesA(path.c_str());
86 if (attr != INVALID_FILE_ATTRIBUTES)
87 {
88 if (attr & FILE_ATTRIBUTE_DIRECTORY)
89 return true;
90 errOut = strprintf("%s exists and is not a directory", path.c_str());
91 return false;
92 }
93 if (CreateDirectoryA(path.c_str(), NULL))
94 return true;
95 DWORD err = GetLastError();
96 if (err == ERROR_ALREADY_EXISTS)
97 return true;
98 errOut = strprintf("could not create %s (error %lu)", path.c_str(), (unsigned long)err);
99 return false;
100#else
101 struct stat st;
102 if (stat(path.c_str(), &st) == 0)
103 {
104 if (S_ISDIR(st.st_mode))
105 {
106 chmod(path.c_str(), 0700);
107 return true;
108 }
109 errOut = strprintf("%s exists and is not a directory", path.c_str());
110 return false;
111 }
112 if (mkdir(path.c_str(), 0700) == 0)
113 return true;
114 if (errno == EEXIST)
115 {
116 if (stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode))
117 {
118 chmod(path.c_str(), 0700);
119 return true;
120 }
121 }
122 errOut = strprintf("could not create %s: %s", path.c_str(), strerror(errno));
123 return false;
124#endif
125}
126
127static bool WriteTextFile(const std::string& path, const std::string& data,
128 std::string& errOut)
129{
130 std::string tmp = path + ".tmp";
131 FILE* f = fopen(tmp.c_str(), "wb");
132 if (!f)
133 {
134 errOut = strprintf("could not write %s", tmp.c_str());
135 return false;
136 }
137 bool ok = fwrite(data.data(), 1, data.size(), f) == data.size();
138 if (fclose(f) != 0)
139 ok = false;
140 if (!ok)
141 {
142 remove(tmp.c_str());
143 errOut = strprintf("could not finish writing %s", tmp.c_str());
144 return false;
145 }
146#ifdef _WIN32
147 if (!MoveFileExA(tmp.c_str(), path.c_str(), MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH))
148 {
149 remove(tmp.c_str());
150 errOut = strprintf("could not install %s atomically (error %lu)",
151 path.c_str(), (unsigned long)GetLastError());
152 return false;
153 }
154#else
155 if (rename(tmp.c_str(), path.c_str()) != 0)
156 {
157 remove(tmp.c_str());
158 errOut = strprintf("could not install %s atomically: %s",
159 path.c_str(), strerror(errno));
160 return false;
161 }
162#endif
163 return true;
164}
165
166static bool ReadBinaryFile(const std::string& path, std::vector<unsigned char>& data,
167 std::string& errOut)
168{
169 data.clear();
170 FILE* f = fopen(path.c_str(), "rb");
171 if (!f)
172 {
173 errOut = strprintf("could not read %s", path.c_str());
174 return false;
175 }
176 unsigned char buf[256];
177 for (;;)
178 {
179 size_t n = fread(buf, 1, sizeof(buf), f);
180 if (n > 0)
181 data.insert(data.end(), buf, buf + n);
182 if (n < sizeof(buf))
183 {
184 if (ferror(f))
185 {
186 fclose(f);
187 errOut = strprintf("could not finish reading %s", path.c_str());
188 return false;
189 }
190 break;
191 }
192 if (data.size() > 1024)
193 {
194 fclose(f);
195 errOut = strprintf("%s is unexpectedly large", path.c_str());
196 return false;
197 }
198 }
199 fclose(f);
200 if (data.empty())
201 {
202 errOut = strprintf("%s is empty", path.c_str());
203 return false;
204 }
205 return true;
206}
207
208static void SetSocketTimeouts(SOCKET s, int millis)
209{
210#ifdef _WIN32
211 DWORD timeout = millis;
212 setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout));
213 setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, (const char*)&timeout, sizeof(timeout));
214#else
215 struct timeval tv;
216 tv.tv_sec = millis / 1000;
217 tv.tv_usec = (millis % 1000) * 1000;
218 setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
219 setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
220#endif
221}
222
223static bool SendAll(SOCKET s, const std::string& data)
224{
225 const char* p = data.data();
226 int left = (int)data.size();
227 while (left > 0)
228 {
229 int n = send(s, p, left, 0);
230 if (n <= 0)
231 return false;
232 p += n;
233 left -= n;
234 }
235 return true;
236}
237
238static bool RecvControlLine(SOCKET s, std::string& line)
239{
240 line.clear();
241 while (line.size() < 4096)
242 {
243 char ch = 0;
244 int n = recv(s, &ch, 1, 0);
245 if (n <= 0)
246 return false;
247 if (ch == '\n')
248 return true;
249 if (ch != '\r')
250 line += ch;
251 }
252 return false;
253}
254
255static SOCKET ConnectLoopback(unsigned short port, std::string& errOut)
256{
257#ifdef _WIN32
258 WSADATA wsadata;
259 int ret = WSAStartup(MAKEWORD(2,2), &wsadata);
260 if (ret != NO_ERROR)
261 {
262 errOut = strprintf("could not start Winsock for Tor control shutdown (WSAStartup returned %d)", ret);
263 return INVALID_SOCKET;
264 }
265#endif
266 SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
267 if (s == INVALID_SOCKET)
268 {
269 errOut = "could not create Tor control socket";
270#ifdef _WIN32
271 WSACleanup();
272#endif
273 return INVALID_SOCKET;
274 }
275 SetSocketTimeouts(s, 3000);
276 struct sockaddr_in addr;
277 memset(&addr, 0, sizeof(addr));
278 addr.sin_family = AF_INET;
279 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
280 addr.sin_port = htons(port);
281 if (connect(s, (struct sockaddr*)&addr, sizeof(addr)) != 0)
282 {
283 closesocket(s);
284 errOut = strprintf("could not connect to Tor control port 127.0.0.1:%u", (unsigned)port);
285#ifdef _WIN32
286 WSACleanup();
287#endif
288 return INVALID_SOCKET;
289 }
290 return s;
291}
292
293static void CloseLoopback(SOCKET s)
294{
295 if (s != INVALID_SOCKET)
296 closesocket(s);
297#ifdef _WIN32
298 WSACleanup();
299#endif
300}
301
302static bool ReadPositiveTorControlReply(SOCKET s, std::string& errOut)
303{
304 std::string line;
305 if (!RecvControlLine(s, line))
306 {
307 errOut = "Tor control port closed before replying";
308 return false;
309 }
310 if (line.size() >= 3 && line.substr(0, 3) == "250")
311 return true;
312 errOut = strprintf("Tor control command failed: %s", line.c_str());
313 return false;
314}
315
316static bool RequestManagedTorShutdown(std::string& errOut)
317{
318 std::string dataDir;
319 unsigned short controlPort = 0;
320 CRITICAL_BLOCK(cs_managedTor)
321 {
322 dataDir = g_managedTorDataDir;
323 controlPort = g_managedTorControlPort;
324 }
325 if (controlPort == 0 || dataDir.empty())
326 {
327 errOut = "Tor control port is not available";
328 return false;
329 }
330
331 std::vector<unsigned char> cookie;
332 if (!ReadBinaryFile(PathJoin(dataDir, "control_auth_cookie"), cookie, errOut))
333 return false;
334
335 SOCKET s = ConnectLoopback(controlPort, errOut);
336 if (s == INVALID_SOCKET)
337 return false;
338
339 bool ok = false;
340 std::string auth = "AUTHENTICATE " + HexStr(cookie.begin(), cookie.end(), false) + "\r\n";
341 if (!SendAll(s, auth) || !ReadPositiveTorControlReply(s, errOut))
342 goto done;
343
344 if (!SendAll(s, "SIGNAL SHUTDOWN\r\n"))
345 {
346 errOut = "could not send Tor shutdown signal";
347 goto done;
348 }
349
350 {
351 std::string reply;
352 if (RecvControlLine(s, reply) && !(reply.size() >= 3 && reply.substr(0, 3) == "250"))
353 {
354 errOut = strprintf("Tor rejected shutdown signal: %s", reply.c_str());
355 goto done;
356 }
357 }
358 ok = true;
359
360done:
361 CloseLoopback(s);
362 return ok;
363}
364
365static bool ReadOneLine(const std::string& path, std::string& out)
366{
367 out.clear();
368 FILE* f = fopen(path.c_str(), "rb");
369 if (!f)
370 return false;
371 char buf[256] = {0};
372 if (!fgets(buf, sizeof(buf), f))
373 {
374 fclose(f);
375 return false;
376 }
377 fclose(f);
378 out = buf;
379 while (!out.empty() && (out[out.size() - 1] == '\n' || out[out.size() - 1] == '\r' ||
380 out[out.size() - 1] == ' ' || out[out.size() - 1] == '\t'))
381 out.erase(out.size() - 1);
382 return !out.empty();
383}
384
385static bool PickLocalPort(unsigned short& portOut, std::string& errOut)
386{
387 portOut = 0;
388 bool ok = false;
389#ifdef _WIN32
390 WSADATA wsadata;
391 int ret = WSAStartup(MAKEWORD(2,2), &wsadata);
392 if (ret != NO_ERROR)
393 {
394 errOut = strprintf("could not start Winsock for local port probe (WSAStartup returned %d)", ret);
395 return false;
396 }
397#endif
398 struct sockaddr_in addr;
399 socklen_t len = sizeof(addr);
400 SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
401 if (s == INVALID_SOCKET)
402 {
403 errOut = "could not create local port probe socket";
404 goto done;
405 }
406 memset(&addr, 0, sizeof(addr));
407 addr.sin_family = AF_INET;
408 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
409 addr.sin_port = 0;
410 if (bind(s, (struct sockaddr*)&addr, sizeof(addr)) != 0)
411 {
412 errOut = "could not bind local port probe socket";
413 goto done;
414 }
415 if (getsockname(s, (struct sockaddr*)&addr, &len) != 0)
416 {
417 errOut = "could not read local port probe socket";
418 goto done;
419 }
420 portOut = ntohs(addr.sin_port);
421 if (portOut == 0)
422 {
423 errOut = "local port probe returned port zero";
424 goto done;
425 }
426 ok = true;
427done:
428 if (s != INVALID_SOCKET)
429 closesocket(s);
430#ifdef _WIN32
431 WSACleanup();
432#endif
433 return ok;
434}
435
436static std::string ExecutableDir()
437{
438#ifdef _WIN32
439 char path[MAX_PATH + 1] = {0};
440 DWORD n = GetModuleFileNameA(NULL, path, MAX_PATH);
441 if (n == 0 || n >= MAX_PATH)
442 return ".";
443 std::string s = path;
444 size_t slash = s.find_last_of("\\/");
445 if (slash == std::string::npos)
446 return ".";
447 return s.substr(0, slash);
448#else
449 char path[4096] = {0};
450 ssize_t n = readlink("/proc/self/exe", path, sizeof(path) - 1);
451 if (n <= 0)
452 return ".";
453 path[n] = 0;
454 std::string s = path;
455 size_t slash = s.find_last_of('/');
456 if (slash == std::string::npos)
457 return ".";
458 return s.substr(0, slash);
459#endif
460}
461
462static bool FileIsExecutableCandidate(const std::string& path)
463{
464#ifdef _WIN32
465 DWORD attr = GetFileAttributesA(path.c_str());
466 return attr != INVALID_FILE_ATTRIBUTES && !(attr & FILE_ATTRIBUTE_DIRECTORY);
467#else
468 return access(path.c_str(), X_OK) == 0;
469#endif
470}
471
472static std::string ResolveTorPath(const std::string& torPathOpt)
473{
474 if (!torPathOpt.empty())
475 return torPathOpt;
476
477 std::vector<std::string> candidates;
478 std::string exeDir = ExecutableDir();
479#ifdef _WIN32
480 candidates.push_back(PathJoin(exeDir, "tor/tor.exe"));
481 candidates.push_back(PathJoin(exeDir, "tor.exe"));
482 candidates.push_back("tor.exe");
483#else
484 candidates.push_back(PathJoin(exeDir, "tor/tor"));
485 candidates.push_back(PathJoin(exeDir, "tor"));
486 candidates.push_back("tor");
487#endif
488 for (size_t i = 0; i < candidates.size(); i++)
489 {
490 if (candidates[i].find('/') == std::string::npos &&
491 candidates[i].find('\\') == std::string::npos)
492 return candidates[i]; // Let the OS search PATH.
493 if (FileIsExecutableCandidate(candidates[i]))
494 return candidates[i];
495 }
496#ifdef _WIN32
497 return "tor.exe";
498#else
499 return "tor";
500#endif
501}
502
503bool BtfBundledTorPath(std::string& torPathOut)
504{
505 torPathOut.clear();
506 std::string exeDir = ExecutableDir();
507#ifdef _WIN32
508 std::string path = PathJoin(exeDir, "tor/tor.exe");
509#else
510 std::string path = PathJoin(exeDir, "tor/tor");
511#endif
512 if (!FileIsExecutableCandidate(path))
513 return false;
514 torPathOut = path;
515 return true;
516}
517
518std::string BtfBuildManagedTorrcForTest(const std::string& dataDir,
519 const std::string& hiddenServiceDir,
520 unsigned short socksPort,
521 unsigned short controlPort,
522 unsigned short p2pPort)
523{
524 std::string torData = NormalizeTorrcPath(dataDir);
525 std::string hsDir = NormalizeTorrcPath(hiddenServiceDir);
526 std::string s;
527 s += "# Generated by Bitflash. Edit only while Bitflash is not managing Tor.\n";
528 s += "DataDirectory " + QuoteTorrcPath(torData) + "\n";
529 s += strprintf("SocksPort 127.0.0.1:%u IsolateSOCKSAuth IsolateClientAddr IsolateDestAddr IsolateDestPort\n",
530 (unsigned)socksPort);
531 s += strprintf("ControlPort 127.0.0.1:%u\n", (unsigned)controlPort);
532 s += "CookieAuthentication 1\n";
533 s += "HiddenServiceDir " + QuoteTorrcPath(hsDir) + "\n";
534 s += "HiddenServiceVersion 3\n";
535 s += strprintf("HiddenServicePort %u 127.0.0.1:%u\n",
536 (unsigned)p2pPort, (unsigned)p2pPort);
537 return s;
538}
539
540static bool StartTorProcess(const std::string& torPath, const std::string& torrcPath,
541 std::string& errOut)
542{
543#ifdef _WIN32
544 memset(&g_managedTorProcess, 0, sizeof(g_managedTorProcess));
545 STARTUPINFOA si;
546 memset(&si, 0, sizeof(si));
547 si.cb = sizeof(si);
548 std::string cmd = QuoteCommandArg(torPath) + " -f " + QuoteCommandArg(torrcPath);
549 std::vector<char> mutableCmd(cmd.begin(), cmd.end());
550 mutableCmd.push_back(0);
551 if (!CreateProcessA(NULL, &mutableCmd[0], NULL, NULL, FALSE, CREATE_NO_WINDOW,
552 NULL, NULL, &si, &g_managedTorProcess))
553 {
554 errOut = strprintf("could not start Tor process %s (error %lu)",
555 torPath.c_str(), (unsigned long)GetLastError());
556 return false;
557 }
558 return true;
559#else
560 g_managedTorPid = fork();
561 if (g_managedTorPid < 0)
562 {
563 errOut = strprintf("could not fork Tor process: %s", strerror(errno));
564 return false;
565 }
566 if (g_managedTorPid == 0)
567 {
568 execlp(torPath.c_str(), torPath.c_str(), "-f", torrcPath.c_str(), (char*)NULL);
569 _exit(127);
570 }
571 return true;
572#endif
573}
574
575static bool ManagedTorProcessAlive(bool& fExited, int& nExitCode)
576{
577 fExited = false;
578 nExitCode = 0;
579#ifdef _WIN32
580 HANDLE hProcess = NULL;
581 CRITICAL_BLOCK(cs_managedTor)
582 hProcess = g_managedTorProcess.hProcess;
583 if (!hProcess)
584 return false;
585 DWORD code = 0;
586 if (!GetExitCodeProcess(hProcess, &code))
587 return false;
588 if (code != STILL_ACTIVE)
589 {
590 fExited = true;
591 nExitCode = (int)code;
592 return false;
593 }
594 return true;
595#else
596 pid_t pid = -1;
597 CRITICAL_BLOCK(cs_managedTor)
598 pid = g_managedTorPid;
599 if (pid <= 0)
600 return false;
601 int status = 0;
602 pid_t r = waitpid(pid, &status, WNOHANG);
603 if (r == 0)
604 return true;
605 if (r == pid)
606 {
607 fExited = true;
608 if (WIFEXITED(status))
609 nExitCode = WEXITSTATUS(status);
610 else if (WIFSIGNALED(status))
611 nExitCode = 128 + WTERMSIG(status);
612 CRITICAL_BLOCK(cs_managedTor)
613 if (g_managedTorPid == pid)
614 g_managedTorPid = -1;
615 }
616 return false;
617#endif
618}
619
620static bool ValidateManagedTorStartup(std::string& errOut)
621{
622 Sleep(500);
623 bool fExited = false;
624 int nExitCode = 0;
625 if (ManagedTorProcessAlive(fExited, nExitCode))
626 return true;
627 if (fExited)
628 errOut = strprintf("Tor exited during startup with code %d", nExitCode);
629 else
630 errOut = "Tor process is not running after startup";
631 return false;
632}
633
634static void MarkManagedTorDead(int nExitCode)
635{
636 BtfStopManagedTor();
637 CRITICAL_BLOCK(cs_managedTor)
638 g_managedTorStatus = strprintf("stopped unexpectedly, Tor exit code %d", nExitCode);
639}
640
641static void ThreadManagedTorWatchdog(void*)
642{
643 for (;;)
644 {
645 Sleep(5000);
646 CRITICAL_BLOCK(cs_managedTor)
647 {
648 if (!g_fManagedTor || g_fManagedTorStopping || fShutdown)
649 return;
650 }
651
652 bool fExited = false;
653 int nExitCode = 0;
654 if (!ManagedTorProcessAlive(fExited, nExitCode))
655 {
656 if (fExited)
657 MarkManagedTorDead(nExitCode);
658 else
659 {
660 BtfStopManagedTor();
661 CRITICAL_BLOCK(cs_managedTor)
662 g_managedTorStatus = "stopped unexpectedly, Tor process handle is gone";
663 }
664 return;
665 }
666 }
667}
668
669static void ThreadManagedTorHostname(void*)
670{
671 std::string hostnamePath;
672 unsigned short p2pPort = 0;
673 CRITICAL_BLOCK(cs_managedTor)
674 {
675 hostnamePath = PathJoin(g_managedTorHiddenServiceDir, "hostname");
676 p2pPort = g_managedTorP2PPort;
677 }
678
679 for (int i = 0; i < 90 && !fShutdown; i++)
680 {
681 std::string onion;
682 if (ReadOneLine(hostnamePath, onion))
683 {
684 std::string endpoint = strprintf("%s:%u", onion.c_str(), (unsigned)p2pPort);
685 std::string err;
686 if (BtfSetLocalOnionEndpoint(endpoint, err))
687 {
688 CRITICAL_BLOCK(cs_managedTor)
689 {
690 g_managedTorOnion = BtfLocalOnionEndpoint();
691 g_managedTorStatus = strprintf("running, SOCKS5 127.0.0.1:%u, onion %s",
692 (unsigned)g_managedTorSocksPort,
693 g_managedTorOnion.c_str());
694 }
695 printf("Managed Tor hidden service ready: %s\n", BtfLocalOnionEndpoint().c_str());
696 return;
697 }
698 CRITICAL_BLOCK(cs_managedTor)
699 g_managedTorStatus = strprintf("running, but generated onion endpoint was rejected: %s",
700 err.c_str());
701 return;
702 }
703 Sleep(1000);
704 }
705
706 BtfStopManagedTor();
707 CRITICAL_BLOCK(cs_managedTor)
708 g_managedTorStatus = "failed: Tor did not create a hidden service hostname within 90s";
709}
710
711bool BtfStartManagedTor(const std::string& torPathOpt, std::string& errOut)
712{
713 CRITICAL_BLOCK(cs_managedTor)
714 {
715 if (g_fManagedTor)
716 {
717 errOut = "managed Tor is already running";
718 return false;
719 }
720 }
721
722 std::string root = PathJoin(GetAppDir(), "managed-tor");
723 std::string dataDir = PathJoin(root, "data");
724 std::string hsDir = PathJoin(root, "onion-service");
725 std::string torrcPath = PathJoin(root, "torrc");
726 if (!EnsureDir(root, errOut) || !EnsureDir(dataDir, errOut) || !EnsureDir(hsDir, errOut))
727 return false;
728
729 unsigned short socksPort = 0;
730 if (!PickLocalPort(socksPort, errOut))
731 return false;
732 unsigned short controlPort = 0;
733 if (!PickLocalPort(controlPort, errOut))
734 return false;
735
736 unsigned short p2pPort = ntohs(nListenPort);
737 std::string torrc = BtfBuildManagedTorrcForTest(dataDir, hsDir, socksPort,
738 controlPort, p2pPort);
739 if (!WriteTextFile(torrcPath, torrc, errOut))
740 return false;
741
742 std::string torPath = ResolveTorPath(torPathOpt);
743 {
744 CRITICAL_BLOCK(cs_managedTor)
745 {
746 g_fManagedTor = true;
747 g_fManagedTorStopping = false;
748 g_managedTorPath = torPath;
749 g_managedTorDataDir = dataDir;
750 g_managedTorHiddenServiceDir = hsDir;
751 g_managedTorSocksPort = socksPort;
752 g_managedTorControlPort = controlPort;
753 g_managedTorP2PPort = p2pPort;
754 g_managedTorOnion.clear();
755 g_managedTorStatus = strprintf("starting %s, SOCKS5 127.0.0.1:%u",
756 torPath.c_str(), (unsigned)socksPort);
757 }
758 }
759
760 if (!StartTorProcess(torPath, torrcPath, errOut))
761 {
762 CRITICAL_BLOCK(cs_managedTor)
763 {
764 g_fManagedTor = false;
765 g_managedTorStatus = strprintf("failed: %s", errOut.c_str());
766 }
767 return false;
768 }
769 if (!ValidateManagedTorStartup(errOut))
770 {
771 BtfStopManagedTor();
772 CRITICAL_BLOCK(cs_managedTor)
773 g_managedTorStatus = strprintf("failed: %s", errOut.c_str());
774 return false;
775 }
776
777 std::string proxyErr;
778 if (!BtfEnableTorProxy(strprintf("127.0.0.1:%u", (unsigned)socksPort), proxyErr))
779 {
780 BtfStopManagedTor();
781 errOut = strprintf("could not enable managed Tor SOCKS5 proxy: %s", proxyErr.c_str());
782 return false;
783 }
784
785 if (_beginthread(ThreadManagedTorHostname, 0, NULL) == (uintptr_t)-1)
786 {
787 BtfStopManagedTor();
788 errOut = "could not start managed Tor hostname watcher";
789 return false;
790 }
791 if (_beginthread(ThreadManagedTorWatchdog, 0, NULL) == (uintptr_t)-1)
792 {
793 BtfStopManagedTor();
794 errOut = "could not start managed Tor watchdog";
795 return false;
796 }
797
798 return true;
799}
800
801void BtfStopManagedTor()
802{
803 bool fWasRunning = false;
804 CRITICAL_BLOCK(cs_managedTor)
805 {
806 fWasRunning = g_fManagedTor;
807 if (!fWasRunning)
808 return;
809 g_fManagedTorStopping = true;
810 g_fManagedTor = false;
811 g_managedTorStatus = "stopping";
812 }
813#ifdef _WIN32
814 if (g_managedTorProcess.hProcess)
815 {
816 std::string shutdownErr;
817 bool fGraceful = RequestManagedTorShutdown(shutdownErr);
818 DWORD waitResult = WaitForSingleObject(g_managedTorProcess.hProcess,
819 fGraceful ? 10000 : 1000);
820 if (waitResult != WAIT_OBJECT_0)
821 {
822 TerminateProcess(g_managedTorProcess.hProcess, 0);
823 WaitForSingleObject(g_managedTorProcess.hProcess, 5000);
824 }
825 CloseHandle(g_managedTorProcess.hProcess);
826 CloseHandle(g_managedTorProcess.hThread);
827 memset(&g_managedTorProcess, 0, sizeof(g_managedTorProcess));
828 }
829#else
830 if (g_managedTorPid > 0)
831 {
832 std::string shutdownErr;
833 if (!RequestManagedTorShutdown(shutdownErr))
834 kill(g_managedTorPid, SIGTERM);
835 bool fExited = false;
836 for (int i = 0; i < 100; i++)
837 {
838 int status = 0;
839 pid_t r = waitpid(g_managedTorPid, &status, WNOHANG);
840 if (r == g_managedTorPid)
841 {
842 fExited = true;
843 break;
844 }
845 Sleep(100);
846 }
847 if (!fExited)
848 {
849 kill(g_managedTorPid, SIGKILL);
850 waitpid(g_managedTorPid, NULL, 0);
851 }
852 g_managedTorPid = -1;
853 }
854#endif
855 CRITICAL_BLOCK(cs_managedTor)
856 {
857 g_managedTorStatus = "stopped";
858 g_managedTorOnion.clear();
859 g_managedTorSocksPort = 0;
860 g_managedTorControlPort = 0;
861 g_managedTorP2PPort = 0;
862 }
863}
864
865bool BtfManagedTorEnabled()
866{
867 CRITICAL_BLOCK(cs_managedTor)
868 return g_fManagedTor;
869 return false;
870}
871
872std::string BtfManagedTorStatus()
873{
874 CRITICAL_BLOCK(cs_managedTor)
875 {
876 if (!g_fManagedTor && g_managedTorStatus.empty())
877 return "disabled";
878 return g_managedTorStatus;
879 }
880 return "disabled";
881}