net: support overriding the proxy selection in ConnectNode()
What changed, and why it matters
This commit is a routine internal refactor of Bitcoin Core's networking code. It lets callers of two connection-opening functions optionally specify a proxy server themselves, instead of always relying on the default automatic proxy selection. It also makes one of those functions report whether the connection succeeded. The change is described by the author as enabling future use cases (for example, routing IPv4/IPv6 peer connections through a Tor SOCKS5 proxy) and is not presented as a security fix. There is no evidence in the commit of a vulnerability being patched.
No security action required. Review as normal code-quality/refactor change. If this commit is part of a larger feature, evaluate the security of the new proxy-override callers once they are introduced.
Security signals we found
No security-relevant keywords in commit title or message
Change is framed as feature/refactor, not bug fix
No input validation or trust-boundary changes observed
Optional proxy override is passed through, not sanitized, but existing proxy path is reused unchanged
No CVE, advisory, or vendor security disclosure referenced in commit
Evidence from the diff
The patch adds an optional proxy_override parameter to CConnman::ConnectNode() and CConnman::OpenNetworkConnection(). When provided, the override bypasses GetProxy() and forces proxy use with the supplied Proxy. OpenNetworkConnection() is changed from void to bool, returning false on early-exit conditions and true after successful node creation. Call sites and tests are updated accordingly, and Doxygen documentation is added. No validation logic, authentication, or proxy-handling internals are modified.
Changed components
src/net.cpp - CConnman::ConnectNode()src/net.cpp - CConnman::OpenNetworkConnection()src/net.h - declarations and documentation for ConnectNode/OpenNetworkConnectionsrc/test/fuzz/connman.cpp - fuzzer call sitesrc/test/util/net.cpp - ConnmanTestMsg::ConnectNodePublic()Inspect captured patch +72 / −14
diff --git a/src/net.cpp b/src/net.cpp
index 584b4280..c4eb0d7b 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -373,7 +373,12 @@ static CService GetBindAddress(const Sock& sock)
return addr_bind;
}
-CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, ConnectionType conn_type, bool use_v2transport)
+CNode* CConnman::ConnectNode(CAddress addrConnect,
+ const char* pszDest,
+ bool fCountFailure,
+ ConnectionType conn_type,
+ bool use_v2transport,
+ const std::optional<Proxy>& proxy_override)
{
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
assert(conn_type != ConnectionType::INBOUND);
@@ -439,7 +444,13 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
for (auto& target_addr: connect_to) {
if (target_addr.IsValid()) {
- const bool use_proxy{GetProxy(target_addr.GetNetwork(), proxy)};
+ bool use_proxy;
+ if (proxy_override.has_value()) {
+ use_proxy = true;
+ proxy = proxy_override.value();
+ } else {
+ use_proxy = GetProxy(target_addr.GetNetwork(), proxy);
+ }
bool proxyConnectionFailed = false;
if (target_addr.IsI2P() && use_proxy) {
@@ -2858,7 +2869,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, std
const bool count_failures{((int)outbound_ipv46_peer_netgroups.size() + outbound_privacy_network_peers) >= std::min(m_max_automatic_connections - 1, 2)};
// Use BIP324 transport when both us and them have NODE_V2_P2P set.
const bool use_v2transport(addrConnect.nServices & GetLocalServices() & NODE_P2P_V2);
- OpenNetworkConnection(addrConnect, count_failures, std::move(grant), /*strDest=*/nullptr, conn_type, use_v2transport);
+ OpenNetworkConnection(addrConnect, count_failures, std::move(grant), /*pszDest=*/nullptr, conn_type, use_v2transport);
}
}
}
@@ -2967,7 +2978,13 @@ void CConnman::ThreadOpenAddedConnections()
}
// if successful, this moves the passed grant to the constructed node
-void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CountingSemaphoreGrant<>&& grant_outbound, const char *pszDest, ConnectionType conn_type, bool use_v2transport)
+bool CConnman::OpenNetworkConnection(const CAddress& addrConnect,
+ bool fCountFailure,
+ CountingSemaphoreGrant<>&& grant_outbound,
+ const char* pszDest,
+ ConnectionType conn_type,
+ bool use_v2transport,
+ const std::optional<Proxy>& proxy_override)
{
AssertLockNotHeld(m_unused_i2p_sessions_mutex);
assert(conn_type != ConnectionType::INBOUND);
@@ -2976,24 +2993,24 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
// Initiate outbound network connection
//
if (m_interrupt_net->interrupted()) {
- return;
+ return false;
}
if (!fNetworkActive) {
- return;
+ return false;
}
if (!pszDest) {
bool banned_or_discouraged = m_banman && (m_banman->IsDiscouraged(addrConnect) || m_banman->IsBanned(addrConnect));
if (IsLocal(addrConnect) || banned_or_discouraged || AlreadyConnectedToAddress(addrConnect)) {
- return;
+ return false;
}
} else if (AlreadyConnectedToHost(pszDest)) {
- return;
+ return false;
}
- CNode* pnode = ConnectNode(addrConnect, pszDest, fCountFailure, conn_type, use_v2transport);
+ CNode* pnode = ConnectNode(addrConnect, pszDest, fCountFailure, conn_type, use_v2transport, proxy_override);
if (!pnode)
- return;
+ return false;
pnode->grantOutbound = std::move(grant_outbound);
m_msgproc->InitializeNode(*pnode, m_local_services);
@@ -3011,6 +3028,8 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
pnode->ConnectionTypeAsString().c_str(),
pnode->ConnectedThroughNetwork(),
GetNodeCount(ConnectionDirection::Out));
+
+ return true;
}
Mutex NetEventsInterface::g_msgproc_mutex;
diff --git a/src/net.h b/src/net.h
index 92aabae6..a6a12df8 100644
--- a/src/net.h
+++ b/src/net.h
@@ -1143,7 +1143,28 @@ public:
bool GetNetworkActive() const { return fNetworkActive; };
bool GetUseAddrmanOutgoing() const { return m_use_addrman_outgoing; };
void SetNetworkActive(bool active);
- void OpenNetworkConnection(const CAddress& addrConnect, bool fCountFailure, CountingSemaphoreGrant<>&& grant_outbound, const char* strDest, ConnectionType conn_type, bool use_v2transport) EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
+
+ /**
+ * Open a new P2P connection and initialize it with the PeerManager at `m_msgproc`.
+ * @param[in] addrConnect Address to connect to, if `pszDest` is `nullptr`.
+ * @param[in] fCountFailure Increment the number of connection attempts to this address in Addrman.
+ * @param[in] grant_outbound Take ownership of this grant, to be released later when the connection is closed.
+ * @param[in] pszDest Address to resolve and connect to.
+ * @param[in] conn_type Type of the connection to open, must not be `ConnectionType::INBOUND`.
+ * @param[in] use_v2transport Use P2P encryption, (aka V2 transport, BIP324).
+ * @param[in] proxy_override Optional proxy to use and override normal proxy selection.
+ * @retval true The connection was opened successfully.
+ * @retval false The connection attempt failed.
+ */
+ bool OpenNetworkConnection(const CAddress& addrConnect,
+ bool fCountFailure,
+ CountingSemaphoreGrant<>&& grant_outbound,
+ const char* pszDest,
+ ConnectionType conn_type,
+ bool use_v2transport,
+ const std::optional<Proxy>& proxy_override = std::nullopt)
+ EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
+
bool CheckIncomingNonce(uint64_t nonce);
void ASMapHealthCheck();
@@ -1394,7 +1415,25 @@ private:
bool AlreadyConnectedToAddress(const CNetAddr& addr) const;
bool AttemptToEvictConnection();
- CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, ConnectionType conn_type, bool use_v2transport) EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
+
+ /**
+ * Open a new P2P connection.
+ * @param[in] addrConnect Address to connect to, if `pszDest` is `nullptr`.
+ * @param[in] pszDest Address to resolve and connect to.
+ * @param[in] fCountFailure Increment the number of connection attempts to this address in Addrman.
+ * @param[in] conn_type Type of the connection to open, must not be `ConnectionType::INBOUND`.
+ * @param[in] use_v2transport Use P2P encryption, (aka V2 transport, BIP324).
+ * @param[in] proxy_override Optional proxy to use and override normal proxy selection.
+ * @return Newly created CNode object or nullptr if the connection failed.
+ */
+ CNode* ConnectNode(CAddress addrConnect,
+ const char* pszDest,
+ bool fCountFailure,
+ ConnectionType conn_type,
+ bool use_v2transport,
+ const std::optional<Proxy>& proxy_override)
+ EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
+
void AddWhitelistPermissionFlags(NetPermissionFlags& flags, std::optional<CNetAddr> addr, const std::vector<NetWhitelistPermissions>& ranges) const;
void DeleteNode(CNode* pnode);
diff --git a/src/test/fuzz/connman.cpp b/src/test/fuzz/connman.cpp
index 100acc4a..5f9841da 100644
--- a/src/test/fuzz/connman.cpp
+++ b/src/test/fuzz/connman.cpp
@@ -177,7 +177,7 @@ FUZZ_TARGET(connman, .init = initialize_connman)
/*addrConnect=*/random_address,
/*fCountFailure=*/fuzzed_data_provider.ConsumeBool(),
/*grant_outbound=*/{},
- /*strDest=*/fuzzed_data_provider.ConsumeBool() ? nullptr : random_string.c_str(),
+ /*pszDest=*/fuzzed_data_provider.ConsumeBool() ? nullptr : random_string.c_str(),
/*conn_type=*/conn_type,
/*use_v2transport=*/fuzzed_data_provider.ConsumeBool());
},
diff --git a/src/test/util/net.cpp b/src/test/util/net.cpp
index 73ae996b..0f59e0d6 100644
--- a/src/test/util/net.cpp
+++ b/src/test/util/net.cpp
@@ -116,7 +116,7 @@ bool ConnmanTestMsg::ReceiveMsgFrom(CNode& node, CSerializedNetMsg&& ser_msg) co
CNode* ConnmanTestMsg::ConnectNodePublic(PeerManager& peerman, const char* pszDest, ConnectionType conn_type)
{
- CNode* node = ConnectNode(CAddress{}, pszDest, /*fCountFailure=*/false, conn_type, /*use_v2transport=*/true);
+ CNode* node = ConnectNode(CAddress{}, pszDest, /*fCountFailure=*/false, conn_type, /*use_v2transport=*/true, /*proxy_override=*/std::nullopt);
if (!node) return nullptr;
node->SetCommonVersion(PROTOCOL_VERSION);
peerman.InitializeNode(*node, ServiceFlags(NODE_NETWORK | NODE_WITNESS));
Why this scored 18/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.