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:
authorNsable-sh <nsable-sh@noreply.git.bitflash.network>2026-08-13 14:09:58 -0300
committerNsable-sh <nsable-sh@noreply.git.bitflash.network>2026-08-13 14:09:58 -0300
commit1bf78c53404d83638177889590ce199c87af63bc (patch)
treee06544e9e7516203bc4309460438a0486b0328e4 /src/tor.cpp
parent20243de8fcf6dc30d7f0b1dbbc2f9db22683ae24 (diff)
net: stop managed Tor through control port
Diffstat (limited to 'src/tor.cpp')
-rw-r--r--src/tor.cpp249
1 files changed, 243 insertions, 6 deletions
diff --git a/src/tor.cpp b/src/tor.cpp
index 180cd2b..e47ee44 100644
--- a/src/tor.cpp
+++ b/src/tor.cpp
@@ -17,6 +17,7 @@ static CCriticalSection cs_managedTor;
17static bool g_fManagedTor = false; 17static bool g_fManagedTor = false;
18static bool g_fManagedTorStopping = false; 18static bool g_fManagedTorStopping = false;
19static unsigned short g_managedTorSocksPort = 0; 19static unsigned short g_managedTorSocksPort = 0;
20static unsigned short g_managedTorControlPort = 0;
20static unsigned short g_managedTorP2PPort = 0; 21static unsigned short g_managedTorP2PPort = 0;
21static std::string g_managedTorPath; 22static std::string g_managedTorPath;
22static std::string g_managedTorDataDir; 23static std::string g_managedTorDataDir;
@@ -108,8 +109,16 @@ static bool EnsureDir(const std::string& path, std::string& errOut)
108 errOut = strprintf("%s exists and is not a directory", path.c_str()); 109 errOut = strprintf("%s exists and is not a directory", path.c_str());
109 return false; 110 return false;
110 } 111 }
111 if (mkdir(path.c_str(), 0700) == 0 || errno == EEXIST) 112 if (mkdir(path.c_str(), 0700) == 0)
112 return true; 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 }
113 errOut = strprintf("could not create %s: %s", path.c_str(), strerror(errno)); 122 errOut = strprintf("could not create %s: %s", path.c_str(), strerror(errno));
114 return false; 123 return false;
115#endif 124#endif
@@ -154,6 +163,205 @@ static bool WriteTextFile(const std::string& path, const std::string& data,
154 return true; 163 return true;
155} 164}
156 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
157static bool ReadOneLine(const std::string& path, std::string& out) 365static bool ReadOneLine(const std::string& path, std::string& out)
158{ 366{
159 out.clear(); 367 out.clear();
@@ -295,6 +503,7 @@ static std::string ResolveTorPath(const std::string& torPathOpt)
295std::string BtfBuildManagedTorrcForTest(const std::string& dataDir, 503std::string BtfBuildManagedTorrcForTest(const std::string& dataDir,
296 const std::string& hiddenServiceDir, 504 const std::string& hiddenServiceDir,
297 unsigned short socksPort, 505 unsigned short socksPort,
506 unsigned short controlPort,
298 unsigned short p2pPort) 507 unsigned short p2pPort)
299{ 508{
300 std::string torData = NormalizeTorrcPath(dataDir); 509 std::string torData = NormalizeTorrcPath(dataDir);
@@ -304,6 +513,8 @@ std::string BtfBuildManagedTorrcForTest(const std::string& dataDir,
304 s += "DataDirectory " + QuoteTorrcPath(torData) + "\n"; 513 s += "DataDirectory " + QuoteTorrcPath(torData) + "\n";
305 s += strprintf("SocksPort 127.0.0.1:%u IsolateSOCKSAuth IsolateClientAddr IsolateDestAddr IsolateDestPort\n", 514 s += strprintf("SocksPort 127.0.0.1:%u IsolateSOCKSAuth IsolateClientAddr IsolateDestAddr IsolateDestPort\n",
306 (unsigned)socksPort); 515 (unsigned)socksPort);
516 s += strprintf("ControlPort 127.0.0.1:%u\n", (unsigned)controlPort);
517 s += "CookieAuthentication 1\n";
307 s += "HiddenServiceDir " + QuoteTorrcPath(hsDir) + "\n"; 518 s += "HiddenServiceDir " + QuoteTorrcPath(hsDir) + "\n";
308 s += "HiddenServiceVersion 3\n"; 519 s += "HiddenServiceVersion 3\n";
309 s += strprintf("HiddenServicePort %u 127.0.0.1:%u\n", 520 s += strprintf("HiddenServicePort %u 127.0.0.1:%u\n",
@@ -503,9 +714,13 @@ bool BtfStartManagedTor(const std::string& torPathOpt, std::string& errOut)
503 unsigned short socksPort = 0; 714 unsigned short socksPort = 0;
504 if (!PickLocalPort(socksPort, errOut)) 715 if (!PickLocalPort(socksPort, errOut))
505 return false; 716 return false;
717 unsigned short controlPort = 0;
718 if (!PickLocalPort(controlPort, errOut))
719 return false;
506 720
507 unsigned short p2pPort = ntohs(nListenPort); 721 unsigned short p2pPort = ntohs(nListenPort);
508 std::string torrc = BtfBuildManagedTorrcForTest(dataDir, hsDir, socksPort, p2pPort); 722 std::string torrc = BtfBuildManagedTorrcForTest(dataDir, hsDir, socksPort,
723 controlPort, p2pPort);
509 if (!WriteTextFile(torrcPath, torrc, errOut)) 724 if (!WriteTextFile(torrcPath, torrc, errOut))
510 return false; 725 return false;
511 726
@@ -519,6 +734,7 @@ bool BtfStartManagedTor(const std::string& torPathOpt, std::string& errOut)
519 g_managedTorDataDir = dataDir; 734 g_managedTorDataDir = dataDir;
520 g_managedTorHiddenServiceDir = hsDir; 735 g_managedTorHiddenServiceDir = hsDir;
521 g_managedTorSocksPort = socksPort; 736 g_managedTorSocksPort = socksPort;
737 g_managedTorControlPort = controlPort;
522 g_managedTorP2PPort = p2pPort; 738 g_managedTorP2PPort = p2pPort;
523 g_managedTorOnion.clear(); 739 g_managedTorOnion.clear();
524 g_managedTorStatus = strprintf("starting %s, SOCKS5 127.0.0.1:%u", 740 g_managedTorStatus = strprintf("starting %s, SOCKS5 127.0.0.1:%u",
@@ -582,8 +798,15 @@ void BtfStopManagedTor()
582#ifdef _WIN32 798#ifdef _WIN32
583 if (g_managedTorProcess.hProcess) 799 if (g_managedTorProcess.hProcess)
584 { 800 {
585 TerminateProcess(g_managedTorProcess.hProcess, 0); 801 std::string shutdownErr;
586 WaitForSingleObject(g_managedTorProcess.hProcess, 5000); 802 bool fGraceful = RequestManagedTorShutdown(shutdownErr);
803 DWORD waitResult = WaitForSingleObject(g_managedTorProcess.hProcess,
804 fGraceful ? 10000 : 1000);
805 if (waitResult != WAIT_OBJECT_0)
806 {
807 TerminateProcess(g_managedTorProcess.hProcess, 0);
808 WaitForSingleObject(g_managedTorProcess.hProcess, 5000);
809 }
587 CloseHandle(g_managedTorProcess.hProcess); 810 CloseHandle(g_managedTorProcess.hProcess);
588 CloseHandle(g_managedTorProcess.hThread); 811 CloseHandle(g_managedTorProcess.hThread);
589 memset(&g_managedTorProcess, 0, sizeof(g_managedTorProcess)); 812 memset(&g_managedTorProcess, 0, sizeof(g_managedTorProcess));
@@ -591,15 +814,26 @@ void BtfStopManagedTor()
591#else 814#else
592 if (g_managedTorPid > 0) 815 if (g_managedTorPid > 0)
593 { 816 {
594 kill(g_managedTorPid, SIGTERM); 817 std::string shutdownErr;
595 for (int i = 0; i < 50; i++) 818 if (!RequestManagedTorShutdown(shutdownErr))
819 kill(g_managedTorPid, SIGTERM);
820 bool fExited = false;
821 for (int i = 0; i < 100; i++)
596 { 822 {
597 int status = 0; 823 int status = 0;
598 pid_t r = waitpid(g_managedTorPid, &status, WNOHANG); 824 pid_t r = waitpid(g_managedTorPid, &status, WNOHANG);
599 if (r == g_managedTorPid) 825 if (r == g_managedTorPid)
826 {
827 fExited = true;
600 break; 828 break;
829 }
601 Sleep(100); 830 Sleep(100);
602 } 831 }
832 if (!fExited)
833 {
834 kill(g_managedTorPid, SIGKILL);
835 waitpid(g_managedTorPid, NULL, 0);
836 }
603 g_managedTorPid = -1; 837 g_managedTorPid = -1;
604 } 838 }
605#endif 839#endif
@@ -607,6 +841,9 @@ void BtfStopManagedTor()
607 { 841 {
608 g_managedTorStatus = "stopped"; 842 g_managedTorStatus = "stopped";
609 g_managedTorOnion.clear(); 843 g_managedTorOnion.clear();
844 g_managedTorSocksPort = 0;
845 g_managedTorControlPort = 0;
846 g_managedTorP2PPort = 0;
610 } 847 }
611} 848}
612 849