Skip to content

Commit

Permalink
Merge bitcoin#22834: net: respect -onlynet= when making outbound conn…
Browse files Browse the repository at this point in the history
…ections

0eea83a scripted-diff: rename `proxyType` to `Proxy` (Vasil Dimov)
e53a850 net: respect -onlynet= when making outbound connections (Vasil Dimov)

Pull request description:

  Do not make outbound connections to hosts which belong to a network
  which is restricted by `-onlynet`.

  This applies to hosts that are automatically chosen to connect to and to
  anchors.

  This does not apply to hosts given to `-connect`, `-addnode`,
  `addnode` RPC, dns seeds, `-seednode`.

  Fixes bitcoin#13378
  Fixes bitcoin#22647
  Supersedes bitcoin#22651

ACKs for top commit:
  naumenkogs:
    utACK 0eea83a
  prayank23:
    reACK bitcoin@0eea83a
  jonatack:
    ACK 0eea83a code review, rebased to master, debug built, and did some manual testing with various config options on signet

Tree-SHA512: 37d68b449dd6d2715843fc84d85f48fa2508be40ea105a7f4a28443b318d0b6bd39e3b2ca2a6186f2913836adf08d91038a8b142928e1282130f39ac81aa741b
  • Loading branch information
laanwj committed Mar 1, 2022
2 parents 024b8e1 + 0eea83a commit 848b116
Show file tree
Hide file tree
Showing 14 changed files with 72 additions and 52 deletions.
6 changes: 1 addition & 5 deletions doc/i2p.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,7 @@ logging` for more information.

Make outgoing connections only to I2P addresses. Incoming connections are not
affected by this option. It can be specified multiple times to allow multiple
network types, e.g. onlynet=ipv4, onlynet=ipv6, onlynet=onion, onlynet=i2p.

Warning: if you use -onlynet with values other than onion, and the -onion or
-proxy option is set, then outgoing onion connections will still be made; use
-noonion or -onion=0 to disable outbound onion connections in this case.
network types, e.g. onlynet=onion, onlynet=i2p.

I2P support was added to Bitcoin Core in version 22.0 and there may be fewer I2P
peers than Tor or IP ones. Therefore, using I2P alone without other networks may
Expand Down
6 changes: 6 additions & 0 deletions doc/release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,12 @@ Updated settings
E.g. `-maxuploadtarget=500g`. No whitespace, +- or fractions allowed.
Default is `M` if no suffix provided. (#23249)

- If `-proxy=` is given together with `-noonion` then the provided proxy will
not be set as a proxy for reaching the Tor network. So it will not be
possible to open manual connections to the Tor network for example with the
`addnode` RPC. To mimic the old behavior use `-proxy=` together with
`-onlynet=` listing all relevant networks except `onion`. (#22834)

Tools and Utilities
-------------------

Expand Down
6 changes: 1 addition & 5 deletions doc/tor.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,7 @@ outgoing connections, but more is possible.
-onlynet=onion Make outgoing connections only to .onion addresses. Incoming
connections are not affected by this option. This option can be
specified multiple times to allow multiple network types, e.g.
onlynet=ipv4, onlynet=ipv6, onlynet=onion, onlynet=i2p.
Warning: if you use -onlynet with values other than onion, and
the -onion or -proxy option is set, then outgoing onion
connections will still be made; use -noonion or -onion=0 to
disable outbound onion connections in this case.
onlynet=onion, onlynet=i2p.

In a typical situation, this suffices to run behind a Tor proxy:

Expand Down
37 changes: 22 additions & 15 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@ void SetupServerArgs(ArgsManager& argsman)
argsman.AddArg("-onion=<ip:port>", "Use separate SOCKS5 proxy to reach peers via Tor onion services, set -noonion to disable (default: -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-i2psam=<ip:port>", "I2P SAM proxy to reach I2P peers and accept I2P connections (default: none)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-i2pacceptincoming", "If set and -i2psam is also set then incoming I2P connections are accepted via the SAM proxy. If this is not set but -i2psam is set then only outgoing connections will be made to the I2P network. Ignored if -i2psam is not set. Listening for incoming I2P connections is done through the SAM proxy, not by binding to a local address and port (default: 1)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-onlynet=<net>", "Make outgoing connections only through network <net> (" + Join(GetNetworkNames(), ", ") + "). Incoming connections are not affected by this option. This option can be specified multiple times to allow multiple networks. Warning: if it is used with non-onion networks and the -onion or -proxy option is set, then outbound onion connections will still be made; use -noonion or -onion=0 to disable outbound onion connections in this case.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-onlynet=<net>", "Make automatic outgoing connections only through network <net> (" + Join(GetNetworkNames(), ", ") + "). Incoming connections are not affected by this option. This option can be specified multiple times to allow multiple networks.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-peerbloomfilters", strprintf("Support filtering of blocks and transaction with bloom filters (default: %u)", DEFAULT_PEERBLOOMFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-peerblockfilters", strprintf("Serve compact block filters to peers per BIP 157 (default: %u)", DEFAULT_PEERBLOCKFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg("-permitbaremultisig", strprintf("Relay non-P2SH multisig (default: %u)", DEFAULT_PERMIT_BAREMULTISIG), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
Expand Down Expand Up @@ -1317,27 +1317,27 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
// Check for host lookup allowed before parsing any network related parameters
fNameLookup = args.GetBoolArg("-dns", DEFAULT_NAME_LOOKUP);

Proxy onion_proxy;

bool proxyRandomize = args.GetBoolArg("-proxyrandomize", DEFAULT_PROXYRANDOMIZE);
// -proxy sets a proxy for all outgoing network traffic
// -noproxy (or -proxy=0) as well as the empty string can be used to not set a proxy, this is the default
std::string proxyArg = args.GetArg("-proxy", "");
SetReachable(NET_ONION, false);
if (proxyArg != "" && proxyArg != "0") {
CService proxyAddr;
if (!Lookup(proxyArg, proxyAddr, 9050, fNameLookup)) {
return InitError(strprintf(_("Invalid -proxy address or hostname: '%s'"), proxyArg));
}

proxyType addrProxy = proxyType(proxyAddr, proxyRandomize);
Proxy addrProxy = Proxy(proxyAddr, proxyRandomize);
if (!addrProxy.IsValid())
return InitError(strprintf(_("Invalid -proxy address or hostname: '%s'"), proxyArg));

SetProxy(NET_IPV4, addrProxy);
SetProxy(NET_IPV6, addrProxy);
SetProxy(NET_ONION, addrProxy);
SetProxy(NET_CJDNS, addrProxy);
SetNameProxy(addrProxy);
SetReachable(NET_ONION, true); // by default, -proxy sets onion as reachable, unless -noonion later
onion_proxy = addrProxy;
}

// -onion can be used to set only a proxy for .onion, or override normal proxy for .onion addresses
Expand All @@ -1346,18 +1346,26 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
std::string onionArg = args.GetArg("-onion", "");
if (onionArg != "") {
if (onionArg == "0") { // Handle -noonion/-onion=0
SetReachable(NET_ONION, false);
onion_proxy = Proxy{};
} else {
CService onionProxy;
if (!Lookup(onionArg, onionProxy, 9050, fNameLookup)) {
CService addr;
if (!Lookup(onionArg, addr, 9050, fNameLookup) || !addr.IsValid()) {
return InitError(strprintf(_("Invalid -onion address or hostname: '%s'"), onionArg));
}
proxyType addrOnion = proxyType(onionProxy, proxyRandomize);
if (!addrOnion.IsValid())
return InitError(strprintf(_("Invalid -onion address or hostname: '%s'"), onionArg));
SetProxy(NET_ONION, addrOnion);
SetReachable(NET_ONION, true);
onion_proxy = Proxy{addr, proxyRandomize};
}
}

if (onion_proxy.IsValid()) {
SetProxy(NET_ONION, onion_proxy);
} else {
if (args.IsArgSet("-onlynet") && IsReachable(NET_ONION)) {
return InitError(
_("Outbound connections restricted to Tor (-onlynet=onion) but the proxy for "
"reaching the Tor network is not provided (no -proxy= and no -onion= given) or "
"it is explicitly forbidden (-onion=0)"));
}
SetReachable(NET_ONION, false);
}

for (const std::string& strAddr : args.GetArgs("-externalip")) {
Expand Down Expand Up @@ -1752,8 +1760,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
if (!Lookup(i2psam_arg, addr, 7656, fNameLookup) || !addr.IsValid()) {
return InitError(strprintf(_("Invalid -i2psam address or hostname: '%s'"), i2psam_arg));
}
SetReachable(NET_I2P, true);
SetProxy(NET_I2P, proxyType{addr});
SetProxy(NET_I2P, Proxy{addr});
} else {
SetReachable(NET_I2P, false);
}
Expand Down
4 changes: 2 additions & 2 deletions src/interfaces/node.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class CNodeStats;
class Coin;
class RPCTimerInterface;
class UniValue;
class proxyType;
class Proxy;
enum class SynchronizationState;
enum class TransactionError;
struct CNodeStateStats;
Expand Down Expand Up @@ -101,7 +101,7 @@ class Node
virtual void mapPort(bool use_upnp, bool use_natpmp) = 0;

//! Get proxy.
virtual bool getProxy(Network net, proxyType& proxy_info) = 0;
virtual bool getProxy(Network net, Proxy& proxy_info) = 0;

//! Get number of connections.
virtual size_t getNodeCount(ConnectionDirection flags) = 0;
Expand Down
4 changes: 2 additions & 2 deletions src/net.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
// Connect
bool connected = false;
std::unique_ptr<Sock> sock;
proxyType proxy;
Proxy proxy;
CAddress addr_bind;
assert(!addr_bind.IsValid());

Expand Down Expand Up @@ -2559,7 +2559,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
return false;
}

proxyType i2p_sam;
Proxy i2p_sam;
if (GetProxy(NET_I2P, i2p_sam)) {
m_i2p_sam_session = std::make_unique<i2p::sam::Session>(gArgs.GetDataDirNet() / "i2p_private_key",
i2p_sam.proxy, &interruptNet);
Expand Down
14 changes: 7 additions & 7 deletions src/netbase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@

// Settings
static Mutex g_proxyinfo_mutex;
static proxyType proxyInfo[NET_MAX] GUARDED_BY(g_proxyinfo_mutex);
static proxyType nameProxy GUARDED_BY(g_proxyinfo_mutex);
static Proxy proxyInfo[NET_MAX] GUARDED_BY(g_proxyinfo_mutex);
static Proxy nameProxy GUARDED_BY(g_proxyinfo_mutex);
int nConnectTimeout = DEFAULT_CONNECT_TIMEOUT;
bool fNameLookup = DEFAULT_NAME_LOOKUP;

Expand Down Expand Up @@ -605,7 +605,7 @@ bool ConnectSocketDirectly(const CService &addrConnect, const Sock& sock, int nT
return true;
}

bool SetProxy(enum Network net, const proxyType &addrProxy) {
bool SetProxy(enum Network net, const Proxy &addrProxy) {
assert(net >= 0 && net < NET_MAX);
if (!addrProxy.IsValid())
return false;
Expand All @@ -614,7 +614,7 @@ bool SetProxy(enum Network net, const proxyType &addrProxy) {
return true;
}

bool GetProxy(enum Network net, proxyType &proxyInfoOut) {
bool GetProxy(enum Network net, Proxy &proxyInfoOut) {
assert(net >= 0 && net < NET_MAX);
LOCK(g_proxyinfo_mutex);
if (!proxyInfo[net].IsValid())
Expand All @@ -623,15 +623,15 @@ bool GetProxy(enum Network net, proxyType &proxyInfoOut) {
return true;
}

bool SetNameProxy(const proxyType &addrProxy) {
bool SetNameProxy(const Proxy &addrProxy) {
if (!addrProxy.IsValid())
return false;
LOCK(g_proxyinfo_mutex);
nameProxy = addrProxy;
return true;
}

bool GetNameProxy(proxyType &nameProxyOut) {
bool GetNameProxy(Proxy &nameProxyOut) {
LOCK(g_proxyinfo_mutex);
if(!nameProxy.IsValid())
return false;
Expand All @@ -653,7 +653,7 @@ bool IsProxy(const CNetAddr &addr) {
return false;
}

bool ConnectThroughProxy(const proxyType& proxy, const std::string& strDest, uint16_t port, const Sock& sock, int nTimeout, bool& outProxyConnectionFailed)
bool ConnectThroughProxy(const Proxy& proxy, const std::string& strDest, uint16_t port, const Sock& sock, int nTimeout, bool& outProxyConnectionFailed)
{
// first connect to proxy server
if (!ConnectSocketDirectly(proxy.proxy, sock, nTimeout, true)) {
Expand Down
16 changes: 8 additions & 8 deletions src/netbase.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@ static inline bool operator&(ConnectionDirection a, ConnectionDirection b) {
return (underlying(a) & underlying(b));
}

class proxyType
class Proxy
{
public:
proxyType(): randomize_credentials(false) {}
explicit proxyType(const CService &_proxy, bool _randomize_credentials=false): proxy(_proxy), randomize_credentials(_randomize_credentials) {}
Proxy(): randomize_credentials(false) {}
explicit Proxy(const CService &_proxy, bool _randomize_credentials=false): proxy(_proxy), randomize_credentials(_randomize_credentials) {}

bool IsValid() const { return proxy.IsValid(); }

Expand All @@ -73,8 +73,8 @@ enum Network ParseNetwork(const std::string& net);
std::string GetNetworkName(enum Network net);
/** Return a vector of publicly routable Network names; optionally append NET_UNROUTABLE. */
std::vector<std::string> GetNetworkNames(bool append_unroutable = false);
bool SetProxy(enum Network net, const proxyType &addrProxy);
bool GetProxy(enum Network net, proxyType &proxyInfoOut);
bool SetProxy(enum Network net, const Proxy &addrProxy);
bool GetProxy(enum Network net, Proxy &proxyInfoOut);
bool IsProxy(const CNetAddr &addr);
/**
* Set the name proxy to use for all connections to nodes specified by a
Expand All @@ -92,9 +92,9 @@ bool IsProxy(const CNetAddr &addr);
* server in common use (most notably Tor) actually implements UDP
* support, and a DNS resolver is beyond the scope of this project.
*/
bool SetNameProxy(const proxyType &addrProxy);
bool SetNameProxy(const Proxy &addrProxy);
bool HaveNameProxy();
bool GetNameProxy(proxyType &nameProxyOut);
bool GetNameProxy(Proxy &nameProxyOut);

using DNSLookupFn = std::function<std::vector<CNetAddr>(const std::string&, bool)>;
extern DNSLookupFn g_dns_lookup;
Expand Down Expand Up @@ -219,7 +219,7 @@ bool ConnectSocketDirectly(const CService &addrConnect, const Sock& sock, int nT
*
* @returns Whether or not the operation succeeded.
*/
bool ConnectThroughProxy(const proxyType& proxy, const std::string& strDest, uint16_t port, const Sock& sock, int nTimeout, bool& outProxyConnectionFailed);
bool ConnectThroughProxy(const Proxy& proxy, const std::string& strDest, uint16_t port, const Sock& sock, int nTimeout, bool& outProxyConnectionFailed);

/** Disable or enable blocking-mode for a socket */
bool SetSocketNonBlocking(const SOCKET& hSocket, bool fNonBlocking);
Expand Down
2 changes: 1 addition & 1 deletion src/node/interfaces.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ class NodeImpl : public Node
}
bool shutdownRequested() override { return ShutdownRequested(); }
void mapPort(bool use_upnp, bool use_natpmp) override { StartMapPort(use_upnp, use_natpmp); }
bool getProxy(Network net, proxyType& proxy_info) override { return GetProxy(net, proxy_info); }
bool getProxy(Network net, Proxy& proxy_info) override { return GetProxy(net, proxy_info); }
size_t getNodeCount(ConnectionDirection flags) override
{
return m_context->connman ? m_context->connman->GetNodeCount(flags) : 0;
Expand Down
2 changes: 1 addition & 1 deletion src/qt/clientmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ void ClientModel::unsubscribeFromCoreSignals()

bool ClientModel::getProxyInfo(std::string& ip_port) const
{
proxyType ipv4, ipv6;
Proxy ipv4, ipv6;
if (m_node.getProxy((Network) 1, ipv4) && m_node.getProxy((Network) 2, ipv6)) {
ip_port = ipv4.proxy.ToStringIPPort();
return true;
Expand Down
4 changes: 2 additions & 2 deletions src/qt/optionsdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,7 @@ void OptionsDialog::updateProxyValidationState()

void OptionsDialog::updateDefaultProxyNets()
{
proxyType proxy;
Proxy proxy;
std::string strProxy;
QString strDefaultProxyGUI;

Expand Down Expand Up @@ -425,7 +425,7 @@ QValidator::State ProxyAddressValidator::validate(QString &input, int &pos) cons
Q_UNUSED(pos);
// Validate the proxy
CService serv(LookupNumeric(input.toStdString(), DEFAULT_GUI_PROXY_PORT));
proxyType addrProxy = proxyType(serv, true);
Proxy addrProxy = Proxy(serv, true);
if (addrProxy.IsValid())
return QValidator::Acceptable;

Expand Down
2 changes: 1 addition & 1 deletion src/rpc/net.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,7 @@ static UniValue GetNetworksInfo()
for (int n = 0; n < NET_MAX; ++n) {
enum Network network = static_cast<enum Network>(n);
if (network == NET_UNROUTABLE || network == NET_INTERNAL) continue;
proxyType proxy;
Proxy proxy;
UniValue obj(UniValue::VOBJ);
GetProxy(network, proxy);
obj.pushKV("name", GetNetworkName(network));
Expand Down
19 changes: 17 additions & 2 deletions src/torcontrol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -382,9 +382,24 @@ void TorController::auth_cb(TorControlConnection& _conn, const TorControlReply&
// if -onion isn't set to something else.
if (gArgs.GetArg("-onion", "") == "") {
CService resolved(LookupNumeric("127.0.0.1", 9050));
proxyType addrOnion = proxyType(resolved, true);
Proxy addrOnion = Proxy(resolved, true);
SetProxy(NET_ONION, addrOnion);
SetReachable(NET_ONION, true);

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);
}
}

// Finally - now create the service
Expand Down
2 changes: 1 addition & 1 deletion test/functional/feature_proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ def networks_dict(d):
n3 = networks_dict(self.nodes[3].getnetworkinfo())
assert_equal(NETWORKS, n3.keys())
for net in NETWORKS:
if net == NET_I2P:
if net == NET_I2P or net == NET_ONION:
expected_proxy = ''
else:
expected_proxy = f'[{self.conf3.addr[0]}]:{self.conf3.addr[1]}'
Expand Down

0 comments on commit 848b116

Please sign in to comment.