net: ensure no direct private broadcast connections
What changed, and why it matters
This change adds a safety check in Bitcoin Core's networking code to make sure a specific kind of private message-sending connection (called a 'private broadcast' connection) never connects directly over the internet. Instead, it must go through a proxy like Tor or I2P. Without this guard, a future code mistake could cause the sender's real IP address to be exposed. The patch is described by the authors as a preventive guard, not a fix for an active bug.
Treat as a hardening/defensive patch. Review whether any existing call sites can actually reach ConnectNode with PRIVATE_BROADCAST and no proxy; if not, this is preventive. Ensure the Assume() is compiled into release builds or consider upgrading to a stronger runtime check if the invariant is security-critical.
Security signals we found
Prevents IP address leak for private broadcast connections
Adds defensive Assume() invariant in network connection path
Targets Tor/I2P and proxied IPv4/IPv6 privacy semantics
Fuzz test updated to exercise private broadcast with proxy override
Commit explicitly frames change as guarding against future mistakes
Evidence from the diff
In CConnman::ConnectNode, when no proxy is configured for the target network, the code previously called ConnectDirectly for any connection type. The patch wraps that direct connection in an Assume() assertion that conn_type is not PRIVATE_BROADCAST. PRIVATE_BROADCAST connections are intended to use Tor/I2P (which always require a proxy) or IPv4/IPv6 only via a proxy, to avoid leaking the originator’s IP. The fuzz test is updated to supply a proxy_override for PRIVATE_BROADCAST or randomly. This is a defensive invariant; the commit message calls it a ‘safety check to guard against future mistakes.’
Changed components
src/net.cppCConnman::ConnectNodeConnectionType::PRIVATE_BROADCASTsrc/test/fuzz/connman.cppInspect captured patch +12 / −3
diff --git a/src/net.cpp b/src/net.cpp
index 7edb8eac..3e2b81b0 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -488,8 +488,11 @@ CNode* CConnman::ConnectNode(CAddress addrConnect,
LogDebug(BCLog::PROXY, "Using proxy: %s to connect to %s\n", use_proxy->ToString(), target_addr.ToStringAddrPort());
sock = ConnectThroughProxy(*use_proxy, target_addr.ToStringAddr(), target_addr.GetPort(), proxyConnectionFailed);
} else {
- // no proxy needed (none set for target network)
- sock = ConnectDirectly(target_addr, conn_type == ConnectionType::MANUAL);
+ // No proxy needed (none set for target network). Private broadcast connections
+ // must always use a proxy, otherwise they would leak the originator's IP address.
+ if (Assume(conn_type != ConnectionType::PRIVATE_BROADCAST)) {
+ sock = ConnectDirectly(target_addr, conn_type == ConnectionType::MANUAL);
+ }
}
if (!proxyConnectionFailed) {
// If a connection to the node was attempted, and failure (if any) is not caused by a problem connecting to
diff --git a/src/test/fuzz/connman.cpp b/src/test/fuzz/connman.cpp
index 1b0859d6..55b5800c 100644
--- a/src/test/fuzz/connman.cpp
+++ b/src/test/fuzz/connman.cpp
@@ -188,13 +188,19 @@ FUZZ_TARGET(connman, .init = initialize_connman)
conn_type = ConnectionType::OUTBOUND_FULL_RELAY;
}
+ std::optional<Proxy> proxy_override;
+ if (conn_type == ConnectionType::PRIVATE_BROADCAST || fuzzed_data_provider.ConsumeBool()) {
+ proxy_override.emplace(ConsumeService(fuzzed_data_provider));
+ }
+
connman.OpenNetworkConnection(
/*addrConnect=*/random_address,
/*fCountFailure=*/fuzzed_data_provider.ConsumeBool(),
/*grant_outbound=*/{},
/*pszDest=*/fuzzed_data_provider.ConsumeBool() ? nullptr : random_string.c_str(),
/*conn_type=*/conn_type,
- /*use_v2transport=*/fuzzed_data_provider.ConsumeBool());
+ /*use_v2transport=*/fuzzed_data_provider.ConsumeBool(),
+ /*proxy_override=*/proxy_override);
},
[&] {
connman.SetNetworkActive(fuzzed_data_provider.ConsumeBool());
Why this scored 47/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.