v29.4-roots.1 · Commit 3: [PATCH 03/13] policy: restore conservative relay and network controls
src/net.cpp
Before: src/net.cpp · After: src/net.cpp
File metadata
index 7684877ec352cfbee891ee61501367d1169e88d5..05ee7224226c339bbe7ad04c8fdf1a4dedc199bd 100644
Download original patch · Release notes
Diff rows 1–397 of 397. 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
|
@@ -115,7 +115,7 @@ const std::string NET_MESSAGE_TYPE_OTHER = "*other*";
|
|
| 115 |
|
| 116 |
static const uint64_t RANDOMIZER_ID_NETGROUP = 0x6c0edd8036ef4036ULL; // SHA256("netgroup")[0:8]
|
| 117 |
static const uint64_t RANDOMIZER_ID_LOCALHOSTNONCE = 0xd93e69e2bbfa5735ULL; // SHA256("localhostnonce")[0:8]
|
| 118 |
-
static const uint64_t RANDOMIZER_ID_ADDRCACHE = 0x1cf2e4ddd306dda9ULL; // SHA256("addrcache")[0:8]
|
| 119 |
//
|
| 120 |
// Global state variables
|
| 121 |
//
|
|
@@ -228,7 +228,7 @@ CService GetLocalAddress(const CNode& peer)
|
|
| 228 |
return GetLocal(peer).value_or(CService{CNetAddr(), GetListenPort()});
|
| 229 |
}
|
| 230 |
|
| 231 |
-
static int GetnScore(const CService& addr)
|
| 232 |
{
|
| 233 |
LOCK(g_maplocalhost_mutex);
|
| 234 |
const auto it = mapLocalHost.find(addr);
|
|
@@ -284,7 +284,8 @@ bool AddLocal(const CService& addr_, int nScore)
|
|
| 284 |
if (!fDiscover && nScore < LOCAL_MANUAL)
|
| 285 |
return false;
|
| 286 |
|
| 287 |
-
if (!g_reachable_nets.Contains(addr))
|
|
|
|
| 288 |
return false;
|
| 289 |
|
| 290 |
LogPrintf("AddLocal(%s,%i)\n", addr.ToStringAddrPort(), nScore);
|
|
@@ -309,9 +310,11 @@ bool AddLocal(const CNetAddr &addr, int nScore)
|
|
| 309 |
|
| 310 |
void RemoveLocal(const CService& addr)
|
| 311 |
{
|
| 312 |
-
LOCK(g_maplocalhost_mutex);
|
| 313 |
-
LogPrintf("RemoveLocal(%s)\n", addr.ToStringAddrPort());
|
| 314 |
-
mapLocalHost.erase(addr);
|
|
|
|
|
|
|
| 315 |
}
|
| 316 |
|
| 317 |
/** vote for a local address */
|
|
@@ -320,7 +323,9 @@ bool SeenLocal(const CService& addr)
|
|
| 320 |
LOCK(g_maplocalhost_mutex);
|
| 321 |
const auto it = mapLocalHost.find(addr);
|
| 322 |
if (it == mapLocalHost.end()) return false;
|
| 323 |
-
++it->second.nScore;
|
|
|
|
|
|
|
| 324 |
return true;
|
| 325 |
}
|
| 326 |
|
|
@@ -463,6 +468,9 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
|
|
| 463 |
std::unique_ptr<i2p::sam::Session> i2p_transient_session;
|
| 464 |
|
| 465 |
for (auto& target_addr: connect_to) {
|
|
|
|
|
|
|
|
|
|
| 466 |
if (target_addr.IsValid()) {
|
| 467 |
const bool use_proxy{GetProxy(target_addr.GetNetwork(), proxy)};
|
| 468 |
bool proxyConnectionFailed = false;
|
|
@@ -522,8 +530,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
|
|
| 522 |
}
|
| 523 |
|
| 524 |
NetPermissionFlags permission_flags = NetPermissionFlags::None;
|
| 525 |
-
std::vector<NetWhitelistPermissions> whitelist_permissions = conn_type == ConnectionType::MANUAL ? vWhitelistedRangeOutgoing : std::vector<NetWhitelistPermissions>{};
|
| 526 |
-
AddWhitelistPermissionFlags(permission_flags, target_addr, whitelist_permissions);
|
| 527 |
|
| 528 |
// Add node
|
| 529 |
NodeId id = GetNewNodeId();
|
|
@@ -531,6 +538,13 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
|
|
| 531 |
if (!addr_bind.IsValid()) {
|
| 532 |
addr_bind = GetBindAddress(*sock);
|
| 533 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 534 |
CNode* pnode = new CNode(id,
|
| 535 |
std::move(sock),
|
| 536 |
target_addr,
|
|
@@ -540,6 +554,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
|
|
| 540 |
pszDest ? pszDest : "",
|
| 541 |
conn_type,
|
| 542 |
/*inbound_onion=*/false,
|
|
|
|
| 543 |
CNodeOptions{
|
| 544 |
.permission_flags = permission_flags,
|
| 545 |
.i2p_sam_session = std::move(i2p_transient_session),
|
|
@@ -587,6 +602,7 @@ void CConnman::AddWhitelistPermissionFlags(NetPermissionFlags& flags, std::optio
|
|
| 587 |
if (whitelist_relay) NetPermissions::AddFlag(flags, NetPermissionFlags::Relay);
|
| 588 |
NetPermissions::AddFlag(flags, NetPermissionFlags::Mempool);
|
| 589 |
NetPermissions::AddFlag(flags, NetPermissionFlags::NoBan);
|
|
|
|
| 590 |
}
|
| 591 |
}
|
| 592 |
|
|
@@ -651,6 +667,7 @@ void CNode::CopyStats(CNodeStats& stats)
|
|
| 651 |
if (info.session_id) stats.m_session_id = HexStr(*info.session_id);
|
| 652 |
}
|
| 653 |
X(m_permission_flags);
|
|
|
|
| 654 |
|
| 655 |
X(m_last_ping_time);
|
| 656 |
X(m_min_ping_time);
|
|
@@ -660,6 +677,8 @@ void CNode::CopyStats(CNodeStats& stats)
|
|
| 660 |
stats.addrLocal = addrLocalUnlocked.IsValid() ? addrLocalUnlocked.ToStringAddrPort() : "";
|
| 661 |
|
| 662 |
X(m_conn_type);
|
|
|
|
|
|
|
| 663 |
}
|
| 664 |
#undef X
|
| 665 |
|
|
@@ -1683,7 +1702,7 @@ std::pair<size_t, bool> CConnman::SocketSendData(CNode& node) const
|
|
| 1683 |
* to forge. In order to partition a node the attacker must be
|
| 1684 |
* simultaneously better at all of them than honest peers.
|
| 1685 |
*/
|
| 1686 |
-
bool CConnman::AttemptToEvictConnection()
|
| 1687 |
{
|
| 1688 |
std::vector<NodeEvictionCandidate> vEvictionCandidates;
|
| 1689 |
{
|
|
@@ -1711,7 +1730,7 @@ bool CConnman::AttemptToEvictConnection()
|
|
| 1711 |
vEvictionCandidates.push_back(candidate);
|
| 1712 |
}
|
| 1713 |
}
|
| 1714 |
-
const std::optional<NodeId> node_id_to_evict = SelectNodeToEvict(std::move(vEvictionCandidates));
|
| 1715 |
if (!node_id_to_evict) {
|
| 1716 |
return false;
|
| 1717 |
}
|
|
@@ -1767,7 +1786,26 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
|
|
| 1767 |
{
|
| 1768 |
int nInbound = 0;
|
| 1769 |
|
| 1770 |
-
const bool inbound_onion = std::find(m_onion_binds.begin(), m_onion_binds.end(), addr_bind) != m_onion_binds.end();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1771 |
|
| 1772 |
// Tor inbound connections do not reveal the peer's actual network address.
|
| 1773 |
// Therefore do not apply address-based whitelist permissions to them.
|
|
@@ -1814,13 +1852,19 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
|
|
| 1814 |
return;
|
| 1815 |
}
|
| 1816 |
|
|
|
|
| 1817 |
if (nInbound >= m_max_inbound)
|
| 1818 |
{
|
| 1819 |
-
if (!AttemptToEvictConnection()) {
|
|
|
|
|
|
|
| 1820 |
// No connection to evict, disconnect the new connection
|
| 1821 |
LogDebug(BCLog::NET, "failed to find an eviction candidate - connection dropped (full)\n");
|
| 1822 |
return;
|
| 1823 |
}
|
|
|
|
|
|
|
|
|
|
| 1824 |
}
|
| 1825 |
|
| 1826 |
NodeId id = GetNewNodeId();
|
|
@@ -1831,6 +1875,11 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
|
|
| 1831 |
ServiceFlags local_services = GetLocalServices();
|
| 1832 |
const bool use_v2transport(local_services & NODE_P2P_V2);
|
| 1833 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1834 |
CNode* pnode = new CNode(id,
|
| 1835 |
std::move(sock),
|
| 1836 |
CAddress{addr, NODE_NONE},
|
|
@@ -1840,9 +1889,11 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
|
|
| 1840 |
/*addrNameIn=*/"",
|
| 1841 |
ConnectionType::INBOUND,
|
| 1842 |
inbound_onion,
|
|
|
|
| 1843 |
CNodeOptions{
|
| 1844 |
.permission_flags = permission_flags,
|
| 1845 |
.prefer_evict = discouraged,
|
|
|
|
| 1846 |
.recv_flood_size = nReceiveFloodSize,
|
| 1847 |
.use_v2transport = use_v2transport,
|
| 1848 |
});
|
|
@@ -1913,7 +1964,8 @@ void CConnman::DisconnectNodes()
|
|
| 1913 |
{
|
| 1914 |
LOCK(m_nodes_mutex);
|
| 1915 |
|
| 1916 |
-
if (!fNetworkActive) {
|
|
|
|
| 1917 |
// Disconnect any connected nodes
|
| 1918 |
for (CNode* pnode : m_nodes) {
|
| 1919 |
if (!pnode->fDisconnect) {
|
|
@@ -1935,7 +1987,7 @@ void CConnman::DisconnectNodes()
|
|
| 1935 |
// Add to reconnection list if appropriate. We don't reconnect right here, because
|
| 1936 |
// the creation of a connection is a blocking operation (up to several seconds),
|
| 1937 |
// and we don't want to hold up the socket handler thread for that long.
|
| 1938 |
-
if (pnode->m_transport->ShouldReconnectV1()) {
|
| 1939 |
reconnections_to_add.push_back({
|
| 1940 |
.addr_connect = pnode->addr,
|
| 1941 |
.grant = std::move(pnode->grantOutbound),
|
|
@@ -1994,16 +2046,15 @@ void CConnman::NotifyNumConnectionsChanged()
|
|
| 1994 |
}
|
| 1995 |
}
|
| 1996 |
|
| 1997 |
-
bool CConnman::ShouldRunInactivityChecks(const CNode& node, std::chrono::seconds now) const
|
| 1998 |
{
|
| 1999 |
return node.m_connected + m_peer_connect_timeout < now;
|
| 2000 |
}
|
| 2001 |
|
| 2002 |
-
bool CConnman::InactivityCheck(const CNode& node) const
|
| 2003 |
{
|
| 2004 |
// Tests that see disconnects after using mocktime can start nodes with a
|
| 2005 |
// large timeout. For example, -peertimeout=999999999.
|
| 2006 |
-
const auto now{GetTime<std::chrono::seconds>()};
|
| 2007 |
const auto last_send{node.m_last_send.load()};
|
| 2008 |
const auto last_recv{node.m_last_recv.load()};
|
| 2009 |
|
|
@@ -2027,7 +2078,7 @@ bool CConnman::InactivityCheck(const CNode& node) const
|
|
| 2027 |
|
| 2028 |
if (now > last_send + TIMEOUT_INTERVAL) {
|
| 2029 |
LogDebug(BCLog::NET,
|
| 2030 |
-
"socket sending timeout: %is, %s\n", count_seconds(now - last_send),
|
| 2031 |
node.DisconnectMsg(fLogIPs)
|
| 2032 |
);
|
| 2033 |
return true;
|
|
@@ -2035,7 +2086,7 @@ bool CConnman::InactivityCheck(const CNode& node) const
|
|
| 2035 |
|
| 2036 |
if (now > last_recv + TIMEOUT_INTERVAL) {
|
| 2037 |
LogDebug(BCLog::NET,
|
| 2038 |
-
"socket receive timeout: %is, %s\n", count_seconds(now - last_recv),
|
| 2039 |
node.DisconnectMsg(fLogIPs)
|
| 2040 |
);
|
| 2041 |
return true;
|
|
@@ -2117,6 +2168,8 @@ void CConnman::SocketHandlerConnected(const std::vector<CNode*>& nodes,
|
|
| 2117 |
{
|
| 2118 |
AssertLockNotHeld(m_total_bytes_sent_mutex);
|
| 2119 |
|
|
|
|
|
|
|
| 2120 |
for (CNode* pnode : nodes) {
|
| 2121 |
if (interruptNet)
|
| 2122 |
return;
|
|
@@ -2207,7 +2260,7 @@ void CConnman::SocketHandlerConnected(const std::vector<CNode*>& nodes,
|
|
| 2207 |
}
|
| 2208 |
}
|
| 2209 |
|
| 2210 |
-
if (InactivityCheck(*pnode)) pnode->fDisconnect = true;
|
| 2211 |
}
|
| 2212 |
}
|
| 2213 |
|
|
@@ -2498,6 +2551,11 @@ bool CConnman::MultipleManualOrFullOutboundConns(Network net) const
|
|
| 2498 |
return m_network_conn_counts[net] > 1;
|
| 2499 |
}
|
| 2500 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2501 |
bool CConnman::MaybePickPreferredNetwork(std::optional<Network>& network)
|
| 2502 |
{
|
| 2503 |
std::array<Network, 5> nets{NET_IPV4, NET_IPV6, NET_ONION, NET_I2P, NET_CJDNS};
|
|
@@ -3050,6 +3108,8 @@ void CConnman::ThreadMessageHandler()
|
|
| 3050 |
if (pnode->fDisconnect)
|
| 3051 |
continue;
|
| 3052 |
|
|
|
|
|
|
|
| 3053 |
// Receive messages
|
| 3054 |
bool fMoreNodeWork = m_msgproc->ProcessMessages(pnode, flagInterruptMsgProc);
|
| 3055 |
fMoreWork |= (fMoreNodeWork && !pnode->fPauseSend);
|
|
@@ -3287,7 +3347,29 @@ bool CConnman::InitBinds(const Options& options)
|
|
| 3287 |
inaddr_any.s_addr = htonl(INADDR_ANY);
|
| 3288 |
const CService ipv4_any{inaddr_any, GetListenPort()}; // 0.0.0.0
|
| 3289 |
if (!Bind(ipv4_any, BF_REPORT_ERROR, NetPermissionFlags::None)) {
|
| 3290 |
-
return false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3291 |
}
|
| 3292 |
}
|
| 3293 |
return true;
|
|
@@ -3458,6 +3540,8 @@ void CConnman::StopThreads()
|
|
| 3458 |
|
| 3459 |
void CConnman::StopNodes()
|
| 3460 |
{
|
|
|
|
|
|
|
| 3461 |
if (fAddressesInitialized) {
|
| 3462 |
DumpAddresses();
|
| 3463 |
fAddressesInitialized = false;
|
|
@@ -3485,6 +3569,7 @@ void CConnman::StopNodes()
|
|
| 3485 |
DeleteNode(pnode);
|
| 3486 |
}
|
| 3487 |
m_nodes_disconnected.clear();
|
|
|
|
| 3488 |
vhListenSocket.clear();
|
| 3489 |
semOutbound.reset();
|
| 3490 |
semAddnode.reset();
|
|
@@ -3517,15 +3602,9 @@ std::vector<CAddress> CConnman::GetAddresses(size_t max_addresses, size_t max_pc
|
|
| 3517 |
std::vector<CAddress> CConnman::GetAddresses(CNode& requestor, size_t max_addresses, size_t max_pct)
|
| 3518 |
{
|
| 3519 |
auto local_socket_bytes = requestor.addrBind.GetAddrBytes();
|
| 3520 |
-
uint64_t cache_id = GetDeterministicRandomizer(RANDOMIZER_ID_ADDRCACHE)
|
| 3521 |
-
.Write(requestor.ConnectedThroughNetwork())
|
| 3522 |
-
.Write(local_socket_bytes)
|
| 3523 |
-
// For outbound connections, the port of the bound address is randomly
|
| 3524 |
-
// assigned by the OS and would therefore not be useful for seeding.
|
| 3525 |
-
.Write(requestor.IsInboundConn() ? requestor.addrBind.GetPort() : 0)
|
| 3526 |
-
.Finalize();
|
| 3527 |
const auto current_time = GetTime<std::chrono::microseconds>();
|
| 3528 |
-
auto r = m_addr_response_caches.emplace(cache_id, CachedAddrResponse{});
|
| 3529 |
CachedAddrResponse& cache_entry = r.first->second;
|
| 3530 |
if (cache_entry.m_cache_entry_expiration < current_time) { // If emplace() added new one it has expiration 0.
|
| 3531 |
cache_entry.m_addrs_response_cache = GetAddresses(max_addresses, max_pct, /*network=*/std::nullopt);
|
|
@@ -3561,12 +3640,17 @@ std::vector<CAddress> CConnman::GetAddresses(CNode& requestor, size_t max_addres
|
|
| 3561 |
|
| 3562 |
bool CConnman::AddNode(const AddedNodeParams& add)
|
| 3563 |
{
|
| 3564 |
-
const CService resolved(LookupNumeric(add.m_added_node, GetDefaultPort(add.m_added_node)));
|
| 3565 |
-
const bool resolved_is_valid{resolved.IsValid()};
|
| 3566 |
|
| 3567 |
LOCK(m_added_nodes_mutex);
|
| 3568 |
for (const auto& it : m_added_node_params) {
|
| 3569 |
-
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;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3570 |
}
|
| 3571 |
|
| 3572 |
m_added_node_params.push_back(add);
|
|
@@ -3802,6 +3886,7 @@ CNode::CNode(NodeId idIn,
|
|
| 3802 |
const std::string& addrNameIn,
|
| 3803 |
ConnectionType conn_type_in,
|
| 3804 |
bool inbound_onion,
|
|
|
|
| 3805 |
CNodeOptions&& node_opts)
|
| 3806 |
: m_transport{MakeTransport(idIn, node_opts.use_v2transport, conn_type_in == ConnectionType::INBOUND)},
|
| 3807 |
m_permission_flags{node_opts.permission_flags},
|
|
@@ -3813,7 +3898,9 @@ CNode::CNode(NodeId idIn,
|
|
| 3813 |
m_dest(addrNameIn),
|
| 3814 |
m_inbound_onion{inbound_onion},
|
| 3815 |
m_prefer_evict{node_opts.prefer_evict},
|
|
|
|
| 3816 |
nKeyedNetGroup{nKeyedNetGroupIn},
|
|
|
|
| 3817 |
m_conn_type{conn_type_in},
|
| 3818 |
id{idIn},
|
| 3819 |
nLocalHostNonce{nLocalHostNonceIn},
|
|
@@ -3875,7 +3962,7 @@ void CConnman::PushMessage(CNode* pnode, CSerializedNetMsg&& msg)
|
|
| 3875 |
AssertLockNotHeld(m_total_bytes_sent_mutex);
|
| 3876 |
size_t nMessageSize = msg.data.size();
|
| 3877 |
LogDebug(BCLog::NET, "sending %s (%d bytes) peer=%d\n", msg.m_type, nMessageSize, pnode->GetId());
|
| 3878 |
-
if (gArgs.GetBoolArg("-capturemessages", false)) {
|
| 3879 |
CaptureMessage(pnode->addr, msg.m_type, msg.data, /*is_incoming=*/false);
|
| 3880 |
}
|
| 3881 |
|
|
@@ -4012,6 +4099,11 @@ static void CaptureMessageToFile(const CAddress& addr,
|
|
| 4012 |
uint32_t size = data.size();
|
| 4013 |
ser_writedata32(f, size);
|
| 4014 |
f << data;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4015 |
}
|
| 4016 |
|
| 4017 |
std::function<void(const CAddress& addr,
|
|
|
|
| 115 |
|
| 116 |
static const uint64_t RANDOMIZER_ID_NETGROUP = 0x6c0edd8036ef4036ULL; // SHA256("netgroup")[0:8]
|
| 117 |
static const uint64_t RANDOMIZER_ID_LOCALHOSTNONCE = 0xd93e69e2bbfa5735ULL; // SHA256("localhostnonce")[0:8]
|
| 118 |
+
static const uint64_t RANDOMIZER_ID_NETWORKKEY = 0x0e8a2b136c592a7dULL; // SHA256("networkkey")[0:8]
|
| 119 |
//
|
| 120 |
// Global state variables
|
| 121 |
//
|
|
|
|
| 228 |
return GetLocal(peer).value_or(CService{CNetAddr(), GetListenPort()});
|
| 229 |
}
|
| 230 |
|
| 231 |
+
int GetnScore(const CService& addr)
|
| 232 |
{
|
| 233 |
LOCK(g_maplocalhost_mutex);
|
| 234 |
const auto it = mapLocalHost.find(addr);
|
|
|
|
| 284 |
if (!fDiscover && nScore < LOCAL_MANUAL)
|
| 285 |
return false;
|
| 286 |
|
| 287 |
+
// IPv4 and IPv6 cannot be connected to unless their networks are reachable, but Tor is not necessarily bidirectional
|
| 288 |
+
if (!(g_reachable_nets.Contains(addr) || addr.IsTor()))
|
| 289 |
return false;
|
| 290 |
|
| 291 |
LogPrintf("AddLocal(%s,%i)\n", addr.ToStringAddrPort(), nScore);
|
|
|
|
| 310 |
|
| 311 |
void RemoveLocal(const CService& addr)
|
| 312 |
{
|
| 313 |
+
{
|
| 314 |
+
LOCK(g_maplocalhost_mutex);
|
| 315 |
+
LogPrintf("RemoveLocal(%s)\n", addr.ToStringAddrPort());
|
| 316 |
+
mapLocalHost.erase(addr);
|
| 317 |
+
}
|
| 318 |
}
|
| 319 |
|
| 320 |
/** vote for a local address */
|
|
|
|
| 323 |
LOCK(g_maplocalhost_mutex);
|
| 324 |
const auto it = mapLocalHost.find(addr);
|
| 325 |
if (it == mapLocalHost.end()) return false;
|
| 326 |
+
if (it->second.nScore < std::numeric_limits<int>::max()) {
|
| 327 |
+
++it->second.nScore;
|
| 328 |
+
}
|
| 329 |
return true;
|
| 330 |
}
|
| 331 |
|
|
|
|
| 468 |
std::unique_ptr<i2p::sam::Session> i2p_transient_session;
|
| 469 |
|
| 470 |
for (auto& target_addr: connect_to) {
|
| 471 |
+
if (DisableV1OnClearnet(target_addr.GetNetClass()) && !use_v2transport) {
|
| 472 |
+
continue;
|
| 473 |
+
}
|
| 474 |
if (target_addr.IsValid()) {
|
| 475 |
const bool use_proxy{GetProxy(target_addr.GetNetwork(), proxy)};
|
| 476 |
bool proxyConnectionFailed = false;
|
|
|
|
| 530 |
}
|
| 531 |
|
| 532 |
NetPermissionFlags permission_flags = NetPermissionFlags::None;
|
| 533 |
+
AddWhitelistPermissionFlags(permission_flags, target_addr, vWhitelistedRangeOutgoing);
|
|
|
|
| 534 |
|
| 535 |
// Add node
|
| 536 |
NodeId id = GetNewNodeId();
|
|
|
|
| 538 |
if (!addr_bind.IsValid()) {
|
| 539 |
addr_bind = GetBindAddress(*sock);
|
| 540 |
}
|
| 541 |
+
uint64_t network_id = GetDeterministicRandomizer(RANDOMIZER_ID_NETWORKKEY)
|
| 542 |
+
.Write(target_addr.GetNetClass())
|
| 543 |
+
.Write(addr_bind.GetAddrBytes())
|
| 544 |
+
// For outbound connections, the port of the bound address is randomly
|
| 545 |
+
// assigned by the OS and would therefore not be useful for seeding.
|
| 546 |
+
.Write(0)
|
| 547 |
+
.Finalize();
|
| 548 |
CNode* pnode = new CNode(id,
|
| 549 |
std::move(sock),
|
| 550 |
target_addr,
|
|
|
|
| 554 |
pszDest ? pszDest : "",
|
| 555 |
conn_type,
|
| 556 |
/*inbound_onion=*/false,
|
| 557 |
+
network_id,
|
| 558 |
CNodeOptions{
|
| 559 |
.permission_flags = permission_flags,
|
| 560 |
.i2p_sam_session = std::move(i2p_transient_session),
|
|
|
|
| 602 |
if (whitelist_relay) NetPermissions::AddFlag(flags, NetPermissionFlags::Relay);
|
| 603 |
NetPermissions::AddFlag(flags, NetPermissionFlags::Mempool);
|
| 604 |
NetPermissions::AddFlag(flags, NetPermissionFlags::NoBan);
|
| 605 |
+
NetPermissions::AddFlag(flags, NetPermissionFlags::Addr);
|
| 606 |
}
|
| 607 |
}
|
| 608 |
|
|
|
|
| 667 |
if (info.session_id) stats.m_session_id = HexStr(*info.session_id);
|
| 668 |
}
|
| 669 |
X(m_permission_flags);
|
| 670 |
+
X(m_forced_inbound);
|
| 671 |
|
| 672 |
X(m_last_ping_time);
|
| 673 |
X(m_min_ping_time);
|
|
|
|
| 677 |
stats.addrLocal = addrLocalUnlocked.IsValid() ? addrLocalUnlocked.ToStringAddrPort() : "";
|
| 678 |
|
| 679 |
X(m_conn_type);
|
| 680 |
+
|
| 681 |
+
X(m_cpu_time);
|
| 682 |
}
|
| 683 |
#undef X
|
| 684 |
|
|
|
|
| 1702 |
* to forge. In order to partition a node the attacker must be
|
| 1703 |
* simultaneously better at all of them than honest peers.
|
| 1704 |
*/
|
| 1705 |
+
bool CConnman::AttemptToEvictConnection(bool force)
|
| 1706 |
{
|
| 1707 |
std::vector<NodeEvictionCandidate> vEvictionCandidates;
|
| 1708 |
{
|
|
|
|
| 1730 |
vEvictionCandidates.push_back(candidate);
|
| 1731 |
}
|
| 1732 |
}
|
| 1733 |
+
const std::optional<NodeId> node_id_to_evict = SelectNodeToEvict(std::move(vEvictionCandidates), force);
|
| 1734 |
if (!node_id_to_evict) {
|
| 1735 |
return false;
|
| 1736 |
}
|
|
|
|
| 1786 |
{
|
| 1787 |
int nInbound = 0;
|
| 1788 |
|
| 1789 |
+
const bool inbound_onion = [this, &addr, &addr_bind]{
|
| 1790 |
+
if (m_onion_binds.empty()) {
|
| 1791 |
+
if (!m_listenonion) {
|
| 1792 |
+
// If -listenonion=0, assume we do not have inbound Tor connections on non-onion listeners
|
| 1793 |
+
return false;
|
| 1794 |
+
}
|
| 1795 |
+
// Tor connections are coming in on the first -bind
|
| 1796 |
+
if ((!m_normal_binds.empty()) && addr_bind == m_normal_binds.front()) {
|
| 1797 |
+
if (addr_bind.IsBindAny()) {
|
| 1798 |
+
// Tor connections should have a source IP that is local
|
| 1799 |
+
return addr.IsLocal();
|
| 1800 |
+
}
|
| 1801 |
+
// Otherwise, the source IP is unpredictable, so assume anything could be onion
|
| 1802 |
+
return true;
|
| 1803 |
+
}
|
| 1804 |
+
return false;
|
| 1805 |
+
} else {
|
| 1806 |
+
return std::find(m_onion_binds.begin(), m_onion_binds.end(), addr_bind) != m_onion_binds.end();
|
| 1807 |
+
}
|
| 1808 |
+
}();
|
| 1809 |
|
| 1810 |
// Tor inbound connections do not reveal the peer's actual network address.
|
| 1811 |
// Therefore do not apply address-based whitelist permissions to them.
|
|
|
|
| 1852 |
return;
|
| 1853 |
}
|
| 1854 |
|
| 1855 |
+
bool forced{false};
|
| 1856 |
if (nInbound >= m_max_inbound)
|
| 1857 |
{
|
| 1858 |
+
// If the inbound connection attempt is granted ForceInbound permission, try a little harder
|
| 1859 |
+
// to make room by evicting a peer we may not have otherwise evicted.
|
| 1860 |
+
if (!AttemptToEvictConnection(NetPermissions::HasFlag(permission_flags, NetPermissionFlags::ForceInbound))) {
|
| 1861 |
// No connection to evict, disconnect the new connection
|
| 1862 |
LogDebug(BCLog::NET, "failed to find an eviction candidate - connection dropped (full)\n");
|
| 1863 |
return;
|
| 1864 |
}
|
| 1865 |
+
|
| 1866 |
+
// We kicked someone out
|
| 1867 |
+
forced = true;
|
| 1868 |
}
|
| 1869 |
|
| 1870 |
NodeId id = GetNewNodeId();
|
|
|
|
| 1875 |
ServiceFlags local_services = GetLocalServices();
|
| 1876 |
const bool use_v2transport(local_services & NODE_P2P_V2);
|
| 1877 |
|
| 1878 |
+
uint64_t network_id = GetDeterministicRandomizer(RANDOMIZER_ID_NETWORKKEY)
|
| 1879 |
+
.Write(inbound_onion ? NET_ONION : addr.GetNetClass())
|
| 1880 |
+
.Write(addr_bind.GetAddrBytes())
|
| 1881 |
+
.Write(addr_bind.GetPort()) // inbound connections use bind port
|
| 1882 |
+
.Finalize();
|
| 1883 |
CNode* pnode = new CNode(id,
|
| 1884 |
std::move(sock),
|
| 1885 |
CAddress{addr, NODE_NONE},
|
|
|
|
| 1889 |
/*addrNameIn=*/"",
|
| 1890 |
ConnectionType::INBOUND,
|
| 1891 |
inbound_onion,
|
| 1892 |
+
network_id,
|
| 1893 |
CNodeOptions{
|
| 1894 |
.permission_flags = permission_flags,
|
| 1895 |
.prefer_evict = discouraged,
|
| 1896 |
+
.forced_inbound = forced,
|
| 1897 |
.recv_flood_size = nReceiveFloodSize,
|
| 1898 |
.use_v2transport = use_v2transport,
|
| 1899 |
});
|
|
|
|
| 1964 |
{
|
| 1965 |
LOCK(m_nodes_mutex);
|
| 1966 |
|
| 1967 |
+
const bool network_active{fNetworkActive};
|
| 1968 |
+
if (!network_active) {
|
| 1969 |
// Disconnect any connected nodes
|
| 1970 |
for (CNode* pnode : m_nodes) {
|
| 1971 |
if (!pnode->fDisconnect) {
|
|
|
|
| 1987 |
// Add to reconnection list if appropriate. We don't reconnect right here, because
|
| 1988 |
// the creation of a connection is a blocking operation (up to several seconds),
|
| 1989 |
// and we don't want to hold up the socket handler thread for that long.
|
| 1990 |
+
if (network_active && pnode->m_transport->ShouldReconnectV1() && !DisableV1OnClearnet(pnode->addr.GetNetClass())) {
|
| 1991 |
reconnections_to_add.push_back({
|
| 1992 |
.addr_connect = pnode->addr,
|
| 1993 |
.grant = std::move(pnode->grantOutbound),
|
|
|
|
| 2046 |
}
|
| 2047 |
}
|
| 2048 |
|
| 2049 |
+
bool CConnman::ShouldRunInactivityChecks(const CNode& node, std::chrono::microseconds now) const
|
| 2050 |
{
|
| 2051 |
return node.m_connected + m_peer_connect_timeout < now;
|
| 2052 |
}
|
| 2053 |
|
| 2054 |
+
bool CConnman::InactivityCheck(const CNode& node, std::chrono::microseconds now) const
|
| 2055 |
{
|
| 2056 |
// Tests that see disconnects after using mocktime can start nodes with a
|
| 2057 |
// large timeout. For example, -peertimeout=999999999.
|
|
|
|
| 2058 |
const auto last_send{node.m_last_send.load()};
|
| 2059 |
const auto last_recv{node.m_last_recv.load()};
|
| 2060 |
|
|
|
|
| 2078 |
|
| 2079 |
if (now > last_send + TIMEOUT_INTERVAL) {
|
| 2080 |
LogDebug(BCLog::NET,
|
| 2081 |
+
"socket sending timeout: %is, %s\n", Ticks<std::chrono::seconds>(now - last_send),
|
| 2082 |
node.DisconnectMsg(fLogIPs)
|
| 2083 |
);
|
| 2084 |
return true;
|
|
|
|
| 2086 |
|
| 2087 |
if (now > last_recv + TIMEOUT_INTERVAL) {
|
| 2088 |
LogDebug(BCLog::NET,
|
| 2089 |
+
"socket receive timeout: %is, %s\n", Ticks<std::chrono::seconds>(now - last_recv),
|
| 2090 |
node.DisconnectMsg(fLogIPs)
|
| 2091 |
);
|
| 2092 |
return true;
|
|
|
|
| 2168 |
{
|
| 2169 |
AssertLockNotHeld(m_total_bytes_sent_mutex);
|
| 2170 |
|
| 2171 |
+
auto now = GetTime<std::chrono::microseconds>();
|
| 2172 |
+
|
| 2173 |
for (CNode* pnode : nodes) {
|
| 2174 |
if (interruptNet)
|
| 2175 |
return;
|
|
|
|
| 2260 |
}
|
| 2261 |
}
|
| 2262 |
|
| 2263 |
+
if (InactivityCheck(*pnode, now)) pnode->fDisconnect = true;
|
| 2264 |
}
|
| 2265 |
}
|
| 2266 |
|
|
|
|
| 2551 |
return m_network_conn_counts[net] > 1;
|
| 2552 |
}
|
| 2553 |
|
| 2554 |
+
bool CConnman::DisableV1OnClearnet(Network net) const
|
| 2555 |
+
{
|
| 2556 |
+
return disable_v1conn_clearnet && (net == NET_IPV4 || net == NET_IPV6);
|
| 2557 |
+
}
|
| 2558 |
+
|
| 2559 |
bool CConnman::MaybePickPreferredNetwork(std::optional<Network>& network)
|
| 2560 |
{
|
| 2561 |
std::array<Network, 5> nets{NET_IPV4, NET_IPV6, NET_ONION, NET_I2P, NET_CJDNS};
|
|
|
|
| 3108 |
if (pnode->fDisconnect)
|
| 3109 |
continue;
|
| 3110 |
|
| 3111 |
+
CpuTimer timer{[&pnode](std::chrono::nanoseconds elapsed) { pnode->m_cpu_time += elapsed; }};
|
| 3112 |
+
|
| 3113 |
// Receive messages
|
| 3114 |
bool fMoreNodeWork = m_msgproc->ProcessMessages(pnode, flagInterruptMsgProc);
|
| 3115 |
fMoreWork |= (fMoreNodeWork && !pnode->fPauseSend);
|
|
|
|
| 3347 |
inaddr_any.s_addr = htonl(INADDR_ANY);
|
| 3348 |
const CService ipv4_any{inaddr_any, GetListenPort()}; // 0.0.0.0
|
| 3349 |
if (!Bind(ipv4_any, BF_REPORT_ERROR, NetPermissionFlags::None)) {
|
| 3350 |
+
int defaultPort = Params().GetDefaultPort();
|
| 3351 |
+
// If listening failed and another port than the standard port was specified,
|
| 3352 |
+
// ask if the user wants to connect via the standard port for the network instead
|
| 3353 |
+
if (GetListenPort() != defaultPort) {
|
| 3354 |
+
bool fRet = uiInterface.ThreadSafeQuestion(
|
| 3355 |
+
strprintf(_("Do you want to use the standard network port for %s (port %s) instead?"), CLIENT_NAME, defaultPort),
|
| 3356 |
+
strprintf(_("Listen on port %s failed."), GetListenPort()).translated,
|
| 3357 |
+
"", CClientUIInterface::MSG_INFORMATION | CClientUIInterface::MODAL | CClientUIInterface::BTN_OK | CClientUIInterface::BTN_ABORT);
|
| 3358 |
+
|
| 3359 |
+
if (fRet) {
|
| 3360 |
+
// FIXME: Unbind IPv6 on the other port
|
| 3361 |
+
|
| 3362 |
+
gArgs.ForceSetArg("-port", defaultPort);
|
| 3363 |
+
// Attempt to use standard port
|
| 3364 |
+
struct in6_addr inaddr6_any = IN6ADDR_ANY_INIT;
|
| 3365 |
+
Bind(CService(inaddr6_any, defaultPort), BF_NONE, NetPermissionFlags::None);
|
| 3366 |
+
struct in_addr inaddr_any;
|
| 3367 |
+
inaddr_any.s_addr = INADDR_ANY;
|
| 3368 |
+
if (!Bind(CService(inaddr_any, defaultPort), BF_REPORT_ERROR, NetPermissionFlags::None)) {
|
| 3369 |
+
return false;
|
| 3370 |
+
}
|
| 3371 |
+
}
|
| 3372 |
+
}
|
| 3373 |
}
|
| 3374 |
}
|
| 3375 |
return true;
|
|
|
|
| 3540 |
|
| 3541 |
void CConnman::StopNodes()
|
| 3542 |
{
|
| 3543 |
+
AssertLockNotHeld(m_reconnections_mutex);
|
| 3544 |
+
|
| 3545 |
if (fAddressesInitialized) {
|
| 3546 |
DumpAddresses();
|
| 3547 |
fAddressesInitialized = false;
|
|
|
|
| 3569 |
DeleteNode(pnode);
|
| 3570 |
}
|
| 3571 |
m_nodes_disconnected.clear();
|
| 3572 |
+
WITH_LOCK(m_reconnections_mutex, m_reconnections.clear());
|
| 3573 |
vhListenSocket.clear();
|
| 3574 |
semOutbound.reset();
|
| 3575 |
semAddnode.reset();
|
|
|
|
| 3602 |
std::vector<CAddress> CConnman::GetAddresses(CNode& requestor, size_t max_addresses, size_t max_pct)
|
| 3603 |
{
|
| 3604 |
auto local_socket_bytes = requestor.addrBind.GetAddrBytes();
|
| 3605 |
+
uint64_t network_id = requestor.m_network_key;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3606 |
const auto current_time = GetTime<std::chrono::microseconds>();
|
| 3607 |
+
auto r = m_addr_response_caches.emplace(network_id, CachedAddrResponse{});
|
| 3608 |
CachedAddrResponse& cache_entry = r.first->second;
|
| 3609 |
if (cache_entry.m_cache_entry_expiration < current_time) { // If emplace() added new one it has expiration 0.
|
| 3610 |
cache_entry.m_addrs_response_cache = GetAddresses(max_addresses, max_pct, /*network=*/std::nullopt);
|
|
|
|
| 3640 |
|
| 3641 |
bool CConnman::AddNode(const AddedNodeParams& add)
|
| 3642 |
{
|
| 3643 |
+
const CService resolved{MaybeFlipIPv6toCJDNS(LookupNumeric(add.m_added_node, GetDefaultPort(add.m_added_node)))};
|
| 3644 |
+
const bool resolved_invalid{!resolved.IsValid()};
|
| 3645 |
|
| 3646 |
LOCK(m_added_nodes_mutex);
|
| 3647 |
for (const auto& it : m_added_node_params) {
|
| 3648 |
+
if (add.m_added_node == it.m_added_node) return false;
|
| 3649 |
+
if (resolved_invalid) continue;
|
| 3650 |
+
const CService service{MaybeFlipIPv6toCJDNS(LookupNumeric(it.m_added_node, GetDefaultPort(it.m_added_node)))};
|
| 3651 |
+
if (resolved == service) return false;
|
| 3652 |
+
// Check if CJDNS address matches regardless of port to detect already-connected inbound peers.
|
| 3653 |
+
if (resolved.IsCJDNS() && static_cast<CNetAddr>(resolved) == static_cast<CNetAddr>(service)) return false;
|
| 3654 |
}
|
| 3655 |
|
| 3656 |
m_added_node_params.push_back(add);
|
|
|
|
| 3886 |
const std::string& addrNameIn,
|
| 3887 |
ConnectionType conn_type_in,
|
| 3888 |
bool inbound_onion,
|
| 3889 |
+
uint64_t network_key,
|
| 3890 |
CNodeOptions&& node_opts)
|
| 3891 |
: m_transport{MakeTransport(idIn, node_opts.use_v2transport, conn_type_in == ConnectionType::INBOUND)},
|
| 3892 |
m_permission_flags{node_opts.permission_flags},
|
|
|
|
| 3898 |
m_dest(addrNameIn),
|
| 3899 |
m_inbound_onion{inbound_onion},
|
| 3900 |
m_prefer_evict{node_opts.prefer_evict},
|
| 3901 |
+
m_forced_inbound{node_opts.forced_inbound},
|
| 3902 |
nKeyedNetGroup{nKeyedNetGroupIn},
|
| 3903 |
+
m_network_key{network_key},
|
| 3904 |
m_conn_type{conn_type_in},
|
| 3905 |
id{idIn},
|
| 3906 |
nLocalHostNonce{nLocalHostNonceIn},
|
|
|
|
| 3962 |
AssertLockNotHeld(m_total_bytes_sent_mutex);
|
| 3963 |
size_t nMessageSize = msg.data.size();
|
| 3964 |
LogDebug(BCLog::NET, "sending %s (%d bytes) peer=%d\n", msg.m_type, nMessageSize, pnode->GetId());
|
| 3965 |
+
if (m_capture_messages) {
|
| 3966 |
CaptureMessage(pnode->addr, msg.m_type, msg.data, /*is_incoming=*/false);
|
| 3967 |
}
|
| 3968 |
|
|
|
|
| 4099 |
uint32_t size = data.size();
|
| 4100 |
ser_writedata32(f, size);
|
| 4101 |
f << data;
|
| 4102 |
+
|
| 4103 |
+
if (f.fclose() != 0) {
|
| 4104 |
+
throw std::ios_base::failure(
|
| 4105 |
+
strprintf("Error closing %s after write, file contents are likely incomplete", fs::PathToString(path)));
|
| 4106 |
+
}
|
| 4107 |
}
|
| 4108 |
|
| 4109 |
std::function<void(const CAddress& addr,
|