Skip to content

Commit 75353a0

Browse files
committed
Merge bitcoin/bitcoin#32326: net: improve the interface around FindNode() and avoid a recursive mutex lock
87e7f37 doc: clarify peer address in getpeerinfo and addnode RPC help (Vasil Dimov) 2a4450c net: change FindNode() to not return a node and rename it (Vasil Dimov) 4268aba net: avoid recursive m_nodes_mutex lock in DisconnectNode() (Vasil Dimov) 3a4d1a2 net: merge AlreadyConnectedToAddress() and FindNode(CNetAddr) (Vasil Dimov) Pull request description: `CConnman::FindNode()` would lock `m_nodes_mutex`, find the node in `m_nodes`, release the mutex and return the node. The current code is safe but it is a dangerous interface where a caller may end up using the node returned from `FindNode()` without owning `m_nodes_mutex` and without having that node's reference count incremented. Change `FindNode()` to return a boolean since all but one of its callers used its return value to check whether a node exists and did not do anything else with the return value. Remove a recursive lock on `m_nodes_mutex`. Rename `FindNode()` to better describe what it does. ACKs for top commit: achow101: ACK 87e7f37 furszy: Code review ACK 87e7f37 hodlinator: re-ACK 87e7f37 Tree-SHA512: 44fb64cd1226eca124ed1f447b4a1ebc42cc5c9e8561fc91949bbeaeaa7fa16fcfd664e85ce142e5abe62cb64197c178ca4ca93b3b3217b913e3c498d0b7d1c9
2 parents acc7f2a + 87e7f37 commit 75353a0

File tree

5 files changed

+40
-55
lines changed

5 files changed

+40
-55
lines changed

src/net.cpp

Lines changed: 16 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -331,42 +331,22 @@ bool IsLocal(const CService& addr)
331331
return mapLocalHost.count(addr) > 0;
332332
}
333333

334-
CNode* CConnman::FindNode(const CNetAddr& ip)
334+
bool CConnman::AlreadyConnectedToHost(const std::string& host) const
335335
{
336336
LOCK(m_nodes_mutex);
337-
for (CNode* pnode : m_nodes) {
338-
if (static_cast<CNetAddr>(pnode->addr) == ip) {
339-
return pnode;
340-
}
341-
}
342-
return nullptr;
337+
return std::ranges::any_of(m_nodes, [&host](CNode* node) { return node->m_addr_name == host; });
343338
}
344339

345-
CNode* CConnman::FindNode(const std::string& addrName)
340+
bool CConnman::AlreadyConnectedToAddressPort(const CService& addr_port) const
346341
{
347342
LOCK(m_nodes_mutex);
348-
for (CNode* pnode : m_nodes) {
349-
if (pnode->m_addr_name == addrName) {
350-
return pnode;
351-
}
352-
}
353-
return nullptr;
343+
return std::ranges::any_of(m_nodes, [&addr_port](CNode* node) { return node->addr == addr_port; });
354344
}
355345

356-
CNode* CConnman::FindNode(const CService& addr)
346+
bool CConnman::AlreadyConnectedToAddress(const CNetAddr& addr) const
357347
{
358348
LOCK(m_nodes_mutex);
359-
for (CNode* pnode : m_nodes) {
360-
if (static_cast<CService>(pnode->addr) == addr) {
361-
return pnode;
362-
}
363-
}
364-
return nullptr;
365-
}
366-
367-
bool CConnman::AlreadyConnectedToAddress(const CAddress& addr)
368-
{
369-
return FindNode(static_cast<CNetAddr>(addr));
349+
return std::ranges::any_of(m_nodes, [&addr](CNode* node) { return node->addr == addr; });
370350
}
371351

372352
bool CConnman::CheckIncomingNonce(uint64_t nonce)
@@ -403,10 +383,8 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
403383
return nullptr;
404384

405385
// Look for an existing connection
406-
CNode* pnode = FindNode(static_cast<CService>(addrConnect));
407-
if (pnode)
408-
{
409-
LogPrintf("Failed to open new connection, already connected\n");
386+
if (AlreadyConnectedToAddressPort(addrConnect)) {
387+
LogInfo("Failed to open new connection to %s, already connected", addrConnect.ToStringAddrPort());
410388
return nullptr;
411389
}
412390
}
@@ -436,9 +414,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
436414
}
437415
// It is possible that we already have a connection to the IP/port pszDest resolved to.
438416
// In that case, drop the connection that was just created.
439-
LOCK(m_nodes_mutex);
440-
CNode* pnode = FindNode(static_cast<CService>(addrConnect));
441-
if (pnode) {
417+
if (AlreadyConnectedToAddressPort(addrConnect)) {
442418
LogPrintf("Not opening a connection to %s, already connected to %s\n", pszDest, addrConnect.ToStringAddrPort());
443419
return nullptr;
444420
}
@@ -3010,8 +2986,9 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
30102986
if (IsLocal(addrConnect) || banned_or_discouraged || AlreadyConnectedToAddress(addrConnect)) {
30112987
return;
30122988
}
3013-
} else if (FindNode(std::string(pszDest)))
2989+
} else if (AlreadyConnectedToHost(pszDest)) {
30142990
return;
2991+
}
30152992

30162993
CNode* pnode = ConnectNode(addrConnect, pszDest, fCountFailure, conn_type, use_v2transport);
30172994

@@ -3651,9 +3628,11 @@ void CConnman::GetNodeStats(std::vector<CNodeStats>& vstats) const
36513628
bool CConnman::DisconnectNode(const std::string& strNode)
36523629
{
36533630
LOCK(m_nodes_mutex);
3654-
if (CNode* pnode = FindNode(strNode)) {
3655-
LogDebug(BCLog::NET, "disconnect by address%s match, %s", (fLogIPs ? strprintf("=%s", strNode) : ""), pnode->DisconnectMsg(fLogIPs));
3656-
pnode->fDisconnect = true;
3631+
auto it = std::ranges::find_if(m_nodes, [&strNode](CNode* node) { return node->m_addr_name == strNode; });
3632+
if (it != m_nodes.end()) {
3633+
CNode* node{*it};
3634+
LogDebug(BCLog::NET, "disconnect by address%s match, %s", (fLogIPs ? strprintf("=%s", strNode) : ""), node->DisconnectMsg(fLogIPs));
3635+
node->fDisconnect = true;
36573636
return true;
36583637
}
36593638
return false;

src/net.h

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1370,15 +1370,28 @@ class CConnman
13701370

13711371
uint64_t CalculateKeyedNetGroup(const CNetAddr& ad) const;
13721372

1373-
CNode* FindNode(const CNetAddr& ip);
1374-
CNode* FindNode(const std::string& addrName);
1375-
CNode* FindNode(const CService& addr);
1373+
/**
1374+
* Determine whether we're already connected to a given "host:port".
1375+
* Note that for inbound connections, the peer is likely using a random outbound
1376+
* port on their side, so this will likely not match any inbound connections.
1377+
* @param[in] host String of the form "host[:port]", e.g. "localhost" or "localhost:8333" or "1.2.3.4:8333".
1378+
* @return true if connected to `host`.
1379+
*/
1380+
bool AlreadyConnectedToHost(const std::string& host) const;
1381+
1382+
/**
1383+
* Determine whether we're already connected to a given address:port.
1384+
* Note that for inbound connections, the peer is likely using a random outbound
1385+
* port on their side, so this will likely not match any inbound connections.
1386+
* @param[in] addr_port Address and port to check.
1387+
* @return true if connected to addr_port.
1388+
*/
1389+
bool AlreadyConnectedToAddressPort(const CService& addr_port) const;
13761390

13771391
/**
1378-
* Determine whether we're already connected to a given address, in order to
1379-
* avoid initiating duplicate connections.
1392+
* Determine whether we're already connected to a given address.
13801393
*/
1381-
bool AlreadyConnectedToAddress(const CAddress& addr);
1394+
bool AlreadyConnectedToAddress(const CNetAddr& addr) const;
13821395

13831396
bool AttemptToEvictConnection();
13841397
CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, ConnectionType conn_type, bool use_v2transport) EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);

src/rpc/net.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ static RPCHelpMan getpeerinfo()
130130
{
131131
{
132132
{RPCResult::Type::NUM, "id", "Peer index"},
133-
{RPCResult::Type::STR, "addr", "(host:port) The IP address and port of the peer"},
133+
{RPCResult::Type::STR, "addr", "(host:port) The IP address/hostname optionally followed by :port of the peer"},
134134
{RPCResult::Type::STR, "addrbind", /*optional=*/true, "(ip:port) Bind address of the connection to the peer"},
135135
{RPCResult::Type::STR, "addrlocal", /*optional=*/true, "(ip:port) Local address as reported by the peer"},
136136
{RPCResult::Type::STR, "network", "Network (" + Join(GetNetworkNames(/*append_unroutable=*/true), ", ") + ")"},
@@ -322,7 +322,7 @@ static RPCHelpMan addnode()
322322
strprintf("Addnode connections are limited to %u at a time", MAX_ADDNODE_CONNECTIONS) +
323323
" and are counted separately from the -maxconnections limit.\n",
324324
{
325-
{"node", RPCArg::Type::STR, RPCArg::Optional::NO, "The address of the peer to connect to"},
325+
{"node", RPCArg::Type::STR, RPCArg::Optional::NO, "The IP address/hostname optionally followed by :port of the peer to connect to"},
326326
{"command", RPCArg::Type::STR, RPCArg::Optional::NO, "'add' to add a node to the list, 'remove' to remove a node from the list, 'onetry' to try a connection to the node once"},
327327
{"v2transport", RPCArg::Type::BOOL, RPCArg::DefaultHint{"set by -v2transport"}, "Attempt to connect using BIP324 v2 transport protocol (ignored for 'remove' command)"},
328328
},

src/test/net_peer_connection_tests.cpp

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -151,15 +151,8 @@ BOOST_FIXTURE_TEST_CASE(test_addnode_getaddednodeinfo_and_connection_detection,
151151
}
152152

153153
BOOST_TEST_MESSAGE("\nCheck that all connected peers are correctly detected as connected");
154-
for (auto node : connman->TestNodes()) {
155-
BOOST_CHECK(connman->AlreadyConnectedPublic(node->addr));
156-
}
157-
158-
BOOST_TEST_MESSAGE("\nCheck that peers with the same addresses as connected peers but different ports are detected as connected.");
159-
for (auto node : connman->TestNodes()) {
160-
uint16_t changed_port = node->addr.GetPort() + 1;
161-
CService address_with_changed_port{node->addr, changed_port};
162-
BOOST_CHECK(connman->AlreadyConnectedPublic(CAddress{address_with_changed_port, NODE_NONE}));
154+
for (const auto& node : connman->TestNodes()) {
155+
BOOST_CHECK(connman->AlreadyConnectedToAddressPublic(node->addr));
163156
}
164157

165158
// Clean up

src/test/util/net.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ struct ConnmanTestMsg : public CConnman {
107107
bool ReceiveMsgFrom(CNode& node, CSerializedNetMsg&& ser_msg) const;
108108
void FlushSendBuffer(CNode& node) const;
109109

110-
bool AlreadyConnectedPublic(const CAddress& addr) { return AlreadyConnectedToAddress(addr); };
110+
bool AlreadyConnectedToAddressPublic(const CNetAddr& addr) { return AlreadyConnectedToAddress(addr); };
111111

112112
CNode* ConnectNodePublic(PeerManager& peerman, const char* pszDest, ConnectionType conn_type)
113113
EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);

0 commit comments

Comments
 (0)