Changeset View
Changeset View
Standalone View
Standalone View
src/torcontrol.cpp
// Copyright (c) 2015-2016 The Bitcoin Core developers | // Copyright (c) 2015-2016 The Bitcoin Core developers | ||||
// Copyright (c) 2017 The Zcash developers | // Copyright (c) 2017 The Zcash developers | ||||
// 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. | ||||
#include <chainparams.h> | |||||
#include <torcontrol.h> | #include <torcontrol.h> | ||||
#include <chainparams.h> | |||||
#include <chainparamsbase.h> | |||||
#include <crypto/hmac_sha256.h> | #include <crypto/hmac_sha256.h> | ||||
#include <net.h> | #include <net.h> | ||||
#include <netaddress.h> | |||||
#include <netbase.h> | #include <netbase.h> | ||||
#include <util/strencodings.h> | #include <util/strencodings.h> | ||||
#include <util/system.h> | #include <util/system.h> | ||||
#include <boost/algorithm/string/classification.hpp> | #include <boost/algorithm/string/classification.hpp> | ||||
#include <boost/algorithm/string/replace.hpp> | #include <boost/algorithm/string/replace.hpp> | ||||
#include <boost/algorithm/string/split.hpp> | #include <boost/algorithm/string/split.hpp> | ||||
#include <boost/signals2/signal.hpp> | #include <boost/signals2/signal.hpp> | ||||
▲ Show 20 Lines • Show All 439 Lines • ▼ Show 20 Lines | |||||
/** | /** | ||||
* Controller that connects to Tor control socket, authenticate, then create | * Controller that connects to Tor control socket, authenticate, then create | ||||
* and maintain an ephemeral onion service. | * and maintain an ephemeral onion service. | ||||
*/ | */ | ||||
class TorController { | class TorController { | ||||
public: | public: | ||||
TorController(struct event_base *base, | TorController(struct event_base *base, | ||||
const std::string &tor_control_center); | const std::string &tor_control_center, | ||||
const CService &target); | |||||
~TorController(); | ~TorController(); | ||||
/** Get name fo file to store private key in */ | /** Get name fo file to store private key in */ | ||||
fs::path GetPrivateKeyFile(); | fs::path GetPrivateKeyFile(); | ||||
/** Reconnect, after getting disconnected */ | /** Reconnect, after getting disconnected */ | ||||
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_tor_control_center; | ||||
TorControlConnection conn; | TorControlConnection conn; | ||||
std::string private_key; | std::string private_key; | ||||
std::string service_id; | std::string service_id; | ||||
bool reconnect; | bool reconnect; | ||||
struct event *reconnect_ev; | struct event *reconnect_ev; | ||||
float reconnect_timeout; | float reconnect_timeout; | ||||
CService service; | CService service; | ||||
const CService m_target; | |||||
/** Cookie for SAFECOOKIE auth */ | /** Cookie for SAFECOOKIE auth */ | ||||
std::vector<uint8_t> cookie; | std::vector<uint8_t> cookie; | ||||
/** ClientNonce for SAFECOOKIE auth */ | /** ClientNonce for SAFECOOKIE auth */ | ||||
std::vector<uint8_t> clientNonce; | std::vector<uint8_t> clientNonce; | ||||
/** Callback for ADD_ONION result */ | /** Callback for ADD_ONION result */ | ||||
void add_onion_cb(TorControlConnection &conn, const TorControlReply &reply); | void add_onion_cb(TorControlConnection &conn, const TorControlReply &reply); | ||||
/** Callback for AUTHENTICATE result */ | /** Callback for AUTHENTICATE result */ | ||||
Show All 9 Lines | private: | ||||
/** Callback after connection lost or failed connection attempt */ | /** Callback after connection lost or failed connection attempt */ | ||||
void disconnected_cb(TorControlConnection &conn); | void disconnected_cb(TorControlConnection &conn); | ||||
/** 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); | ||||
}; | }; | ||||
TorController::TorController(struct event_base *_base, | TorController::TorController(struct event_base *_base, | ||||
const std::string &tor_control_center) | const std::string &tor_control_center, | ||||
const CService &target) | |||||
: base(_base), m_tor_control_center(tor_control_center), conn(base), | : base(_base), m_tor_control_center(tor_control_center), conn(base), | ||||
reconnect(true), reconnect_ev(0), | reconnect(true), reconnect_ev(0), | ||||
reconnect_timeout(RECONNECT_TIMEOUT_START) { | reconnect_timeout(RECONNECT_TIMEOUT_START), 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( | LogPrintf( | ||||
"tor: Failed to create event for reconnection: out of memory?\n"); | "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, | if (!conn.Connect(m_tor_control_center, | ||||
std::bind(&TorController::connected_cb, this, | std::bind(&TorController::connected_cb, this, | ||||
▲ Show 20 Lines • Show All 83 Lines • ▼ Show 20 Lines | if (reply.code == 250) { | ||||
if (private_key.empty()) { | if (private_key.empty()) { | ||||
// Explicitly request key type - see issue #9214 | // Explicitly request key type - see issue #9214 | ||||
private_key = "NEW:ED25519-V3"; | private_key = "NEW:ED25519-V3"; | ||||
} | } | ||||
// Request onion service, redirect port. | // Request onion service, redirect port. | ||||
// Note that the 'virtual' port doesn't have to be the same as our | // Note that the 'virtual' port doesn't have to be the same as our | ||||
// internal port, but this is just a convenient choice. TODO; refactor | // internal port, but this is just a convenient choice. TODO; refactor | ||||
// the shutdown sequence some day. | // the shutdown sequence some day. | ||||
_conn.Command(strprintf("ADD_ONION %s Port=%i,127.0.0.1:%i", | _conn.Command(strprintf("ADD_ONION %s Port=%i,%s", private_key, | ||||
private_key, Params().GetDefaultPort(), | Params().GetDefaultPort(), | ||||
GetListenPort()), | m_target.ToStringIPPort()), | ||||
std::bind(&TorController::add_onion_cb, this, | std::bind(&TorController::add_onion_cb, this, | ||||
std::placeholders::_1, std::placeholders::_2)); | std::placeholders::_1, std::placeholders::_2)); | ||||
} else { | } else { | ||||
LogPrintf("tor: Authentication failed\n"); | LogPrintf("tor: Authentication failed\n"); | ||||
} | } | ||||
} | } | ||||
/** Compute Tor SAFECOOKIE response. | /** Compute Tor SAFECOOKIE response. | ||||
▲ Show 20 Lines • Show All 237 Lines • ▼ Show 20 Lines | void TorController::reconnect_cb(evutil_socket_t fd, short what, void *arg) { | ||||
TorController *self = static_cast<TorController *>(arg); | TorController *self = static_cast<TorController *>(arg); | ||||
self->Reconnect(); | self->Reconnect(); | ||||
} | } | ||||
/****** Thread ********/ | /****** Thread ********/ | ||||
static struct event_base *gBase; | static struct event_base *gBase; | ||||
static std::thread torControlThread; | static std::thread torControlThread; | ||||
static void TorControlThread() { | static void TorControlThread(CService onion_service_target) { | ||||
TorController ctrl(gBase, gArgs.GetArg("-torcontrol", DEFAULT_TOR_CONTROL)); | TorController ctrl(gBase, gArgs.GetArg("-torcontrol", DEFAULT_TOR_CONTROL), | ||||
onion_service_target); | |||||
event_base_dispatch(gBase); | event_base_dispatch(gBase); | ||||
} | } | ||||
void StartTorControl() { | void StartTorControl(CService onion_service_target) { | ||||
assert(!gBase); | assert(!gBase); | ||||
#ifdef WIN32 | #ifdef WIN32 | ||||
evthread_use_windows_threads(); | evthread_use_windows_threads(); | ||||
#else | #else | ||||
evthread_use_pthreads(); | evthread_use_pthreads(); | ||||
#endif | #endif | ||||
gBase = event_base_new(); | gBase = event_base_new(); | ||||
if (!gBase) { | if (!gBase) { | ||||
LogPrintf("tor: Unable to create event_base\n"); | LogPrintf("tor: Unable to create event_base\n"); | ||||
return; | return; | ||||
} | } | ||||
torControlThread = std::thread( | torControlThread = std::thread( | ||||
std::bind(&TraceThread<void (*)()>, "torcontrol", &TorControlThread)); | &TraceThread<std::function<void()>>, "torcontrol", | ||||
[onion_service_target] { TorControlThread(onion_service_target); }); | |||||
} | } | ||||
void InterruptTorControl() { | void InterruptTorControl() { | ||||
if (gBase) { | if (gBase) { | ||||
LogPrintf("tor: Thread interrupt\n"); | LogPrintf("tor: Thread interrupt\n"); | ||||
event_base_once( | event_base_once( | ||||
gBase, -1, EV_TIMEOUT, | gBase, -1, EV_TIMEOUT, | ||||
[](evutil_socket_t, short, void *) { event_base_loopbreak(gBase); }, | [](evutil_socket_t, short, void *) { event_base_loopbreak(gBase); }, | ||||
nullptr, nullptr); | nullptr, nullptr); | ||||
} | } | ||||
} | } | ||||
void StopTorControl() { | void StopTorControl() { | ||||
if (gBase) { | if (gBase) { | ||||
torControlThread.join(); | torControlThread.join(); | ||||
event_base_free(gBase); | event_base_free(gBase); | ||||
gBase = nullptr; | gBase = nullptr; | ||||
} | } | ||||
} | } | ||||
CService DefaultOnionServiceTarget() { | |||||
struct in_addr onion_service_target; | |||||
onion_service_target.s_addr = htonl(INADDR_LOOPBACK); | |||||
return {onion_service_target, BaseParams().OnionServiceTargetPort()}; | |||||
} |