v30.3-roots.1 · Commit 3: [PATCH 03/68] feat(policy): preserve Roots relay and network controls on Core 30.3
src/net.cpp
Before: src/net.cpp · After: src/net.cpp
File metadata
index 50988114d0b137dd47b388c2678484376612d806..b29cb951b141922e1a5b3c75fc29b7c55ff154df 100644
Download original patch · Release notes
Diff rows 1–377 of 377. Line numbers refer to the original before/after files.
+ Added− RemovedUnmarked lines are context.
Scroll the code area horizontally to read long lines. Before is on the left; after is on the right. Unified view fits narrow screens more easily.
src/net.cpp
CHANGED
|
@@ -108,7 +108,7 @@ const std::string NET_MESSAGE_TYPE_OTHER = "*other*";
|
|
| 108 |
|
| 109 |
static const uint64_t RANDOMIZER_ID_NETGROUP = 0x6c0edd8036ef4036ULL; // SHA256("netgroup")[0:8]
|
| 110 |
static const uint64_t RANDOMIZER_ID_LOCALHOSTNONCE = 0xd93e69e2bbfa5735ULL; // SHA256("localhostnonce")[0:8]
|
| 111 |
-
static const uint64_t RANDOMIZER_ID_ADDRCACHE = 0x1cf2e4ddd306dda9ULL; // SHA256("addrcache")[0:8]
|
| 112 |
//
|
| 113 |
// Global state variables
|
| 114 |
//
|
|
@@ -221,7 +221,7 @@ CService GetLocalAddress(const CNode& peer)
|
|
| 221 |
return GetLocal(peer).value_or(CService{CNetAddr(), GetListenPort()});
|
| 222 |
}
|
| 223 |
|
| 224 |
-
static int GetnScore(const CService& addr)
|
| 225 |
{
|
| 226 |
LOCK(g_maplocalhost_mutex);
|
| 227 |
const auto it = mapLocalHost.find(addr);
|
|
@@ -283,7 +283,8 @@ bool AddLocal(const CService& addr_, int nScore)
|
|
| 283 |
if (!fDiscover && nScore < LOCAL_MANUAL)
|
| 284 |
return false;
|
| 285 |
|
| 286 |
-
if (!g_reachable_nets.Contains(addr))
|
|
|
|
| 287 |
return false;
|
| 288 |
|
| 289 |
LogPrintf("AddLocal(%s,%i)\n", addr.ToStringAddrPort(), nScore);
|
|
@@ -308,9 +309,11 @@ bool AddLocal(const CNetAddr &addr, int nScore)
|
|
| 308 |
|
| 309 |
void RemoveLocal(const CService& addr)
|
| 310 |
{
|
| 311 |
-
LOCK(g_maplocalhost_mutex);
|
| 312 |
-
LogPrintf("RemoveLocal(%s)\n", addr.ToStringAddrPort());
|
| 313 |
-
mapLocalHost.erase(addr);
|
|
|
|
|
|
|
| 314 |
}
|
| 315 |
|
| 316 |
/** vote for a local address */
|
|
@@ -319,7 +322,9 @@ bool SeenLocal(const CService& addr)
|
|
| 319 |
LOCK(g_maplocalhost_mutex);
|
| 320 |
const auto it = mapLocalHost.find(addr);
|
| 321 |
if (it == mapLocalHost.end()) return false;
|
| 322 |
-
++it->second.nScore;
|
|
|
|
|
|
|
| 323 |
return true;
|
| 324 |
}
|
| 325 |
|
|
@@ -462,6 +467,9 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
|
|
| 462 |
std::unique_ptr<i2p::sam::Session> i2p_transient_session;
|
| 463 |
|
| 464 |
for (auto& target_addr: connect_to) {
|
|
|
|
|
|
|
|
|
|
| 465 |
if (target_addr.IsValid()) {
|
| 466 |
const bool use_proxy{GetProxy(target_addr.GetNetwork(), proxy)};
|
| 467 |
bool proxyConnectionFailed = false;
|
|
@@ -521,8 +529,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
|
|
| 521 |
}
|
| 522 |
|
| 523 |
NetPermissionFlags permission_flags = NetPermissionFlags::None;
|
| 524 |
-
std::vector<NetWhitelistPermissions> whitelist_permissions = conn_type == ConnectionType::MANUAL ? vWhitelistedRangeOutgoing : std::vector<NetWhitelistPermissions>{};
|
| 525 |
-
AddWhitelistPermissionFlags(permission_flags, target_addr, whitelist_permissions);
|
| 526 |
|
| 527 |
// Add node
|
| 528 |
NodeId id = GetNewNodeId();
|
|
@@ -530,6 +537,13 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
|
|
| 530 |
if (!addr_bind.IsValid()) {
|
| 531 |
addr_bind = GetBindAddress(*sock);
|
| 532 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 533 |
CNode* pnode = new CNode(id,
|
| 534 |
std::move(sock),
|
| 535 |
target_addr,
|
|
@@ -539,6 +553,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
|
|
| 539 |
pszDest ? pszDest : "",
|
| 540 |
conn_type,
|
| 541 |
/*inbound_onion=*/false,
|
|
|
|
| 542 |
CNodeOptions{
|
| 543 |
.permission_flags = permission_flags,
|
| 544 |
.i2p_sam_session = std::move(i2p_transient_session),
|
|
@@ -586,6 +601,7 @@ void CConnman::AddWhitelistPermissionFlags(NetPermissionFlags& flags, std::optio
|
|
| 586 |
if (whitelist_relay) NetPermissions::AddFlag(flags, NetPermissionFlags::Relay);
|
| 587 |
NetPermissions::AddFlag(flags, NetPermissionFlags::Mempool);
|
| 588 |
NetPermissions::AddFlag(flags, NetPermissionFlags::NoBan);
|
|
|
|
| 589 |
}
|
| 590 |
}
|
| 591 |
|
|
@@ -650,6 +666,7 @@ void CNode::CopyStats(CNodeStats& stats)
|
|
| 650 |
if (info.session_id) stats.m_session_id = HexStr(*info.session_id);
|
| 651 |
}
|
| 652 |
X(m_permission_flags);
|
|
|
|
| 653 |
|
| 654 |
X(m_last_ping_time);
|
| 655 |
X(m_min_ping_time);
|
|
@@ -659,6 +676,8 @@ void CNode::CopyStats(CNodeStats& stats)
|
|
| 659 |
stats.addrLocal = addrLocalUnlocked.IsValid() ? addrLocalUnlocked.ToStringAddrPort() : "";
|
| 660 |
|
| 661 |
X(m_conn_type);
|
|
|
|
|
|
|
| 662 |
}
|
| 663 |
#undef X
|
| 664 |
|
|
@@ -1684,7 +1703,7 @@ std::pair<size_t, bool> CConnman::SocketSendData(CNode& node) const
|
|
| 1684 |
* to forge. In order to partition a node the attacker must be
|
| 1685 |
* simultaneously better at all of them than honest peers.
|
| 1686 |
*/
|
| 1687 |
-
bool CConnman::AttemptToEvictConnection()
|
| 1688 |
{
|
| 1689 |
std::vector<NodeEvictionCandidate> vEvictionCandidates;
|
| 1690 |
{
|
|
@@ -1712,7 +1731,7 @@ bool CConnman::AttemptToEvictConnection()
|
|
| 1712 |
vEvictionCandidates.push_back(candidate);
|
| 1713 |
}
|
| 1714 |
}
|
| 1715 |
-
const std::optional<NodeId> node_id_to_evict = SelectNodeToEvict(std::move(vEvictionCandidates));
|
| 1716 |
if (!node_id_to_evict) {
|
| 1717 |
return false;
|
| 1718 |
}
|
|
@@ -1768,7 +1787,26 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
|
|
| 1768 |
{
|
| 1769 |
int nInbound = 0;
|
| 1770 |
|
| 1771 |
-
const bool inbound_onion = std::find(m_onion_binds.begin(), m_onion_binds.end(), addr_bind) != m_onion_binds.end();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1772 |
|
| 1773 |
// Tor inbound connections do not reveal the peer's actual network address.
|
| 1774 |
// Therefore do not apply address-based whitelist permissions to them.
|
|
@@ -1815,13 +1853,19 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
|
|
| 1815 |
return;
|
| 1816 |
}
|
| 1817 |
|
|
|
|
| 1818 |
if (nInbound >= m_max_inbound)
|
| 1819 |
{
|
| 1820 |
-
if (!AttemptToEvictConnection()) {
|
|
|
|
|
|
|
| 1821 |
// No connection to evict, disconnect the new connection
|
| 1822 |
LogDebug(BCLog::NET, "failed to find an eviction candidate - connection dropped (full)\n");
|
| 1823 |
return;
|
| 1824 |
}
|
|
|
|
|
|
|
|
|
|
| 1825 |
}
|
| 1826 |
|
| 1827 |
NodeId id = GetNewNodeId();
|
|
@@ -1832,6 +1876,11 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
|
|
| 1832 |
ServiceFlags local_services = GetLocalServices();
|
| 1833 |
const bool use_v2transport(local_services & NODE_P2P_V2);
|
| 1834 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1835 |
CNode* pnode = new CNode(id,
|
| 1836 |
std::move(sock),
|
| 1837 |
CAddress{addr, NODE_NONE},
|
|
@@ -1841,9 +1890,11 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
|
|
| 1841 |
/*addrNameIn=*/"",
|
| 1842 |
ConnectionType::INBOUND,
|
| 1843 |
inbound_onion,
|
|
|
|
| 1844 |
CNodeOptions{
|
| 1845 |
.permission_flags = permission_flags,
|
| 1846 |
.prefer_evict = discouraged,
|
|
|
|
| 1847 |
.recv_flood_size = nReceiveFloodSize,
|
| 1848 |
.use_v2transport = use_v2transport,
|
| 1849 |
});
|
|
@@ -1937,7 +1988,7 @@ void CConnman::DisconnectNodes()
|
|
| 1937 |
// Add to reconnection list if appropriate. We don't reconnect right here, because
|
| 1938 |
// the creation of a connection is a blocking operation (up to several seconds),
|
| 1939 |
// and we don't want to hold up the socket handler thread for that long.
|
| 1940 |
-
if (network_active && pnode->m_transport->ShouldReconnectV1()) {
|
| 1941 |
reconnections_to_add.push_back({
|
| 1942 |
.addr_connect = pnode->addr,
|
| 1943 |
.grant = std::move(pnode->grantOutbound),
|
|
@@ -1996,16 +2047,15 @@ void CConnman::NotifyNumConnectionsChanged()
|
|
| 1996 |
}
|
| 1997 |
}
|
| 1998 |
|
| 1999 |
-
bool CConnman::ShouldRunInactivityChecks(const CNode& node, std::chrono::seconds now) const
|
| 2000 |
{
|
| 2001 |
return node.m_connected + m_peer_connect_timeout < now;
|
| 2002 |
}
|
| 2003 |
|
| 2004 |
-
bool CConnman::InactivityCheck(const CNode& node) const
|
| 2005 |
{
|
| 2006 |
// Tests that see disconnects after using mocktime can start nodes with a
|
| 2007 |
// large timeout. For example, -peertimeout=999999999.
|
| 2008 |
-
const auto now{GetTime<std::chrono::seconds>()};
|
| 2009 |
const auto last_send{node.m_last_send.load()};
|
| 2010 |
const auto last_recv{node.m_last_recv.load()};
|
| 2011 |
|
|
@@ -2029,7 +2079,7 @@ bool CConnman::InactivityCheck(const CNode& node) const
|
|
| 2029 |
|
| 2030 |
if (now > last_send + TIMEOUT_INTERVAL) {
|
| 2031 |
LogDebug(BCLog::NET,
|
| 2032 |
-
"socket sending timeout: %is, %s\n", count_seconds(now - last_send),
|
| 2033 |
node.DisconnectMsg(fLogIPs)
|
| 2034 |
);
|
| 2035 |
return true;
|
|
@@ -2037,7 +2087,7 @@ bool CConnman::InactivityCheck(const CNode& node) const
|
|
| 2037 |
|
| 2038 |
if (now > last_recv + TIMEOUT_INTERVAL) {
|
| 2039 |
LogDebug(BCLog::NET,
|
| 2040 |
-
"socket receive timeout: %is, %s\n", count_seconds(now - last_recv),
|
| 2041 |
node.DisconnectMsg(fLogIPs)
|
| 2042 |
);
|
| 2043 |
return true;
|
|
@@ -2119,6 +2169,8 @@ void CConnman::SocketHandlerConnected(const std::vector<CNode*>& nodes,
|
|
| 2119 |
{
|
| 2120 |
AssertLockNotHeld(m_total_bytes_sent_mutex);
|
| 2121 |
|
|
|
|
|
|
|
| 2122 |
for (CNode* pnode : nodes) {
|
| 2123 |
if (interruptNet)
|
| 2124 |
return;
|
|
@@ -2209,7 +2261,7 @@ void CConnman::SocketHandlerConnected(const std::vector<CNode*>& nodes,
|
|
| 2209 |
}
|
| 2210 |
}
|
| 2211 |
|
| 2212 |
-
if (InactivityCheck(*pnode)) pnode->fDisconnect = true;
|
| 2213 |
}
|
| 2214 |
}
|
| 2215 |
|
|
@@ -2500,6 +2552,11 @@ bool CConnman::MultipleManualOrFullOutboundConns(Network net) const
|
|
| 2500 |
return m_network_conn_counts[net] > 1;
|
| 2501 |
}
|
| 2502 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2503 |
bool CConnman::MaybePickPreferredNetwork(std::optional<Network>& network)
|
| 2504 |
{
|
| 2505 |
std::array<Network, 5> nets{NET_IPV4, NET_IPV6, NET_ONION, NET_I2P, NET_CJDNS};
|
|
@@ -3052,6 +3109,8 @@ void CConnman::ThreadMessageHandler()
|
|
| 3052 |
if (pnode->fDisconnect)
|
| 3053 |
continue;
|
| 3054 |
|
|
|
|
|
|
|
| 3055 |
// Receive messages
|
| 3056 |
bool fMoreNodeWork = m_msgproc->ProcessMessages(pnode, flagInterruptMsgProc);
|
| 3057 |
fMoreWork |= (fMoreNodeWork && !pnode->fPauseSend);
|
|
@@ -3289,7 +3348,29 @@ bool CConnman::InitBinds(const Options& options)
|
|
| 3289 |
inaddr_any.s_addr = htonl(INADDR_ANY);
|
| 3290 |
const CService ipv4_any{inaddr_any, GetListenPort()}; // 0.0.0.0
|
| 3291 |
if (!Bind(ipv4_any, BF_REPORT_ERROR, NetPermissionFlags::None)) {
|
| 3292 |
-
return false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3293 |
}
|
| 3294 |
}
|
| 3295 |
return true;
|
|
@@ -3460,6 +3541,8 @@ void CConnman::StopThreads()
|
|
| 3460 |
|
| 3461 |
void CConnman::StopNodes()
|
| 3462 |
{
|
|
|
|
|
|
|
| 3463 |
if (fAddressesInitialized) {
|
| 3464 |
DumpAddresses();
|
| 3465 |
fAddressesInitialized = false;
|
|
@@ -3487,6 +3570,7 @@ void CConnman::StopNodes()
|
|
| 3487 |
DeleteNode(pnode);
|
| 3488 |
}
|
| 3489 |
m_nodes_disconnected.clear();
|
|
|
|
| 3490 |
vhListenSocket.clear();
|
| 3491 |
semOutbound.reset();
|
| 3492 |
semAddnode.reset();
|
|
@@ -3519,15 +3603,9 @@ std::vector<CAddress> CConnman::GetAddressesUnsafe(size_t max_addresses, size_t
|
|
| 3519 |
std::vector<CAddress> CConnman::GetAddresses(CNode& requestor, size_t max_addresses, size_t max_pct)
|
| 3520 |
{
|
| 3521 |
auto local_socket_bytes = requestor.addrBind.GetAddrBytes();
|
| 3522 |
-
uint64_t cache_id = GetDeterministicRandomizer(RANDOMIZER_ID_ADDRCACHE)
|
| 3523 |
-
.Write(requestor.ConnectedThroughNetwork())
|
| 3524 |
-
.Write(local_socket_bytes)
|
| 3525 |
-
// For outbound connections, the port of the bound address is randomly
|
| 3526 |
-
// assigned by the OS and would therefore not be useful for seeding.
|
| 3527 |
-
.Write(requestor.IsInboundConn() ? requestor.addrBind.GetPort() : 0)
|
| 3528 |
-
.Finalize();
|
| 3529 |
const auto current_time = GetTime<std::chrono::microseconds>();
|
| 3530 |
-
auto r = m_addr_response_caches.emplace(cache_id, CachedAddrResponse{});
|
| 3531 |
CachedAddrResponse& cache_entry = r.first->second;
|
| 3532 |
if (cache_entry.m_cache_entry_expiration < current_time) { // If emplace() added new one it has expiration 0.
|
| 3533 |
cache_entry.m_addrs_response_cache = GetAddressesUnsafe(max_addresses, max_pct, /*network=*/std::nullopt);
|
|
@@ -3563,12 +3641,17 @@ std::vector<CAddress> CConnman::GetAddresses(CNode& requestor, size_t max_addres
|
|
| 3563 |
|
| 3564 |
bool CConnman::AddNode(const AddedNodeParams& add)
|
| 3565 |
{
|
| 3566 |
-
const CService resolved(LookupNumeric(add.m_added_node, GetDefaultPort(add.m_added_node)));
|
| 3567 |
-
const bool resolved_is_valid{resolved.IsValid()};
|
| 3568 |
|
| 3569 |
LOCK(m_added_nodes_mutex);
|
| 3570 |
for (const auto& it : m_added_node_params) {
|
| 3571 |
-
if (add.m_added_node == it.m_added_node || (resolved_is_valid && resolved == LookupNumeric(it.m_added_node, GetDefaultPort(it.m_added_node)))) return false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3572 |
}
|
| 3573 |
|
| 3574 |
m_added_node_params.push_back(add);
|
|
@@ -3804,6 +3887,7 @@ CNode::CNode(NodeId idIn,
|
|
| 3804 |
const std::string& addrNameIn,
|
| 3805 |
ConnectionType conn_type_in,
|
| 3806 |
bool inbound_onion,
|
|
|
|
| 3807 |
CNodeOptions&& node_opts)
|
| 3808 |
: m_transport{MakeTransport(idIn, node_opts.use_v2transport, conn_type_in == ConnectionType::INBOUND)},
|
| 3809 |
m_permission_flags{node_opts.permission_flags},
|
|
@@ -3815,7 +3899,9 @@ CNode::CNode(NodeId idIn,
|
|
| 3815 |
m_dest(addrNameIn),
|
| 3816 |
m_inbound_onion{inbound_onion},
|
| 3817 |
m_prefer_evict{node_opts.prefer_evict},
|
|
|
|
| 3818 |
nKeyedNetGroup{nKeyedNetGroupIn},
|
|
|
|
| 3819 |
m_conn_type{conn_type_in},
|
| 3820 |
id{idIn},
|
| 3821 |
nLocalHostNonce{nLocalHostNonceIn},
|
|
@@ -3877,7 +3963,7 @@ void CConnman::PushMessage(CNode* pnode, CSerializedNetMsg&& msg)
|
|
| 3877 |
AssertLockNotHeld(m_total_bytes_sent_mutex);
|
| 3878 |
size_t nMessageSize = msg.data.size();
|
| 3879 |
LogDebug(BCLog::NET, "sending %s (%d bytes) peer=%d\n", msg.m_type, nMessageSize, pnode->GetId());
|
| 3880 |
-
if (gArgs.GetBoolArg("-capturemessages", false)) {
|
| 3881 |
CaptureMessage(pnode->addr, msg.m_type, msg.data, /*is_incoming=*/false);
|
| 3882 |
}
|
| 3883 |
|
|
|
|
| 108 |
|
| 109 |
static const uint64_t RANDOMIZER_ID_NETGROUP = 0x6c0edd8036ef4036ULL; // SHA256("netgroup")[0:8]
|
| 110 |
static const uint64_t RANDOMIZER_ID_LOCALHOSTNONCE = 0xd93e69e2bbfa5735ULL; // SHA256("localhostnonce")[0:8]
|
| 111 |
+
static const uint64_t RANDOMIZER_ID_NETWORKKEY = 0x0e8a2b136c592a7dULL; // SHA256("networkkey")[0:8]
|
| 112 |
//
|
| 113 |
// Global state variables
|
| 114 |
//
|
|
|
|
| 221 |
return GetLocal(peer).value_or(CService{CNetAddr(), GetListenPort()});
|
| 222 |
}
|
| 223 |
|
| 224 |
+
int GetnScore(const CService& addr)
|
| 225 |
{
|
| 226 |
LOCK(g_maplocalhost_mutex);
|
| 227 |
const auto it = mapLocalHost.find(addr);
|
|
|
|
| 283 |
if (!fDiscover && nScore < LOCAL_MANUAL)
|
| 284 |
return false;
|
| 285 |
|
| 286 |
+
// IPv4 and IPv6 cannot be connected to unless their networks are reachable, but Tor is not necessarily bidirectional
|
| 287 |
+
if (!(g_reachable_nets.Contains(addr) || addr.IsTor()))
|
| 288 |
return false;
|
| 289 |
|
| 290 |
LogPrintf("AddLocal(%s,%i)\n", addr.ToStringAddrPort(), nScore);
|
|
|
|
| 309 |
|
| 310 |
void RemoveLocal(const CService& addr)
|
| 311 |
{
|
| 312 |
+
{
|
| 313 |
+
LOCK(g_maplocalhost_mutex);
|
| 314 |
+
LogPrintf("RemoveLocal(%s)\n", addr.ToStringAddrPort());
|
| 315 |
+
mapLocalHost.erase(addr);
|
| 316 |
+
}
|
| 317 |
}
|
| 318 |
|
| 319 |
/** vote for a local address */
|
|
|
|
| 322 |
LOCK(g_maplocalhost_mutex);
|
| 323 |
const auto it = mapLocalHost.find(addr);
|
| 324 |
if (it == mapLocalHost.end()) return false;
|
| 325 |
+
if (it->second.nScore < std::numeric_limits<int>::max()) {
|
| 326 |
+
++it->second.nScore;
|
| 327 |
+
}
|
| 328 |
return true;
|
| 329 |
}
|
| 330 |
|
|
|
|
| 467 |
std::unique_ptr<i2p::sam::Session> i2p_transient_session;
|
| 468 |
|
| 469 |
for (auto& target_addr: connect_to) {
|
| 470 |
+
if (DisableV1OnClearnet(target_addr.GetNetClass()) && !use_v2transport) {
|
| 471 |
+
continue;
|
| 472 |
+
}
|
| 473 |
if (target_addr.IsValid()) {
|
| 474 |
const bool use_proxy{GetProxy(target_addr.GetNetwork(), proxy)};
|
| 475 |
bool proxyConnectionFailed = false;
|
|
|
|
| 529 |
}
|
| 530 |
|
| 531 |
NetPermissionFlags permission_flags = NetPermissionFlags::None;
|
| 532 |
+
AddWhitelistPermissionFlags(permission_flags, target_addr, vWhitelistedRangeOutgoing);
|
|
|
|
| 533 |
|
| 534 |
// Add node
|
| 535 |
NodeId id = GetNewNodeId();
|
|
|
|
| 537 |
if (!addr_bind.IsValid()) {
|
| 538 |
addr_bind = GetBindAddress(*sock);
|
| 539 |
}
|
| 540 |
+
uint64_t network_id = GetDeterministicRandomizer(RANDOMIZER_ID_NETWORKKEY)
|
| 541 |
+
.Write(target_addr.GetNetClass())
|
| 542 |
+
.Write(addr_bind.GetAddrBytes())
|
| 543 |
+
// For outbound connections, the port of the bound address is randomly
|
| 544 |
+
// assigned by the OS and would therefore not be useful for seeding.
|
| 545 |
+
.Write(0)
|
| 546 |
+
.Finalize();
|
| 547 |
CNode* pnode = new CNode(id,
|
| 548 |
std::move(sock),
|
| 549 |
target_addr,
|
|
|
|
| 553 |
pszDest ? pszDest : "",
|
| 554 |
conn_type,
|
| 555 |
/*inbound_onion=*/false,
|
| 556 |
+
network_id,
|
| 557 |
CNodeOptions{
|
| 558 |
.permission_flags = permission_flags,
|
| 559 |
.i2p_sam_session = std::move(i2p_transient_session),
|
|
|
|
| 601 |
if (whitelist_relay) NetPermissions::AddFlag(flags, NetPermissionFlags::Relay);
|
| 602 |
NetPermissions::AddFlag(flags, NetPermissionFlags::Mempool);
|
| 603 |
NetPermissions::AddFlag(flags, NetPermissionFlags::NoBan);
|
| 604 |
+
NetPermissions::AddFlag(flags, NetPermissionFlags::Addr);
|
| 605 |
}
|
| 606 |
}
|
| 607 |
|
|
|
|
| 666 |
if (info.session_id) stats.m_session_id = HexStr(*info.session_id);
|
| 667 |
}
|
| 668 |
X(m_permission_flags);
|
| 669 |
+
X(m_forced_inbound);
|
| 670 |
|
| 671 |
X(m_last_ping_time);
|
| 672 |
X(m_min_ping_time);
|
|
|
|
| 676 |
stats.addrLocal = addrLocalUnlocked.IsValid() ? addrLocalUnlocked.ToStringAddrPort() : "";
|
| 677 |
|
| 678 |
X(m_conn_type);
|
| 679 |
+
|
| 680 |
+
X(m_cpu_time);
|
| 681 |
}
|
| 682 |
#undef X
|
| 683 |
|
|
|
|
| 1703 |
* to forge. In order to partition a node the attacker must be
|
| 1704 |
* simultaneously better at all of them than honest peers.
|
| 1705 |
*/
|
| 1706 |
+
bool CConnman::AttemptToEvictConnection(bool force)
|
| 1707 |
{
|
| 1708 |
std::vector<NodeEvictionCandidate> vEvictionCandidates;
|
| 1709 |
{
|
|
|
|
| 1731 |
vEvictionCandidates.push_back(candidate);
|
| 1732 |
}
|
| 1733 |
}
|
| 1734 |
+
const std::optional<NodeId> node_id_to_evict = SelectNodeToEvict(std::move(vEvictionCandidates), force);
|
| 1735 |
if (!node_id_to_evict) {
|
| 1736 |
return false;
|
| 1737 |
}
|
|
|
|
| 1787 |
{
|
| 1788 |
int nInbound = 0;
|
| 1789 |
|
| 1790 |
+
const bool inbound_onion = [this, &addr, &addr_bind]{
|
| 1791 |
+
if (m_onion_binds.empty()) {
|
| 1792 |
+
if (!m_listenonion) {
|
| 1793 |
+
// If -listenonion=0, assume we do not have inbound Tor connections on non-onion listeners
|
| 1794 |
+
return false;
|
| 1795 |
+
}
|
| 1796 |
+
// Tor connections are coming in on the first -bind
|
| 1797 |
+
if ((!m_normal_binds.empty()) && addr_bind == m_normal_binds.front()) {
|
| 1798 |
+
if (addr_bind.IsBindAny()) {
|
| 1799 |
+
// Tor connections should have a source IP that is local
|
| 1800 |
+
return addr.IsLocal();
|
| 1801 |
+
}
|
| 1802 |
+
// Otherwise, the source IP is unpredictable, so assume anything could be onion
|
| 1803 |
+
return true;
|
| 1804 |
+
}
|
| 1805 |
+
return false;
|
| 1806 |
+
} else {
|
| 1807 |
+
return std::find(m_onion_binds.begin(), m_onion_binds.end(), addr_bind) != m_onion_binds.end();
|
| 1808 |
+
}
|
| 1809 |
+
}();
|
| 1810 |
|
| 1811 |
// Tor inbound connections do not reveal the peer's actual network address.
|
| 1812 |
// Therefore do not apply address-based whitelist permissions to them.
|
|
|
|
| 1853 |
return;
|
| 1854 |
}
|
| 1855 |
|
| 1856 |
+
bool forced{false};
|
| 1857 |
if (nInbound >= m_max_inbound)
|
| 1858 |
{
|
| 1859 |
+
// If the inbound connection attempt is granted ForceInbound permission, try a little harder
|
| 1860 |
+
// to make room by evicting a peer we may not have otherwise evicted.
|
| 1861 |
+
if (!AttemptToEvictConnection(NetPermissions::HasFlag(permission_flags, NetPermissionFlags::ForceInbound))) {
|
| 1862 |
// No connection to evict, disconnect the new connection
|
| 1863 |
LogDebug(BCLog::NET, "failed to find an eviction candidate - connection dropped (full)\n");
|
| 1864 |
return;
|
| 1865 |
}
|
| 1866 |
+
|
| 1867 |
+
// We kicked someone out
|
| 1868 |
+
forced = true;
|
| 1869 |
}
|
| 1870 |
|
| 1871 |
NodeId id = GetNewNodeId();
|
|
|
|
| 1876 |
ServiceFlags local_services = GetLocalServices();
|
| 1877 |
const bool use_v2transport(local_services & NODE_P2P_V2);
|
| 1878 |
|
| 1879 |
+
uint64_t network_id = GetDeterministicRandomizer(RANDOMIZER_ID_NETWORKKEY)
|
| 1880 |
+
.Write(inbound_onion ? NET_ONION : addr.GetNetClass())
|
| 1881 |
+
.Write(addr_bind.GetAddrBytes())
|
| 1882 |
+
.Write(addr_bind.GetPort()) // inbound connections use bind port
|
| 1883 |
+
.Finalize();
|
| 1884 |
CNode* pnode = new CNode(id,
|
| 1885 |
std::move(sock),
|
| 1886 |
CAddress{addr, NODE_NONE},
|
|
|
|
| 1890 |
/*addrNameIn=*/"",
|
| 1891 |
ConnectionType::INBOUND,
|
| 1892 |
inbound_onion,
|
| 1893 |
+
network_id,
|
| 1894 |
CNodeOptions{
|
| 1895 |
.permission_flags = permission_flags,
|
| 1896 |
.prefer_evict = discouraged,
|
| 1897 |
+
.forced_inbound = forced,
|
| 1898 |
.recv_flood_size = nReceiveFloodSize,
|
| 1899 |
.use_v2transport = use_v2transport,
|
| 1900 |
});
|
|
|
|
| 1988 |
// Add to reconnection list if appropriate. We don't reconnect right here, because
|
| 1989 |
// the creation of a connection is a blocking operation (up to several seconds),
|
| 1990 |
// and we don't want to hold up the socket handler thread for that long.
|
| 1991 |
+
if (network_active && pnode->m_transport->ShouldReconnectV1() && !DisableV1OnClearnet(pnode->addr.GetNetClass())) {
|
| 1992 |
reconnections_to_add.push_back({
|
| 1993 |
.addr_connect = pnode->addr,
|
| 1994 |
.grant = std::move(pnode->grantOutbound),
|
|
|
|
| 2047 |
}
|
| 2048 |
}
|
| 2049 |
|
| 2050 |
+
bool CConnman::ShouldRunInactivityChecks(const CNode& node, std::chrono::microseconds now) const
|
| 2051 |
{
|
| 2052 |
return node.m_connected + m_peer_connect_timeout < now;
|
| 2053 |
}
|
| 2054 |
|
| 2055 |
+
bool CConnman::InactivityCheck(const CNode& node, std::chrono::microseconds now) const
|
| 2056 |
{
|
| 2057 |
// Tests that see disconnects after using mocktime can start nodes with a
|
| 2058 |
// large timeout. For example, -peertimeout=999999999.
|
|
|
|
| 2059 |
const auto last_send{node.m_last_send.load()};
|
| 2060 |
const auto last_recv{node.m_last_recv.load()};
|
| 2061 |
|
|
|
|
| 2079 |
|
| 2080 |
if (now > last_send + TIMEOUT_INTERVAL) {
|
| 2081 |
LogDebug(BCLog::NET,
|
| 2082 |
+
"socket sending timeout: %is, %s\n", Ticks<std::chrono::seconds>(now - last_send),
|
| 2083 |
node.DisconnectMsg(fLogIPs)
|
| 2084 |
);
|
| 2085 |
return true;
|
|
|
|
| 2087 |
|
| 2088 |
if (now > last_recv + TIMEOUT_INTERVAL) {
|
| 2089 |
LogDebug(BCLog::NET,
|
| 2090 |
+
"socket receive timeout: %is, %s\n", Ticks<std::chrono::seconds>(now - last_recv),
|
| 2091 |
node.DisconnectMsg(fLogIPs)
|
| 2092 |
);
|
| 2093 |
return true;
|
|
|
|
| 2169 |
{
|
| 2170 |
AssertLockNotHeld(m_total_bytes_sent_mutex);
|
| 2171 |
|
| 2172 |
+
auto now = GetTime<std::chrono::microseconds>();
|
| 2173 |
+
|
| 2174 |
for (CNode* pnode : nodes) {
|
| 2175 |
if (interruptNet)
|
| 2176 |
return;
|
|
|
|
| 2261 |
}
|
| 2262 |
}
|
| 2263 |
|
| 2264 |
+
if (InactivityCheck(*pnode, now)) pnode->fDisconnect = true;
|
| 2265 |
}
|
| 2266 |
}
|
| 2267 |
|
|
|
|
| 2552 |
return m_network_conn_counts[net] > 1;
|
| 2553 |
}
|
| 2554 |
|
| 2555 |
+
bool CConnman::DisableV1OnClearnet(Network net) const
|
| 2556 |
+
{
|
| 2557 |
+
return disable_v1conn_clearnet && (net == NET_IPV4 || net == NET_IPV6);
|
| 2558 |
+
}
|
| 2559 |
+
|
| 2560 |
bool CConnman::MaybePickPreferredNetwork(std::optional<Network>& network)
|
| 2561 |
{
|
| 2562 |
std::array<Network, 5> nets{NET_IPV4, NET_IPV6, NET_ONION, NET_I2P, NET_CJDNS};
|
|
|
|
| 3109 |
if (pnode->fDisconnect)
|
| 3110 |
continue;
|
| 3111 |
|
| 3112 |
+
CpuTimer timer{[&pnode](std::chrono::nanoseconds elapsed) { pnode->m_cpu_time += elapsed; }};
|
| 3113 |
+
|
| 3114 |
// Receive messages
|
| 3115 |
bool fMoreNodeWork = m_msgproc->ProcessMessages(pnode, flagInterruptMsgProc);
|
| 3116 |
fMoreWork |= (fMoreNodeWork && !pnode->fPauseSend);
|
|
|
|
| 3348 |
inaddr_any.s_addr = htonl(INADDR_ANY);
|
| 3349 |
const CService ipv4_any{inaddr_any, GetListenPort()}; // 0.0.0.0
|
| 3350 |
if (!Bind(ipv4_any, BF_REPORT_ERROR, NetPermissionFlags::None)) {
|
| 3351 |
+
int defaultPort = Params().GetDefaultPort();
|
| 3352 |
+
// If listening failed and another port than the standard port was specified,
|
| 3353 |
+
// ask if the user wants to connect via the standard port for the network instead
|
| 3354 |
+
if (GetListenPort() != defaultPort) {
|
| 3355 |
+
bool fRet = uiInterface.ThreadSafeQuestion(
|
| 3356 |
+
strprintf(_("Do you want to use the standard network port for %s (port %s) instead?"), CLIENT_NAME, defaultPort),
|
| 3357 |
+
strprintf(_("Listen on port %s failed."), GetListenPort()).translated,
|
| 3358 |
+
"", CClientUIInterface::MSG_INFORMATION | CClientUIInterface::MODAL | CClientUIInterface::BTN_OK | CClientUIInterface::BTN_ABORT);
|
| 3359 |
+
|
| 3360 |
+
if (fRet) {
|
| 3361 |
+
// FIXME: Unbind IPv6 on the other port
|
| 3362 |
+
|
| 3363 |
+
gArgs.ForceSetArg("-port", defaultPort);
|
| 3364 |
+
// Attempt to use standard port
|
| 3365 |
+
struct in6_addr inaddr6_any = IN6ADDR_ANY_INIT;
|
| 3366 |
+
Bind(CService(inaddr6_any, defaultPort), BF_NONE, NetPermissionFlags::None);
|
| 3367 |
+
struct in_addr inaddr_any;
|
| 3368 |
+
inaddr_any.s_addr = INADDR_ANY;
|
| 3369 |
+
if (!Bind(CService(inaddr_any, defaultPort), BF_REPORT_ERROR, NetPermissionFlags::None)) {
|
| 3370 |
+
return false;
|
| 3371 |
+
}
|
| 3372 |
+
}
|
| 3373 |
+
}
|
| 3374 |
}
|
| 3375 |
}
|
| 3376 |
return true;
|
|
|
|
| 3541 |
|
| 3542 |
void CConnman::StopNodes()
|
| 3543 |
{
|
| 3544 |
+
AssertLockNotHeld(m_reconnections_mutex);
|
| 3545 |
+
|
| 3546 |
if (fAddressesInitialized) {
|
| 3547 |
DumpAddresses();
|
| 3548 |
fAddressesInitialized = false;
|
|
|
|
| 3570 |
DeleteNode(pnode);
|
| 3571 |
}
|
| 3572 |
m_nodes_disconnected.clear();
|
| 3573 |
+
WITH_LOCK(m_reconnections_mutex, m_reconnections.clear());
|
| 3574 |
vhListenSocket.clear();
|
| 3575 |
semOutbound.reset();
|
| 3576 |
semAddnode.reset();
|
|
|
|
| 3603 |
std::vector<CAddress> CConnman::GetAddresses(CNode& requestor, size_t max_addresses, size_t max_pct)
|
| 3604 |
{
|
| 3605 |
auto local_socket_bytes = requestor.addrBind.GetAddrBytes();
|
| 3606 |
+
uint64_t network_id = requestor.m_network_key;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3607 |
const auto current_time = GetTime<std::chrono::microseconds>();
|
| 3608 |
+
auto r = m_addr_response_caches.emplace(network_id, CachedAddrResponse{});
|
| 3609 |
CachedAddrResponse& cache_entry = r.first->second;
|
| 3610 |
if (cache_entry.m_cache_entry_expiration < current_time) { // If emplace() added new one it has expiration 0.
|
| 3611 |
cache_entry.m_addrs_response_cache = GetAddressesUnsafe(max_addresses, max_pct, /*network=*/std::nullopt);
|
|
|
|
| 3641 |
|
| 3642 |
bool CConnman::AddNode(const AddedNodeParams& add)
|
| 3643 |
{
|
| 3644 |
+
const CService resolved{MaybeFlipIPv6toCJDNS(LookupNumeric(add.m_added_node, GetDefaultPort(add.m_added_node)))};
|
| 3645 |
+
const bool resolved_invalid{!resolved.IsValid()};
|
| 3646 |
|
| 3647 |
LOCK(m_added_nodes_mutex);
|
| 3648 |
for (const auto& it : m_added_node_params) {
|
| 3649 |
+
if (add.m_added_node == it.m_added_node) return false;
|
| 3650 |
+
if (resolved_invalid) continue;
|
| 3651 |
+
const CService service{MaybeFlipIPv6toCJDNS(LookupNumeric(it.m_added_node, GetDefaultPort(it.m_added_node)))};
|
| 3652 |
+
if (resolved == service) return false;
|
| 3653 |
+
// Check if CJDNS address matches regardless of port to detect already-connected inbound peers.
|
| 3654 |
+
if (resolved.IsCJDNS() && static_cast<CNetAddr>(resolved) == static_cast<CNetAddr>(service)) return false;
|
| 3655 |
}
|
| 3656 |
|
| 3657 |
m_added_node_params.push_back(add);
|
|
|
|
| 3887 |
const std::string& addrNameIn,
|
| 3888 |
ConnectionType conn_type_in,
|
| 3889 |
bool inbound_onion,
|
| 3890 |
+
uint64_t network_key,
|
| 3891 |
CNodeOptions&& node_opts)
|
| 3892 |
: m_transport{MakeTransport(idIn, node_opts.use_v2transport, conn_type_in == ConnectionType::INBOUND)},
|
| 3893 |
m_permission_flags{node_opts.permission_flags},
|
|
|
|
| 3899 |
m_dest(addrNameIn),
|
| 3900 |
m_inbound_onion{inbound_onion},
|
| 3901 |
m_prefer_evict{node_opts.prefer_evict},
|
| 3902 |
+
m_forced_inbound{node_opts.forced_inbound},
|
| 3903 |
nKeyedNetGroup{nKeyedNetGroupIn},
|
| 3904 |
+
m_network_key{network_key},
|
| 3905 |
m_conn_type{conn_type_in},
|
| 3906 |
id{idIn},
|
| 3907 |
nLocalHostNonce{nLocalHostNonceIn},
|
|
|
|
| 3963 |
AssertLockNotHeld(m_total_bytes_sent_mutex);
|
| 3964 |
size_t nMessageSize = msg.data.size();
|
| 3965 |
LogDebug(BCLog::NET, "sending %s (%d bytes) peer=%d\n", msg.m_type, nMessageSize, pnode->GetId());
|
| 3966 |
+
if (m_capture_messages) {
|
| 3967 |
CaptureMessage(pnode->addr, msg.m_type, msg.data, /*is_incoming=*/false);
|
| 3968 |
}
|
| 3969 |
|