aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/tor.md9
-rw-r--r--src/chainparamsbase.cpp12
-rw-r--r--src/chainparamsbase.h9
-rw-r--r--src/init.cpp41
-rw-r--r--src/net.cpp28
-rw-r--r--src/net.h7
-rw-r--r--src/torcontrol.cpp68
-rw-r--r--src/torcontrol.h7
8 files changed, 125 insertions, 56 deletions
diff --git a/doc/tor.md b/doc/tor.md
index 17807856e5..12b5f70245 100644
--- a/doc/tor.md
+++ b/doc/tor.md
@@ -45,11 +45,12 @@ config file): *Needed for Tor version 0.2.7.0 and older versions of Tor only. Fo
versions of Tor see [Section 3](#3-automatically-listen-on-tor).*
HiddenServiceDir /var/lib/tor/bitcoin-service/
- HiddenServicePort 8333 127.0.0.1:8333
- HiddenServicePort 18333 127.0.0.1:18333
+ HiddenServicePort 8333 127.0.0.1:8334
+ HiddenServicePort 18333 127.0.0.1:18334
-The directory can be different of course, but (both) port numbers should be equal to
-your bitcoind's P2P listen port (8333 by default).
+The directory can be different of course, but virtual port numbers should be equal to
+your bitcoind's P2P listen port (8333 by default), and target addresses and ports
+should be equal to binding address and port for inbound Tor connections (127.0.0.1:8334 by default).
-externalip=X You can tell bitcoin about its publicly reachable address using
this option, and this can be a .onion address. Given the above
diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp
index f19e72e9ab..603969aaea 100644
--- a/src/chainparamsbase.cpp
+++ b/src/chainparamsbase.cpp
@@ -37,16 +37,20 @@ const CBaseChainParams& BaseParams()
return *globalChainBaseParams;
}
+/**
+ * Port numbers for incoming Tor connections (8334, 18334, 38334, 18445) have
+ * been chosen arbitrarily to keep ranges of used ports tight.
+ */
std::unique_ptr<CBaseChainParams> CreateBaseChainParams(const std::string& chain)
{
if (chain == CBaseChainParams::MAIN) {
- return MakeUnique<CBaseChainParams>("", 8332);
+ return MakeUnique<CBaseChainParams>("", 8332, 8334);
} else if (chain == CBaseChainParams::TESTNET) {
- return MakeUnique<CBaseChainParams>("testnet3", 18332);
+ return MakeUnique<CBaseChainParams>("testnet3", 18332, 18334);
} else if (chain == CBaseChainParams::SIGNET) {
- return MakeUnique<CBaseChainParams>("signet", 38332);
+ return MakeUnique<CBaseChainParams>("signet", 38332, 38334);
} else if (chain == CBaseChainParams::REGTEST) {
- return MakeUnique<CBaseChainParams>("regtest", 18443);
+ return MakeUnique<CBaseChainParams>("regtest", 18443, 18445);
}
throw std::runtime_error(strprintf("%s: Unknown chain %s.", __func__, chain));
}
diff --git a/src/chainparamsbase.h b/src/chainparamsbase.h
index 9852446b3c..9b4ae2f7ab 100644
--- a/src/chainparamsbase.h
+++ b/src/chainparamsbase.h
@@ -26,13 +26,16 @@ public:
///@}
const std::string& DataDir() const { return strDataDir; }
- int RPCPort() const { return nRPCPort; }
+ uint16_t RPCPort() const { return m_rpc_port; }
+ uint16_t OnionServiceTargetPort() const { return m_onion_service_target_port; }
CBaseChainParams() = delete;
- CBaseChainParams(const std::string& data_dir, int rpc_port) : nRPCPort(rpc_port), strDataDir(data_dir) {}
+ CBaseChainParams(const std::string& data_dir, uint16_t rpc_port, uint16_t onion_service_target_port)
+ : m_rpc_port(rpc_port), m_onion_service_target_port(onion_service_target_port), strDataDir(data_dir) {}
private:
- int nRPCPort;
+ const uint16_t m_rpc_port;
+ const uint16_t m_onion_service_target_port;
std::string strDataDir;
};
diff --git a/src/init.cpp b/src/init.cpp
index edf2a79998..1387d6b982 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -442,7 +442,7 @@ void SetupServerArgs(NodeContext& node)
argsman.AddArg("-addnode=<ip>", "Add a node to connect to and attempt to keep the connection open (see the `addnode` RPC command help for more info). This option can be specified multiple times to add multiple nodes.", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
argsman.AddArg("-asmap=<file>", strprintf("Specify asn mapping used for bucketing of the peers (default: %s). Relative paths will be prefixed by the net-specific datadir location.", DEFAULT_ASMAP_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-bantime=<n>", strprintf("Default duration (in seconds) of manually configured bans (default: %u)", DEFAULT_MISBEHAVING_BANTIME), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
- argsman.AddArg("-bind=<addr>", "Bind to given address and always listen on it. Use [host]:port notation for IPv6", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
+ argsman.AddArg("-bind=<addr>[:<port>][=onion]", strprintf("Bind to given address and always listen on it (default: 0.0.0.0). Use [host]:port notation for IPv6. Append =onion to tag any incoming connections to that address and port as incoming Tor connections (default: 127.0.0.1:%u=onion, testnet: 127.0.0.1:%u=onion, signet: 127.0.0.1:%u=onion, regtest: 127.0.0.1:%u=onion)", defaultBaseParams->OnionServiceTargetPort(), testnetBaseParams->OnionServiceTargetPort(), signetBaseParams->OnionServiceTargetPort(), regtestBaseParams->OnionServiceTargetPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
argsman.AddArg("-connect=<ip>", "Connect only to the specified node; -noconnect disables automatic connections (the rules for this peer are the same as for -addnode). This option can be specified multiple times to connect to multiple nodes.", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION);
argsman.AddArg("-discover", "Discover own IP addresses (default: 1 when listening and no -externalip or -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-dns", strprintf("Allow DNS lookups for -addnode, -seednode and -connect (default: %u)", DEFAULT_NAME_LOOKUP), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
@@ -1918,9 +1918,6 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
}
LogPrintf("nBestHeight = %d\n", chain_active_height);
- if (args.GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION))
- StartTorControl();
-
Discover();
// Map ports with UPnP
@@ -1947,13 +1944,39 @@ bool AppInitMain(const util::Ref& context, NodeContext& node, interfaces::BlockA
connOptions.nMaxOutboundLimit = nMaxOutboundLimit;
connOptions.m_peer_connect_timeout = peer_connect_timeout;
- for (const std::string& strBind : args.GetArgs("-bind")) {
- CService addrBind;
- if (!Lookup(strBind, addrBind, GetListenPort(), false)) {
- return InitError(ResolveErrMsg("bind", strBind));
+ for (const std::string& bind_arg : args.GetArgs("-bind")) {
+ CService bind_addr;
+ const size_t index = bind_arg.rfind('=');
+ if (index == std::string::npos) {
+ if (Lookup(bind_arg, bind_addr, GetListenPort(), false)) {
+ connOptions.vBinds.push_back(bind_addr);
+ continue;
+ }
+ } else {
+ const std::string network_type = bind_arg.substr(index + 1);
+ if (network_type == "onion") {
+ const std::string truncated_bind_arg = bind_arg.substr(0, index);
+ if (Lookup(truncated_bind_arg, bind_addr, BaseParams().OnionServiceTargetPort(), false)) {
+ connOptions.onion_binds.push_back(bind_addr);
+ continue;
+ }
+ }
}
- connOptions.vBinds.push_back(addrBind);
+ return InitError(ResolveErrMsg("bind", bind_arg));
}
+
+ if (connOptions.onion_binds.empty()) {
+ connOptions.onion_binds.push_back(DefaultOnionServiceTarget());
+ }
+
+ if (args.GetBoolArg("-listenonion", DEFAULT_LISTEN_ONION)) {
+ const auto bind_addr = connOptions.onion_binds.front();
+ if (connOptions.onion_binds.size() > 1) {
+ InitWarning(strprintf(_("More than one onion bind address is provided. Using %s for the automatically created Tor onion service."), bind_addr.ToStringIPPort()));
+ }
+ StartTorControl(bind_addr);
+ }
+
for (const std::string& strBind : args.GetArgs("-whitebind")) {
NetWhitebindPermissions whitebind;
bilingual_str error;
diff --git a/src/net.cpp b/src/net.cpp
index 7dec4abfb9..95ba6da819 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -83,6 +83,11 @@ enum BindFlags {
BF_NONE = 0,
BF_EXPLICIT = (1U << 0),
BF_REPORT_ERROR = (1U << 1),
+ /**
+ * Do not call AddLocal() for our special addresses, e.g., for incoming
+ * Tor connections, to prevent gossiping them over the network.
+ */
+ BF_DONT_ADVERTISE = (1U << 2),
};
// The set of sockets cannot be modified while waiting
@@ -2241,10 +2246,6 @@ bool CConnman::BindListenPort(const CService& addrBind, bilingual_str& strError,
}
vhListenSocket.push_back(ListenSocket(hListenSocket, permissions));
-
- if (addrBind.IsRoutable() && fDiscover && (permissions & PF_NOBAN) == 0)
- AddLocal(addrBind, LOCAL_BIND);
-
return true;
}
@@ -2338,10 +2339,18 @@ bool CConnman::Bind(const CService &addr, unsigned int flags, NetPermissionFlags
}
return false;
}
+
+ if (addr.IsRoutable() && fDiscover && !(flags & BF_DONT_ADVERTISE) && !(permissions & PF_NOBAN)) {
+ AddLocal(addr, LOCAL_BIND);
+ }
+
return true;
}
-bool CConnman::InitBinds(const std::vector<CService>& binds, const std::vector<NetWhitebindPermissions>& whiteBinds)
+bool CConnman::InitBinds(
+ const std::vector<CService>& binds,
+ const std::vector<NetWhitebindPermissions>& whiteBinds,
+ const std::vector<CService>& onion_binds)
{
bool fBound = false;
for (const auto& addrBind : binds) {
@@ -2352,11 +2361,16 @@ bool CConnman::InitBinds(const std::vector<CService>& binds, const std::vector<N
}
if (binds.empty() && whiteBinds.empty()) {
struct in_addr inaddr_any;
- inaddr_any.s_addr = INADDR_ANY;
+ inaddr_any.s_addr = htonl(INADDR_ANY);
struct in6_addr inaddr6_any = IN6ADDR_ANY_INIT;
fBound |= Bind(CService(inaddr6_any, GetListenPort()), BF_NONE, NetPermissionFlags::PF_NONE);
fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE, NetPermissionFlags::PF_NONE);
}
+
+ for (const auto& addr_bind : onion_binds) {
+ fBound |= Bind(addr_bind, BF_EXPLICIT | BF_DONT_ADVERTISE, NetPermissionFlags::PF_NONE);
+ }
+
return fBound;
}
@@ -2375,7 +2389,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
nMaxOutboundCycleStartTime = 0;
}
- if (fListen && !InitBinds(connOptions.vBinds, connOptions.vWhiteBinds)) {
+ if (fListen && !InitBinds(connOptions.vBinds, connOptions.vWhiteBinds, connOptions.onion_binds)) {
if (clientInterface) {
clientInterface->ThreadSafeMessageBox(
_("Failed to listen on any port. Use -listen=0 if you want this."),
diff --git a/src/net.h b/src/net.h
index fb68827144..1051511f54 100644
--- a/src/net.h
+++ b/src/net.h
@@ -220,6 +220,7 @@ public:
std::vector<NetWhitelistPermissions> vWhitelistedRange;
std::vector<NetWhitebindPermissions> vWhiteBinds;
std::vector<CService> vBinds;
+ std::vector<CService> onion_binds;
bool m_use_addrman_outgoing = true;
std::vector<std::string> m_specified_outgoing;
std::vector<std::string> m_added_nodes;
@@ -417,7 +418,11 @@ private:
bool BindListenPort(const CService& bindAddr, bilingual_str& strError, NetPermissionFlags permissions);
bool Bind(const CService& addr, unsigned int flags, NetPermissionFlags permissions);
- bool InitBinds(const std::vector<CService>& binds, const std::vector<NetWhitebindPermissions>& whiteBinds);
+ bool InitBinds(
+ const std::vector<CService>& binds,
+ const std::vector<NetWhitebindPermissions>& whiteBinds,
+ const std::vector<CService>& onion_binds);
+
void ThreadOpenAddedConnections();
void AddAddrFetch(const std::string& strDest);
void ProcessAddrFetch();
diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp
index 5d56d1ff89..666e7a37a5 100644
--- a/src/torcontrol.cpp
+++ b/src/torcontrol.cpp
@@ -3,13 +3,16 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
-#include <chainparams.h>
#include <torcontrol.h>
-#include <util/strencodings.h>
-#include <netbase.h>
+
+#include <chainparams.h>
+#include <chainparamsbase.h>
+#include <crypto/hmac_sha256.h>
#include <net.h>
+#include <netaddress.h>
+#include <netbase.h>
+#include <util/strencodings.h>
#include <util/system.h>
-#include <crypto/hmac_sha256.h>
#include <vector>
#include <deque>
@@ -81,12 +84,12 @@ public:
/**
* 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 established.
* disconnected is a handler that is called when the connection is broken.
* Return true on success.
*/
- bool Connect(const std::string &target, const ConnectionCB& connected, const ConnectionCB& disconnected);
+ bool Connect(const std::string& tor_control_center, const ConnectionCB& connected, const ConnectionCB& disconnected);
/**
* Disconnect from Tor control port.
@@ -193,16 +196,16 @@ void TorControlConnection::eventcb(struct bufferevent *bev, short what, void *ct
}
}
-bool TorControlConnection::Connect(const std::string &target, const ConnectionCB& _connected, const ConnectionCB& _disconnected)
+bool TorControlConnection::Connect(const std::string& tor_control_center, const ConnectionCB& _connected, const ConnectionCB& _disconnected)
{
if (b_conn)
Disconnect();
- // Parse target address:port
+ // Parse tor_control_center address:port
struct sockaddr_storage 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, &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;
}
@@ -215,9 +218,9 @@ bool TorControlConnection::Connect(const std::string &target, const ConnectionCB
this->connected = _connected;
this->disconnected = _disconnected;
- // Finally, connect to target
+ // Finally, connect to tor_control_center
if (bufferevent_socket_connect(b_conn, (struct sockaddr*)&connect_to_addr, 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 true;
@@ -410,7 +413,7 @@ static bool WriteBinaryFile(const fs::path &filename, const std::string &data)
class TorController
{
public:
- TorController(struct event_base* base, const std::string& target);
+ TorController(struct event_base* base, const std::string& tor_control_center, const CService& target);
~TorController();
/** Get name of file to store private key in */
@@ -420,7 +423,7 @@ public:
void Reconnect();
private:
struct event_base* base;
- std::string target;
+ const std::string m_tor_control_center;
TorControlConnection conn;
std::string private_key;
std::string service_id;
@@ -428,6 +431,7 @@ private:
struct event *reconnect_ev;
float reconnect_timeout;
CService service;
+ const CService m_target;
/** Cookie for SAFECOOKIE auth */
std::vector<uint8_t> cookie;
/** ClientNonce for SAFECOOKIE auth */
@@ -450,18 +454,19 @@ private:
static void reconnect_cb(evutil_socket_t fd, short what, void *arg);
};
-TorController::TorController(struct event_base* _base, const std::string& _target):
+TorController::TorController(struct event_base* _base, const std::string& tor_control_center, const CService& target):
base(_base),
- target(_target), conn(base), reconnect(true), reconnect_ev(0),
- reconnect_timeout(RECONNECT_TIMEOUT_START)
+ m_tor_control_center(tor_control_center), conn(base), reconnect(true), reconnect_ev(0),
+ reconnect_timeout(RECONNECT_TIMEOUT_START),
+ m_target(target)
{
reconnect_ev = event_new(base, -1, 0, reconnect_cb, this);
if (!reconnect_ev)
LogPrintf("tor: Failed to create event for reconnection: out of memory?\n");
// Start connection attempts immediately
- if (!conn.Connect(_target, std::bind(&TorController::connected_cb, this, std::placeholders::_1),
+ if (!conn.Connect(m_tor_control_center, std::bind(&TorController::connected_cb, this, std::placeholders::_1),
std::bind(&TorController::disconnected_cb, this, std::placeholders::_1) )) {
- LogPrintf("tor: Initiating connection to Tor control port %s failed\n", _target);
+ LogPrintf("tor: Initiating connection to Tor control port %s failed\n", m_tor_control_center);
}
// Read service private key if cached
std::pair<bool,std::string> pkf = ReadBinaryFile(GetPrivateKeyFile());
@@ -536,7 +541,7 @@ void TorController::auth_cb(TorControlConnection& _conn, const TorControlReply&
private_key = "NEW:RSA1024"; // Explicitly request RSA1024 - see issue #9214
// Request onion service, redirect port.
// Note that the 'virtual' port is always the default port to avoid decloaking nodes using other ports.
- _conn.Command(strprintf("ADD_ONION %s Port=%i,127.0.0.1:%i", private_key, Params().GetDefaultPort(), GetListenPort()),
+ _conn.Command(strprintf("ADD_ONION %s Port=%i,%s", private_key, Params().GetDefaultPort(), m_target.ToStringIPPort()),
std::bind(&TorController::add_onion_cb, this, std::placeholders::_1, std::placeholders::_2));
} else {
LogPrintf("tor: Authentication failed\n");
@@ -696,7 +701,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", target);
+ LogPrint(BCLog::TOR, "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));
@@ -710,9 +715,9 @@ void TorController::Reconnect()
/* Try to reconnect and reestablish if we get booted - for example, Tor
* may be restarting.
*/
- if (!conn.Connect(target, std::bind(&TorController::connected_cb, this, std::placeholders::_1),
+ if (!conn.Connect(m_tor_control_center, std::bind(&TorController::connected_cb, this, std::placeholders::_1),
std::bind(&TorController::disconnected_cb, this, std::placeholders::_1) )) {
- LogPrintf("tor: Re-initiating connection to Tor control port %s failed\n", target);
+ LogPrintf("tor: Re-initiating connection to Tor control port %s failed\n", m_tor_control_center);
}
}
@@ -731,14 +736,14 @@ void TorController::reconnect_cb(evutil_socket_t fd, short what, void *arg)
static struct event_base *gBase;
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);
}
-void StartTorControl()
+void StartTorControl(CService onion_service_target)
{
assert(!gBase);
#ifdef WIN32
@@ -752,7 +757,9 @@ void StartTorControl()
return;
}
- torControlThread = std::thread(std::bind(&TraceThread<void (*)()>, "torcontrol", &TorControlThread));
+ torControlThread = std::thread(&TraceThread<std::function<void()>>, "torcontrol", [onion_service_target] {
+ TorControlThread(onion_service_target);
+ });
}
void InterruptTorControl()
@@ -773,3 +780,10 @@ void StopTorControl()
gBase = nullptr;
}
}
+
+CService DefaultOnionServiceTarget()
+{
+ struct in_addr onion_service_target;
+ onion_service_target.s_addr = htonl(INADDR_LOOPBACK);
+ return {onion_service_target, BaseParams().OnionServiceTargetPort()};
+}
diff --git a/src/torcontrol.h b/src/torcontrol.h
index 474a4d87d9..71a6960e54 100644
--- a/src/torcontrol.h
+++ b/src/torcontrol.h
@@ -8,12 +8,17 @@
#ifndef BITCOIN_TORCONTROL_H
#define BITCOIN_TORCONTROL_H
+#include <string>
+
+class CService;
extern const std::string DEFAULT_TOR_CONTROL;
static const bool DEFAULT_LISTEN_ONION = true;
-void StartTorControl();
+void StartTorControl(CService onion_service_target);
void InterruptTorControl();
void StopTorControl();
+CService DefaultOnionServiceTarget();
+
#endif /* BITCOIN_TORCONTROL_H */