v5.1.7: Add Tor process manager for .onion connectivity
Build All Platforms / build-windows-qt (push) Waiting to run
Build All Platforms / build-windows-daemon (push) Waiting to run
Build All Platforms / build-linux-qt (push) Waiting to run
Build All Platforms / build-linux-daemon (push) Waiting to run
Build All Platforms / build-macos (push) Waiting to run
Build All Platforms / release (push) Blocked by required conditions
Build All Platforms / build-windows-qt (push) Waiting to run
Build All Platforms / build-windows-daemon (push) Waiting to run
Build All Platforms / build-linux-qt (push) Waiting to run
Build All Platforms / build-linux-daemon (push) Waiting to run
Build All Platforms / build-macos (push) Waiting to run
Build All Platforms / release (push) Blocked by required conditions
Adds CTorProcess which finds and launches an external Tor binary as a subprocess, providing a SOCKS5 proxy on port 19099 and a v3 hidden service on port 24112. The wallet auto-detects Tor from common install locations or the app directory. Falls back gracefully to clearnet-only if Tor is not found. Also fixes Tor-only network restriction that blocked IPv4/IPv6, and bumps version to 5.1.7. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -8,7 +8,7 @@
|
||||
// These need to be macros, as version.cpp's and triangles-qt.rc's voodoo requires it
|
||||
#define CLIENT_VERSION_MAJOR 5
|
||||
#define CLIENT_VERSION_MINOR 1
|
||||
#define CLIENT_VERSION_REVISION 6
|
||||
#define CLIENT_VERSION_REVISION 7
|
||||
#define CLIENT_VERSION_BUILD 0
|
||||
|
||||
// Converts the parameter X to a string after macro replacement on X has been performed.
|
||||
|
||||
+38
-3
@@ -12,6 +12,7 @@
|
||||
#include "checkpoints.h"
|
||||
#include "smessage.h"
|
||||
#include "tor/onion_v3.h"
|
||||
#include "tor/tor_process.h"
|
||||
#ifdef ENABLE_ZMQ
|
||||
#include "zmqpublishnotifier.h"
|
||||
#endif
|
||||
@@ -154,6 +155,7 @@ void Shutdown(void* parg)
|
||||
|
||||
SecureMsgShutdown();
|
||||
ShutdownTorV3();
|
||||
StopTorProcess();
|
||||
|
||||
#ifdef ENABLE_ZMQ
|
||||
if (pzmqNotifier)
|
||||
@@ -962,18 +964,33 @@ bool AppInit2()
|
||||
printf(" rescan %15"PRId64"ms\n", GetTimeMillis() - nStart);
|
||||
}
|
||||
|
||||
// ********************************************************* Step 8.5: initialize Tor V3 identity
|
||||
// ********************************************************* Step 8.5: start Tor and initialize V3 identity
|
||||
{
|
||||
uiInterface.InitMessage(_("Starting Tor..."));
|
||||
printf("Starting Tor process...\n");
|
||||
|
||||
// Start the Tor process (finds/launches tor binary, provides SOCKS proxy)
|
||||
std::string torDataPath = (GetDataDir() / "tor_data").string();
|
||||
bool torStarted = StartTorProcess(torDataPath);
|
||||
|
||||
if (torStarted) {
|
||||
printf("Tor process running, SOCKS proxy at %s\n",
|
||||
CTorProcess::GetInstance()->GetSocksProxy().c_str());
|
||||
} else {
|
||||
printf("WARNING: Tor not available. .onion peers will not be reachable.\n");
|
||||
printf(" Clearnet connections will still work normally.\n");
|
||||
}
|
||||
|
||||
// Initialize Tor V3 identity (Ed25519 keys, onion address)
|
||||
uiInterface.InitMessage(_("Initializing Tor V3 identity..."));
|
||||
printf("Initializing Tor V3 onion identity...\n");
|
||||
|
||||
// Tor V3 identity is innate to Triangles — always enabled
|
||||
LoadTorV3Config();
|
||||
TorV3Config& torConfig = GetTorV3Config();
|
||||
torConfig.enableTor = true;
|
||||
torConfig.enableHiddenService = true;
|
||||
torConfig.hiddenServicePort = GetListenPort();
|
||||
torConfig.torDataDirectory = (GetDataDir() / "tor_data").string();
|
||||
torConfig.torDataDirectory = torDataPath;
|
||||
|
||||
if (InitTorV3()) {
|
||||
string onionAddr = CTorV3Manager::GetInstance()->GetWalletOnionAddress();
|
||||
@@ -996,6 +1013,24 @@ bool AppInit2()
|
||||
} else {
|
||||
printf("WARNING: Failed to initialize Tor V3 identity\n");
|
||||
}
|
||||
|
||||
// Also check if Tor gave us a hidden service hostname
|
||||
if (torStarted) {
|
||||
fs::path torHsHostname = fs::path(torDataPath) / "hidden_service" / "hostname";
|
||||
if (fs::exists(torHsHostname)) {
|
||||
ifstream f(torHsHostname.string().c_str());
|
||||
string torOnion;
|
||||
if (f.is_open() && getline(f, torOnion)) {
|
||||
// Trim whitespace
|
||||
while (!torOnion.empty() && (torOnion.back() == '\n' || torOnion.back() == '\r' || torOnion.back() == ' '))
|
||||
torOnion.pop_back();
|
||||
if (!torOnion.empty()) {
|
||||
AddLocal(CService(torOnion, GetListenPort(), fNameLookup), LOCAL_MANUAL);
|
||||
printf("Tor hidden service (from Tor process): %s\n", torOnion.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ********************************************************* Step 9: import blocks
|
||||
|
||||
+9
-1
@@ -135,7 +135,8 @@ OBJS= \
|
||||
obj/scrypt-x86.o \
|
||||
obj/scrypt-x86_64.o \
|
||||
obj/smessage.o \
|
||||
obj/onion_v3.o
|
||||
obj/onion_v3.o \
|
||||
obj/tor_process.o
|
||||
|
||||
all: trianglesd.exe
|
||||
|
||||
@@ -200,6 +201,13 @@ obj/onion_v3.o: tor/onion_v3.cpp
|
||||
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
|
||||
rm -f $(@:%.o=%.d)
|
||||
|
||||
obj/tor_process.o: tor/tor_process.cpp
|
||||
$(CXX) -c $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $<
|
||||
@cp $(@:%.o=%.d) $(@:%.o=%.P); \
|
||||
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
|
||||
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
|
||||
rm -f $(@:%.o=%.d)
|
||||
|
||||
obj/net_bootstrap.o: net_bootstrap.cpp
|
||||
$(CXX) -c $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $<
|
||||
@cp $(@:%.o=%.d) $(@:%.o=%.P); \
|
||||
|
||||
+9
-1
@@ -170,7 +170,8 @@ OBJS= \
|
||||
obj/scrypt-x86.o \
|
||||
obj/scrypt-x86_64.o \
|
||||
obj/smessage.o \
|
||||
obj/onion_v3.o
|
||||
obj/onion_v3.o \
|
||||
obj/tor_process.o
|
||||
|
||||
# ZMQ support (optional)
|
||||
# Build with: make -f makefile.unix USE_ZMQ=1
|
||||
@@ -246,6 +247,13 @@ obj/onion_v3.o: tor/onion_v3.cpp
|
||||
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
|
||||
rm -f $(@:%.o=%.d)
|
||||
|
||||
obj/tor_process.o: tor/tor_process.cpp
|
||||
$(CXX) -c $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $<
|
||||
@cp $(@:%.o=%.d) $(@:%.o=%.P); \
|
||||
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
|
||||
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
|
||||
rm -f $(@:%.o=%.d)
|
||||
|
||||
obj/net_bootstrap.o: net_bootstrap.cpp
|
||||
$(CXX) -c $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $<
|
||||
@cp $(@:%.o=%.d) $(@:%.o=%.P); \
|
||||
|
||||
+4
-3
@@ -2006,9 +2006,10 @@ void static Discover()
|
||||
}
|
||||
|
||||
static void run_tor() {
|
||||
// Old embedded Tor v2 client removed - incompatible with OpenSSL 3.x.
|
||||
// Tor v3 onion services are handled by onion_v3.cpp via external Tor/SOCKS5.
|
||||
printf("Tor v3 mode: using external Tor process via SOCKS5 proxy.\n");
|
||||
// Tor process is now managed by CTorProcess (tor_process.cpp)
|
||||
// which starts an external Tor binary with SOCKS5 proxy and v3 hidden service.
|
||||
// The old embedded Tor v2 code was removed (incompatible with OpenSSL 3.x).
|
||||
printf("Tor v3 mode: using managed Tor process via SOCKS5 proxy.\n");
|
||||
set_initialized();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,413 @@
|
||||
// Copyright (c) 2024-2025 Triangles developers
|
||||
// Tor Process Manager - launches and manages an external Tor binary
|
||||
// Distributed under the MIT/X11 software license
|
||||
|
||||
#ifdef WIN32
|
||||
#define NOMINMAX
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#ifndef _WIN32_WINNT
|
||||
#define _WIN32_WINNT 0x0600
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "tor_process.h"
|
||||
#include "../util.h"
|
||||
#include "../net.h"
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <fstream>
|
||||
#include <cstdio>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <tlhelp32.h>
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
|
||||
static CTorProcess* torProcessInstance = nullptr;
|
||||
|
||||
CTorProcess* CTorProcess::GetInstance()
|
||||
{
|
||||
if (!torProcessInstance) {
|
||||
torProcessInstance = new CTorProcess();
|
||||
}
|
||||
return torProcessInstance;
|
||||
}
|
||||
|
||||
CTorProcess::CTorProcess()
|
||||
: socksPort(19099)
|
||||
, hiddenServicePort(24112)
|
||||
, running(false)
|
||||
#ifdef WIN32
|
||||
, hProcess(NULL)
|
||||
, processId(0)
|
||||
#else
|
||||
, processId(0)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
CTorProcess::~CTorProcess()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
std::string CTorProcess::FindTorBinary()
|
||||
{
|
||||
// List of candidate paths to search for the Tor binary
|
||||
std::vector<std::string> candidates;
|
||||
|
||||
#ifdef WIN32
|
||||
// Same directory as the wallet executable
|
||||
char exePath[MAX_PATH];
|
||||
if (GetModuleFileNameA(NULL, exePath, MAX_PATH)) {
|
||||
fs::path exeDir = fs::path(exePath).parent_path();
|
||||
candidates.push_back((exeDir / "tor.exe").string());
|
||||
candidates.push_back((exeDir / "tor" / "tor.exe").string());
|
||||
candidates.push_back((exeDir / "Tor" / "tor.exe").string());
|
||||
}
|
||||
|
||||
// Data directory
|
||||
candidates.push_back((GetDataDir() / "tor.exe").string());
|
||||
candidates.push_back((GetDataDir() / "tor" / "tor.exe").string());
|
||||
|
||||
// Common Windows install locations
|
||||
const char* programFiles = getenv("ProgramFiles");
|
||||
if (programFiles) {
|
||||
candidates.push_back(std::string(programFiles) + "\\Tor\\tor.exe");
|
||||
candidates.push_back(std::string(programFiles) + "\\Tor Browser\\Browser\\TorBrowser\\Tor\\tor.exe");
|
||||
}
|
||||
const char* programFilesX86 = getenv("ProgramFiles(x86)");
|
||||
if (programFilesX86) {
|
||||
candidates.push_back(std::string(programFilesX86) + "\\Tor\\tor.exe");
|
||||
candidates.push_back(std::string(programFilesX86) + "\\Tor Browser\\Browser\\TorBrowser\\Tor\\tor.exe");
|
||||
}
|
||||
const char* localAppData = getenv("LOCALAPPDATA");
|
||||
if (localAppData) {
|
||||
candidates.push_back(std::string(localAppData) + "\\Tor Browser\\Browser\\TorBrowser\\Tor\\tor.exe");
|
||||
}
|
||||
|
||||
// Tor Expert Bundle (common install)
|
||||
candidates.push_back("C:\\Tor\\tor.exe");
|
||||
|
||||
#else
|
||||
// Same directory as the wallet executable
|
||||
char exePath[4096];
|
||||
ssize_t len = readlink("/proc/self/exe", exePath, sizeof(exePath) - 1);
|
||||
if (len > 0) {
|
||||
exePath[len] = '\0';
|
||||
fs::path exeDir = fs::path(exePath).parent_path();
|
||||
candidates.push_back((exeDir / "tor").string());
|
||||
candidates.push_back((exeDir / "tor" / "tor").string());
|
||||
}
|
||||
|
||||
// Data directory
|
||||
candidates.push_back((GetDataDir() / "tor").string());
|
||||
candidates.push_back((GetDataDir() / "tor" / "tor").string());
|
||||
|
||||
// Standard Linux/macOS locations
|
||||
candidates.push_back("/usr/bin/tor");
|
||||
candidates.push_back("/usr/local/bin/tor");
|
||||
candidates.push_back("/usr/sbin/tor");
|
||||
candidates.push_back("/opt/tor/bin/tor");
|
||||
candidates.push_back("/snap/bin/tor");
|
||||
|
||||
// Homebrew (macOS)
|
||||
candidates.push_back("/opt/homebrew/bin/tor");
|
||||
candidates.push_back("/usr/local/opt/tor/bin/tor");
|
||||
#endif
|
||||
|
||||
// Check each candidate
|
||||
for (const std::string& path : candidates) {
|
||||
if (fs::exists(path)) {
|
||||
printf("Found Tor binary at: %s\n", path.c_str());
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
bool CTorProcess::IsPortInUse(int port)
|
||||
{
|
||||
#ifdef WIN32
|
||||
SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (sock == INVALID_SOCKET) return false;
|
||||
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
|
||||
addr.sin_port = htons(port);
|
||||
|
||||
int result = connect(sock, (struct sockaddr*)&addr, sizeof(addr));
|
||||
closesocket(sock);
|
||||
return (result == 0);
|
||||
#else
|
||||
int sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (sock < 0) return false;
|
||||
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
|
||||
addr.sin_port = htons(port);
|
||||
|
||||
int result = connect(sock, (struct sockaddr*)&addr, sizeof(addr));
|
||||
close(sock);
|
||||
return (result == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CTorProcess::WriteTorrc()
|
||||
{
|
||||
fs::path dataPath(torDataDir);
|
||||
fs::create_directories(dataPath);
|
||||
|
||||
torrcPath = (dataPath / "torrc").string();
|
||||
|
||||
// Hidden service directory
|
||||
fs::path hsDir = dataPath / "hidden_service";
|
||||
fs::create_directories(hsDir);
|
||||
|
||||
std::ofstream torrc(torrcPath.c_str());
|
||||
if (!torrc.is_open()) {
|
||||
printf("ERROR: Cannot write torrc to %s\n", torrcPath.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
torrc << "# Triangles Wallet Tor Configuration (auto-generated)\n";
|
||||
torrc << "# Do not edit - this file is overwritten on startup\n\n";
|
||||
|
||||
// SOCKS proxy for wallet connections
|
||||
torrc << "SocksPort " << socksPort << "\n";
|
||||
|
||||
// Data directory for Tor state
|
||||
fs::path torStateDir = dataPath / "state";
|
||||
fs::create_directories(torStateDir);
|
||||
torrc << "DataDirectory " << torStateDir.string() << "\n";
|
||||
|
||||
// V3 hidden service so this node is reachable via .onion
|
||||
torrc << "HiddenServiceDir " << hsDir.string() << "\n";
|
||||
torrc << "HiddenServiceVersion 3\n";
|
||||
torrc << "HiddenServicePort " << hiddenServicePort
|
||||
<< " 127.0.0.1:" << hiddenServicePort << "\n";
|
||||
|
||||
// Reduce bandwidth/resource usage for wallet use
|
||||
torrc << "ClientOnly 1\n";
|
||||
|
||||
// Disable unused features
|
||||
torrc << "AvoidDiskWrites 1\n";
|
||||
torrc << "Log notice stderr\n";
|
||||
|
||||
torrc.close();
|
||||
|
||||
printf("Wrote torrc to %s (SOCKS %d, HS port %d)\n",
|
||||
torrcPath.c_str(), socksPort, hiddenServicePort);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CTorProcess::Start(const std::string& dataDir, int socks, int hsPort)
|
||||
{
|
||||
socksPort = socks;
|
||||
hiddenServicePort = hsPort;
|
||||
torDataDir = dataDir;
|
||||
|
||||
// Check if something is already listening on our SOCKS port
|
||||
if (IsPortInUse(socksPort)) {
|
||||
printf("Tor SOCKS port %d already in use - assuming Tor is running\n", socksPort);
|
||||
running = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Find Tor binary
|
||||
torBinaryPath = FindTorBinary();
|
||||
if (torBinaryPath.empty()) {
|
||||
printf("WARNING: Tor binary not found. Install Tor for .onion connectivity.\n");
|
||||
printf(" Windows: Download from https://www.torproject.org/download/tor/\n");
|
||||
printf(" Linux: apt install tor or yum install tor\n");
|
||||
printf(" Place tor executable next to the wallet binary for auto-detection.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write configuration
|
||||
if (!WriteTorrc()) {
|
||||
printf("ERROR: Failed to write Tor configuration\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("Starting Tor process: %s -f %s\n", torBinaryPath.c_str(), torrcPath.c_str());
|
||||
|
||||
#ifdef WIN32
|
||||
STARTUPINFOA si;
|
||||
PROCESS_INFORMATION pi;
|
||||
ZeroMemory(&si, sizeof(si));
|
||||
si.cb = sizeof(si);
|
||||
si.dwFlags = STARTF_USESHOWWINDOW;
|
||||
si.wShowWindow = SW_HIDE; // Run hidden
|
||||
ZeroMemory(&pi, sizeof(pi));
|
||||
|
||||
std::string cmdLine = "\"" + torBinaryPath + "\" -f \"" + torrcPath + "\"";
|
||||
|
||||
if (!CreateProcessA(
|
||||
NULL,
|
||||
(LPSTR)cmdLine.c_str(),
|
||||
NULL, NULL,
|
||||
FALSE,
|
||||
CREATE_NO_WINDOW,
|
||||
NULL, NULL,
|
||||
&si, &pi))
|
||||
{
|
||||
printf("ERROR: Failed to start Tor process (error %lu)\n", GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
hProcess = pi.hProcess;
|
||||
processId = pi.dwProcessId;
|
||||
CloseHandle(pi.hThread);
|
||||
|
||||
printf("Tor process started (PID %lu)\n", processId);
|
||||
#else
|
||||
pid_t pid = fork();
|
||||
if (pid < 0) {
|
||||
printf("ERROR: Failed to fork for Tor process\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pid == 0) {
|
||||
// Child process - exec Tor
|
||||
// Redirect stdout/stderr to /dev/null to avoid cluttering wallet output
|
||||
freopen("/dev/null", "w", stdout);
|
||||
freopen("/dev/null", "w", stderr);
|
||||
execl(torBinaryPath.c_str(), torBinaryPath.c_str(),
|
||||
"-f", torrcPath.c_str(), (char*)NULL);
|
||||
// If exec fails, exit child
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
processId = pid;
|
||||
printf("Tor process started (PID %d)\n", processId);
|
||||
#endif
|
||||
|
||||
running = true;
|
||||
|
||||
// Wait a few seconds for Tor to bootstrap, then check if SOCKS is up
|
||||
printf("Waiting for Tor to bootstrap...\n");
|
||||
for (int i = 0; i < 30; i++) {
|
||||
MilliSleep(1000);
|
||||
if (fShutdown) {
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
if (IsPortInUse(socksPort)) {
|
||||
printf("Tor SOCKS proxy ready on port %d (took %ds)\n", socksPort, i + 1);
|
||||
|
||||
// Read and display the hidden service hostname if available
|
||||
fs::path hsHostname = fs::path(torDataDir) / "hidden_service" / "hostname";
|
||||
if (fs::exists(hsHostname)) {
|
||||
std::ifstream f(hsHostname.string().c_str());
|
||||
std::string hostname;
|
||||
if (f.is_open() && std::getline(f, hostname)) {
|
||||
printf("Tor hidden service: %s\n", hostname.c_str());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if Tor process is still alive
|
||||
if (!IsRunning()) {
|
||||
printf("ERROR: Tor process exited prematurely\n");
|
||||
running = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
printf("WARNING: Tor started but SOCKS proxy not yet ready after 30s\n");
|
||||
printf(" Tor may still be bootstrapping. .onion connections will work once ready.\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
void CTorProcess::Stop()
|
||||
{
|
||||
if (!running) return;
|
||||
|
||||
#ifdef WIN32
|
||||
if (hProcess != NULL) {
|
||||
printf("Stopping Tor process (PID %lu)...\n", processId);
|
||||
TerminateProcess(hProcess, 0);
|
||||
WaitForSingleObject(hProcess, 5000);
|
||||
CloseHandle(hProcess);
|
||||
hProcess = NULL;
|
||||
}
|
||||
#else
|
||||
if (processId > 0) {
|
||||
printf("Stopping Tor process (PID %d)...\n", processId);
|
||||
kill(processId, SIGTERM);
|
||||
// Wait up to 5 seconds for graceful shutdown
|
||||
for (int i = 0; i < 50; i++) {
|
||||
int status;
|
||||
pid_t result = waitpid(processId, &status, WNOHANG);
|
||||
if (result != 0) break;
|
||||
MilliSleep(100);
|
||||
}
|
||||
// Force kill if still running
|
||||
kill(processId, SIGKILL);
|
||||
waitpid(processId, NULL, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
processId = 0;
|
||||
running = false;
|
||||
printf("Tor process stopped\n");
|
||||
}
|
||||
|
||||
bool CTorProcess::IsRunning()
|
||||
{
|
||||
if (!running) return false;
|
||||
|
||||
#ifdef WIN32
|
||||
if (hProcess == NULL) return false;
|
||||
DWORD exitCode;
|
||||
if (GetExitCodeProcess(hProcess, &exitCode)) {
|
||||
return (exitCode == STILL_ACTIVE);
|
||||
}
|
||||
return false;
|
||||
#else
|
||||
if (processId <= 0) return false;
|
||||
int status;
|
||||
pid_t result = waitpid(processId, &status, WNOHANG);
|
||||
if (result == 0) return true; // Still running
|
||||
if (result == processId) {
|
||||
running = false;
|
||||
return false; // Exited
|
||||
}
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string CTorProcess::GetSocksProxy() const
|
||||
{
|
||||
return "127.0.0.1:" + std::to_string(socksPort);
|
||||
}
|
||||
|
||||
// Global convenience functions
|
||||
bool StartTorProcess(const std::string& dataDir)
|
||||
{
|
||||
return CTorProcess::GetInstance()->Start(dataDir);
|
||||
}
|
||||
|
||||
void StopTorProcess()
|
||||
{
|
||||
CTorProcess::GetInstance()->Stop();
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// Copyright (c) 2024-2025 Triangles developers
|
||||
// Tor Process Manager - launches and manages an external Tor binary
|
||||
// Distributed under the MIT/X11 software license
|
||||
|
||||
#ifndef TRIANGLES_TOR_PROCESS_H
|
||||
#define TRIANGLES_TOR_PROCESS_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
// Manages starting/stopping an external Tor process
|
||||
// Provides SOCKS5 proxy for .onion connectivity and v3 hidden service
|
||||
class CTorProcess
|
||||
{
|
||||
private:
|
||||
std::string torBinaryPath;
|
||||
std::string torDataDir;
|
||||
std::string torrcPath;
|
||||
int socksPort;
|
||||
int hiddenServicePort;
|
||||
bool running;
|
||||
|
||||
#ifdef WIN32
|
||||
HANDLE hProcess;
|
||||
DWORD processId;
|
||||
#else
|
||||
pid_t processId;
|
||||
#endif
|
||||
|
||||
// Find the Tor binary in standard locations
|
||||
std::string FindTorBinary();
|
||||
|
||||
// Generate torrc configuration file
|
||||
bool WriteTorrc();
|
||||
|
||||
// Check if SOCKS port is already in use (another Tor running)
|
||||
bool IsPortInUse(int port);
|
||||
|
||||
public:
|
||||
CTorProcess();
|
||||
~CTorProcess();
|
||||
|
||||
// Start the Tor process
|
||||
// Returns true if Tor was started or is already running
|
||||
bool Start(const std::string& dataDir, int socksPort = 19099, int hsPort = 24112);
|
||||
|
||||
// Stop the Tor process
|
||||
void Stop();
|
||||
|
||||
// Check if Tor is running
|
||||
bool IsRunning();
|
||||
|
||||
// Get the SOCKS proxy address
|
||||
std::string GetSocksProxy() const;
|
||||
|
||||
// Get the Tor binary path (for diagnostics)
|
||||
std::string GetBinaryPath() const { return torBinaryPath; }
|
||||
|
||||
// Singleton access
|
||||
static CTorProcess* GetInstance();
|
||||
};
|
||||
|
||||
// Global convenience functions
|
||||
bool StartTorProcess(const std::string& dataDir);
|
||||
void StopTorProcess();
|
||||
|
||||
#endif // TRIANGLES_TOR_PROCESS_H
|
||||
+1
-1
@@ -53,7 +53,7 @@ static const int MEMPOOL_GD_VERSION = 60002;
|
||||
|
||||
#define DISPLAY_VERSION_MAJOR 5
|
||||
#define DISPLAY_VERSION_MINOR 1
|
||||
#define DISPLAY_VERSION_REVISION 6
|
||||
#define DISPLAY_VERSION_REVISION 7
|
||||
#define DISPLAY_VERSION_BUILD 0
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user