Changeset View
Changeset View
Standalone View
Standalone View
src/torcontrol.cpp
Show First 20 Lines • Show All 79 Lines • ▼ Show 20 Lines | public: | ||||
/** Create a new TorControlConnection. | /** Create a new TorControlConnection. | ||||
*/ | */ | ||||
explicit TorControlConnection(struct event_base *base); | explicit TorControlConnection(struct event_base *base); | ||||
~TorControlConnection(); | ~TorControlConnection(); | ||||
/** | /** | ||||
* Connect to a Tor control port. | * Connect to a Tor control port. | ||||
* target is address of the form host:port. | * tor_control_center is address of the form host:port. | ||||
* connected is the handler that is called when connection is successfully | * connected is the handler that is called when connection is successfully | ||||
* established. | * established. | ||||
* disconnected is a handler that is called when the connection is broken. | * disconnected is a handler that is called when the connection is broken. | ||||
* Return true on success. | * Return true on success. | ||||
*/ | */ | ||||
bool Connect(const std::string &target, const ConnectionCB &connected, | bool Connect(const std::string &tor_control_center, | ||||
const ConnectionCB &connected, | |||||
const ConnectionCB &disconnected); | const ConnectionCB &disconnected); | ||||
/** | /** | ||||
* Disconnect from Tor control port. | * Disconnect from Tor control port. | ||||
*/ | */ | ||||
void Disconnect(); | void Disconnect(); | ||||
/** | /** | ||||
▲ Show 20 Lines • Show All 99 Lines • ▼ Show 20 Lines | if (what & BEV_EVENT_CONNECTED) { | ||||
} else { | } else { | ||||
LogPrint(BCLog::TOR, "tor: End of stream\n"); | LogPrint(BCLog::TOR, "tor: End of stream\n"); | ||||
} | } | ||||
self->Disconnect(); | self->Disconnect(); | ||||
self->disconnected(*self); | self->disconnected(*self); | ||||
} | } | ||||
} | } | ||||
bool TorControlConnection::Connect(const std::string &target, | bool TorControlConnection::Connect(const std::string &tor_control_center, | ||||
const ConnectionCB &_connected, | const ConnectionCB &_connected, | ||||
const ConnectionCB &_disconnected) { | const ConnectionCB &_disconnected) { | ||||
if (b_conn) { | if (b_conn) { | ||||
Disconnect(); | Disconnect(); | ||||
} | } | ||||
// Parse target address:port | // Parse tor_control_center address:port | ||||
struct sockaddr_storage connect_to_addr; | struct sockaddr_storage connect_to_addr; | ||||
int connect_to_addrlen = sizeof(connect_to_addr); | int connect_to_addrlen = sizeof(connect_to_addr); | ||||
if (evutil_parse_sockaddr_port(target.c_str(), | if (evutil_parse_sockaddr_port(tor_control_center.c_str(), | ||||
(struct sockaddr *)&connect_to_addr, | (struct sockaddr *)&connect_to_addr, | ||||
&connect_to_addrlen) < 0) { | &connect_to_addrlen) < 0) { | ||||
LogPrintf("tor: Error parsing socket address %s\n", target); | LogPrintf("tor: Error parsing socket address %s\n", tor_control_center); | ||||
return false; | return false; | ||||
} | } | ||||
// Create a new socket, set up callbacks and enable notification bits | // Create a new socket, set up callbacks and enable notification bits | ||||
b_conn = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE); | b_conn = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE); | ||||
if (!b_conn) { | if (!b_conn) { | ||||
return false; | return false; | ||||
} | } | ||||
bufferevent_setcb(b_conn, TorControlConnection::readcb, nullptr, | bufferevent_setcb(b_conn, TorControlConnection::readcb, nullptr, | ||||
TorControlConnection::eventcb, this); | TorControlConnection::eventcb, this); | ||||
bufferevent_enable(b_conn, EV_READ | EV_WRITE); | bufferevent_enable(b_conn, EV_READ | EV_WRITE); | ||||
this->connected = _connected; | this->connected = _connected; | ||||
this->disconnected = _disconnected; | this->disconnected = _disconnected; | ||||
// Finally, connect to target | // Finally, connect to tor_control_center | ||||
if (bufferevent_socket_connect(b_conn, (struct sockaddr *)&connect_to_addr, | if (bufferevent_socket_connect(b_conn, (struct sockaddr *)&connect_to_addr, | ||||
connect_to_addrlen) < 0) { | connect_to_addrlen) < 0) { | ||||
LogPrintf("tor: Error connecting to address %s\n", target); | LogPrintf("tor: Error connecting to address %s\n", tor_control_center); | ||||
return false; | return false; | ||||
} | } | ||||
return true; | return true; | ||||
} | } | ||||
void TorControlConnection::Disconnect() { | void TorControlConnection::Disconnect() { | ||||
if (b_conn) { | if (b_conn) { | ||||
bufferevent_free(b_conn); | bufferevent_free(b_conn); | ||||
▲ Show 20 Lines • Show All 207 Lines • ▼ Show 20 Lines | |||||
/****** Bitcoin specific TorController implementation ********/ | /****** Bitcoin specific TorController implementation ********/ | ||||
/** | /** | ||||
* 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, const std::string &target); | TorController(struct event_base *base, | ||||
const std::string &tor_control_center); | |||||
~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; | ||||
std::string target; | 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; | ||||
/** Cookie for SAFECOOKIE auth */ | /** Cookie for SAFECOOKIE auth */ | ||||
Show All 16 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 &_target) | const std::string &tor_control_center) | ||||
: base(_base), target(_target), conn(base), reconnect(true), | : base(_base), m_tor_control_center(tor_control_center), conn(base), | ||||
reconnect_ev(0), reconnect_timeout(RECONNECT_TIMEOUT_START) { | reconnect(true), reconnect_ev(0), | ||||
reconnect_timeout(RECONNECT_TIMEOUT_START) { | |||||
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(_target, | if (!conn.Connect(m_tor_control_center, | ||||
std::bind(&TorController::connected_cb, this, | std::bind(&TorController::connected_cb, this, | ||||
std::placeholders::_1), | std::placeholders::_1), | ||||
std::bind(&TorController::disconnected_cb, this, | std::bind(&TorController::disconnected_cb, this, | ||||
std::placeholders::_1))) { | std::placeholders::_1))) { | ||||
LogPrintf("tor: Initiating connection to Tor control port %s failed\n", | LogPrintf("tor: Initiating connection to Tor control port %s failed\n", | ||||
_target); | m_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()); | ||||
if (pkf.first) { | if (pkf.first) { | ||||
LogPrint(BCLog::TOR, "tor: Reading cached private key from %s\n", | LogPrint(BCLog::TOR, "tor: Reading cached private key from %s\n", | ||||
GetPrivateKeyFile()); | GetPrivateKeyFile()); | ||||
private_key = pkf.second; | private_key = pkf.second; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 288 Lines • ▼ Show 20 Lines | void TorController::disconnected_cb(TorControlConnection &_conn) { | ||||
} | } | ||||
service = CService(); | service = CService(); | ||||
if (!reconnect) { | if (!reconnect) { | ||||
return; | return; | ||||
} | } | ||||
LogPrint(BCLog::TOR, | LogPrint(BCLog::TOR, | ||||
"tor: Not connected to Tor control port %s, trying to reconnect\n", | "tor: Not connected to Tor control port %s, trying to reconnect\n", | ||||
target); | m_tor_control_center); | ||||
// Single-shot timer for reconnect. Use exponential backoff. | // Single-shot timer for reconnect. Use exponential backoff. | ||||
struct timeval time = MillisToTimeval(int64_t(reconnect_timeout * 1000.0)); | struct timeval time = MillisToTimeval(int64_t(reconnect_timeout * 1000.0)); | ||||
if (reconnect_ev) { | if (reconnect_ev) { | ||||
event_add(reconnect_ev, &time); | event_add(reconnect_ev, &time); | ||||
} | } | ||||
reconnect_timeout *= RECONNECT_TIMEOUT_EXP; | reconnect_timeout *= RECONNECT_TIMEOUT_EXP; | ||||
} | } | ||||
void TorController::Reconnect() { | void TorController::Reconnect() { | ||||
/* Try to reconnect and reestablish if we get booted - for example, Tor may | /* Try to reconnect and reestablish if we get booted - for example, Tor may | ||||
* be restarting. | * be restarting. | ||||
*/ | */ | ||||
if (!conn.Connect(target, | if (!conn.Connect(m_tor_control_center, | ||||
std::bind(&TorController::connected_cb, this, | std::bind(&TorController::connected_cb, this, | ||||
std::placeholders::_1), | std::placeholders::_1), | ||||
std::bind(&TorController::disconnected_cb, this, | std::bind(&TorController::disconnected_cb, this, | ||||
std::placeholders::_1))) { | std::placeholders::_1))) { | ||||
LogPrintf( | LogPrintf( | ||||
"tor: Re-initiating connection to Tor control port %s failed\n", | "tor: Re-initiating connection to Tor control port %s failed\n", | ||||
target); | m_tor_control_center); | ||||
} | } | ||||
} | } | ||||
fs::path TorController::GetPrivateKeyFile() { | fs::path TorController::GetPrivateKeyFile() { | ||||
return GetDataDir() / "onion_v3_private_key"; | return GetDataDir() / "onion_v3_private_key"; | ||||
} | } | ||||
void TorController::reconnect_cb(evutil_socket_t fd, short what, void *arg) { | void TorController::reconnect_cb(evutil_socket_t fd, short what, void *arg) { | ||||
▲ Show 20 Lines • Show All 48 Lines • Show Last 20 Lines |