Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions doc/release-notes-35319.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
P2P and network changes
-----------------------

- Fix a possible leak of the originator's IP address for transactions sent with

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested alternative:

"Fix ip address leak when using the -privatebroadcast feature. Under certain circumstances connections were being made over clearnet rather than the enabled privacy network."

I think long explanation could go on the website.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Took something like this for the rel note in #35331. Can take further suggestion / additions to it there.

`sendrawtransaction` RPC when `-privatebroadcast=1`. When Bitcoin Core connects
to a peer, if that peer has been advertised to support P2P protocol v2, then
Bitcoin Core tries to use the v2 protocol and if that fails it retries the
connection using the v1 protocol. When the private broadcast is about to send a
transaction to an IPv4 or IPv6 peer it overrides the normal proxy selection and
forces the connection through the Tor proxy (and thus through the Tor network,
protecting the sender's IP address). However if v2 protocol is tried and it
fails, then the v1 retry connection would be made disregarding the "override
proxy" request, possibly making an IPv4 or IPv6 direct connection. In other
words, for this to happen the following must be true:
1. An IPv4 or IPv6 address has been advertised to the sender, indicating v2
support.
2. The sender must have Tor configured.
3. The sender's configuration must be such that connections to IPv4 or IPv6
are made directly (no `-proxy=` is used for IPv4 or IPv6).
4. The recipient does not support v2 (the flags from 1. are bogus). (#35319)
55 changes: 45 additions & 10 deletions src/net.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -539,6 +542,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect,
network_id,
CNodeOptions{
.permission_flags = permission_flags,
.proxy_override = proxy_override,
.i2p_sam_session = std::move(i2p_transient_session),
.recv_flood_size = nReceiveFloodSize,
.use_v2transport = use_v2transport,
Expand Down Expand Up @@ -1915,7 +1919,13 @@ bool CConnman::AddConnection(const std::string& address, ConnectionType conn_typ
CountingSemaphoreGrant<> grant(*semOutbound, true);
if (!grant) return false;

OpenNetworkConnection(CAddress(), false, std::move(grant), address.c_str(), conn_type, /*use_v2transport=*/use_v2transport);
OpenNetworkConnection(/*addrConnect=*/CAddress{},
/*fCountFailure=*/false,
/*grant_outbound=*/std::move(grant),
/*pszDest=*/address.c_str(),
/*conn_type=*/conn_type,
/*use_v2transport=*/use_v2transport,
/*proxy_override=*/std::nullopt);
return true;
}

Expand Down Expand Up @@ -1956,6 +1966,7 @@ void CConnman::DisconnectNodes()
// and we don't want to hold up the socket handler thread for that long.
if (network_active && pnode->m_transport->ShouldReconnectV1()) {
reconnections_to_add.push_back({
.proxy_override = pnode->m_proxy_override,
.addr_connect = pnode->addr,
.grant = std::move(pnode->grantOutbound),
.destination = pnode->m_dest,
Expand Down Expand Up @@ -2438,7 +2449,13 @@ void CConnman::ProcessAddrFetch()
CAddress addr;
CountingSemaphoreGrant<> grant(*semOutbound, /*fTry=*/true);
if (grant) {
OpenNetworkConnection(addr, false, std::move(grant), strDest.c_str(), ConnectionType::ADDR_FETCH, use_v2transport);
OpenNetworkConnection(/*addrConnect=*/addr,
/*fCountFailure=*/false,
/*grant_outbound=*/std::move(grant),
/*pszDest=*/strDest.c_str(),
/*conn_type=*/ConnectionType::ADDR_FETCH,
/*use_v2transport=*/use_v2transport,
/*proxy_override=*/std::nullopt);
}
}

Expand Down Expand Up @@ -2566,8 +2583,13 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect, std
{
for (const std::string& strAddr : connect)
{
CAddress addr(CService(), NODE_NONE);
OpenNetworkConnection(addr, false, {}, strAddr.c_str(), ConnectionType::MANUAL, /*use_v2transport=*/use_v2transport);
OpenNetworkConnection(/*addrConnect=*/CAddress{CService{}, NODE_NONE},
/*fCountFailure=*/false,
/*grant_outbound=*/{},
/*pszDest=*/strAddr.c_str(),
/*conn_type=*/ConnectionType::MANUAL,
/*use_v2transport=*/use_v2transport,
/*proxy_override=*/std::nullopt);
for (int i = 0; i < 10 && i < nLoop; i++)
{
if (!m_interrupt_net->sleep_for(500ms)) {
Expand Down Expand Up @@ -2917,7 +2939,13 @@ 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), /*pszDest=*/nullptr, conn_type, use_v2transport);
OpenNetworkConnection(/*addrConnect=*/addrConnect,
/*fCountFailure=*/count_failures,
/*grant_outbound=*/std::move(grant),
/*pszDest=*/nullptr,
/*conn_type=*/conn_type,
/*use_v2transport=*/use_v2transport,
/*proxy_override=*/std::nullopt);
}
}
}
Expand Down Expand Up @@ -3016,8 +3044,13 @@ void CConnman::ThreadOpenAddedConnections()
break;
}
tried = true;
CAddress addr(CService(), NODE_NONE);
OpenNetworkConnection(addr, false, std::move(grant), info.m_params.m_added_node.c_str(), ConnectionType::MANUAL, info.m_params.m_use_v2transport);
OpenNetworkConnection(/*addrConnect=*/CAddress{CService{}, NODE_NONE},
/*fCountFailure=*/false,
/*grant_outbound=*/std::move(grant),
/*pszDest=*/info.m_params.m_added_node.c_str(),
/*conn_type=*/ConnectionType::MANUAL,
/*use_v2transport=*/info.m_params.m_use_v2transport,
/*proxy_override=*/std::nullopt);
if (!m_interrupt_net->sleep_for(500ms)) return;
grant = CountingSemaphoreGrant<>(*semAddnode, /*fTry=*/true);
}
Expand Down Expand Up @@ -4028,6 +4061,7 @@ CNode::CNode(NodeId idIn,
m_permission_flags{node_opts.permission_flags},
m_sock{sock},
m_connected{NodeClock::now()},
m_proxy_override{std::move(node_opts.proxy_override)},
addr{addrIn},
addrBind{addrBindIn},
m_addr_name{addrNameIn.empty() ? addr.ToStringAddrPort() : addrNameIn},
Expand Down Expand Up @@ -4214,7 +4248,8 @@ void CConnman::PerformReconnections()
std::move(item.grant),
item.destination.empty() ? nullptr : item.destination.c_str(),
item.conn_type,
item.use_v2transport);
item.use_v2transport,
item.proxy_override);
}
}

Expand Down
8 changes: 7 additions & 1 deletion src/net.h
Original file line number Diff line number Diff line change
Expand Up @@ -669,6 +669,7 @@ class V2Transport final : public Transport
struct CNodeOptions
{
NetPermissionFlags permission_flags = NetPermissionFlags::None;
std::optional<Proxy> proxy_override = {};
std::unique_ptr<i2p::sam::Session> i2p_sam_session = nullptr;
bool prefer_evict = false;
size_t recv_flood_size{DEFAULT_MAXRECEIVEBUFFER * 1000};
Expand Down Expand Up @@ -711,6 +712,10 @@ class CNode
std::atomic<NodeClock::time_point> m_last_recv{NodeClock::epoch};
//! Unix epoch time at peer connection
const NodeClock::time_point m_connected;

//! Proxy to use regardless of global proxy settings if reconnecting to this node.
const std::optional<Proxy> m_proxy_override;

// Address of this peer
const CAddress addr;
// Bind address of our side of the connection
Expand Down Expand Up @@ -1186,7 +1191,7 @@ class CConnman
const char* pszDest,
ConnectionType conn_type,
bool use_v2transport,
const std::optional<Proxy>& proxy_override = std::nullopt)
const std::optional<Proxy>& proxy_override)
EXCLUSIVE_LOCKS_REQUIRED(!m_nodes_mutex, !m_unused_i2p_sessions_mutex);

/// Group of private broadcast related members.
Expand Down Expand Up @@ -1796,6 +1801,7 @@ class CConnman
/** Struct for entries in m_reconnections. */
struct ReconnectionInfo
{
std::optional<Proxy> proxy_override;
CAddress addr_connect;
CountingSemaphoreGrant<> grant;
std::string destination;
Expand Down
8 changes: 7 additions & 1 deletion src/rpc/net.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,13 @@ static RPCMethod addnode()
if (command == "onetry")
{
CAddress addr;
connman.OpenNetworkConnection(addr, /*fCountFailure=*/false, /*grant_outbound=*/{}, std::string{node_arg}.c_str(), ConnectionType::MANUAL, use_v2transport);
connman.OpenNetworkConnection(/*addrConnect=*/addr,
/*fCountFailure=*/false,
/*grant_outbound=*/{},
/*pszDest=*/std::string{node_arg}.c_str(),
/*conn_type=*/ConnectionType::MANUAL,
/*use_v2transport=*/use_v2transport,
/*proxy_override=*/std::nullopt);
return UniValue::VNULL;
}

Expand Down
8 changes: 7 additions & 1 deletion src/test/fuzz/connman.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
Loading
Loading