Changeset View
Changeset View
Standalone View
Standalone View
src/torcontrol.cpp
Show All 9 Lines | |||||
#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/predicate.hpp> | #include <boost/algorithm/string/predicate.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/bind.hpp> | |||||
#include <boost/signals2/signal.hpp> | #include <boost/signals2/signal.hpp> | ||||
#include <event2/buffer.h> | #include <event2/buffer.h> | ||||
#include <event2/bufferevent.h> | #include <event2/bufferevent.h> | ||||
#include <event2/event.h> | #include <event2/event.h> | ||||
#include <event2/thread.h> | #include <event2/thread.h> | ||||
#include <event2/util.h> | #include <event2/util.h> | ||||
▲ Show 20 Lines • Show All 485 Lines • ▼ Show 20 Lines | : base(_base), target(_target), conn(base), reconnect(true), | ||||
reconnect_ev(0), reconnect_timeout(RECONNECT_TIMEOUT_START) { | 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(_target, | ||||
boost::bind(&TorController::connected_cb, this, _1), | std::bind(&TorController::connected_cb, this, | ||||
boost::bind(&TorController::disconnected_cb, this, _1))) { | std::placeholders::_1), | ||||
std::bind(&TorController::disconnected_cb, this, | |||||
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); | _target); | ||||
} | } | ||||
// 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()); | ||||
▲ Show 20 Lines • Show All 74 Lines • ▼ Show 20 Lines | if (reply.code == 250) { | ||||
private_key = "NEW:RSA1024"; | private_key = "NEW:RSA1024"; | ||||
} | } | ||||
// Request hidden service, redirect port. | // Request hidden 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,127.0.0.1:%i", | ||||
private_key, GetListenPort(), GetListenPort()), | private_key, GetListenPort(), GetListenPort()), | ||||
boost::bind(&TorController::add_onion_cb, this, _1, _2)); | std::bind(&TorController::add_onion_cb, this, | ||||
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. | ||||
* | * | ||||
* ServerHash is computed as: | * ServerHash is computed as: | ||||
▲ Show 20 Lines • Show All 56 Lines • ▼ Show 20 Lines | if (reply.code == 250) { | ||||
"ServerHash %s\n", | "ServerHash %s\n", | ||||
HexStr(serverHash), HexStr(computedServerHash)); | HexStr(serverHash), HexStr(computedServerHash)); | ||||
return; | return; | ||||
} | } | ||||
std::vector<uint8_t> computedClientHash = ComputeResponse( | std::vector<uint8_t> computedClientHash = ComputeResponse( | ||||
TOR_SAFE_CLIENTKEY, cookie, clientNonce, serverNonce); | TOR_SAFE_CLIENTKEY, cookie, clientNonce, serverNonce); | ||||
_conn.Command("AUTHENTICATE " + HexStr(computedClientHash), | _conn.Command("AUTHENTICATE " + HexStr(computedClientHash), | ||||
boost::bind(&TorController::auth_cb, this, _1, _2)); | std::bind(&TorController::auth_cb, this, | ||||
std::placeholders::_1, | |||||
std::placeholders::_2)); | |||||
} else { | } else { | ||||
LogPrintf("tor: Invalid reply to AUTHCHALLENGE\n"); | LogPrintf("tor: Invalid reply to AUTHCHALLENGE\n"); | ||||
} | } | ||||
} else { | } else { | ||||
LogPrintf("tor: SAFECOOKIE authentication challenge failed\n"); | LogPrintf("tor: SAFECOOKIE authentication challenge failed\n"); | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | if (reply.code == 250) { | ||||
* password: "password" | * password: "password" | ||||
*/ | */ | ||||
std::string torpassword = gArgs.GetArg("-torpassword", ""); | std::string torpassword = gArgs.GetArg("-torpassword", ""); | ||||
if (!torpassword.empty()) { | if (!torpassword.empty()) { | ||||
if (methods.count("HASHEDPASSWORD")) { | if (methods.count("HASHEDPASSWORD")) { | ||||
LogPrint(BCLog::TOR, | LogPrint(BCLog::TOR, | ||||
"tor: Using HASHEDPASSWORD authentication\n"); | "tor: Using HASHEDPASSWORD authentication\n"); | ||||
boost::replace_all(torpassword, "\"", "\\\""); | boost::replace_all(torpassword, "\"", "\\\""); | ||||
_conn.Command( | _conn.Command("AUTHENTICATE \"" + torpassword + "\"", | ||||
"AUTHENTICATE \"" + torpassword + "\"", | std::bind(&TorController::auth_cb, this, | ||||
boost::bind(&TorController::auth_cb, this, _1, _2)); | std::placeholders::_1, | ||||
std::placeholders::_2)); | |||||
} else { | } else { | ||||
LogPrintf("tor: Password provided with -torpassword, but " | LogPrintf("tor: Password provided with -torpassword, but " | ||||
"HASHEDPASSWORD authentication is not available\n"); | "HASHEDPASSWORD authentication is not available\n"); | ||||
} | } | ||||
} else if (methods.count("NULL")) { | } else if (methods.count("NULL")) { | ||||
LogPrint(BCLog::TOR, "tor: Using NULL authentication\n"); | LogPrint(BCLog::TOR, "tor: Using NULL authentication\n"); | ||||
_conn.Command("AUTHENTICATE", | _conn.Command("AUTHENTICATE", std::bind(&TorController::auth_cb, | ||||
boost::bind(&TorController::auth_cb, this, _1, _2)); | this, std::placeholders::_1, | ||||
std::placeholders::_2)); | |||||
} else if (methods.count("SAFECOOKIE")) { | } else if (methods.count("SAFECOOKIE")) { | ||||
// Cookie: hexdump -e '32/1 "%02x""\n"' ~/.tor/control_auth_cookie | // Cookie: hexdump -e '32/1 "%02x""\n"' ~/.tor/control_auth_cookie | ||||
LogPrint(BCLog::TOR, | LogPrint(BCLog::TOR, | ||||
"tor: Using SAFECOOKIE authentication, " | "tor: Using SAFECOOKIE authentication, " | ||||
"reading cookie authentication from %s\n", | "reading cookie authentication from %s\n", | ||||
cookiefile); | cookiefile); | ||||
std::pair<bool, std::string> status_cookie = | std::pair<bool, std::string> status_cookie = | ||||
ReadBinaryFile(cookiefile, TOR_COOKIE_SIZE); | ReadBinaryFile(cookiefile, TOR_COOKIE_SIZE); | ||||
if (status_cookie.first && | if (status_cookie.first && | ||||
status_cookie.second.size() == TOR_COOKIE_SIZE) { | status_cookie.second.size() == TOR_COOKIE_SIZE) { | ||||
// _conn.Command("AUTHENTICATE " + HexStr(status_cookie.second), | // _conn.Command("AUTHENTICATE " + HexStr(status_cookie.second), | ||||
// boost::bind(&TorController::auth_cb, this, _1, _2)); | // std::bind(&TorController::auth_cb, this, | ||||
// std::placeholders::_1, std::placeholders::_2)); | |||||
cookie = std::vector<uint8_t>(status_cookie.second.begin(), | cookie = std::vector<uint8_t>(status_cookie.second.begin(), | ||||
status_cookie.second.end()); | status_cookie.second.end()); | ||||
clientNonce = std::vector<uint8_t>(TOR_NONCE_SIZE, 0); | clientNonce = std::vector<uint8_t>(TOR_NONCE_SIZE, 0); | ||||
GetRandBytes(&clientNonce[0], TOR_NONCE_SIZE); | GetRandBytes(&clientNonce[0], TOR_NONCE_SIZE); | ||||
_conn.Command("AUTHCHALLENGE SAFECOOKIE " + HexStr(clientNonce), | _conn.Command("AUTHCHALLENGE SAFECOOKIE " + HexStr(clientNonce), | ||||
boost::bind(&TorController::authchallenge_cb, | std::bind(&TorController::authchallenge_cb, this, | ||||
this, _1, _2)); | std::placeholders::_1, | ||||
std::placeholders::_2)); | |||||
} else { | } else { | ||||
if (status_cookie.first) { | if (status_cookie.first) { | ||||
LogPrintf("tor: Authentication cookie %s is not exactly %i " | LogPrintf("tor: Authentication cookie %s is not exactly %i " | ||||
"bytes, as is required by the spec\n", | "bytes, as is required by the spec\n", | ||||
cookiefile, TOR_COOKIE_SIZE); | cookiefile, TOR_COOKIE_SIZE); | ||||
} else { | } else { | ||||
LogPrintf("tor: Authentication cookie %s could not be " | LogPrintf("tor: Authentication cookie %s could not be " | ||||
"opened (check permissions)\n", | "opened (check permissions)\n", | ||||
Show All 11 Lines | if (reply.code == 250) { | ||||
LogPrintf("tor: Requesting protocol info failed\n"); | LogPrintf("tor: Requesting protocol info failed\n"); | ||||
} | } | ||||
} | } | ||||
void TorController::connected_cb(TorControlConnection &_conn) { | void TorController::connected_cb(TorControlConnection &_conn) { | ||||
reconnect_timeout = RECONNECT_TIMEOUT_START; | reconnect_timeout = RECONNECT_TIMEOUT_START; | ||||
// First send a PROTOCOLINFO command to figure out what authentication is | // First send a PROTOCOLINFO command to figure out what authentication is | ||||
// expected | // expected | ||||
if (!_conn.Command( | if (!_conn.Command("PROTOCOLINFO 1", | ||||
"PROTOCOLINFO 1", | std::bind(&TorController::protocolinfo_cb, this, | ||||
boost::bind(&TorController::protocolinfo_cb, this, _1, _2))) { | std::placeholders::_1, | ||||
std::placeholders::_2))) { | |||||
LogPrintf("tor: Error sending initial protocolinfo command\n"); | LogPrintf("tor: Error sending initial protocolinfo command\n"); | ||||
} | } | ||||
} | } | ||||
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); | ||||
Show All 15 Lines | void TorController::disconnected_cb(TorControlConnection &_conn) { | ||||
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(target, | ||||
boost::bind(&TorController::connected_cb, this, _1), | std::bind(&TorController::connected_cb, this, | ||||
boost::bind(&TorController::disconnected_cb, this, _1))) { | std::placeholders::_1), | ||||
std::bind(&TorController::disconnected_cb, this, | |||||
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); | target); | ||||
} | } | ||||
} | } | ||||
fs::path TorController::GetPrivateKeyFile() { | fs::path TorController::GetPrivateKeyFile() { | ||||
return GetDataDir() / "onion_private_key"; | return GetDataDir() / "onion_private_key"; | ||||
▲ Show 20 Lines • Show All 48 Lines • Show Last 20 Lines |