2ba0ecf428
Migration from boost to std-library equivalents and removal of unreachable code paths. Touches infrastructure only — no consensus rule or wallet serialization changes. Dead code removed: - IRC bootstrap (irc.cpp/h, 417 lines): orphan from pre-Tor era, no callers. - Alert system (alert.cpp/h + sendalert RPC + Qt UI signal, ~500 lines): retired post-V5 fork; old peers' alert messages now hit the unknown-cmd default branch, logged + ignored. - Legacy P2P handlers in main.cpp: "checkpoint" (already a no-op stub since V5 fork master-key removal), "checkorder"/"reply" (2010-era Receive-by-IP feature), plus their unused supporting structures (CRequestTracker, PushRequest overloads, mapRequests/cs_mapRequests, mapReuseKey). - Unreachable RPCs clearwallettransactions and scanforalltxns (~175 lines): defined in rpcwallet.cpp but never registered in the dispatch table. - Stale -alertnotify CLI help text (option was advertised but never wired). boost::filesystem -> std::filesystem (C++17): - 30 source files, 5 headers. namespace fs = boost::filesystem swapped to namespace fs = std::filesystem; boost::filesystem::ifstream/ofstream replaced with std::ifstream/ofstream (path-aware in C++17); fs::system_complete -> fs::absolute; boost::filesystem::filesystem_error -> std::filesystem::filesystem_error. - Build system: dropped Boost::filesystem from link libs and Boost components; PCH includes updated. - Added explicit <filesystem> includes where types were previously available only transitively (db.h, rpcblockchain.cpp). boost::thread -> std::thread (12 files): - sync.h CCriticalSection/CWaitableCriticalSection now alias std::recursive_mutex/std::mutex. boost::unique_lock and boost::condition_variable / boost::mutex::scoped_lock swapped to std equivalents; sync.cpp boost::thread_specific_ptr -> thread_local std::unique_ptr. - init.cpp boost::thread_group rewritten as std::vector<std::thread> with manual join loop. boost::thread::hardware_concurrency -> std::thread::hardware_concurrency. - main.cpp/wallet.cpp -blocknotify/-walletnotify shell-out threads now use std::thread(...).detach() — fixes a latent bug where modern boost::thread destructor would call std::terminate on the joinable thread. - util.cpp NewThread now catches std::system_error. - No interruption_point/interrupt usage anywhere — pure mechanical swap. boost::chrono / boost::posix_time -> std::chrono (3 of 5 files): - util.h: MilliSleep, GetTimeMillis, GetTimeMicros rewritten on std::chrono (system_clock for epoch math, sleep_for for delays). - snapshotnet.cpp: sleep_for swapped. - DoS_tests.cpp: timing harness uses steady_clock. - Skipped: rpcdump.cpp (boost::posix_time::time_input_facet has no clean std::get_time equivalent) and qt/qtipcserver.cpp (locked to boost::posix_time by boost::interprocess::message_queue::timed_receive). Other housekeeping: - Dropped unnecessary "using namespace boost;" from txdb-leveldb.cpp, txdb-rocksdb.cpp, walletdb.cpp, db.cpp (verified no unqualified boost names in those TUs). - Removed unused extern declaration for clearwallettransactions. Build fixes for non-unity builds on MinGW64/GCC 15: - net.cpp: dropped stale #include "irc.h". - addrman.cpp + main.cpp: explicit <cmath> include for sqrt/pow (was arriving transitively via boost headers). - rpcblockchain.cpp + init.cpp: defensive #undef STRICT/ADVISORY/PERMISSIVE since windows.h macros collide with the Checkpoints:: enum values when std headers reorder include flow. - tor_embed_hooks.cpp: triangles_tor_check_interrupted now polls fShutdown instead of boost::this_thread::interruption_requested (we never used boost interruption — the hook was always effectively a no-op). - snapshotnet.cpp: fs::remove error handle uses std::error_code. - serialize.h: added <ios> for std::ios::badbit/failbit (was relying on transitive include via boost). Note: unity builds currently fail on this branch due to std::byte (C++17) colliding with COM 'byte' typedef from shlobj.h when 'using namespace std;' from earlier files in the unity slice leaks into util.cpp's parse of shlobj.h. Build with -DENABLE_UNITY_BUILD=OFF (the default). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
285 lines
8.7 KiB
C++
285 lines
8.7 KiB
C++
// Copyright (c) 2025-2026 Triangles developers
|
|
// Embedded Tor integration - runs Tor in-process as a library
|
|
// Distributed under the MIT/X11 software license
|
|
//
|
|
// BUILD REQUIREMENT: Link against libtor.a built from the official Tor source.
|
|
//
|
|
// This file compiles in two modes:
|
|
// 1. ENABLE_TOR_EMBEDDED defined: full embedded Tor via tor_api.h
|
|
// 2. ENABLE_TOR_EMBEDDED not defined: stubs that fall back to external tor_process
|
|
|
|
#include "tor_embedded.h"
|
|
#include "../util.h"
|
|
#include "../net.h"
|
|
|
|
#include <filesystem>
|
|
#include <thread>
|
|
#include <fstream>
|
|
#include <cstring>
|
|
#include <vector>
|
|
#include <string>
|
|
|
|
#ifdef WIN32
|
|
#include <winsock2.h>
|
|
#include <ws2tcpip.h>
|
|
#else
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#ifdef ENABLE_TOR_EMBEDDED
|
|
// Official Tor C API (tor >= 0.4.5)
|
|
extern "C" {
|
|
#include <tor_api.h>
|
|
}
|
|
#endif
|
|
|
|
namespace fs = std::filesystem;
|
|
|
|
// Singleton
|
|
CTorEmbedded* CTorEmbedded::instance = nullptr;
|
|
|
|
CTorEmbedded* CTorEmbedded::GetInstance()
|
|
{
|
|
if (!instance)
|
|
instance = new CTorEmbedded();
|
|
return instance;
|
|
}
|
|
|
|
CTorEmbedded::CTorEmbedded()
|
|
: running(false)
|
|
, socksPort(19099)
|
|
, hiddenServicePort(24112)
|
|
, hiddenServiceEnabled(true)
|
|
{
|
|
}
|
|
|
|
CTorEmbedded::~CTorEmbedded()
|
|
{
|
|
Stop();
|
|
}
|
|
|
|
std::string CTorEmbedded::GetSocksProxy() const
|
|
{
|
|
return "127.0.0.1:" + std::to_string(socksPort);
|
|
}
|
|
|
|
#ifdef ENABLE_TOR_EMBEDDED
|
|
|
|
// ========================================================================
|
|
// Embedded mode: Tor runs in-process via libtor.a / tor_api.h
|
|
// ========================================================================
|
|
|
|
// The Tor thread entry point. tor_run_main() blocks until Tor shuts down.
|
|
static void TorThreadFunc(std::vector<std::string> argv_strings)
|
|
{
|
|
// Build a C argv array that tor_run_main expects.
|
|
std::vector<char*> argv_ptrs;
|
|
for (auto& s : argv_strings)
|
|
argv_ptrs.push_back(&s[0]);
|
|
argv_ptrs.push_back(nullptr);
|
|
|
|
tor_main_configuration_t* cfg = tor_main_configuration_new();
|
|
if (!cfg) {
|
|
printf("ERROR: tor_main_configuration_new() failed\n");
|
|
CTorEmbedded::GetInstance()->SetRunning(false);
|
|
return;
|
|
}
|
|
|
|
// argc does not count the trailing nullptr
|
|
int rc = tor_main_configuration_set_command_line(
|
|
cfg, (int)(argv_ptrs.size() - 1), argv_ptrs.data());
|
|
if (rc != 0) {
|
|
printf("ERROR: tor_main_configuration_set_command_line() returned %d\n", rc);
|
|
tor_main_configuration_free(cfg);
|
|
CTorEmbedded::GetInstance()->SetRunning(false);
|
|
return;
|
|
}
|
|
|
|
printf("Embedded Tor starting (SOCKS %d, HS port %d)...\n",
|
|
CTorEmbedded::GetInstance()->GetSocksPort(),
|
|
CTorEmbedded::GetInstance()->GetHiddenServicePort());
|
|
|
|
// This blocks until Tor exits
|
|
rc = tor_run_main(cfg);
|
|
tor_main_configuration_free(cfg);
|
|
|
|
printf("Embedded Tor exited with code %d\n", rc);
|
|
CTorEmbedded::GetInstance()->SetRunning(false);
|
|
}
|
|
|
|
bool CTorEmbedded::Start(int socks, int hsPort, bool enableHiddenService)
|
|
{
|
|
if (running.load()) return true;
|
|
|
|
lastError.clear();
|
|
socksPort = socks;
|
|
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);
|
|
|
|
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;
|
|
argv.push_back("tor"); // program name (argv[0])
|
|
argv.push_back("--SocksPort");
|
|
argv.push_back(std::to_string(socksPort));
|
|
argv.push_back("--DataDirectory");
|
|
argv.push_back(torDataDir);
|
|
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");
|
|
argv.push_back("notice stderr");
|
|
|
|
running.store(true);
|
|
|
|
// Launch Tor on a dedicated thread (tor_run_main blocks)
|
|
std::thread torThread(TorThreadFunc, argv);
|
|
torThread.detach();
|
|
|
|
// Wait for SOCKS port to become available (up to 60s)
|
|
printf("Waiting for embedded Tor to bootstrap...\n");
|
|
for (int i = 0; i < 60; i++) {
|
|
MilliSleep(1000);
|
|
if (fShutdown) {
|
|
Stop();
|
|
return false;
|
|
}
|
|
|
|
// Quick port check
|
|
#ifdef WIN32
|
|
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
|
|
if (sock != INVALID_SOCKET) {
|
|
#else
|
|
int sock = socket(AF_INET, SOCK_STREAM, 0);
|
|
if (sock >= 0) {
|
|
#endif
|
|
struct sockaddr_in addr;
|
|
memset(&addr, 0, sizeof(addr));
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
addr.sin_port = htons(socksPort);
|
|
bool up = (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == 0);
|
|
#ifdef WIN32
|
|
closesocket(sock);
|
|
#else
|
|
close(sock);
|
|
#endif
|
|
if (up) {
|
|
printf("Embedded Tor SOCKS proxy ready on port %d (took %ds)\n", socksPort, i + 1);
|
|
|
|
// Read .onion hostname if available
|
|
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;
|
|
}
|
|
}
|
|
|
|
if (!running.load()) {
|
|
lastError = "Embedded Tor thread exited during bootstrap before the SOCKS proxy became available.";
|
|
printf("ERROR: Embedded Tor thread exited during bootstrap\n");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
lastError = strprintf("Embedded Tor did not expose SOCKS port %d within 60 seconds.", socksPort);
|
|
printf("WARNING: Embedded Tor started but SOCKS not ready after 60s (still bootstrapping)\n");
|
|
return true;
|
|
}
|
|
|
|
void CTorEmbedded::Stop()
|
|
{
|
|
if (!running.load()) return;
|
|
printf("Requesting embedded Tor shutdown...\n");
|
|
// tor_run_main respects signals; raise SIGTERM to trigger graceful exit
|
|
#ifndef WIN32
|
|
// On Unix we can signal our own process; the Tor thread handles it
|
|
// Actually, tor_api doesn't provide a clean shutdown function in 0.4.x
|
|
// For now, the thread will exit when the process exits.
|
|
// TODO: Tor 0.4.9+ may add tor_api_shutdown(), use it when available
|
|
#endif
|
|
running.store(false);
|
|
}
|
|
|
|
#else // !ENABLE_TOR_EMBEDDED
|
|
|
|
// ========================================================================
|
|
// Fallback stubs: embedded Tor not compiled in, use external tor_process
|
|
// ========================================================================
|
|
|
|
#include "tor_process.h"
|
|
|
|
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;
|
|
hiddenServiceEnabled = enableHiddenService;
|
|
hiddenServicePort = hiddenServiceEnabled ? hsPort : 0;
|
|
onionHostname.clear();
|
|
lastError.clear();
|
|
torDataDir = (::GetDataDir() / "tor_data").string();
|
|
running.store(StartTorProcess(torDataDir, socksPort, hiddenServicePort, hiddenServiceEnabled));
|
|
if (!running.load()) {
|
|
lastError = CTorProcess::GetInstance()->GetStartupError();
|
|
}
|
|
return running.load();
|
|
}
|
|
|
|
void CTorEmbedded::Stop()
|
|
{
|
|
StopTorProcess();
|
|
running.store(false);
|
|
}
|
|
|
|
#endif // ENABLE_TOR_EMBEDDED
|
|
|
|
// ========================================================================
|
|
// Global hooks (called from init.cpp)
|
|
// ========================================================================
|
|
|
|
bool StartEmbeddedTor()
|
|
{
|
|
if (GetBoolArg("-notor", false)) {
|
|
printf("Tor disabled by -notor flag\n");
|
|
return false;
|
|
}
|
|
|
|
bool enableHiddenService = GetBoolArg("-torhiddenservice", true);
|
|
int socksPort = GetArg("-torsocks", 19099);
|
|
int hsPort = enableHiddenService ? GetArg("-torhsport", GetListenPort()) : 0;
|
|
|
|
return CTorEmbedded::GetInstance()->Start(socksPort, hsPort, enableHiddenService);
|
|
}
|
|
|
|
void StopEmbeddedTor()
|
|
{
|
|
CTorEmbedded::GetInstance()->Stop();
|
|
}
|