Skip to content

Commit 2948d6d

Browse files
committed
Merge bitcoin/bitcoin#15423: torcontrol: Query Tor for correct -onion configuration
b2774fc torcontrol: Query Tor for correct -onion configuration (Luke Dashjr) Pull request description: Currently, we just assume any running Tor instance provides localhost port 9050 for SOCKS, and configure `-onion` accordingly when we get a Tor control connection. This actually queries the Tor node for its SOCKS listeners, and uses the configured port instead. For backward compatibility, it falls back to localhost:9050 if it can't get any better port info. I'm not sure if that's the correct action to take when the Tor daemon explicitly says there are no ports listening... ACKs for top commit: laanwj: Tested ACK (FreeBSD) b2774fc vasild: ACK b2774fc jonatack: ACK b2774fc review, rebased to master, debug build, ran unit tests, tested happy path only Tree-SHA512: 2fa93a3cf0cb675801d1b51322ce953ea9b2317f78154a53b603244d74252f434cc1eaa5ae48cb3fe6bdc4ce984a6d976ff95bb046f7933b9740332942378c02
2 parents 44dbf91 + b2774fc commit 2948d6d

File tree

2 files changed

+71
-19
lines changed

2 files changed

+71
-19
lines changed

src/torcontrol.cpp

Lines changed: 69 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ static const float RECONNECT_TIMEOUT_EXP = 1.5;
5353
* this is belt-and-suspenders sanity limit to prevent memory exhaustion.
5454
*/
5555
static const int MAX_LINE_LENGTH = 100000;
56+
static const uint16_t DEFAULT_TOR_SOCKS_PORT = 9050;
5657

5758
/****** Low-level TorControlConnection ********/
5859

@@ -338,6 +339,73 @@ TorController::~TorController()
338339
}
339340
}
340341

342+
void TorController::get_socks_cb(TorControlConnection& _conn, const TorControlReply& reply)
343+
{
344+
// NOTE: We can only get here if -onion is unset
345+
std::string socks_location;
346+
if (reply.code == 250) {
347+
for (const auto& line : reply.lines) {
348+
if (0 == line.compare(0, 20, "net/listeners/socks=")) {
349+
const std::string port_list_str = line.substr(20);
350+
std::vector<std::string> port_list;
351+
boost::split(port_list, port_list_str, boost::is_any_of(" "));
352+
for (auto& portstr : port_list) {
353+
if (portstr.empty()) continue;
354+
if ((portstr[0] == '"' || portstr[0] == '\'') && portstr.size() >= 2 && (*portstr.rbegin() == portstr[0])) {
355+
portstr = portstr.substr(1, portstr.size() - 2);
356+
if (portstr.empty()) continue;
357+
}
358+
socks_location = portstr;
359+
if (0 == portstr.compare(0, 10, "127.0.0.1:")) {
360+
// Prefer localhost - ignore other ports
361+
break;
362+
}
363+
}
364+
}
365+
}
366+
if (!socks_location.empty()) {
367+
LogPrint(BCLog::TOR, "tor: Get SOCKS port command yielded %s\n", socks_location);
368+
} else {
369+
LogPrintf("tor: Get SOCKS port command returned nothing\n");
370+
}
371+
} else if (reply.code == 510) { // 510 Unrecognized command
372+
LogPrintf("tor: Get SOCKS port command failed with unrecognized command (You probably should upgrade Tor)\n");
373+
} else {
374+
LogPrintf("tor: Get SOCKS port command failed; error code %d\n", reply.code);
375+
}
376+
377+
CService resolved;
378+
Assume(!resolved.IsValid());
379+
if (!socks_location.empty()) {
380+
resolved = LookupNumeric(socks_location, DEFAULT_TOR_SOCKS_PORT);
381+
}
382+
if (!resolved.IsValid()) {
383+
// Fallback to old behaviour
384+
resolved = LookupNumeric("127.0.0.1", DEFAULT_TOR_SOCKS_PORT);
385+
}
386+
387+
Assume(resolved.IsValid());
388+
LogPrint(BCLog::TOR, "tor: Configuring onion proxy for %s\n", resolved.ToStringIPPort());
389+
Proxy addrOnion = Proxy(resolved, true);
390+
SetProxy(NET_ONION, addrOnion);
391+
392+
const auto onlynets = gArgs.GetArgs("-onlynet");
393+
394+
const bool onion_allowed_by_onlynet{
395+
!gArgs.IsArgSet("-onlynet") ||
396+
std::any_of(onlynets.begin(), onlynets.end(), [](const auto& n) {
397+
return ParseNetwork(n) == NET_ONION;
398+
})};
399+
400+
if (onion_allowed_by_onlynet) {
401+
// If NET_ONION is reachable, then the below is a noop.
402+
//
403+
// If NET_ONION is not reachable, then none of -proxy or -onion was given.
404+
// Since we are here, then -torcontrol and -torpassword were given.
405+
SetReachable(NET_ONION, true);
406+
}
407+
}
408+
341409
void TorController::add_onion_cb(TorControlConnection& _conn, const TorControlReply& reply)
342410
{
343411
if (reply.code == 250) {
@@ -381,25 +449,7 @@ void TorController::auth_cb(TorControlConnection& _conn, const TorControlReply&
381449
// Now that we know Tor is running setup the proxy for onion addresses
382450
// if -onion isn't set to something else.
383451
if (gArgs.GetArg("-onion", "") == "") {
384-
CService resolved(LookupNumeric("127.0.0.1", 9050));
385-
Proxy addrOnion = Proxy(resolved, true);
386-
SetProxy(NET_ONION, addrOnion);
387-
388-
const auto onlynets = gArgs.GetArgs("-onlynet");
389-
390-
const bool onion_allowed_by_onlynet{
391-
!gArgs.IsArgSet("-onlynet") ||
392-
std::any_of(onlynets.begin(), onlynets.end(), [](const auto& n) {
393-
return ParseNetwork(n) == NET_ONION;
394-
})};
395-
396-
if (onion_allowed_by_onlynet) {
397-
// If NET_ONION is reachable, then the below is a noop.
398-
//
399-
// If NET_ONION is not reachable, then none of -proxy or -onion was given.
400-
// Since we are here, then -torcontrol and -torpassword were given.
401-
SetReachable(NET_ONION, true);
402-
}
452+
_conn.Command("GETINFO net/listeners/socks", std::bind(&TorController::get_socks_cb, this, std::placeholders::_1, std::placeholders::_2));
403453
}
404454

405455
// Finally - now create the service

src/torcontrol.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,8 @@ class TorController
140140
std::vector<uint8_t> clientNonce;
141141

142142
public:
143+
/** Callback for GETINFO net/listeners/socks result */
144+
void get_socks_cb(TorControlConnection& conn, const TorControlReply& reply);
143145
/** Callback for ADD_ONION result */
144146
void add_onion_cb(TorControlConnection& conn, const TorControlReply& reply);
145147
/** Callback for AUTHENTICATE result */

0 commit comments

Comments
 (0)