Skip to content

Commit 2574b7e

Browse files
committed
net/rpc: Check all resolved addresses in ConnectNode rather than just one
The current `addnode` rpc command has some edge cases in where it is possible to connect to the same node twice by combining ip and address requests. This can happen under two situations: The two commands are run one right after each other, in which case they will be processed under the same loop in `CConnman::ThreadOpenAddedConnections` without refreshing `vInfo`, so both will go trough. An example of this would be: ``` bitcoin-cli addnode "localhost:port" add ``` A node is added by IP using `addnode "add"` while the other is added by name using `addnode "onetry"` with an address that resolves to multiple IPs. In this case, we currently only check one of the resolved IPs (picked at random), instead of all the resolved ones, meaning this will only probabilistically fail/succeed. An example of this would be: ``` bitcoin-cli addnode "127.0.0.1:port" add [...] bitcoin-cli addnode "localhost:port" onetry ``` Both cases can be fixed by iterating over all resolved addresses in `CConnman::ConnectNode` instead of picking one at random
1 parent 97f756b commit 2574b7e

File tree

1 file changed

+18
-14
lines changed

1 file changed

+18
-14
lines changed

src/net.cpp

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -464,21 +464,25 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
464464
const uint16_t default_port{pszDest != nullptr ? GetDefaultPort(pszDest) :
465465
m_params.GetDefaultPort()};
466466
if (pszDest) {
467-
const std::vector<CService> resolved{Lookup(pszDest, default_port, fNameLookup && !HaveNameProxy(), 256)};
467+
std::vector<CService> resolved{Lookup(pszDest, default_port, fNameLookup && !HaveNameProxy(), 256)};
468468
if (!resolved.empty()) {
469-
const CService& rnd{resolved[GetRand(resolved.size())]};
470-
addrConnect = CAddress{MaybeFlipIPv6toCJDNS(rnd), NODE_NONE};
471-
if (!addrConnect.IsValid()) {
472-
LogPrint(BCLog::NET, "Resolver returned invalid address %s for %s\n", addrConnect.ToStringAddrPort(), pszDest);
473-
return nullptr;
474-
}
475-
// It is possible that we already have a connection to the IP/port pszDest resolved to.
476-
// In that case, drop the connection that was just created.
477-
LOCK(m_nodes_mutex);
478-
CNode* pnode = FindNode(static_cast<CService>(addrConnect));
479-
if (pnode) {
480-
LogPrintf("Failed to open new connection, already connected\n");
481-
return nullptr;
469+
Shuffle(resolved.begin(), resolved.end(), FastRandomContext());
470+
// If the connection is made by name, it can be the case that the name resolves to more than one address.
471+
// We don't want to connect any more of them if we are already connected to one
472+
for (const auto& r : resolved) {
473+
addrConnect = CAddress{MaybeFlipIPv6toCJDNS(r), NODE_NONE};
474+
if (!addrConnect.IsValid()) {
475+
LogPrint(BCLog::NET, "Resolver returned invalid address %s for %s\n", addrConnect.ToStringAddrPort(), pszDest);
476+
return nullptr;
477+
}
478+
// It is possible that we already have a connection to the IP/port pszDest resolved to.
479+
// In that case, drop the connection that was just created.
480+
LOCK(m_nodes_mutex);
481+
CNode* pnode = FindNode(static_cast<CService>(addrConnect));
482+
if (pnode) {
483+
LogPrintf("Not opening a connection to %s, already connected to %s\n", pszDest, addrConnect.ToStringAddrPort());
484+
return nullptr;
485+
}
482486
}
483487
}
484488
}

0 commit comments

Comments
 (0)