net: attempts to connect to all resolved addresses when connecting to a node

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 will 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 we find one
we can connect to or until we exhaust them.
This commit is contained in:
Sergi Delgado Segura 2023-10-18 15:44:24 -04:00
parent 2ffaa92702
commit fd81a37239
1 changed files with 95 additions and 79 deletions

View File

@ -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()) {
@ -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
@ -443,16 +454,17 @@ 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)};
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) {
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);
connected = m_i2p_sam_session->Connect(target_addr, conn, proxyConnectionFailed);
} else {
{
LOCK(m_unused_i2p_sessions_mutex);
@ -464,7 +476,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
m_unused_i2p_sessions.pop();
}
}
connected = i2p_transient_session->Connect(addrConnect, conn, proxyConnectionFailed);
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) {
@ -478,16 +490,16 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
addr_bind = CAddress{conn.me, NODE_NONE};
}
} 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);
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(addrConnect, conn_type == ConnectionType::MANUAL);
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
// the proxy, mark this as an attempt.
addrman.Attempt(addrConnect, fCountFailure);
addrman.Attempt(target_addr, fCountFailure);
}
} else if (pszDest && GetNameProxy(proxy)) {
std::string host;
@ -496,13 +508,14 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
bool proxyConnectionFailed;
sock = ConnectThroughProxy(proxy, host, port, proxyConnectionFailed);
}
// Check any other resolved address (if any) if we fail to connect
if (!sock) {
return nullptr;
continue;
}
NetPermissionFlags permission_flags = NetPermissionFlags::None;
std::vector<NetWhitelistPermissions> whitelist_permissions = conn_type == ConnectionType::MANUAL ? vWhitelistedRangeOutgoing : std::vector<NetWhitelistPermissions>{};
AddWhitelistPermissionFlags(permission_flags, addrConnect, whitelist_permissions);
AddWhitelistPermissionFlags(permission_flags, target_addr, whitelist_permissions);
// Add node
NodeId id = GetNewNodeId();
@ -512,8 +525,8 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
}
CNode* pnode = new CNode(id,
std::move(sock),
addrConnect,
CalculateKeyedNetGroup(addrConnect),
target_addr,
CalculateKeyedNetGroup(target_addr),
nonce,
addr_bind,
pszDest ? pszDest : "",
@ -531,6 +544,9 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
RandAddEvent((uint32_t)id);
return pnode;
}
return nullptr;
}
void CNode::CloseSocketDisconnect()