Skip to content

Commit 2eff198

Browse files
committed
Merge bitcoin/bitcoin#28834: net: attempts to connect to all resolved addresses when connecting to a node
fd81a37 net: attempts to connect to all resolved addresses when connecting to a node (Sergi Delgado Segura) Pull request description: This is a follow-up of #28155 motivated by bitcoin/bitcoin#28155 (comment) ## Rationale Prior to this, when establishing a network connection via `CConnman::ConnectNode`, if the connection needed address resolution, a single address would be picked at random from the resolved addresses and our node would try to connect to it. However, this would lead to the behavior of `ConnectNode` being unpredictable when the address was resolved to various ips (e.g. the address resolving to IPv4 and IPv6, but we only support one of them). This patches the aforementioned behavior by going over all resolved IPs until a valid one is found or until we exhaust them. ACKs for top commit: mzumsande: re-ACK fd81a37 (just looked at diff, only small logging change) achow101: ACK fd81a37 vasild: ACK fd81a37 Tree-SHA512: fa1ebc5c84fe61dd0a7fe1113ae2d594a75ad661c43ed8984a31fc9bc50f166b2759b0d8d84ee5dc247691eff78c8156fac970af797bbcbf67492eec0353fb58
2 parents 0e2e7d1 + fd81a37 commit 2eff198

File tree

1 file changed

+91
-75
lines changed

1 file changed

+91
-75
lines changed

src/net.cpp

+91-75
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,9 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
412412
// Resolve
413413
const uint16_t default_port{pszDest != nullptr ? GetDefaultPort(pszDest) :
414414
m_params.GetDefaultPort()};
415+
416+
// Collection of addresses to try to connect to: either all dns resolved addresses if a domain name (pszDest) is provided, or addrConnect otherwise.
417+
std::vector<CAddress> connect_to{};
415418
if (pszDest) {
416419
std::vector<CService> resolved{Lookup(pszDest, default_port, fNameLookup && !HaveNameProxy(), 256)};
417420
if (!resolved.empty()) {
@@ -432,8 +435,16 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
432435
LogPrintf("Not opening a connection to %s, already connected to %s\n", pszDest, addrConnect.ToStringAddrPort());
433436
return nullptr;
434437
}
438+
// Add the address to the resolved addresses vector so we can try to connect to it later on
439+
connect_to.push_back(addrConnect);
435440
}
441+
} else {
442+
// For resolution via proxy
443+
connect_to.push_back(addrConnect);
436444
}
445+
} else {
446+
// Connect via addrConnect directly
447+
connect_to.push_back(addrConnect);
437448
}
438449

439450
// Connect
@@ -443,94 +454,99 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
443454
assert(!addr_bind.IsValid());
444455
std::unique_ptr<i2p::sam::Session> i2p_transient_session;
445456

446-
if (addrConnect.IsValid()) {
447-
const bool use_proxy{GetProxy(addrConnect.GetNetwork(), proxy)};
448-
bool proxyConnectionFailed = false;
457+
for (auto& target_addr: connect_to) {
458+
if (target_addr.IsValid()) {
459+
const bool use_proxy{GetProxy(target_addr.GetNetwork(), proxy)};
460+
bool proxyConnectionFailed = false;
449461

450-
if (addrConnect.IsI2P() && use_proxy) {
451-
i2p::Connection conn;
452-
bool connected{false};
462+
if (target_addr.IsI2P() && use_proxy) {
463+
i2p::Connection conn;
464+
bool connected{false};
453465

454-
if (m_i2p_sam_session) {
455-
connected = m_i2p_sam_session->Connect(addrConnect, conn, proxyConnectionFailed);
456-
} else {
457-
{
458-
LOCK(m_unused_i2p_sessions_mutex);
459-
if (m_unused_i2p_sessions.empty()) {
460-
i2p_transient_session =
461-
std::make_unique<i2p::sam::Session>(proxy, &interruptNet);
462-
} else {
463-
i2p_transient_session.swap(m_unused_i2p_sessions.front());
464-
m_unused_i2p_sessions.pop();
466+
if (m_i2p_sam_session) {
467+
connected = m_i2p_sam_session->Connect(target_addr, conn, proxyConnectionFailed);
468+
} else {
469+
{
470+
LOCK(m_unused_i2p_sessions_mutex);
471+
if (m_unused_i2p_sessions.empty()) {
472+
i2p_transient_session =
473+
std::make_unique<i2p::sam::Session>(proxy, &interruptNet);
474+
} else {
475+
i2p_transient_session.swap(m_unused_i2p_sessions.front());
476+
m_unused_i2p_sessions.pop();
477+
}
465478
}
466-
}
467-
connected = i2p_transient_session->Connect(addrConnect, conn, proxyConnectionFailed);
468-
if (!connected) {
469-
LOCK(m_unused_i2p_sessions_mutex);
470-
if (m_unused_i2p_sessions.size() < MAX_UNUSED_I2P_SESSIONS_SIZE) {
471-
m_unused_i2p_sessions.emplace(i2p_transient_session.release());
479+
connected = i2p_transient_session->Connect(target_addr, conn, proxyConnectionFailed);
480+
if (!connected) {
481+
LOCK(m_unused_i2p_sessions_mutex);
482+
if (m_unused_i2p_sessions.size() < MAX_UNUSED_I2P_SESSIONS_SIZE) {
483+
m_unused_i2p_sessions.emplace(i2p_transient_session.release());
484+
}
472485
}
473486
}
474-
}
475487

476-
if (connected) {
477-
sock = std::move(conn.sock);
478-
addr_bind = CAddress{conn.me, NODE_NONE};
488+
if (connected) {
489+
sock = std::move(conn.sock);
490+
addr_bind = CAddress{conn.me, NODE_NONE};
491+
}
492+
} else if (use_proxy) {
493+
LogPrintLevel(BCLog::PROXY, BCLog::Level::Debug, "Using proxy: %s to connect to %s\n", proxy.ToString(), target_addr.ToStringAddrPort());
494+
sock = ConnectThroughProxy(proxy, target_addr.ToStringAddr(), target_addr.GetPort(), proxyConnectionFailed);
495+
} else {
496+
// no proxy needed (none set for target network)
497+
sock = ConnectDirectly(target_addr, conn_type == ConnectionType::MANUAL);
479498
}
480-
} else if (use_proxy) {
481-
LogPrintLevel(BCLog::PROXY, BCLog::Level::Debug, "Using proxy: %s to connect to %s:%s\n", proxy.ToString(), addrConnect.ToStringAddr(), addrConnect.GetPort());
482-
sock = ConnectThroughProxy(proxy, addrConnect.ToStringAddr(), addrConnect.GetPort(), proxyConnectionFailed);
483-
} else {
484-
// no proxy needed (none set for target network)
485-
sock = ConnectDirectly(addrConnect, conn_type == ConnectionType::MANUAL);
499+
if (!proxyConnectionFailed) {
500+
// If a connection to the node was attempted, and failure (if any) is not caused by a problem connecting to
501+
// the proxy, mark this as an attempt.
502+
addrman.Attempt(target_addr, fCountFailure);
503+
}
504+
} else if (pszDest && GetNameProxy(proxy)) {
505+
std::string host;
506+
uint16_t port{default_port};
507+
SplitHostPort(std::string(pszDest), port, host);
508+
bool proxyConnectionFailed;
509+
sock = ConnectThroughProxy(proxy, host, port, proxyConnectionFailed);
510+
}
511+
// Check any other resolved address (if any) if we fail to connect
512+
if (!sock) {
513+
continue;
486514
}
487-
if (!proxyConnectionFailed) {
488-
// If a connection to the node was attempted, and failure (if any) is not caused by a problem connecting to
489-
// the proxy, mark this as an attempt.
490-
addrman.Attempt(addrConnect, fCountFailure);
515+
516+
NetPermissionFlags permission_flags = NetPermissionFlags::None;
517+
std::vector<NetWhitelistPermissions> whitelist_permissions = conn_type == ConnectionType::MANUAL ? vWhitelistedRangeOutgoing : std::vector<NetWhitelistPermissions>{};
518+
AddWhitelistPermissionFlags(permission_flags, target_addr, whitelist_permissions);
519+
520+
// Add node
521+
NodeId id = GetNewNodeId();
522+
uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();
523+
if (!addr_bind.IsValid()) {
524+
addr_bind = GetBindAddress(*sock);
491525
}
492-
} else if (pszDest && GetNameProxy(proxy)) {
493-
std::string host;
494-
uint16_t port{default_port};
495-
SplitHostPort(std::string(pszDest), port, host);
496-
bool proxyConnectionFailed;
497-
sock = ConnectThroughProxy(proxy, host, port, proxyConnectionFailed);
498-
}
499-
if (!sock) {
500-
return nullptr;
501-
}
526+
CNode* pnode = new CNode(id,
527+
std::move(sock),
528+
target_addr,
529+
CalculateKeyedNetGroup(target_addr),
530+
nonce,
531+
addr_bind,
532+
pszDest ? pszDest : "",
533+
conn_type,
534+
/*inbound_onion=*/false,
535+
CNodeOptions{
536+
.permission_flags = permission_flags,
537+
.i2p_sam_session = std::move(i2p_transient_session),
538+
.recv_flood_size = nReceiveFloodSize,
539+
.use_v2transport = use_v2transport,
540+
});
541+
pnode->AddRef();
502542

503-
NetPermissionFlags permission_flags = NetPermissionFlags::None;
504-
std::vector<NetWhitelistPermissions> whitelist_permissions = conn_type == ConnectionType::MANUAL ? vWhitelistedRangeOutgoing : std::vector<NetWhitelistPermissions>{};
505-
AddWhitelistPermissionFlags(permission_flags, addrConnect, whitelist_permissions);
543+
// We're making a new connection, harvest entropy from the time (and our peer count)
544+
RandAddEvent((uint32_t)id);
506545

507-
// Add node
508-
NodeId id = GetNewNodeId();
509-
uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();
510-
if (!addr_bind.IsValid()) {
511-
addr_bind = GetBindAddress(*sock);
546+
return pnode;
512547
}
513-
CNode* pnode = new CNode(id,
514-
std::move(sock),
515-
addrConnect,
516-
CalculateKeyedNetGroup(addrConnect),
517-
nonce,
518-
addr_bind,
519-
pszDest ? pszDest : "",
520-
conn_type,
521-
/*inbound_onion=*/false,
522-
CNodeOptions{
523-
.permission_flags = permission_flags,
524-
.i2p_sam_session = std::move(i2p_transient_session),
525-
.recv_flood_size = nReceiveFloodSize,
526-
.use_v2transport = use_v2transport,
527-
});
528-
pnode->AddRef();
529-
530-
// We're making a new connection, harvest entropy from the time (and our peer count)
531-
RandAddEvent((uint32_t)id);
532548

533-
return pnode;
549+
return nullptr;
534550
}
535551

536552
void CNode::CloseSocketDisconnect()

0 commit comments

Comments
 (0)