Skip to content

Commit

Permalink
Merge #28834: net: attempts to connect to all resolved addresses when…
Browse files Browse the repository at this point in the history
… 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 #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
  • Loading branch information
achow101 committed Apr 25, 2024
2 parents 0e2e7d1 + fd81a37 commit 2eff198
Showing 1 changed file with 91 additions and 75 deletions.
166 changes: 91 additions & 75 deletions src/net.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,9 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
// Resolve
const uint16_t default_port{pszDest != nullptr ? GetDefaultPort(pszDest) :
m_params.GetDefaultPort()};

// Collection of addresses to try to connect to: either all dns resolved addresses if a domain name (pszDest) is provided, or addrConnect otherwise.
std::vector<CAddress> connect_to{};
if (pszDest) {
std::vector<CService> resolved{Lookup(pszDest, default_port, fNameLookup && !HaveNameProxy(), 256)};
if (!resolved.empty()) {
Expand All @@ -432,8 +435,16 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
LogPrintf("Not opening a connection to %s, already connected to %s\n", pszDest, addrConnect.ToStringAddrPort());
return nullptr;
}
// Add the address to the resolved addresses vector so we can try to connect to it later on
connect_to.push_back(addrConnect);
}
} else {
// For resolution via proxy
connect_to.push_back(addrConnect);
}
} else {
// Connect via addrConnect directly
connect_to.push_back(addrConnect);
}

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

if (addrConnect.IsValid()) {
const bool use_proxy{GetProxy(addrConnect.GetNetwork(), proxy)};
bool proxyConnectionFailed = false;
for (auto& target_addr: connect_to) {
if (target_addr.IsValid()) {
const bool use_proxy{GetProxy(target_addr.GetNetwork(), proxy)};
bool proxyConnectionFailed = false;

if (addrConnect.IsI2P() && use_proxy) {
i2p::Connection conn;
bool connected{false};
if (target_addr.IsI2P() && use_proxy) {
i2p::Connection conn;
bool connected{false};

if (m_i2p_sam_session) {
connected = m_i2p_sam_session->Connect(addrConnect, conn, proxyConnectionFailed);
} else {
{
LOCK(m_unused_i2p_sessions_mutex);
if (m_unused_i2p_sessions.empty()) {
i2p_transient_session =
std::make_unique<i2p::sam::Session>(proxy, &interruptNet);
} else {
i2p_transient_session.swap(m_unused_i2p_sessions.front());
m_unused_i2p_sessions.pop();
if (m_i2p_sam_session) {
connected = m_i2p_sam_session->Connect(target_addr, conn, proxyConnectionFailed);
} else {
{
LOCK(m_unused_i2p_sessions_mutex);
if (m_unused_i2p_sessions.empty()) {
i2p_transient_session =
std::make_unique<i2p::sam::Session>(proxy, &interruptNet);
} else {
i2p_transient_session.swap(m_unused_i2p_sessions.front());
m_unused_i2p_sessions.pop();
}
}
}
connected = i2p_transient_session->Connect(addrConnect, conn, proxyConnectionFailed);
if (!connected) {
LOCK(m_unused_i2p_sessions_mutex);
if (m_unused_i2p_sessions.size() < MAX_UNUSED_I2P_SESSIONS_SIZE) {
m_unused_i2p_sessions.emplace(i2p_transient_session.release());
connected = i2p_transient_session->Connect(target_addr, conn, proxyConnectionFailed);
if (!connected) {
LOCK(m_unused_i2p_sessions_mutex);
if (m_unused_i2p_sessions.size() < MAX_UNUSED_I2P_SESSIONS_SIZE) {
m_unused_i2p_sessions.emplace(i2p_transient_session.release());
}
}
}
}

if (connected) {
sock = std::move(conn.sock);
addr_bind = CAddress{conn.me, NODE_NONE};
if (connected) {
sock = std::move(conn.sock);
addr_bind = CAddress{conn.me, NODE_NONE};
}
} else if (use_proxy) {
LogPrintLevel(BCLog::PROXY, BCLog::Level::Debug, "Using proxy: %s to connect to %s\n", proxy.ToString(), target_addr.ToStringAddrPort());
sock = ConnectThroughProxy(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);
}
} else if (use_proxy) {
LogPrintLevel(BCLog::PROXY, BCLog::Level::Debug, "Using proxy: %s to connect to %s:%s\n", proxy.ToString(), addrConnect.ToStringAddr(), addrConnect.GetPort());
sock = ConnectThroughProxy(proxy, addrConnect.ToStringAddr(), addrConnect.GetPort(), proxyConnectionFailed);
} else {
// no proxy needed (none set for target network)
sock = ConnectDirectly(addrConnect, 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
// the proxy, mark this as an attempt.
addrman.Attempt(target_addr, fCountFailure);
}
} else if (pszDest && GetNameProxy(proxy)) {
std::string host;
uint16_t port{default_port};
SplitHostPort(std::string(pszDest), port, host);
bool proxyConnectionFailed;
sock = ConnectThroughProxy(proxy, host, port, proxyConnectionFailed);
}
// Check any other resolved address (if any) if we fail to connect
if (!sock) {
continue;
}
if (!proxyConnectionFailed) {
// If a connection to the node was attempted, and failure (if any) is not caused by a problem connecting to
// the proxy, mark this as an attempt.
addrman.Attempt(addrConnect, fCountFailure);

NetPermissionFlags permission_flags = NetPermissionFlags::None;
std::vector<NetWhitelistPermissions> whitelist_permissions = conn_type == ConnectionType::MANUAL ? vWhitelistedRangeOutgoing : std::vector<NetWhitelistPermissions>{};
AddWhitelistPermissionFlags(permission_flags, target_addr, whitelist_permissions);

// Add node
NodeId id = GetNewNodeId();
uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();
if (!addr_bind.IsValid()) {
addr_bind = GetBindAddress(*sock);
}
} else if (pszDest && GetNameProxy(proxy)) {
std::string host;
uint16_t port{default_port};
SplitHostPort(std::string(pszDest), port, host);
bool proxyConnectionFailed;
sock = ConnectThroughProxy(proxy, host, port, proxyConnectionFailed);
}
if (!sock) {
return nullptr;
}
CNode* pnode = new CNode(id,
std::move(sock),
target_addr,
CalculateKeyedNetGroup(target_addr),
nonce,
addr_bind,
pszDest ? pszDest : "",
conn_type,
/*inbound_onion=*/false,
CNodeOptions{
.permission_flags = permission_flags,
.i2p_sam_session = std::move(i2p_transient_session),
.recv_flood_size = nReceiveFloodSize,
.use_v2transport = use_v2transport,
});
pnode->AddRef();

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

// Add node
NodeId id = GetNewNodeId();
uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();
if (!addr_bind.IsValid()) {
addr_bind = GetBindAddress(*sock);
return pnode;
}
CNode* pnode = new CNode(id,
std::move(sock),
addrConnect,
CalculateKeyedNetGroup(addrConnect),
nonce,
addr_bind,
pszDest ? pszDest : "",
conn_type,
/*inbound_onion=*/false,
CNodeOptions{
.permission_flags = permission_flags,
.i2p_sam_session = std::move(i2p_transient_session),
.recv_flood_size = nReceiveFloodSize,
.use_v2transport = use_v2transport,
});
pnode->AddRef();

// We're making a new connection, harvest entropy from the time (and our peer count)
RandAddEvent((uint32_t)id);

return pnode;
return nullptr;
}

void CNode::CloseSocketDisconnect()
Expand Down

0 comments on commit 2eff198

Please sign in to comment.