diff options
Diffstat (limited to 'src/torcontrol.cpp')
-rw-r--r-- | src/torcontrol.cpp | 141 |
1 files changed, 94 insertions, 47 deletions
diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp index 7ae384ceb3..3a21a79a34 100644 --- a/src/torcontrol.cpp +++ b/src/torcontrol.cpp @@ -7,7 +7,7 @@ #include <chainparams.h> #include <chainparamsbase.h> -#include <compat.h> +#include <compat/compat.h> #include <crypto/hmac_sha256.h> #include <net.h> #include <netaddress.h> @@ -24,10 +24,6 @@ #include <set> #include <vector> -#include <boost/algorithm/string/classification.hpp> -#include <boost/algorithm/string/replace.hpp> -#include <boost/algorithm/string/split.hpp> - #include <event2/buffer.h> #include <event2/bufferevent.h> #include <event2/event.h> @@ -53,6 +49,7 @@ static const float RECONNECT_TIMEOUT_EXP = 1.5; * this is belt-and-suspenders sanity limit to prevent memory exhaustion. */ static const int MAX_LINE_LENGTH = 100000; +static const uint16_t DEFAULT_TOR_SOCKS_PORT = 9050; /****** Low-level TorControlConnection ********/ @@ -97,7 +94,7 @@ void TorControlConnection::readcb(struct bufferevent *bev, void *ctx) self->reply_handlers.front()(*self, self->message); self->reply_handlers.pop_front(); } else { - LogPrint(BCLog::TOR, "tor: Received unexpected sync reply %i\n", self->message.code); + LogPrint(BCLog::TOR, "Received unexpected sync reply %i\n", self->message.code); } } self->message.Clear(); @@ -116,13 +113,13 @@ void TorControlConnection::eventcb(struct bufferevent *bev, short what, void *ct { TorControlConnection *self = static_cast<TorControlConnection*>(ctx); if (what & BEV_EVENT_CONNECTED) { - LogPrint(BCLog::TOR, "tor: Successfully connected!\n"); + LogPrint(BCLog::TOR, "Successfully connected!\n"); self->connected(*self); } else if (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) { if (what & BEV_EVENT_ERROR) { - LogPrint(BCLog::TOR, "tor: Error connecting to Tor control socket\n"); + LogPrint(BCLog::TOR, "Error connecting to Tor control socket\n"); } else { - LogPrint(BCLog::TOR, "tor: End of stream\n"); + LogPrint(BCLog::TOR, "End of stream\n"); } self->Disconnect(); self->disconnected(*self); @@ -307,8 +304,7 @@ std::map<std::string,std::string> ParseTorReplyMapping(const std::string &s) TorController::TorController(struct event_base* _base, const std::string& tor_control_center, const CService& target): base(_base), - m_tor_control_center(tor_control_center), conn(base), reconnect(true), reconnect_ev(0), - reconnect_timeout(RECONNECT_TIMEOUT_START), + m_tor_control_center(tor_control_center), conn(base), reconnect(true), reconnect_timeout(RECONNECT_TIMEOUT_START), m_target(target) { reconnect_ev = event_new(base, -1, 0, reconnect_cb, this); @@ -322,7 +318,7 @@ TorController::TorController(struct event_base* _base, const std::string& tor_co // Read service private key if cached std::pair<bool,std::string> pkf = ReadBinaryFile(GetPrivateKeyFile()); if (pkf.first) { - LogPrint(BCLog::TOR, "tor: Reading cached private key from %s\n", fs::PathToString(GetPrivateKeyFile())); + LogPrint(BCLog::TOR, "Reading cached private key from %s\n", fs::PathToString(GetPrivateKeyFile())); private_key = pkf.second; } } @@ -338,10 +334,77 @@ TorController::~TorController() } } +void TorController::get_socks_cb(TorControlConnection& _conn, const TorControlReply& reply) +{ + // NOTE: We can only get here if -onion is unset + std::string socks_location; + if (reply.code == 250) { + for (const auto& line : reply.lines) { + if (0 == line.compare(0, 20, "net/listeners/socks=")) { + const std::string port_list_str = line.substr(20); + std::vector<std::string> port_list = SplitString(port_list_str, ' '); + + for (auto& portstr : port_list) { + if (portstr.empty()) continue; + if ((portstr[0] == '"' || portstr[0] == '\'') && portstr.size() >= 2 && (*portstr.rbegin() == portstr[0])) { + portstr = portstr.substr(1, portstr.size() - 2); + if (portstr.empty()) continue; + } + socks_location = portstr; + if (0 == portstr.compare(0, 10, "127.0.0.1:")) { + // Prefer localhost - ignore other ports + break; + } + } + } + } + if (!socks_location.empty()) { + LogPrint(BCLog::TOR, "Get SOCKS port command yielded %s\n", socks_location); + } else { + LogPrintf("tor: Get SOCKS port command returned nothing\n"); + } + } else if (reply.code == 510) { // 510 Unrecognized command + LogPrintf("tor: Get SOCKS port command failed with unrecognized command (You probably should upgrade Tor)\n"); + } else { + LogPrintf("tor: Get SOCKS port command failed; error code %d\n", reply.code); + } + + CService resolved; + Assume(!resolved.IsValid()); + if (!socks_location.empty()) { + resolved = LookupNumeric(socks_location, DEFAULT_TOR_SOCKS_PORT); + } + if (!resolved.IsValid()) { + // Fallback to old behaviour + resolved = LookupNumeric("127.0.0.1", DEFAULT_TOR_SOCKS_PORT); + } + + Assume(resolved.IsValid()); + LogPrint(BCLog::TOR, "Configuring onion proxy for %s\n", resolved.ToStringIPPort()); + Proxy addrOnion = Proxy(resolved, true); + SetProxy(NET_ONION, addrOnion); + + const auto onlynets = gArgs.GetArgs("-onlynet"); + + const bool onion_allowed_by_onlynet{ + !gArgs.IsArgSet("-onlynet") || + std::any_of(onlynets.begin(), onlynets.end(), [](const auto& n) { + return ParseNetwork(n) == NET_ONION; + })}; + + if (onion_allowed_by_onlynet) { + // If NET_ONION is reachable, then the below is a noop. + // + // If NET_ONION is not reachable, then none of -proxy or -onion was given. + // Since we are here, then -torcontrol and -torpassword were given. + SetReachable(NET_ONION, true); + } +} + void TorController::add_onion_cb(TorControlConnection& _conn, const TorControlReply& reply) { if (reply.code == 250) { - LogPrint(BCLog::TOR, "tor: ADD_ONION successful\n"); + LogPrint(BCLog::TOR, "ADD_ONION successful\n"); for (const std::string &s : reply.lines) { std::map<std::string,std::string> m = ParseTorReplyMapping(s); std::map<std::string,std::string>::iterator i; @@ -358,9 +421,9 @@ void TorController::add_onion_cb(TorControlConnection& _conn, const TorControlRe return; } service = LookupNumeric(std::string(service_id+".onion"), Params().GetDefaultPort()); - LogPrintf("tor: Got service ID %s, advertising service %s\n", service_id, service.ToString()); + LogPrintfCategory(BCLog::TOR, "Got service ID %s, advertising service %s\n", service_id, service.ToString()); if (WriteBinaryFile(GetPrivateKeyFile(), private_key)) { - LogPrint(BCLog::TOR, "tor: Cached service private key to %s\n", fs::PathToString(GetPrivateKeyFile())); + LogPrint(BCLog::TOR, "Cached service private key to %s\n", fs::PathToString(GetPrivateKeyFile())); } else { LogPrintf("tor: Error writing service private key to %s\n", fs::PathToString(GetPrivateKeyFile())); } @@ -376,30 +439,12 @@ void TorController::add_onion_cb(TorControlConnection& _conn, const TorControlRe void TorController::auth_cb(TorControlConnection& _conn, const TorControlReply& reply) { if (reply.code == 250) { - LogPrint(BCLog::TOR, "tor: Authentication successful\n"); + LogPrint(BCLog::TOR, "Authentication successful\n"); // Now that we know Tor is running setup the proxy for onion addresses // if -onion isn't set to something else. if (gArgs.GetArg("-onion", "") == "") { - CService resolved(LookupNumeric("127.0.0.1", 9050)); - Proxy addrOnion = Proxy(resolved, true); - SetProxy(NET_ONION, addrOnion); - - const auto onlynets = gArgs.GetArgs("-onlynet"); - - const bool onion_allowed_by_onlynet{ - !gArgs.IsArgSet("-onlynet") || - std::any_of(onlynets.begin(), onlynets.end(), [](const auto& n) { - return ParseNetwork(n) == NET_ONION; - })}; - - if (onion_allowed_by_onlynet) { - // If NET_ONION is reachable, then the below is a noop. - // - // If NET_ONION is not reachable, then none of -proxy or -onion was given. - // Since we are here, then -torcontrol and -torpassword were given. - SetReachable(NET_ONION, true); - } + _conn.Command("GETINFO net/listeners/socks", std::bind(&TorController::get_socks_cb, this, std::placeholders::_1, std::placeholders::_2)); } // Finally - now create the service @@ -445,7 +490,7 @@ static std::vector<uint8_t> ComputeResponse(const std::string &key, const std::v void TorController::authchallenge_cb(TorControlConnection& _conn, const TorControlReply& reply) { if (reply.code == 250) { - LogPrint(BCLog::TOR, "tor: SAFECOOKIE authentication challenge successful\n"); + LogPrint(BCLog::TOR, "SAFECOOKIE authentication challenge successful\n"); std::pair<std::string,std::string> l = SplitTorReplyLine(reply.lines[0]); if (l.first == "AUTHCHALLENGE") { std::map<std::string,std::string> m = ParseTorReplyMapping(l.second); @@ -455,7 +500,7 @@ void TorController::authchallenge_cb(TorControlConnection& _conn, const TorContr } std::vector<uint8_t> serverHash = ParseHex(m["SERVERHASH"]); std::vector<uint8_t> serverNonce = ParseHex(m["SERVERNONCE"]); - LogPrint(BCLog::TOR, "tor: AUTHCHALLENGE ServerHash %s ServerNonce %s\n", HexStr(serverHash), HexStr(serverNonce)); + LogPrint(BCLog::TOR, "AUTHCHALLENGE ServerHash %s ServerNonce %s\n", HexStr(serverHash), HexStr(serverNonce)); if (serverNonce.size() != 32) { LogPrintf("tor: ServerNonce is not 32 bytes, as required by spec\n"); return; @@ -492,20 +537,22 @@ void TorController::protocolinfo_cb(TorControlConnection& _conn, const TorContro if (l.first == "AUTH") { std::map<std::string,std::string> m = ParseTorReplyMapping(l.second); std::map<std::string,std::string>::iterator i; - if ((i = m.find("METHODS")) != m.end()) - boost::split(methods, i->second, boost::is_any_of(",")); + if ((i = m.find("METHODS")) != m.end()) { + std::vector<std::string> m_vec = SplitString(i->second, ','); + methods = std::set<std::string>(m_vec.begin(), m_vec.end()); + } if ((i = m.find("COOKIEFILE")) != m.end()) cookiefile = i->second; } else if (l.first == "VERSION") { std::map<std::string,std::string> m = ParseTorReplyMapping(l.second); std::map<std::string,std::string>::iterator i; if ((i = m.find("Tor")) != m.end()) { - LogPrint(BCLog::TOR, "tor: Connected to Tor version %s\n", i->second); + LogPrint(BCLog::TOR, "Connected to Tor version %s\n", i->second); } } } for (const std::string &s : methods) { - LogPrint(BCLog::TOR, "tor: Supported authentication method: %s\n", s); + LogPrint(BCLog::TOR, "Supported authentication method: %s\n", s); } // Prefer NULL, otherwise SAFECOOKIE. If a password is provided, use HASHEDPASSWORD /* Authentication: @@ -515,24 +562,24 @@ void TorController::protocolinfo_cb(TorControlConnection& _conn, const TorContro std::string torpassword = gArgs.GetArg("-torpassword", ""); if (!torpassword.empty()) { if (methods.count("HASHEDPASSWORD")) { - LogPrint(BCLog::TOR, "tor: Using HASHEDPASSWORD authentication\n"); - boost::replace_all(torpassword, "\"", "\\\""); + LogPrint(BCLog::TOR, "Using HASHEDPASSWORD authentication\n"); + ReplaceAll(torpassword, "\"", "\\\""); _conn.Command("AUTHENTICATE \"" + torpassword + "\"", std::bind(&TorController::auth_cb, this, std::placeholders::_1, std::placeholders::_2)); } else { LogPrintf("tor: Password provided with -torpassword, but HASHEDPASSWORD authentication is not available\n"); } } else if (methods.count("NULL")) { - LogPrint(BCLog::TOR, "tor: Using NULL authentication\n"); + LogPrint(BCLog::TOR, "Using NULL authentication\n"); _conn.Command("AUTHENTICATE", std::bind(&TorController::auth_cb, this, std::placeholders::_1, std::placeholders::_2)); } else if (methods.count("SAFECOOKIE")) { // Cookie: hexdump -e '32/1 "%02x""\n"' ~/.tor/control_auth_cookie - LogPrint(BCLog::TOR, "tor: Using SAFECOOKIE authentication, reading cookie authentication from %s\n", cookiefile); + LogPrint(BCLog::TOR, "Using SAFECOOKIE authentication, reading cookie authentication from %s\n", cookiefile); std::pair<bool,std::string> status_cookie = ReadBinaryFile(fs::PathFromString(cookiefile), TOR_COOKIE_SIZE); if (status_cookie.first && status_cookie.second.size() == TOR_COOKIE_SIZE) { // _conn.Command("AUTHENTICATE " + HexStr(status_cookie.second), std::bind(&TorController::auth_cb, this, std::placeholders::_1, std::placeholders::_2)); cookie = std::vector<uint8_t>(status_cookie.second.begin(), status_cookie.second.end()); clientNonce = std::vector<uint8_t>(TOR_NONCE_SIZE, 0); - GetRandBytes(clientNonce.data(), TOR_NONCE_SIZE); + GetRandBytes(clientNonce); _conn.Command("AUTHCHALLENGE SAFECOOKIE " + HexStr(clientNonce), std::bind(&TorController::authchallenge_cb, this, std::placeholders::_1, std::placeholders::_2)); } else { if (status_cookie.first) { @@ -568,7 +615,7 @@ void TorController::disconnected_cb(TorControlConnection& _conn) if (!reconnect) return; - LogPrint(BCLog::TOR, "tor: Not connected to Tor control port %s, trying to reconnect\n", m_tor_control_center); + LogPrint(BCLog::TOR, "Not connected to Tor control port %s, trying to reconnect\n", m_tor_control_center); // Single-shot timer for reconnect. Use exponential backoff. struct timeval time = MillisToTimeval(int64_t(reconnect_timeout * 1000.0)); |