Right. Comparing ports for incoming connections is problematic also. I think the right approach for determining if we are connected to addr:port, for the purposes of making an outbound connection:
- For incoming connections, compare just
addr and ignore port
- For outgoing connections, compare both
addr and port.
This can't be done with a single map of either CService or CNetAddr. I guess two containers are needed, as this:
<details>
<summary>[patch] compare differently inbound and outbound addresses</summary>
diff --git i/src/net.cpp w/src/net.cpp
index 91282d927d..0eb0023112 100644
--- i/src/net.cpp
+++ w/src/net.cpp
@@ -2809,41 +2809,49 @@ std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo() const
LOCK(m_added_nodes_mutex);
ret.reserve(m_added_node_params.size());
std::copy(m_added_node_params.cbegin(), m_added_node_params.cend(), std::back_inserter(lAddresses));
}
- // Build a map of all already connected addresses (by IP:port and by name) to inbound/outbound and resolved CService
- std::map<CNetAddr, bool> mapConnected;
+ // Collect all addresses to which we are already connected.
+ std::unordered_set<CNetAddr, CNetAddrHash> connected_to_inbound;
+ std::unordered_set<CService, CServiceHash> connected_to_outbound;
std::map<std::string, std::pair<bool, CService>> mapConnectedByName;
{
LOCK(m_nodes_mutex);
for (const CNode* pnode : m_nodes) {
if (pnode->addr.IsValid()) {
- mapConnected[static_cast<CNetAddr>(pnode->addr)] = pnode->IsInboundConn();
+ if (pnode->IsInboundConn()) {
+ connected_to_inbound.insert(pnode->addr);
+ } else {
+ connected_to_outbound.insert(pnode->addr);
+ }
}
std::string addrName{pnode->m_addr_name};
if (!addrName.empty()) {
mapConnectedByName[std::move(addrName)] = std::make_pair(pnode->IsInboundConn(), static_cast<const CService&>(pnode->addr));
}
}
}
for (const auto& addr : lAddresses) {
CService service(LookupNumeric(addr.m_added_node, GetDefaultPort(addr.m_added_node)));
AddedNodeInfo addedNode{addr, CService(), false, false};
if (service.IsValid()) {
- // strAddNode is an IP:port
- auto it = mapConnected.find(static_cast<CNetAddr>(service));
- if (it != mapConnected.end()) {
+ // addr.m_added_node is an addr:port
+ if (connected_to_outbound.count(service) > 0) {
+ addedNode.resolvedAddress = service;
+ addedNode.fConnected = true;
+ addedNode.fInbound = false;
+ } else if (connected_to_inbound.count(service) > 0) {
addedNode.resolvedAddress = service;
addedNode.fConnected = true;
- addedNode.fInbound = it->second;
+ addedNode.fInbound = true;
}
} else {
- // strAddNode is a name
+ // addr.m_added_node is a name
auto it = mapConnectedByName.find(addr.m_added_node);
if (it != mapConnectedByName.end()) {
addedNode.resolvedAddress = it->second.second;
addedNode.fConnected = true;
addedNode.fInbound = it->second.first;
}
diff --git i/src/netaddress.h w/src/netaddress.h
index ad09f16799..99a3b0fc22 100644
--- i/src/netaddress.h
+++ w/src/netaddress.h
@@ -254,12 +254,13 @@ public:
UnserializeV2Stream(s);
} else {
UnserializeV1Stream(s);
}
}
+ friend class CNetAddrHash;
friend class CSubNet;
private:
/**
* Parse a Tor address and set this object to it.
* [@param](/bitcoin-bitcoin/contributor/param/)[in] addr Address to parse, must be a valid C string, for example
@@ -473,12 +474,36 @@ private:
// will not be gossiped, but continue reading next addresses from the stream.
m_net = NET_IPV6;
m_addr.assign(ADDR_IPV6_SIZE, 0x0);
}
};
+class CNetAddrHash
+{
+public:
+ CNetAddrHash()
+ : m_salt_k0{GetRand<uint64_t>()},
+ m_salt_k1{GetRand<uint64_t>()}
+ {
+ }
+
+ CNetAddrHash(uint64_t salt_k0, uint64_t salt_k1) : m_salt_k0{salt_k0}, m_salt_k1{salt_k1} {}
+
+ size_t operator()(const CNetAddr& a) const noexcept
+ {
+ CSipHasher hasher(m_salt_k0, m_salt_k1);
+ hasher.Write(a.m_net);
+ hasher.Write(a.m_addr);
+ return static_cast<size_t>(hasher.Finalize());
+ }
+
+private:
+ const uint64_t m_salt_k0;
+ const uint64_t m_salt_k1;
+};
+
class CSubNet
{
protected:
/// Network (base) address
CNetAddr network;
/// Netmask, in network byte order
</details>