Tor v3: fix hidden service backend, add key persistence and health monitoring

Codex changes: delegate hidden service management to the actual Tor
backend instead of generating keys the wallet never served. The new
AttachToBackendService() reads the hostname Tor creates, and the
torrc/process plumbing properly gates HiddenService directives behind
the -torhiddenservice flag.

Additional fixes:
- Back up hs_ed25519_secret_key (96 bytes) to wallet.dat so the onion
  identity survives deletion of tor_data/
- Restore the key before Tor starts so the same .onion address is
  regenerated automatically
- Add ThreadTorMaintenance: checks Tor health every 30s, auto-restarts
  with exponential backoff on crash, re-attaches the hidden service
  and re-registers the onion address with AddLocal()
- Seeder maintenance: every 30 min re-announces to peers and refreshes
  known seeder lists (when -torseeder is enabled)
- Clean up ScheduleSeederReannouncement() stub (real work now in thread)
- Respect -torsocks port in onion proxy registration

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-27 00:27:22 -07:00
parent 5ad0bb53b4
commit d242c2f37e
7 changed files with 355 additions and 99 deletions
+53 -8
View File
@@ -355,6 +355,7 @@ std::string HelpMessage()
" -tor=<ip:port> " + _("Use proxy to reach tor hidden services (default: same as -proxy)") + "\n"
" -notor " + _("Disable Tor startup and .onion connectivity") + "\n" +
" -torsocks=<port> " + _("Set embedded or managed Tor SOCKS proxy port (default: 19099)") + "\n" +
" -torhiddenservice " + _("Enable the managed Tor hidden service (default: 1)") + "\n" +
" -torhsport=<port> " + _("Set embedded or managed Tor hidden service port (default: wallet listen port)") + "\n" +
//" -dns " + _("Allow DNS lookups for -addnode, -seednode and -connect") + "\n" +
" -port=<port> " + _("Listen for connections on <port> (default: 24112 or testnet: 24111)") + "\n" +
@@ -752,7 +753,7 @@ bool AppInit2()
// Tor proxy: always configured for .onion connectivity
CService addrOnion;
unsigned short const onion_port = 19099;
unsigned short const onion_port = static_cast<unsigned short>(GetArg("-torsocks", 19099));
if (mapArgs.count("-tor") && mapArgs["-tor"] != "0") {
addrOnion = CService(mapArgs["-tor"], onion_port);
@@ -1078,6 +1079,37 @@ bool AppInit2()
uiInterface.InitMessage(_("Starting Tor..."));
printf("Starting Tor process...\n");
// Restore hidden service secret key from wallet backup if the key
// file is missing on disk. This preserves the .onion identity even
// if the tor_data directory was deleted.
if (pwalletMain && !GetBoolArg("-notor", false)) {
std::string restoreDataPath = GetArg("-tordatadir", (GetDataDir() / "tor_data").string());
fs::path secretKeyPath = fs::path(restoreDataPath) / "hidden_service" / "hs_ed25519_secret_key";
if (!fs::exists(secretKeyPath)) {
CWalletDB walletdb(pwalletMain->strWalletFile);
std::vector<unsigned char> backedUpKey;
if (walletdb.ReadSetting("tor_v3_hs_secret_key_backup", backedUpKey) &&
backedUpKey.size() == 96) {
fs::create_directories(secretKeyPath.parent_path());
std::ofstream keyFile(secretKeyPath.string().c_str(), std::ios::binary);
if (keyFile.is_open()) {
keyFile.write(reinterpret_cast<const char*>(backedUpKey.data()),
backedUpKey.size());
keyFile.close();
printf("Restored Tor hidden service secret key from wallet backup\n");
} else {
printf("WARNING: Failed to write restored hs_ed25519_secret_key to %s\n",
secretKeyPath.string().c_str());
}
}
OPENSSL_cleanse(backedUpKey.data(), backedUpKey.size());
}
}
int64_t nTorStart = GetTimeMillis();
bool torStarted = StartEmbeddedTor();
StartupPerfLog("tor_start", GetTimeMillis() - nTorStart, strprintf("started=%d", torStarted));
@@ -1100,13 +1132,14 @@ bool AppInit2()
int64_t nTorIdentityStart = GetTimeMillis();
LoadTorV3Config();
TorV3Config& torConfig = GetTorV3Config();
torConfig.enableTor = true;
torConfig.enableHiddenService = true;
torConfig.hiddenServicePort = GetListenPort();
torConfig.enableTor = torStarted;
torConfig.enableHiddenService = torStarted && CTorEmbedded::GetInstance()->IsHiddenServiceEnabled();
torConfig.hiddenServicePort = CTorEmbedded::GetInstance()->GetHiddenServicePort();
torConfig.torDataDirectory = torDataPath;
std::string onionAddr;
if (InitTorV3()) {
string onionAddr = CTorV3Manager::GetInstance()->GetWalletOnionAddress();
if (torConfig.enableTor && torConfig.enableHiddenService && InitTorV3()) {
onionAddr = CTorV3Manager::GetInstance()->GetWalletOnionAddress();
if (!onionAddr.empty()) {
// Write onion/hostname for compatibility with existing code paths
fs::path onionDir = GetDataDir() / "onion";
@@ -1118,11 +1151,15 @@ bool AppInit2()
}
// Register onion address as local address for peer discovery
AddLocal(CService(onionAddr, GetListenPort(), fNameLookup), LOCAL_MANUAL);
AddLocal(CService(onionAddr, torConfig.hiddenServicePort, fNameLookup), LOCAL_MANUAL);
printf("Tor V3 identity: %s\n", onionAddr.c_str());
} else {
printf("WARNING: Tor V3 initialized but no onion address available\n");
}
} else if (torStarted && !torConfig.enableHiddenService) {
printf("Tor hidden service disabled by configuration\n");
} else if (!torStarted) {
printf("Skipping Tor V3 identity because the Tor backend is unavailable\n");
} else {
printf("WARNING: Failed to initialize Tor V3 identity\n");
}
@@ -1139,13 +1176,21 @@ bool AppInit2()
while (!torOnion.empty() && (torOnion.back() == '\n' || torOnion.back() == '\r' || torOnion.back() == ' '))
torOnion.pop_back();
if (!torOnion.empty()) {
AddLocal(CService(torOnion, GetListenPort(), fNameLookup), LOCAL_MANUAL);
if (torOnion != onionAddr) {
AddLocal(CService(torOnion, torConfig.hiddenServicePort, fNameLookup), LOCAL_MANUAL);
}
printf("Tor hidden service (from Tor process): %s\n", torOnion.c_str());
}
}
}
}
StartupPerfLog("tor_setup_total", GetTimeMillis() - nTorStart);
// Launch background thread for Tor health monitoring and seeder maintenance
if (torStarted) {
if (!NewThread(ThreadTorMaintenance, NULL))
printf("Warning: ThreadTorMaintenance could not be started\n");
}
}
// ********************************************************* Step 9: import blocks
+227 -46
View File
@@ -18,6 +18,7 @@
#endif
#include "onion_v3.h"
#include "tor_embedded.h"
#include "tor_crypto_compat.h"
#include "../util.h"
#include "../net.h"
@@ -59,6 +60,53 @@ extern CWallet* pwalletMain;
CTorV3Manager* CTorV3Manager::instance = nullptr;
static TorV3Config torV3Config;
static boost::filesystem::path GetBackendHiddenServiceDir(const std::string& torDataDir)
{
return boost::filesystem::path(torDataDir) / "hidden_service";
}
static bool ReadTrimmedFirstLine(const boost::filesystem::path& path, std::string& valueOut)
{
valueOut.clear();
std::ifstream file(path.string().c_str());
if (!file.is_open() || !std::getline(file, valueOut)) {
return false;
}
while (!valueOut.empty()) {
const char ch = valueOut[valueOut.size() - 1];
if (ch != '\n' && ch != '\r' && ch != ' ' && ch != '\t') {
break;
}
valueOut.erase(valueOut.size() - 1);
}
return !valueOut.empty();
}
static std::string GetEffectiveTorProxy()
{
proxyType proxy;
if (GetProxy(NET_TOR, proxy)) {
return proxy.first.ToStringIPPort();
}
if (mapArgs.count("-tor") && mapArgs["-tor"] != "0") {
CService torProxy(mapArgs["-tor"], GetArg("-torsocks", 19099));
if (torProxy.IsValid()) {
return torProxy.ToStringIPPort();
}
}
std::string explicitProxy = GetArg("-torproxy", "");
if (!explicitProxy.empty()) {
return explicitProxy;
}
return strprintf("127.0.0.1:%d", GetArg("-torsocks", 19099));
}
// Utility function for proper base32 encoding (RFC 4648) - Tor variant
std::string EncodeBase32Proper(const unsigned char* data, size_t len)
{
@@ -503,40 +551,95 @@ bool CTorV3Service::LoadFromPrivateKey(const std::string& privKey)
bool CTorV3Service::StartOnionService()
{
if (onionAddress.empty()) {
printf("ERROR: No onion address generated\n");
if (!torV3Config.enableTor || !torV3Config.enableHiddenService) {
printf("ERROR: Tor hidden service backend is disabled\n");
return false;
}
// Use data directory for Tor service files
boost::filesystem::path serviceDir = GetDataDir() / "tor_data" / "triangles_v3";
boost::filesystem::create_directories(serviceDir);
return AttachToBackendService(torV3Config.torDataDirectory, port);
}
// Create Tor configuration for hidden service
std::string torrcContent = strprintf(
"HiddenServiceDir %s\n"
"HiddenServiceVersion 3\n"
"HiddenServicePort %d 127.0.0.1:%d\n",
serviceDir.string().c_str(), port, port
);
// Write torrc file
std::ofstream torrcFile((serviceDir / "torrc").string().c_str());
if (torrcFile.is_open()) {
torrcFile << torrcContent;
torrcFile.close();
bool CTorV3Service::AttachToBackendService(const std::string& torDataDir, int servicePort, int waitSeconds)
{
if (servicePort <= 0 || servicePort > 65535) {
printf("ERROR: Invalid backend hidden service port: %d\n", servicePort);
return false;
}
// Write private key
std::ofstream keyFile((serviceDir / "hs_ed25519_secret_key").string().c_str());
if (keyFile.is_open()) {
keyFile << "== ed25519v1-secret: type0 ==\n";
keyFile << privateKey << "\n";
keyFile.close();
if (torDataDir.empty()) {
printf("ERROR: Tor data directory is empty\n");
return false;
}
port = servicePort;
const boost::filesystem::path serviceDir = GetBackendHiddenServiceDir(torDataDir);
const boost::filesystem::path hostnamePath = serviceDir / "hostname";
std::string backendOnion;
for (int waited = 0; waited <= waitSeconds; ++waited) {
if (boost::filesystem::exists(hostnamePath) &&
ReadTrimmedFirstLine(hostnamePath, backendOnion)) {
break;
}
if (waited == waitSeconds) {
printf("ERROR: Timed out waiting for Tor hidden service hostname at %s\n",
hostnamePath.string().c_str());
return false;
}
if (fShutdown) {
printf("ERROR: Shutdown requested while waiting for Tor hidden service hostname\n");
return false;
}
MilliSleep(1000);
}
if (!ValidateOnionAddress(backendOnion)) {
printf("ERROR: Tor backend produced invalid onion address: %s\n", backendOnion.c_str());
return false;
}
if (!onionAddress.empty() && onionAddress != backendOnion) {
printf("WARNING: Replacing wallet-managed onion address %s with Tor backend address %s\n",
onionAddress.c_str(), backendOnion.c_str());
}
onionAddress = backendOnion;
isActive = true;
printf("Started V3 onion service on %s:%d\n", onionAddress.c_str(), port);
if (pwalletMain) {
CWalletDB walletdb(pwalletMain->strWalletFile);
walletdb.WriteSetting("tor_v3_onion_address", onionAddress);
// Back up the Tor-generated secret key to wallet.dat so the onion
// identity survives deletion of the tor_data directory.
boost::filesystem::path secretKeyPath = serviceDir / "hs_ed25519_secret_key";
if (boost::filesystem::exists(secretKeyPath)) {
std::ifstream keyFile(secretKeyPath.string().c_str(), std::ios::binary);
if (keyFile.is_open()) {
std::vector<unsigned char> keyData(
(std::istreambuf_iterator<char>(keyFile)),
std::istreambuf_iterator<char>());
keyFile.close();
if (keyData.size() == 96) {
walletdb.WriteSetting("tor_v3_hs_secret_key_backup", keyData);
printf("Backed up Tor hidden service secret key to wallet (%d bytes)\n",
(int)keyData.size());
} else {
printf("WARNING: hs_ed25519_secret_key has unexpected size %d (expected 96), not backing up\n",
(int)keyData.size());
}
OPENSSL_cleanse(keyData.data(), keyData.size());
}
}
}
printf("Attached V3 onion service to Tor backend at %s:%d\n", onionAddress.c_str(), port);
return true;
}
@@ -1142,20 +1245,10 @@ void CTorV3Manager::ShutdownTor()
bool CTorV3Manager::CreateWalletHiddenService(int port)
{
CTorV3Service* service = new CTorV3Service();
// Try to load existing service first
if (!service->LoadFromWallet()) {
// Generate new service
if (!service->GenerateV3Service(port)) {
delete service;
return false;
}
service->SaveToWallet();
}
if (service->StartOnionService()) {
if (service->AttachToBackendService(torDataDir, port)) {
services[port] = service;
printf("Wallet hidden service created: %s\n", service->GetOnionAddress().c_str());
printf("Wallet hidden service attached: %s\n", service->GetOnionAddress().c_str());
// If seeder mode is enabled, automatically register as seeder
if (torV3Config.enableSeederMode) {
@@ -1968,12 +2061,12 @@ void CTorV3Manager::UpdateDiscoveryStats(int connected, int attempted)
bool LoadTorV3Config()
{
// Tor V3 identity is innate to Triangles — enabled by default
torV3Config.enableTor = GetBoolArg("-tor", true);
torV3Config.enableHiddenService = GetBoolArg("-torhiddenservice", true);
torV3Config.enableTor = !GetBoolArg("-notor", false);
torV3Config.enableHiddenService = torV3Config.enableTor && GetBoolArg("-torhiddenservice", true);
torV3Config.enableSeederMode = GetBoolArg("-torseeder", false);
torV3Config.hiddenServicePort = GetArg("-torhiddenserviceport", GetDefaultPort());
torV3Config.hiddenServicePort = GetArg("-torhsport", GetListenPort());
torV3Config.torDataDirectory = GetArg("-tordatadir", (GetDataDir() / "tor_data").string());
torV3Config.socksProxy = GetArg("-torproxy", "127.0.0.1:9050");
torV3Config.socksProxy = GetEffectiveTorProxy();
torV3Config.maxConnections = GetArg("-tormaxconnections", 8);
printf("Loaded Tor V3 configuration: enabled=%s, hidden_service=%s, seeder=%s, proxy=%s\n",
@@ -2093,15 +2186,103 @@ void CTorV3Manager::RequestSeederListFromPeer(const std::string& peerAddress)
// Schedule periodic seeder re-announcements
void CTorV3Manager::ScheduleSeederReannouncement()
{
// This would typically be handled by a timer or scheduler
// For now, we'll just update the last announcement time
if (pwalletMain) {
CWalletDB walletdb(pwalletMain->strWalletFile);
walletdb.WriteSetting("seeder_last_announcement", (int64_t)GetTime());
printf("Scheduled seeder re-announcement\n");
}
}
// ---------------------------------------------------------------------------
// Background thread: Tor health monitoring + seeder maintenance
// ---------------------------------------------------------------------------
void ThreadTorMaintenance(void* parg)
{
RenameThread("Triangles-tormaint");
printf("Tor maintenance thread started\n");
int restartBackoffSec = 30;
int64_t lastSeederMaint = GetTime();
static const int SEEDER_INTERVAL = 1800; // 30 minutes
while (!fShutdown)
{
MilliSleep(30000); // check every 30 seconds
if (fShutdown) break;
// --- Tor health check & auto-restart ---
if (!CTorEmbedded::GetInstance()->IsRunning())
{
printf("WARNING: Tor process is no longer running, attempting restart...\n");
if (StartEmbeddedTor())
{
printf("Tor restarted successfully\n");
restartBackoffSec = 30;
// Re-attach the hidden service identity
TorV3Config& torConfig = GetTorV3Config();
std::string torDataPath = CTorEmbedded::GetInstance()->GetDataDir();
if (torDataPath.empty())
torDataPath = torConfig.torDataDirectory;
torConfig.enableTor = true;
torConfig.enableHiddenService = CTorEmbedded::GetInstance()->IsHiddenServiceEnabled();
torConfig.hiddenServicePort = CTorEmbedded::GetInstance()->GetHiddenServicePort();
torConfig.torDataDirectory = torDataPath;
if (torConfig.enableHiddenService && InitTorV3())
{
std::string onionAddr = CTorV3Manager::GetInstance()->GetWalletOnionAddress();
if (!onionAddr.empty())
{
AddLocal(CService(onionAddr, torConfig.hiddenServicePort), LOCAL_MANUAL);
printf("Re-registered Tor V3 identity after restart: %s\n", onionAddr.c_str());
}
}
}
else
{
printf("WARNING: Tor restart failed, retrying in %d seconds\n", restartBackoffSec);
MilliSleep(restartBackoffSec * 1000);
if (restartBackoffSec < 300)
restartBackoffSec *= 2;
}
continue;
}
// --- Seeder maintenance (every 30 minutes) ---
TorV3Config& cfg = GetTorV3Config();
if (cfg.enableSeederMode && (GetTime() - lastSeederMaint) >= SEEDER_INTERVAL)
{
CTorV3Manager* mgr = CTorV3Manager::GetInstance();
std::string ownAddr = mgr->GetWalletOnionAddress();
if (!ownAddr.empty())
{
// Re-announce ourselves as a seeder to all peers
{
LOCK(cs_vNodes);
for (CNode* pnode : vNodes)
{
try {
pnode->PushMessage("seeder", ownAddr, cfg.hiddenServicePort);
} catch (...) {}
}
}
printf("Seeder re-announcement sent to %d peers\n", (int)vNodes.size());
}
// Refresh our knowledge of other seeders
mgr->RequestSeederListFromPeers();
mgr->ScheduleSeederReannouncement();
lastSeederMaint = GetTime();
}
}
printf("Tor maintenance thread exited\n");
}
// Global functions
bool InitTorV3()
{
@@ -2111,4 +2292,4 @@ bool InitTorV3()
void ShutdownTorV3()
{
CTorV3Manager::GetInstance()->ShutdownTor();
}
}
+6 -2
View File
@@ -34,7 +34,10 @@ public:
// Start the onion service
bool StartOnionService();
// Attach to the hidden service managed by the running Tor backend
bool AttachToBackendService(const std::string& torDataDir, int servicePort, int waitSeconds = 30);
// Stop the onion service
bool StopService();
@@ -169,5 +172,6 @@ void ShutdownTorV3();
TorV3Config& GetTorV3Config();
bool SaveTorV3Config();
bool LoadTorV3Config();
void ThreadTorMaintenance(void* parg);
#endif // TRIANGLES_TOR_ONION_V3_H
#endif // TRIANGLES_TOR_ONION_V3_H
+35 -23
View File
@@ -52,6 +52,7 @@ CTorEmbedded::CTorEmbedded()
: running(false)
, socksPort(19099)
, hiddenServicePort(24112)
, hiddenServiceEnabled(true)
{
}
@@ -109,20 +110,24 @@ static void TorThreadFunc(std::vector<std::string> argv_strings)
CTorEmbedded::GetInstance()->SetRunning(false);
}
bool CTorEmbedded::Start(int socks, int hsPort)
bool CTorEmbedded::Start(int socks, int hsPort, bool enableHiddenService)
{
if (running.load()) return true;
socksPort = socks;
hiddenServicePort = hsPort;
hiddenServiceEnabled = enableHiddenService;
hiddenServicePort = hiddenServiceEnabled ? hsPort : 0;
onionHostname.clear();
// Prepare Tor data directory under the wallet's data dir
torDataDir = (::GetDataDir() / "tor_data").string();
fs::create_directories(torDataDir);
// Hidden service directory
std::string hsDir = (fs::path(torDataDir) / "hidden_service").string();
fs::create_directories(hsDir);
std::string hsDir;
if (hiddenServiceEnabled) {
hsDir = (fs::path(torDataDir) / "hidden_service").string();
fs::create_directories(hsDir);
}
// Build the argv for tor_run_main
std::vector<std::string> argv;
@@ -131,12 +136,14 @@ bool CTorEmbedded::Start(int socks, int hsPort)
argv.push_back(std::to_string(socksPort));
argv.push_back("--DataDirectory");
argv.push_back(torDataDir);
argv.push_back("--HiddenServiceDir");
argv.push_back(hsDir);
argv.push_back("--HiddenServiceVersion");
argv.push_back("3");
argv.push_back("--HiddenServicePort");
argv.push_back(std::to_string(hiddenServicePort) + " 127.0.0.1:" + std::to_string(hiddenServicePort));
if (hiddenServiceEnabled) {
argv.push_back("--HiddenServiceDir");
argv.push_back(hsDir);
argv.push_back("--HiddenServiceVersion");
argv.push_back("3");
argv.push_back("--HiddenServicePort");
argv.push_back(std::to_string(hiddenServicePort) + " 127.0.0.1:" + std::to_string(hiddenServicePort));
}
argv.push_back("--AvoidDiskWrites");
argv.push_back("1");
argv.push_back("--Log");
@@ -175,13 +182,15 @@ bool CTorEmbedded::Start(int socks, int hsPort)
printf("Embedded Tor SOCKS proxy ready on port %d (took %ds)\n", socksPort, i + 1);
// Read .onion hostname if available
fs::path hostnameFile = fs::path(hsDir) / "hostname";
if (fs::exists(hostnameFile)) {
std::ifstream f(hostnameFile.string().c_str());
if (f.is_open())
std::getline(f, onionHostname);
if (!onionHostname.empty())
printf("Tor hidden service: %s\n", onionHostname.c_str());
if (hiddenServiceEnabled) {
fs::path hostnameFile = fs::path(hsDir) / "hostname";
if (fs::exists(hostnameFile)) {
std::ifstream f(hostnameFile.string().c_str());
if (f.is_open())
std::getline(f, onionHostname);
if (!onionHostname.empty())
printf("Tor hidden service: %s\n", onionHostname.c_str());
}
}
return true;
}
@@ -219,14 +228,16 @@ void CTorEmbedded::Stop()
#include "tor_process.h"
bool CTorEmbedded::Start(int socks, int hsPort)
bool CTorEmbedded::Start(int socks, int hsPort, bool enableHiddenService)
{
printf("Embedded Tor not compiled in. Using external Tor process.\n");
// Delegate to the external process manager
socksPort = socks;
hiddenServicePort = hsPort;
hiddenServiceEnabled = enableHiddenService;
hiddenServicePort = hiddenServiceEnabled ? hsPort : 0;
onionHostname.clear();
torDataDir = (::GetDataDir() / "tor_data").string();
running.store(StartTorProcess(torDataDir, socksPort, hiddenServicePort));
running.store(StartTorProcess(torDataDir, socksPort, hiddenServicePort, hiddenServiceEnabled));
return running.load();
}
@@ -249,10 +260,11 @@ bool StartEmbeddedTor()
return false;
}
bool enableHiddenService = GetBoolArg("-torhiddenservice", true);
int socksPort = GetArg("-torsocks", 19099);
int hsPort = GetArg("-torhsport", GetListenPort());
int hsPort = enableHiddenService ? GetArg("-torhsport", GetListenPort()) : 0;
return CTorEmbedded::GetInstance()->Start(socksPort, hsPort);
return CTorEmbedded::GetInstance()->Start(socksPort, hsPort, enableHiddenService);
}
void StopEmbeddedTor()
+3 -1
View File
@@ -16,6 +16,7 @@ private:
std::atomic<bool> running;
int socksPort;
int hiddenServicePort;
bool hiddenServiceEnabled;
std::string torDataDir;
std::string onionHostname;
@@ -26,7 +27,7 @@ public:
~CTorEmbedded();
// Start Tor in a background thread (blocks that thread until shutdown)
bool Start(int socksPort = 19099, int hsPort = 24112);
bool Start(int socksPort = 19099, int hsPort = 24112, bool enableHiddenService = true);
// Request Tor to shut down
void Stop();
@@ -45,6 +46,7 @@ public:
// Get the hidden service port
int GetHiddenServicePort() const { return hiddenServicePort; }
bool IsHiddenServiceEnabled() const { return hiddenServiceEnabled; }
};
// Global init/shutdown hooks (called from init.cpp)
+28 -17
View File
@@ -48,6 +48,7 @@ CTorProcess* CTorProcess::GetInstance()
CTorProcess::CTorProcess()
: socksPort(19099)
, hiddenServicePort(24112)
, hiddenServiceEnabled(true)
, running(false)
#ifdef WIN32
, hProcess(NULL)
@@ -198,11 +199,13 @@ bool CTorProcess::WriteTorrc()
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";
if (hiddenServiceEnabled) {
// 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";
@@ -213,15 +216,21 @@ bool CTorProcess::WriteTorrc()
torrc.close();
printf("Wrote torrc to %s (SOCKS %d, HS port %d)\n",
torrcPath.c_str(), socksPort, hiddenServicePort);
if (hiddenServiceEnabled) {
printf("Wrote torrc to %s (SOCKS %d, HS port %d)\n",
torrcPath.c_str(), socksPort, hiddenServicePort);
} else {
printf("Wrote torrc to %s (SOCKS %d, hidden service disabled)\n",
torrcPath.c_str(), socksPort);
}
return true;
}
bool CTorProcess::Start(const std::string& dataDir, int socks, int hsPort)
bool CTorProcess::Start(const std::string& dataDir, int socks, int hsPort, bool enableHiddenService)
{
socksPort = socks;
hiddenServicePort = hsPort;
hiddenServiceEnabled = enableHiddenService;
hiddenServicePort = hiddenServiceEnabled ? hsPort : 0;
torDataDir = dataDir;
// Check if something is already listening on our SOCKS port
@@ -314,12 +323,14 @@ bool CTorProcess::Start(const std::string& dataDir, int socks, int hsPort)
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());
if (hiddenServiceEnabled) {
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;
@@ -402,9 +413,9 @@ std::string CTorProcess::GetSocksProxy() const
}
// Global convenience functions
bool StartTorProcess(const std::string& dataDir, int socksPort, int hsPort)
bool StartTorProcess(const std::string& dataDir, int socksPort, int hsPort, bool enableHiddenService)
{
return CTorProcess::GetInstance()->Start(dataDir, socksPort, hsPort);
return CTorProcess::GetInstance()->Start(dataDir, socksPort, hsPort, enableHiddenService);
}
void StopTorProcess()
+3 -2
View File
@@ -21,6 +21,7 @@ private:
std::string torrcPath;
int socksPort;
int hiddenServicePort;
bool hiddenServiceEnabled;
bool running;
#ifdef WIN32
@@ -45,7 +46,7 @@ public:
// 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);
bool Start(const std::string& dataDir, int socksPort = 19099, int hsPort = 24112, bool enableHiddenService = true);
// Stop the Tor process
void Stop();
@@ -64,7 +65,7 @@ public:
};
// Global convenience functions
bool StartTorProcess(const std::string& dataDir, int socksPort = 19099, int hsPort = 24112);
bool StartTorProcess(const std::string& dataDir, int socksPort = 19099, int hsPort = 24112, bool enableHiddenService = true);
void StopTorProcess();
#endif // TRIANGLES_TOR_PROCESS_H