mirror of
https://github.com/Retropex/bitcoin.git
synced 2025-05-28 04:52:36 +02:00
Merge 15421 via tor_subprocess-28+knots
This commit is contained in:
commit
0ef0693711
13
configure.ac
13
configure.ac
@ -298,6 +298,11 @@ AC_ARG_ENABLE([external-signer],
|
|||||||
[use_external_signer=$enableval],
|
[use_external_signer=$enableval],
|
||||||
[use_external_signer=yes])
|
[use_external_signer=yes])
|
||||||
|
|
||||||
|
AC_ARG_ENABLE([tor-subprocess],
|
||||||
|
[AS_HELP_STRING([--disable-tor-subprocess],[compile support for executing a dedicated Tor node (default is yes)])],
|
||||||
|
[use_tor_subprocess=$enableval],
|
||||||
|
[use_tor_subprocess=yes])
|
||||||
|
|
||||||
AC_LANG_PUSH([C++])
|
AC_LANG_PUSH([C++])
|
||||||
|
|
||||||
dnl Always set -g -O2 in our CXXFLAGS. Autoconf will try and set CXXFLAGS to "-g -O2" by default,
|
dnl Always set -g -O2 in our CXXFLAGS. Autoconf will try and set CXXFLAGS to "-g -O2" by default,
|
||||||
@ -1323,6 +1328,7 @@ if test "$enable_fuzz" = "yes"; then
|
|||||||
bitcoin_enable_qt_dbus=no
|
bitcoin_enable_qt_dbus=no
|
||||||
use_bench=no
|
use_bench=no
|
||||||
use_tests=no
|
use_tests=no
|
||||||
|
use_tor_subprocess=no
|
||||||
use_external_signer=no
|
use_external_signer=no
|
||||||
use_upnp=no
|
use_upnp=no
|
||||||
use_natpmp=no
|
use_natpmp=no
|
||||||
@ -1533,6 +1539,11 @@ if test "$use_external_signer" = "yes"; then
|
|||||||
fi
|
fi
|
||||||
AM_CONDITIONAL([ENABLE_EXTERNAL_SIGNER], [test "$use_external_signer" = "yes"])
|
AM_CONDITIONAL([ENABLE_EXTERNAL_SIGNER], [test "$use_external_signer" = "yes"])
|
||||||
|
|
||||||
|
if test "$use_tor_subprocess" = "yes"; then
|
||||||
|
AC_DEFINE([ENABLE_TOR_SUBPROCESS], [1], [Define if Tor subprocess support is enabled])
|
||||||
|
fi
|
||||||
|
AM_CONDITIONAL([ENABLE_TOR_SUBPROCESS], [test "$use_tor_subprocess" = "yes"])
|
||||||
|
|
||||||
dnl Check for reduced exports
|
dnl Check for reduced exports
|
||||||
if test "$use_reduce_exports" = "yes"; then
|
if test "$use_reduce_exports" = "yes"; then
|
||||||
AX_CHECK_COMPILE_FLAG([-fvisibility=hidden], [CORE_CXXFLAGS="$CORE_CXXFLAGS -fvisibility=hidden"],
|
AX_CHECK_COMPILE_FLAG([-fvisibility=hidden], [CORE_CXXFLAGS="$CORE_CXXFLAGS -fvisibility=hidden"],
|
||||||
@ -1907,6 +1918,7 @@ AC_SUBST(LIBTOOL_APP_LDFLAGS)
|
|||||||
AC_SUBST(USE_SQLITE)
|
AC_SUBST(USE_SQLITE)
|
||||||
AC_SUBST(USE_BDB)
|
AC_SUBST(USE_BDB)
|
||||||
AC_SUBST(ENABLE_EXTERNAL_SIGNER)
|
AC_SUBST(ENABLE_EXTERNAL_SIGNER)
|
||||||
|
AC_SUBST(ENABLE_TOR_SUBPROCESS)
|
||||||
AC_SUBST(USE_UPNP)
|
AC_SUBST(USE_UPNP)
|
||||||
AC_SUBST(USE_QRCODE)
|
AC_SUBST(USE_QRCODE)
|
||||||
AC_SUBST(TESTDEFS)
|
AC_SUBST(TESTDEFS)
|
||||||
@ -1975,6 +1987,7 @@ esac
|
|||||||
echo
|
echo
|
||||||
echo "Options used to compile and link:"
|
echo "Options used to compile and link:"
|
||||||
echo " external signer = $use_external_signer"
|
echo " external signer = $use_external_signer"
|
||||||
|
echo " tor subprocess = $use_tor_subprocess"
|
||||||
echo " multiprocess = $build_multiprocess"
|
echo " multiprocess = $build_multiprocess"
|
||||||
echo " with wallet = $enable_wallet"
|
echo " with wallet = $enable_wallet"
|
||||||
if test "$enable_wallet" != "no"; then
|
if test "$enable_wallet" != "no"; then
|
||||||
|
@ -579,6 +579,11 @@ void SetupServerArgs(ArgsManager& argsman)
|
|||||||
argsman.AddArg("-timeout=<n>", strprintf("Specify socket connection timeout in milliseconds. If an initial attempt to connect is unsuccessful after this amount of time, drop it (minimum: 1, default: %d)", DEFAULT_CONNECT_TIMEOUT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
argsman.AddArg("-timeout=<n>", strprintf("Specify socket connection timeout in milliseconds. If an initial attempt to connect is unsuccessful after this amount of time, drop it (minimum: 1, default: %d)", DEFAULT_CONNECT_TIMEOUT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
||||||
argsman.AddArg("-peertimeout=<n>", strprintf("Specify a p2p connection timeout delay in seconds. After connecting to a peer, wait this amount of time before considering disconnection based on inactivity (minimum: 1, default: %d)", DEFAULT_PEER_CONNECT_TIMEOUT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CONNECTION);
|
argsman.AddArg("-peertimeout=<n>", strprintf("Specify a p2p connection timeout delay in seconds. After connecting to a peer, wait this amount of time before considering disconnection based on inactivity (minimum: 1, default: %d)", DEFAULT_PEER_CONNECT_TIMEOUT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CONNECTION);
|
||||||
argsman.AddArg("-torcontrol=<ip>:<port>", strprintf("Tor control host and port to use if onion listening enabled (default: %s). If no port is specified, the default port of %i will be used.", DEFAULT_TOR_CONTROL, DEFAULT_TOR_CONTROL_PORT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
argsman.AddArg("-torcontrol=<ip>:<port>", strprintf("Tor control host and port to use if onion listening enabled (default: %s). If no port is specified, the default port of %i will be used.", DEFAULT_TOR_CONTROL, DEFAULT_TOR_CONTROL_PORT), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
||||||
|
#ifdef HAVE_TOR_SUBPROCESS
|
||||||
|
argsman.AddArg("-torexecute=<command>", strprintf("Tor command to use if not already running (default: %s)", DEFAULT_TOR_EXECUTE), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
||||||
|
#else
|
||||||
|
hidden_args.emplace_back("-torexecute=<command>");
|
||||||
|
#endif
|
||||||
argsman.AddArg("-torpassword=<pass>", "Tor control port password (default: empty)", ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::CONNECTION);
|
argsman.AddArg("-torpassword=<pass>", "Tor control port password (default: empty)", ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::CONNECTION);
|
||||||
#ifdef USE_UPNP
|
#ifdef USE_UPNP
|
||||||
argsman.AddArg("-upnp", strprintf("Use UPnP to map the listening port (default: %u)", DEFAULT_UPNP), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
argsman.AddArg("-upnp", strprintf("Use UPnP to map the listening port (default: %u)", DEFAULT_UPNP), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
|
||||||
|
@ -273,7 +273,8 @@ bool AddLocal(const CService& addr_, int nScore)
|
|||||||
if (!fDiscover && nScore < LOCAL_MANUAL)
|
if (!fDiscover && nScore < LOCAL_MANUAL)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!g_reachable_nets.Contains(addr))
|
// IPv4 and IPv6 cannot be connected to unless their networks are reachable, but Tor is not necessarily bidirectional
|
||||||
|
if (!(g_reachable_nets.Contains(addr) || addr.IsTor()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
LogPrintf("AddLocal(%s,%i)\n", addr.ToStringAddrPort(), nScore);
|
LogPrintf("AddLocal(%s,%i)\n", addr.ToStringAddrPort(), nScore);
|
||||||
|
@ -3,6 +3,10 @@
|
|||||||
// Distributed under the MIT software license, see the accompanying
|
// Distributed under the MIT software license, see the accompanying
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#if defined(HAVE_CONFIG_H)
|
||||||
|
#include <config/bitcoin-config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <torcontrol.h>
|
#include <torcontrol.h>
|
||||||
|
|
||||||
#include <chainparams.h>
|
#include <chainparams.h>
|
||||||
@ -24,11 +28,17 @@
|
|||||||
#include <util/thread.h>
|
#include <util/thread.h>
|
||||||
#include <util/time.h>
|
#include <util/time.h>
|
||||||
|
|
||||||
|
#ifdef ENABLE_TOR_SUBPROCESS
|
||||||
|
#include <util/subprocess.h>
|
||||||
|
#endif // ENABLE_TOR_SUBPROCESS
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <chrono>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
#include <fstream>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
@ -49,6 +59,7 @@ using util::ToString;
|
|||||||
|
|
||||||
/** Default control ip and port */
|
/** Default control ip and port */
|
||||||
const std::string DEFAULT_TOR_CONTROL = "127.0.0.1:" + ToString(DEFAULT_TOR_CONTROL_PORT);
|
const std::string DEFAULT_TOR_CONTROL = "127.0.0.1:" + ToString(DEFAULT_TOR_CONTROL_PORT);
|
||||||
|
const std::string DEFAULT_TOR_EXECUTE = "tor";
|
||||||
/** Tor cookie size (from control-spec.txt) */
|
/** Tor cookie size (from control-spec.txt) */
|
||||||
static const int TOR_COOKIE_SIZE = 32;
|
static const int TOR_COOKIE_SIZE = 32;
|
||||||
/** Size of client/server nonce for SAFECOOKIE */
|
/** Size of client/server nonce for SAFECOOKIE */
|
||||||
@ -83,6 +94,10 @@ TorControlConnection::~TorControlConnection()
|
|||||||
bufferevent_free(b_conn);
|
bufferevent_free(b_conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TorControlConnection::IgnoreReplyHandler(TorControlConnection &a, const TorControlReply &b)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void TorControlConnection::readcb(struct bufferevent *bev, void *ctx)
|
void TorControlConnection::readcb(struct bufferevent *bev, void *ctx)
|
||||||
{
|
{
|
||||||
TorControlConnection *self = static_cast<TorControlConnection*>(ctx);
|
TorControlConnection *self = static_cast<TorControlConnection*>(ctx);
|
||||||
@ -321,18 +336,20 @@ std::map<std::string,std::string> ParseTorReplyMapping(const std::string &s)
|
|||||||
return mapping;
|
return mapping;
|
||||||
}
|
}
|
||||||
|
|
||||||
TorController::TorController(struct event_base* _base, const std::string& tor_control_center, const CService& target):
|
TorController::TorController(struct event_base* _base, const std::string& tor_control_center, const CService& target, const std::string& execute):
|
||||||
base(_base),
|
base(_base),
|
||||||
m_tor_control_center(tor_control_center), conn(base), reconnect(true), reconnect_timeout(RECONNECT_TIMEOUT_START),
|
m_connect_tor_control_center(tor_control_center), conn(base), reconnect(true), reconnect_timeout(RECONNECT_TIMEOUT_START),
|
||||||
|
m_execute(execute),
|
||||||
m_target(target)
|
m_target(target)
|
||||||
{
|
{
|
||||||
reconnect_ev = event_new(base, -1, 0, reconnect_cb, this);
|
reconnect_ev = event_new(base, -1, 0, reconnect_cb, this);
|
||||||
if (!reconnect_ev)
|
if (!reconnect_ev)
|
||||||
LogPrintf("tor: Failed to create event for reconnection: out of memory?\n");
|
LogPrintf("tor: Failed to create event for reconnection: out of memory?\n");
|
||||||
// Start connection attempts immediately
|
// Start connection attempts immediately
|
||||||
if (!conn.Connect(m_tor_control_center, std::bind(&TorController::connected_cb, this, std::placeholders::_1),
|
m_current_tor_control_center = tor_control_center;
|
||||||
|
if (!conn.Connect(m_current_tor_control_center, std::bind(&TorController::connected_cb, this, std::placeholders::_1),
|
||||||
std::bind(&TorController::disconnected_cb, this, std::placeholders::_1) )) {
|
std::bind(&TorController::disconnected_cb, this, std::placeholders::_1) )) {
|
||||||
LogPrintf("tor: Initiating connection to Tor control port %s failed\n", m_tor_control_center);
|
LogPrintf("tor: Initiating connection to Tor control port %s failed\n", m_current_tor_control_center);
|
||||||
}
|
}
|
||||||
// Read service private key if cached
|
// Read service private key if cached
|
||||||
std::pair<bool,std::string> pkf = ReadBinaryFile(GetPrivateKeyFile());
|
std::pair<bool,std::string> pkf = ReadBinaryFile(GetPrivateKeyFile());
|
||||||
@ -351,6 +368,12 @@ TorController::~TorController()
|
|||||||
if (service.IsValid()) {
|
if (service.IsValid()) {
|
||||||
RemoveLocal(service);
|
RemoveLocal(service);
|
||||||
}
|
}
|
||||||
|
#ifdef ENABLE_TOR_SUBPROCESS
|
||||||
|
if (m_process) {
|
||||||
|
conn.Command("SIGNAL SHUTDOWN");
|
||||||
|
delete m_process;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void TorController::get_socks_cb(TorControlConnection& _conn, const TorControlReply& reply)
|
void TorController::get_socks_cb(TorControlConnection& _conn, const TorControlReply& reply)
|
||||||
@ -460,9 +483,20 @@ void TorController::auth_cb(TorControlConnection& _conn, const TorControlReply&
|
|||||||
if (reply.code == 250) {
|
if (reply.code == 250) {
|
||||||
LogPrint(BCLog::TOR, "Authentication successful\n");
|
LogPrint(BCLog::TOR, "Authentication successful\n");
|
||||||
|
|
||||||
|
#ifdef ENABLE_TOR_SUBPROCESS
|
||||||
|
if (m_process) {
|
||||||
|
_conn.Command("TAKEOWNERSHIP");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Now that we know Tor is running setup the proxy for onion addresses
|
// Now that we know Tor is running setup the proxy for onion addresses
|
||||||
// if -onion isn't set to something else.
|
// if -onion isn't set to something else.
|
||||||
if (gArgs.GetArg("-onion", "") == "") {
|
// NOTE: Our own private Tor doesn't do SOCKS, so don't configure it
|
||||||
|
if (gArgs.GetArg("-onion", "") == ""
|
||||||
|
#ifdef ENABLE_TOR_SUBPROCESS
|
||||||
|
&& !m_process
|
||||||
|
#endif
|
||||||
|
) {
|
||||||
_conn.Command("GETINFO net/listeners/socks", std::bind(&TorController::get_socks_cb, this, std::placeholders::_1, std::placeholders::_2));
|
_conn.Command("GETINFO net/listeners/socks", std::bind(&TorController::get_socks_cb, this, std::placeholders::_1, std::placeholders::_2));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -619,23 +653,94 @@ void TorController::protocolinfo_cb(TorControlConnection& _conn, const TorContro
|
|||||||
|
|
||||||
void TorController::connected_cb(TorControlConnection& _conn)
|
void TorController::connected_cb(TorControlConnection& _conn)
|
||||||
{
|
{
|
||||||
|
m_try_exec = false;
|
||||||
reconnect_timeout = RECONNECT_TIMEOUT_START;
|
reconnect_timeout = RECONNECT_TIMEOUT_START;
|
||||||
// First send a PROTOCOLINFO command to figure out what authentication is expected
|
// First send a PROTOCOLINFO command to figure out what authentication is expected
|
||||||
if (!_conn.Command("PROTOCOLINFO 1", std::bind(&TorController::protocolinfo_cb, this, std::placeholders::_1, std::placeholders::_2)))
|
if (!_conn.Command("PROTOCOLINFO 1", std::bind(&TorController::protocolinfo_cb, this, std::placeholders::_1, std::placeholders::_2)))
|
||||||
LogPrintf("tor: Error sending initial protocolinfo command\n");
|
LogPrintf("tor: Error sending initial protocolinfo command\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string TorController::LaunchTor()
|
||||||
|
{
|
||||||
|
#ifdef ENABLE_TOR_SUBPROCESS
|
||||||
|
fs::path tor_datadir = gArgs.GetDataDirNet() / "tor";
|
||||||
|
const fs::path controlport_env_filepath = tor_datadir / "controlport.env";
|
||||||
|
fs::remove(controlport_env_filepath); // may throw exceptions
|
||||||
|
|
||||||
|
if (m_process) {
|
||||||
|
try {
|
||||||
|
m_process->kill();
|
||||||
|
} catch (...) {
|
||||||
|
// ignore any exceptions
|
||||||
|
}
|
||||||
|
delete m_process;
|
||||||
|
m_process = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
m_process = new subprocess::Popen(m_execute + " -f -", subprocess::input{subprocess::PIPE}, subprocess::close_fds{true});
|
||||||
|
} catch (...) {
|
||||||
|
LogPrint(BCLog::TOR, "tor: Failed to execute Tor process\n");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
m_process->send(std::string{"SOCKSPort 0\n"});
|
||||||
|
m_process->send(std::string{"DataDirectory "} + fs::PathToString(tor_datadir) + "\n");
|
||||||
|
m_process->send(std::string{"ControlPort auto\n"});
|
||||||
|
m_process->send(std::string{"ControlPortWriteToFile "} + fs::PathToString(controlport_env_filepath) + "\n");
|
||||||
|
m_process->send(std::string{"CookieAuthentication 1\n"});
|
||||||
|
|
||||||
|
while (!fs::exists(controlport_env_filepath)) {
|
||||||
|
if (m_process->poll() != -1) {
|
||||||
|
LogPrint(BCLog::TOR, "tor: Tor process died before making control port file\n");
|
||||||
|
throw std::runtime_error("tor process died");
|
||||||
|
}
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ifstream controlport_file(controlport_env_filepath);
|
||||||
|
std::string portline;
|
||||||
|
controlport_file >> portline;
|
||||||
|
if (portline.compare(0, 5, "PORT=")) {
|
||||||
|
LogPrint(BCLog::TOR, "tor: Unrecognized control port line in file\n");
|
||||||
|
m_process->kill();
|
||||||
|
delete m_process;
|
||||||
|
m_process = nullptr;
|
||||||
|
throw std::runtime_error("port line unrecognized");
|
||||||
|
}
|
||||||
|
|
||||||
|
return portline.substr(5);
|
||||||
|
#else
|
||||||
|
throw std::runtime_error("not supported");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void TorController::disconnected_cb(TorControlConnection& _conn)
|
void TorController::disconnected_cb(TorControlConnection& _conn)
|
||||||
{
|
{
|
||||||
// Stop advertising service when disconnected
|
// Stop advertising service when disconnected
|
||||||
if (service.IsValid())
|
if (service.IsValid())
|
||||||
RemoveLocal(service);
|
RemoveLocal(service);
|
||||||
service = CService();
|
service = CService();
|
||||||
|
|
||||||
|
#ifdef ENABLE_TOR_SUBPROCESS
|
||||||
|
if (m_try_exec && !m_execute.empty()) {
|
||||||
|
LogPrint(BCLog::TOR, "tor: Not connected to Tor control port %s, trying to launch via %s\n", m_current_tor_control_center, m_execute);
|
||||||
|
try {
|
||||||
|
m_current_tor_control_center = LaunchTor();
|
||||||
|
Reconnect();
|
||||||
|
return;
|
||||||
|
} catch (...) {
|
||||||
|
// fall through to normal reconnect logic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!reconnect)
|
if (!reconnect)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
LogDebug(BCLog::TOR, "Not connected to Tor control port %s, retrying in %.2f s\n",
|
LogDebug(BCLog::TOR, "Not connected to Tor control port %s, retrying in %.2f s\n",
|
||||||
m_tor_control_center, reconnect_timeout);
|
m_current_tor_control_center, reconnect_timeout);
|
||||||
|
m_current_tor_control_center = m_connect_tor_control_center;
|
||||||
|
m_try_exec = true; // if this fails
|
||||||
|
|
||||||
// Single-shot timer for reconnect. Use exponential backoff with a maximum.
|
// Single-shot timer for reconnect. Use exponential backoff with a maximum.
|
||||||
struct timeval time = MillisToTimeval(int64_t(reconnect_timeout * 1000.0));
|
struct timeval time = MillisToTimeval(int64_t(reconnect_timeout * 1000.0));
|
||||||
@ -650,9 +755,9 @@ void TorController::Reconnect()
|
|||||||
/* Try to reconnect and reestablish if we get booted - for example, Tor
|
/* Try to reconnect and reestablish if we get booted - for example, Tor
|
||||||
* may be restarting.
|
* may be restarting.
|
||||||
*/
|
*/
|
||||||
if (!conn.Connect(m_tor_control_center, std::bind(&TorController::connected_cb, this, std::placeholders::_1),
|
if (!conn.Connect(m_current_tor_control_center, std::bind(&TorController::connected_cb, this, std::placeholders::_1),
|
||||||
std::bind(&TorController::disconnected_cb, this, std::placeholders::_1) )) {
|
std::bind(&TorController::disconnected_cb, this, std::placeholders::_1) )) {
|
||||||
LogPrintf("tor: Re-initiating connection to Tor control port %s failed\n", m_tor_control_center);
|
LogPrintf("tor: Re-initiating connection to Tor control port %s failed\n", m_current_tor_control_center);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -673,7 +778,17 @@ static std::thread torControlThread;
|
|||||||
|
|
||||||
static void TorControlThread(CService onion_service_target)
|
static void TorControlThread(CService onion_service_target)
|
||||||
{
|
{
|
||||||
TorController ctrl(gBase, gArgs.GetArg("-torcontrol", DEFAULT_TOR_CONTROL), onion_service_target);
|
#ifdef ENABLE_TOR_SUBPROCESS
|
||||||
|
std::string execute_command = gArgs.GetArg("-torexecute", DEFAULT_TOR_EXECUTE);
|
||||||
|
if (execute_command == "1") {
|
||||||
|
execute_command = DEFAULT_TOR_EXECUTE;
|
||||||
|
} else if (execute_command == "0") {
|
||||||
|
execute_command.clear();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
const std::string execute_command;
|
||||||
|
#endif
|
||||||
|
TorController ctrl(gBase, gArgs.GetArg("-torcontrol", DEFAULT_TOR_CONTROL), onion_service_target, execute_command);
|
||||||
|
|
||||||
event_base_dispatch(gBase);
|
event_base_dispatch(gBase);
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,10 @@
|
|||||||
#ifndef BITCOIN_TORCONTROL_H
|
#ifndef BITCOIN_TORCONTROL_H
|
||||||
#define BITCOIN_TORCONTROL_H
|
#define BITCOIN_TORCONTROL_H
|
||||||
|
|
||||||
|
#if defined(HAVE_CONFIG_H)
|
||||||
|
#include <config/bitcoin-config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <netaddress.h>
|
#include <netaddress.h>
|
||||||
#include <util/fs.h>
|
#include <util/fs.h>
|
||||||
|
|
||||||
@ -19,8 +23,13 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
namespace subprocess {
|
||||||
|
class Popen;
|
||||||
|
}
|
||||||
|
|
||||||
constexpr int DEFAULT_TOR_CONTROL_PORT = 9051;
|
constexpr int DEFAULT_TOR_CONTROL_PORT = 9051;
|
||||||
extern const std::string DEFAULT_TOR_CONTROL;
|
extern const std::string DEFAULT_TOR_CONTROL;
|
||||||
|
extern const std::string DEFAULT_TOR_EXECUTE;
|
||||||
static const bool DEFAULT_LISTEN_ONION = true;
|
static const bool DEFAULT_LISTEN_ONION = true;
|
||||||
|
|
||||||
void StartTorControl(CService onion_service_target);
|
void StartTorControl(CService onion_service_target);
|
||||||
@ -53,6 +62,7 @@ class TorControlConnection
|
|||||||
public:
|
public:
|
||||||
typedef std::function<void(TorControlConnection&)> ConnectionCB;
|
typedef std::function<void(TorControlConnection&)> ConnectionCB;
|
||||||
typedef std::function<void(TorControlConnection &,const TorControlReply &)> ReplyHandlerCB;
|
typedef std::function<void(TorControlConnection &,const TorControlReply &)> ReplyHandlerCB;
|
||||||
|
static void IgnoreReplyHandler(TorControlConnection &, const TorControlReply &);
|
||||||
|
|
||||||
/** Create a new TorControlConnection.
|
/** Create a new TorControlConnection.
|
||||||
*/
|
*/
|
||||||
@ -77,7 +87,7 @@ public:
|
|||||||
* A trailing CRLF is automatically added.
|
* A trailing CRLF is automatically added.
|
||||||
* Return true on success.
|
* Return true on success.
|
||||||
*/
|
*/
|
||||||
bool Command(const std::string &cmd, const ReplyHandlerCB& reply_handler);
|
bool Command(const std::string &cmd, const ReplyHandlerCB& reply_handler = IgnoreReplyHandler);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/** Callback when ready for use */
|
/** Callback when ready for use */
|
||||||
@ -106,7 +116,7 @@ private:
|
|||||||
class TorController
|
class TorController
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TorController(struct event_base* base, const std::string& tor_control_center, const CService& target);
|
TorController(struct event_base* base, const std::string& tor_control_center, const CService& target, const std::string& execute);
|
||||||
TorController() : conn{nullptr} {
|
TorController() : conn{nullptr} {
|
||||||
// Used for testing only.
|
// Used for testing only.
|
||||||
}
|
}
|
||||||
@ -119,13 +129,19 @@ public:
|
|||||||
void Reconnect();
|
void Reconnect();
|
||||||
private:
|
private:
|
||||||
struct event_base* base;
|
struct event_base* base;
|
||||||
const std::string m_tor_control_center;
|
const std::string m_connect_tor_control_center;
|
||||||
|
std::string m_current_tor_control_center;
|
||||||
TorControlConnection conn;
|
TorControlConnection conn;
|
||||||
std::string private_key;
|
std::string private_key;
|
||||||
std::string service_id;
|
std::string service_id;
|
||||||
|
bool m_try_exec{true};
|
||||||
bool reconnect;
|
bool reconnect;
|
||||||
struct event *reconnect_ev = nullptr;
|
struct event *reconnect_ev = nullptr;
|
||||||
float reconnect_timeout;
|
float reconnect_timeout;
|
||||||
|
std::string m_execute{DEFAULT_TOR_EXECUTE};
|
||||||
|
#ifdef ENABLE_TOR_SUBPROCESS
|
||||||
|
subprocess::Popen *m_process{nullptr};
|
||||||
|
#endif
|
||||||
CService service;
|
CService service;
|
||||||
const CService m_target;
|
const CService m_target;
|
||||||
/** Cookie for SAFECOOKIE auth */
|
/** Cookie for SAFECOOKIE auth */
|
||||||
@ -151,6 +167,8 @@ public:
|
|||||||
|
|
||||||
/** Callback for reconnect timer */
|
/** Callback for reconnect timer */
|
||||||
static void reconnect_cb(evutil_socket_t fd, short what, void *arg);
|
static void reconnect_cb(evutil_socket_t fd, short what, void *arg);
|
||||||
|
|
||||||
|
std::string LaunchTor();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // BITCOIN_TORCONTROL_H
|
#endif // BITCOIN_TORCONTROL_H
|
||||||
|
@ -923,6 +923,8 @@ private:
|
|||||||
* Command provided in a single string.
|
* Command provided in a single string.
|
||||||
* wait() - Wait for the child to exit.
|
* wait() - Wait for the child to exit.
|
||||||
* retcode() - The return code of the exited child.
|
* retcode() - The return code of the exited child.
|
||||||
|
* poll() - Check the status of the running child.
|
||||||
|
* kill(sig_num) - Kill the child. SIGTERM used by default.
|
||||||
* send(...) - Send input to the input channel of the child.
|
* send(...) - Send input to the input channel of the child.
|
||||||
* communicate(...) - Get the output/error from the child and close the channels
|
* communicate(...) - Get the output/error from the child and close the channels
|
||||||
* from the parent side.
|
* from the parent side.
|
||||||
@ -973,6 +975,12 @@ public:
|
|||||||
|
|
||||||
int wait() noexcept(false);
|
int wait() noexcept(false);
|
||||||
|
|
||||||
|
int poll() noexcept(false);
|
||||||
|
|
||||||
|
// Does not fail, Caller is expected to recheck the
|
||||||
|
// status with a call to poll()
|
||||||
|
void kill(int sig_num = 9);
|
||||||
|
|
||||||
void set_out_buf_cap(size_t cap) { stream_.set_out_buf_cap(cap); }
|
void set_out_buf_cap(size_t cap) { stream_.set_out_buf_cap(cap); }
|
||||||
|
|
||||||
void set_err_buf_cap(size_t cap) { stream_.set_err_buf_cap(cap); }
|
void set_err_buf_cap(size_t cap) { stream_.set_err_buf_cap(cap); }
|
||||||
@ -1035,6 +1043,7 @@ private:
|
|||||||
std::vector<std::string> vargs_;
|
std::vector<std::string> vargs_;
|
||||||
std::vector<char*> cargv_;
|
std::vector<char*> cargv_;
|
||||||
|
|
||||||
|
bool child_created_ = false;
|
||||||
// Pid of the child process
|
// Pid of the child process
|
||||||
int child_pid_ = -1;
|
int child_pid_ = -1;
|
||||||
|
|
||||||
@ -1089,6 +1098,68 @@ inline int Popen::wait() noexcept(false)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline int Popen::poll() noexcept(false)
|
||||||
|
{
|
||||||
|
#ifdef __USING_WINDOWS__
|
||||||
|
int ret = WaitForSingleObject(process_handle_, 0);
|
||||||
|
if (ret != WAIT_OBJECT_0) return -1;
|
||||||
|
|
||||||
|
DWORD dretcode_;
|
||||||
|
if (FALSE == GetExitCodeProcess(process_handle_, &dretcode_))
|
||||||
|
throw OSError("GetExitCodeProcess", 0);
|
||||||
|
|
||||||
|
retcode_ = (int)dretcode_;
|
||||||
|
CloseHandle(process_handle_);
|
||||||
|
|
||||||
|
return retcode_;
|
||||||
|
#else
|
||||||
|
if (!child_created_) return -1; // TODO: ??
|
||||||
|
|
||||||
|
int status;
|
||||||
|
|
||||||
|
// Returns zero if child is still running
|
||||||
|
int ret = waitpid(child_pid_, &status, WNOHANG);
|
||||||
|
if (ret == 0) return -1;
|
||||||
|
|
||||||
|
if (ret == child_pid_) {
|
||||||
|
if (WIFSIGNALED(status)) {
|
||||||
|
retcode_ = WTERMSIG(status);
|
||||||
|
} else if (WIFEXITED(status)) {
|
||||||
|
retcode_ = WEXITSTATUS(status);
|
||||||
|
} else {
|
||||||
|
retcode_ = 255;
|
||||||
|
}
|
||||||
|
return retcode_;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret == -1) {
|
||||||
|
// From subprocess.py
|
||||||
|
// This happens if SIGCHLD is set to be ignored
|
||||||
|
// or waiting for child process has otherwise been disabled
|
||||||
|
// for our process. This child is dead, we cannot get the
|
||||||
|
// status.
|
||||||
|
if (errno == ECHILD) retcode_ = 0;
|
||||||
|
else throw OSError("waitpid failed", errno);
|
||||||
|
} else {
|
||||||
|
retcode_ = ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return retcode_;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void Popen::kill(int sig_num)
|
||||||
|
{
|
||||||
|
#ifdef __USING_WINDOWS__
|
||||||
|
if (!TerminateProcess(this->process_handle_, (UINT)sig_num)) {
|
||||||
|
throw OSError("TerminateProcess", 0);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
::kill(child_pid_, sig_num);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
inline void Popen::execute_process() noexcept(false)
|
inline void Popen::execute_process() noexcept(false)
|
||||||
{
|
{
|
||||||
#ifdef __USING_WINDOWS__
|
#ifdef __USING_WINDOWS__
|
||||||
@ -1191,6 +1262,8 @@ inline void Popen::execute_process() noexcept(false)
|
|||||||
throw OSError("fork failed", errno);
|
throw OSError("fork failed", errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
child_created_ = true;
|
||||||
|
|
||||||
if (child_pid_ == 0)
|
if (child_pid_ == 0)
|
||||||
{
|
{
|
||||||
// Close descriptors belonging to parent
|
// Close descriptors belonging to parent
|
||||||
|
Loading…
Reference in New Issue
Block a user