aboutsummaryrefslogtreecommitdiff
path: root/src/net.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/net.cpp')
-rw-r--r--src/net.cpp537
1 files changed, 308 insertions, 229 deletions
diff --git a/src/net.cpp b/src/net.cpp
index 81176785a2..6f9f17ed4e 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -11,17 +11,20 @@
#include <banman.h>
#include <clientversion.h>
+#include <compat.h>
#include <consensus/consensus.h>
#include <crypto/sha256.h>
+#include <i2p.h>
#include <net_permissions.h>
+#include <netaddress.h>
#include <netbase.h>
#include <node/ui_interface.h>
-#include <optional.h>
#include <protocol.h>
#include <random.h>
#include <scheduler.h>
#include <util/sock.h>
#include <util/strencodings.h>
+#include <util/thread.h>
#include <util/translation.h>
#ifdef WIN32
@@ -30,6 +33,10 @@
#include <fcntl.h>
#endif
+#if HAVE_DECL_GETIFADDRS && HAVE_DECL_FREEIFADDRS
+#include <ifaddrs.h>
+#endif
+
#ifdef USE_POLL
#include <poll.h>
#endif
@@ -37,6 +44,7 @@
#include <algorithm>
#include <cstdint>
#include <functional>
+#include <optional>
#include <unordered_map>
#include <math.h>
@@ -72,16 +80,6 @@ static constexpr std::chrono::seconds MAX_UPLOAD_TIMEFRAME{60 * 60 * 24};
// We add a random period time (0 to 1 seconds) to feeler connections to prevent synchronization.
#define FEELER_SLEEP_WINDOW 1
-// MSG_NOSIGNAL is not available on some platforms, if it doesn't exist define it as 0
-#if !defined(MSG_NOSIGNAL)
-#define MSG_NOSIGNAL 0
-#endif
-
-// MSG_DONTWAIT is not available on some platforms, if it doesn't exist define it as 0
-#if !defined(MSG_DONTWAIT)
-#define MSG_DONTWAIT 0
-#endif
-
/** Used to pass flags to the Bind() function */
enum BindFlags {
BF_NONE = 0,
@@ -121,7 +119,7 @@ void CConnman::AddAddrFetch(const std::string& strDest)
uint16_t GetListenPort()
{
- return (uint16_t)(gArgs.GetArg("-port", Params().GetDefaultPort()));
+ return static_cast<uint16_t>(gArgs.GetArg("-port", Params().GetDefaultPort()));
}
// find 'best' local address for a particular peer
@@ -149,8 +147,8 @@ bool GetLocal(CService& addr, const CNetAddr *paddrPeer)
return nBestScore >= 0;
}
-//! Convert the pnSeed6 array into usable address objects.
-static std::vector<CAddress> convertSeed6(const std::vector<SeedSpec6> &vSeedsIn)
+//! Convert the serialized seeds into usable address objects.
+static std::vector<CAddress> ConvertSeeds(const std::vector<uint8_t> &vSeedsIn)
{
// It'll only connect to one or two seed nodes because once it connects,
// it'll get a pile of addresses with newer timestamps.
@@ -158,13 +156,14 @@ static std::vector<CAddress> convertSeed6(const std::vector<SeedSpec6> &vSeedsIn
// weeks ago.
const int64_t nOneWeek = 7*24*60*60;
std::vector<CAddress> vSeedsOut;
- vSeedsOut.reserve(vSeedsIn.size());
FastRandomContext rng;
- for (const auto& seed_in : vSeedsIn) {
- struct in6_addr ip;
- memcpy(&ip, seed_in.addr, sizeof(ip));
- CAddress addr(CService(ip, seed_in.port), GetDesirableServiceFlags(NODE_NONE));
+ CDataStream s(vSeedsIn, SER_NETWORK, PROTOCOL_VERSION | ADDRV2_FORMAT);
+ while (!s.eof()) {
+ CService endpoint;
+ s >> endpoint;
+ CAddress addr{endpoint, GetDesirableServiceFlags(NODE_NONE)};
addr.nTime = GetTime() - rng.randrange(nOneWeek) - nOneWeek;
+ LogPrint(BCLog::NET, "Added hardcoded seed: %s\n", addr.ToString());
vSeedsOut.push_back(addr);
}
return vSeedsOut;
@@ -201,31 +200,29 @@ bool IsPeerAddrLocalGood(CNode *pnode)
IsReachable(addrLocal.GetNetwork());
}
-// pushes our own address to a peer
-void AdvertiseLocal(CNode *pnode)
+std::optional<CAddress> GetLocalAddrForPeer(CNode *pnode)
{
- if (fListen && pnode->fSuccessfullyConnected)
+ CAddress addrLocal = GetLocalAddress(&pnode->addr, pnode->GetLocalServices());
+ if (gArgs.GetBoolArg("-addrmantest", false)) {
+ // use IPv4 loopback during addrmantest
+ addrLocal = CAddress(CService(LookupNumeric("127.0.0.1", GetListenPort())), pnode->GetLocalServices());
+ }
+ // If discovery is enabled, sometimes give our peer the address it
+ // tells us that it sees us as in case it has a better idea of our
+ // address than we do.
+ FastRandomContext rng;
+ if (IsPeerAddrLocalGood(pnode) && (!addrLocal.IsRoutable() ||
+ rng.randbits((GetnScore(addrLocal) > LOCAL_MANUAL) ? 3 : 1) == 0))
{
- CAddress addrLocal = GetLocalAddress(&pnode->addr, pnode->GetLocalServices());
- if (gArgs.GetBoolArg("-addrmantest", false)) {
- // use IPv4 loopback during addrmantest
- addrLocal = CAddress(CService(LookupNumeric("127.0.0.1", GetListenPort())), pnode->GetLocalServices());
- }
- // If discovery is enabled, sometimes give our peer the address it
- // tells us that it sees us as in case it has a better idea of our
- // address than we do.
- FastRandomContext rng;
- if (IsPeerAddrLocalGood(pnode) && (!addrLocal.IsRoutable() ||
- rng.randbits((GetnScore(addrLocal) > LOCAL_MANUAL) ? 3 : 1) == 0))
- {
- addrLocal.SetIP(pnode->GetAddrLocal());
- }
- if (addrLocal.IsRoutable() || gArgs.GetBoolArg("-addrmantest", false))
- {
- LogPrint(BCLog::NET, "AdvertiseLocal: advertising address %s\n", addrLocal.ToString());
- pnode->PushAddress(addrLocal, rng);
- }
+ addrLocal.SetIP(pnode->GetAddrLocal());
}
+ if (addrLocal.IsRoutable() || gArgs.GetBoolArg("-addrmantest", false))
+ {
+ LogPrint(BCLog::NET, "Advertising address %s to peer=%d\n", addrLocal.ToString(), pnode->GetId());
+ return addrLocal;
+ }
+ // Address is unroutable. Don't advertise.
+ return std::nullopt;
}
// learn a new local address
@@ -404,7 +401,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
pszDest ? 0.0 : (double)(GetAdjustedTime() - addrConnect.nTime)/3600.0);
// Resolve
- const int default_port = Params().GetDefaultPort();
+ const uint16_t default_port{Params().GetDefaultPort()};
if (pszDest) {
std::vector<CService> resolved;
if (Lookup(pszDest, resolved, default_port, fNameLookup && !HaveNameProxy(), 256) && !resolved.empty()) {
@@ -432,10 +429,20 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
bool connected = false;
std::unique_ptr<Sock> sock;
proxyType proxy;
+ CAddress addr_bind;
+ assert(!addr_bind.IsValid());
+
if (addrConnect.IsValid()) {
bool proxyConnectionFailed = false;
- if (GetProxy(addrConnect.GetNetwork(), proxy)) {
+ if (addrConnect.GetNetwork() == NET_I2P && m_i2p_sam_session.get() != nullptr) {
+ i2p::Connection conn;
+ if (m_i2p_sam_session->Connect(addrConnect, conn, proxyConnectionFailed)) {
+ connected = true;
+ sock = std::move(conn.sock);
+ addr_bind = CAddress{conn.me, NODE_NONE};
+ }
+ } else if (GetProxy(addrConnect.GetNetwork(), proxy)) {
sock = CreateSock(proxy.proxy);
if (!sock) {
return nullptr;
@@ -448,7 +455,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
if (!sock) {
return nullptr;
}
- connected = ConnectSocketDirectly(addrConnect, sock->Get(), nConnectTimeout,
+ connected = ConnectSocketDirectly(addrConnect, *sock, nConnectTimeout,
conn_type == ConnectionType::MANUAL);
}
if (!proxyConnectionFailed) {
@@ -462,7 +469,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
return nullptr;
}
std::string host;
- int port = default_port;
+ uint16_t port{default_port};
SplitHostPort(std::string(pszDest), port, host);
bool proxyConnectionFailed;
connected = ConnectThroughProxy(proxy, host, port, *sock, nConnectTimeout,
@@ -475,7 +482,9 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
// Add node
NodeId id = GetNewNodeId();
uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();
- CAddress addr_bind = GetBindAddress(sock->Get());
+ if (!addr_bind.IsValid()) {
+ addr_bind = GetBindAddress(sock->Get());
+ }
CNode* pnode = new CNode(id, nLocalServices, sock->Release(), addrConnect, CalculateKeyedNetGroup(addrConnect), nonce, addr_bind, pszDest ? pszDest : "", conn_type, /* inbound_onion */ false);
pnode->AddRef();
@@ -601,8 +610,8 @@ void CNode::copyStats(CNodeStats &stats, const std::vector<bool> &m_asmap)
stats.minFeeFilter = 0;
}
- stats.m_ping_usec = m_last_ping_time;
- stats.m_min_ping_usec = m_min_ping_time;
+ X(m_last_ping_time);
+ X(m_min_ping_time);
// Leave string empty if addrLocal invalid (not filled in yet)
CService addrLocalUnlocked = GetAddrLocal();
@@ -630,7 +639,7 @@ bool CNode::ReceiveMsgBytes(Span<const uint8_t> msg_bytes, bool& complete)
if (m_deserializer->Complete()) {
// decompose a transport agnostic CNetMessage from the deserializer
uint32_t out_err_raw_size{0};
- Optional<CNetMessage> result{m_deserializer->GetMessage(time, out_err_raw_size)};
+ std::optional<CNetMessage> result{m_deserializer->GetMessage(time, out_err_raw_size)};
if (!result) {
// Message deserialization failed. Drop the message but don't disconnect the peer.
// store the size of the corrupt message
@@ -674,19 +683,19 @@ int V1TransportDeserializer::readHeader(Span<const uint8_t> msg_bytes)
hdrbuf >> hdr;
}
catch (const std::exception&) {
- LogPrint(BCLog::NET, "HEADER ERROR - UNABLE TO DESERIALIZE, peer=%d\n", m_node_id);
+ LogPrint(BCLog::NET, "Header error: Unable to deserialize, peer=%d\n", m_node_id);
return -1;
}
// Check start string, network magic
if (memcmp(hdr.pchMessageStart, m_chain_params.MessageStart(), CMessageHeader::MESSAGE_START_SIZE) != 0) {
- LogPrint(BCLog::NET, "HEADER ERROR - MESSAGESTART (%s, %u bytes), received %s, peer=%d\n", hdr.GetCommand(), hdr.nMessageSize, HexStr(hdr.pchMessageStart), m_node_id);
+ LogPrint(BCLog::NET, "Header error: Wrong MessageStart %s received, peer=%d\n", HexStr(hdr.pchMessageStart), m_node_id);
return -1;
}
// reject messages larger than MAX_SIZE or MAX_PROTOCOL_MESSAGE_LENGTH
if (hdr.nMessageSize > MAX_SIZE || hdr.nMessageSize > MAX_PROTOCOL_MESSAGE_LENGTH) {
- LogPrint(BCLog::NET, "HEADER ERROR - SIZE (%s, %u bytes), peer=%d\n", hdr.GetCommand(), hdr.nMessageSize, m_node_id);
+ LogPrint(BCLog::NET, "Header error: Size too large (%s, %u bytes), peer=%d\n", SanitizeString(hdr.GetCommand()), hdr.nMessageSize, m_node_id);
return -1;
}
@@ -721,10 +730,10 @@ const uint256& V1TransportDeserializer::GetMessageHash() const
return data_hash;
}
-Optional<CNetMessage> V1TransportDeserializer::GetMessage(const std::chrono::microseconds time, uint32_t& out_err_raw_size)
+std::optional<CNetMessage> V1TransportDeserializer::GetMessage(const std::chrono::microseconds time, uint32_t& out_err_raw_size)
{
// decompose a single CNetMessage from the TransportDeserializer
- Optional<CNetMessage> msg(std::move(vRecv));
+ std::optional<CNetMessage> msg(std::move(vRecv));
// store command string, time, and sizes
msg->m_command = hdr.GetCommand();
@@ -739,18 +748,18 @@ Optional<CNetMessage> V1TransportDeserializer::GetMessage(const std::chrono::mic
// Check checksum and header command string
if (memcmp(hash.begin(), hdr.pchChecksum, CMessageHeader::CHECKSUM_SIZE) != 0) {
- LogPrint(BCLog::NET, "CHECKSUM ERROR (%s, %u bytes), expected %s was %s, peer=%d\n",
+ LogPrint(BCLog::NET, "Header error: Wrong checksum (%s, %u bytes), expected %s was %s, peer=%d\n",
SanitizeString(msg->m_command), msg->m_message_size,
HexStr(Span<uint8_t>(hash.begin(), hash.begin() + CMessageHeader::CHECKSUM_SIZE)),
HexStr(hdr.pchChecksum),
m_node_id);
out_err_raw_size = msg->m_raw_message_size;
- msg = nullopt;
+ msg = std::nullopt;
} else if (!hdr.IsCommandValid()) {
- LogPrint(BCLog::NET, "HEADER ERROR - COMMAND (%s, %u bytes), peer=%d\n",
- hdr.GetCommand(), msg->m_message_size, m_node_id);
+ LogPrint(BCLog::NET, "Header error: Invalid message type (%s, %u bytes), peer=%d\n",
+ SanitizeString(hdr.GetCommand()), msg->m_message_size, m_node_id);
out_err_raw_size = msg->m_raw_message_size;
- msg = nullopt;
+ msg.reset();
}
// Always reset the network deserializer (prepare for the next message)
@@ -787,7 +796,7 @@ size_t CConnman::SocketSendData(CNode& node) const
nBytes = send(node.hSocket, reinterpret_cast<const char*>(data.data()) + node.nSendOffset, data.size() - node.nSendOffset, MSG_NOSIGNAL | MSG_DONTWAIT);
}
if (nBytes > 0) {
- node.nLastSend = GetSystemTimeInSeconds();
+ node.nLastSend = GetTimeSeconds();
node.nSendBytes += nBytes;
node.nSendOffset += nBytes;
nSentSize += nBytes;
@@ -838,6 +847,12 @@ static bool CompareLocalHostTimeConnected(const NodeEvictionCandidate &a, const
return a.nTimeConnected > b.nTimeConnected;
}
+static bool CompareOnionTimeConnected(const NodeEvictionCandidate& a, const NodeEvictionCandidate& b)
+{
+ if (a.m_is_onion != b.m_is_onion) return b.m_is_onion;
+ return a.nTimeConnected > b.nTimeConnected;
+}
+
static bool CompareNetGroupKeyed(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b) {
return a.nKeyedNetGroup < b.nKeyedNetGroup;
}
@@ -868,16 +883,54 @@ static bool CompareNodeBlockRelayOnlyTime(const NodeEvictionCandidate &a, const
return a.nTimeConnected > b.nTimeConnected;
}
-//! Sort an array by the specified comparator, then erase the last K elements.
-template<typename T, typename Comparator>
-static void EraseLastKElements(std::vector<T> &elements, Comparator comparator, size_t k)
+//! Sort an array by the specified comparator, then erase the last K elements where predicate is true.
+template <typename T, typename Comparator>
+static void EraseLastKElements(
+ std::vector<T>& elements, Comparator comparator, size_t k,
+ std::function<bool(const NodeEvictionCandidate&)> predicate = [](const NodeEvictionCandidate& n) { return true; })
{
std::sort(elements.begin(), elements.end(), comparator);
size_t eraseSize = std::min(k, elements.size());
- elements.erase(elements.end() - eraseSize, elements.end());
+ elements.erase(std::remove_if(elements.end() - eraseSize, elements.end(), predicate), elements.end());
+}
+
+void ProtectEvictionCandidatesByRatio(std::vector<NodeEvictionCandidate>& vEvictionCandidates)
+{
+ // Protect the half of the remaining nodes which have been connected the longest.
+ // This replicates the non-eviction implicit behavior, and precludes attacks that start later.
+ // To favorise the diversity of our peer connections, reserve up to (half + 2) of
+ // these protected spots for onion and localhost peers, if any, even if they're not
+ // longest uptime overall. This helps protect tor peers, which tend to be otherwise
+ // disadvantaged under our eviction criteria.
+ const size_t initial_size = vEvictionCandidates.size();
+ size_t total_protect_size = initial_size / 2;
+ const size_t onion_protect_size = total_protect_size / 2;
+
+ if (onion_protect_size) {
+ // Pick out up to 1/4 peers connected via our onion service, sorted by longest uptime.
+ EraseLastKElements(vEvictionCandidates, CompareOnionTimeConnected, onion_protect_size,
+ [](const NodeEvictionCandidate& n) { return n.m_is_onion; });
+ }
+
+ const size_t localhost_min_protect_size{2};
+ if (onion_protect_size >= localhost_min_protect_size) {
+ // Allocate any remaining slots of the 1/4, or minimum 2 additional slots,
+ // to localhost peers, sorted by longest uptime, as manually configured
+ // hidden services not using `-bind=addr[:port]=onion` will not be detected
+ // as inbound onion connections.
+ const size_t remaining_tor_slots{onion_protect_size - (initial_size - vEvictionCandidates.size())};
+ const size_t localhost_protect_size{std::max(remaining_tor_slots, localhost_min_protect_size)};
+ EraseLastKElements(vEvictionCandidates, CompareLocalHostTimeConnected, localhost_protect_size,
+ [](const NodeEvictionCandidate& n) { return n.m_is_local; });
+ }
+
+ // Calculate how many we removed, and update our total number of peers that
+ // we want to protect based on uptime accordingly.
+ total_protect_size -= initial_size - vEvictionCandidates.size();
+ EraseLastKElements(vEvictionCandidates, ReverseCompareNodeTimeConnected, total_protect_size);
}
-[[nodiscard]] Optional<NodeId> SelectNodeToEvict(std::vector<NodeEvictionCandidate>&& vEvictionCandidates)
+[[nodiscard]] std::optional<NodeId> SelectNodeToEvict(std::vector<NodeEvictionCandidate>&& vEvictionCandidates)
{
// Protect connections with certain characteristics
@@ -891,32 +944,19 @@ static void EraseLastKElements(std::vector<T> &elements, Comparator comparator,
// An attacker cannot manipulate this metric without performing useful work.
EraseLastKElements(vEvictionCandidates, CompareNodeTXTime, 4);
// Protect up to 8 non-tx-relay peers that have sent us novel blocks.
- std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareNodeBlockRelayOnlyTime);
- size_t erase_size = std::min(size_t(8), vEvictionCandidates.size());
- vEvictionCandidates.erase(std::remove_if(vEvictionCandidates.end() - erase_size, vEvictionCandidates.end(), [](NodeEvictionCandidate const &n) { return !n.fRelayTxes && n.fRelevantServices; }), vEvictionCandidates.end());
+ const size_t erase_size = std::min(size_t(8), vEvictionCandidates.size());
+ EraseLastKElements(vEvictionCandidates, CompareNodeBlockRelayOnlyTime, erase_size,
+ [](const NodeEvictionCandidate& n) { return !n.fRelayTxes && n.fRelevantServices; });
// Protect 4 nodes that most recently sent us novel blocks.
// An attacker cannot manipulate this metric without performing useful work.
EraseLastKElements(vEvictionCandidates, CompareNodeBlockTime, 4);
- // Protect the half of the remaining nodes which have been connected the longest.
- // This replicates the non-eviction implicit behavior, and precludes attacks that start later.
- // Reserve half of these protected spots for localhost peers, even if
- // they're not longest-uptime overall. This helps protect tor peers, which
- // tend to be otherwise disadvantaged under our eviction criteria.
- size_t initial_size = vEvictionCandidates.size();
- size_t total_protect_size = initial_size / 2;
-
- // Pick out up to 1/4 peers that are localhost, sorted by longest uptime.
- std::sort(vEvictionCandidates.begin(), vEvictionCandidates.end(), CompareLocalHostTimeConnected);
- size_t local_erase_size = total_protect_size / 2;
- vEvictionCandidates.erase(std::remove_if(vEvictionCandidates.end() - local_erase_size, vEvictionCandidates.end(), [](NodeEvictionCandidate const &n) { return n.m_is_local; }), vEvictionCandidates.end());
- // Calculate how many we removed, and update our total number of peers that
- // we want to protect based on uptime accordingly.
- total_protect_size -= initial_size - vEvictionCandidates.size();
- EraseLastKElements(vEvictionCandidates, ReverseCompareNodeTimeConnected, total_protect_size);
+ // Protect some of the remaining eviction candidates by ratios of desirable
+ // or disadvantaged characteristics.
+ ProtectEvictionCandidatesByRatio(vEvictionCandidates);
- if (vEvictionCandidates.empty()) return nullopt;
+ if (vEvictionCandidates.empty()) return std::nullopt;
// If any remaining peers are preferred for eviction consider only them.
// This happens after the other preferences since if a peer is really the best by other criteria (esp relaying blocks)
@@ -935,7 +975,7 @@ static void EraseLastKElements(std::vector<T> &elements, Comparator comparator,
for (const NodeEvictionCandidate &node : vEvictionCandidates) {
std::vector<NodeEvictionCandidate> &group = mapNetGroupNodes[node.nKeyedNetGroup];
group.push_back(node);
- int64_t grouptime = group[0].nTimeConnected;
+ const int64_t grouptime = group[0].nTimeConnected;
if (group.size() > nMostConnections || (group.size() == nMostConnections && grouptime > nMostConnectionsTime)) {
nMostConnections = group.size();
@@ -966,7 +1006,7 @@ bool CConnman::AttemptToEvictConnection()
LOCK(cs_vNodes);
for (const CNode* node : vNodes) {
- if (node->HasPermission(PF_NOBAN))
+ if (node->HasPermission(NetPermissionFlags::NoBan))
continue;
if (!node->IsInboundConn())
continue;
@@ -983,11 +1023,12 @@ bool CConnman::AttemptToEvictConnection()
node->nLastBlockTime, node->nLastTXTime,
HasAllDesirableServiceFlags(node->nServices),
peer_relay_txes, peer_filter_not_null, node->nKeyedNetGroup,
- node->m_prefer_evict, node->addr.IsLocal()};
+ node->m_prefer_evict, node->addr.IsLocal(),
+ node->m_inbound_onion};
vEvictionCandidates.push_back(candidate);
}
}
- const Optional<NodeId> node_id_to_evict = SelectNodeToEvict(std::move(vEvictionCandidates));
+ const std::optional<NodeId> node_id_to_evict = SelectNodeToEvict(std::move(vEvictionCandidates));
if (!node_id_to_evict) {
return false;
}
@@ -1007,24 +1048,42 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
socklen_t len = sizeof(sockaddr);
SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr*)&sockaddr, &len);
CAddress addr;
- int nInbound = 0;
- int nMaxInbound = nMaxConnections - m_max_outbound;
- if (hSocket != INVALID_SOCKET) {
- if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr)) {
- LogPrintf("Warning: Unknown socket family\n");
+ if (hSocket == INVALID_SOCKET) {
+ const int nErr = WSAGetLastError();
+ if (nErr != WSAEWOULDBLOCK) {
+ LogPrintf("socket error accept failed: %s\n", NetworkErrorString(nErr));
}
+ return;
}
- NetPermissionFlags permissionFlags = NetPermissionFlags::PF_NONE;
+ if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr)) {
+ LogPrintf("Warning: Unknown socket family\n");
+ }
+
+ const CAddress addr_bind = GetBindAddress(hSocket);
+
+ NetPermissionFlags permissionFlags = NetPermissionFlags::None;
hListenSocket.AddSocketPermissionFlags(permissionFlags);
+
+ CreateNodeFromAcceptedSocket(hSocket, permissionFlags, addr_bind, addr);
+}
+
+void CConnman::CreateNodeFromAcceptedSocket(SOCKET hSocket,
+ NetPermissionFlags permissionFlags,
+ const CAddress& addr_bind,
+ const CAddress& addr)
+{
+ int nInbound = 0;
+ int nMaxInbound = nMaxConnections - m_max_outbound;
+
AddWhitelistPermissionFlags(permissionFlags, addr);
- if (NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::PF_ISIMPLICIT)) {
- NetPermissions::ClearFlag(permissionFlags, PF_ISIMPLICIT);
- if (gArgs.GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) NetPermissions::AddFlag(permissionFlags, PF_FORCERELAY);
- if (gArgs.GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY)) NetPermissions::AddFlag(permissionFlags, PF_RELAY);
- NetPermissions::AddFlag(permissionFlags, PF_MEMPOOL);
- NetPermissions::AddFlag(permissionFlags, PF_NOBAN);
+ if (NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::Implicit)) {
+ NetPermissions::ClearFlag(permissionFlags, NetPermissionFlags::Implicit);
+ if (gArgs.GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) NetPermissions::AddFlag(permissionFlags, NetPermissionFlags::ForceRelay);
+ if (gArgs.GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY)) NetPermissions::AddFlag(permissionFlags, NetPermissionFlags::Relay);
+ NetPermissions::AddFlag(permissionFlags, NetPermissionFlags::Mempool);
+ NetPermissions::AddFlag(permissionFlags, NetPermissionFlags::NoBan);
}
{
@@ -1034,14 +1093,6 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
}
}
- if (hSocket == INVALID_SOCKET)
- {
- int nErr = WSAGetLastError();
- if (nErr != WSAEWOULDBLOCK)
- LogPrintf("socket error accept failed: %s\n", NetworkErrorString(nErr));
- return;
- }
-
if (!fNetworkActive) {
LogPrint(BCLog::NET, "connection from %s dropped: not accepting new connections\n", addr.ToString());
CloseSocket(hSocket);
@@ -1061,7 +1112,7 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
// Don't accept connections from banned peers.
bool banned = m_banman && m_banman->IsBanned(addr);
- if (!NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::PF_NOBAN) && banned)
+ if (!NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::NoBan) && banned)
{
LogPrint(BCLog::NET, "connection from %s dropped (banned)\n", addr.ToString());
CloseSocket(hSocket);
@@ -1070,7 +1121,7 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
// Only accept connections from discouraged peers if our inbound slots aren't (almost) full.
bool discouraged = m_banman && m_banman->IsDiscouraged(addr);
- if (!NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::PF_NOBAN) && nInbound + 1 >= nMaxInbound && discouraged)
+ if (!NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::NoBan) && nInbound + 1 >= nMaxInbound && discouraged)
{
LogPrint(BCLog::NET, "connection from %s dropped (discouraged)\n", addr.ToString());
CloseSocket(hSocket);
@@ -1089,10 +1140,9 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
NodeId id = GetNewNodeId();
uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();
- CAddress addr_bind = GetBindAddress(hSocket);
ServiceFlags nodeServices = nLocalServices;
- if (NetPermissions::HasFlag(permissionFlags, PF_BLOOMFILTER)) {
+ if (NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::BloomFilter)) {
nodeServices = static_cast<ServiceFlags>(nodeServices | NODE_BLOOM);
}
@@ -1176,19 +1226,10 @@ void CConnman::DisconnectNodes()
std::list<CNode*> vNodesDisconnectedCopy = vNodesDisconnected;
for (CNode* pnode : vNodesDisconnectedCopy)
{
- // wait until threads are done using it
+ // Destroy the object only after other threads have stopped using it.
if (pnode->GetRefCount() <= 0) {
- bool fDelete = false;
- {
- TRY_LOCK(pnode->cs_vSend, lockSend);
- if (lockSend) {
- fDelete = true;
- }
- }
- if (fDelete) {
- vNodesDisconnected.remove(pnode);
- DeleteNode(pnode);
- }
+ vNodesDisconnected.remove(pnode);
+ DeleteNode(pnode);
}
}
}
@@ -1208,16 +1249,19 @@ void CConnman::NotifyNumConnectionsChanged()
}
}
-bool CConnman::RunInactivityChecks(const CNode& node) const
+bool CConnman::ShouldRunInactivityChecks(const CNode& node, std::optional<int64_t> now_in) const
{
- return GetSystemTimeInSeconds() > node.nTimeConnected + m_peer_connect_timeout;
+ const int64_t now = now_in ? now_in.value() : GetTimeSeconds();
+ return node.nTimeConnected + m_peer_connect_timeout < now;
}
bool CConnman::InactivityCheck(const CNode& node) const
{
// Use non-mockable system time (otherwise these timers will pop when we
// use setmocktime in the tests).
- int64_t now = GetSystemTimeInSeconds();
+ int64_t now = GetTimeSeconds();
+
+ if (!ShouldRunInactivityChecks(node, now)) return false;
if (node.nLastRecv == 0 || node.nLastSend == 0) {
LogPrint(BCLog::NET, "socket no message in first %i seconds, %d %d peer=%d\n", m_peer_connect_timeout, node.nLastRecv != 0, node.nLastSend != 0, node.GetId());
@@ -1515,7 +1559,7 @@ void CConnman::SocketHandler()
if (bytes_sent) RecordBytesSent(bytes_sent);
}
- if (RunInactivityChecks(*pnode) && InactivityCheck(*pnode)) pnode->fDisconnect = true;
+ if (InactivityCheck(*pnode)) pnode->fDisconnect = true;
}
{
LOCK(cs_vNodes);
@@ -1594,7 +1638,7 @@ void CConnman::ThreadDNSAddressSeed()
{
LOCK(cs_vNodes);
for (const CNode* pnode : vNodes) {
- if (pnode->fSuccessfullyConnected && pnode->IsOutboundOrBlockRelayConn()) ++nRelevant;
+ if (pnode->fSuccessfullyConnected && pnode->IsFullOutboundConn()) ++nRelevant;
}
}
if (nRelevant >= 2) {
@@ -1681,7 +1725,7 @@ void CConnman::ProcessAddrFetch()
}
}
-bool CConnman::GetTryNewOutboundPeer()
+bool CConnman::GetTryNewOutboundPeer() const
{
return m_try_another_outbound_peer;
}
@@ -1698,7 +1742,7 @@ void CConnman::SetTryNewOutboundPeer(bool flag)
// Also exclude peers that haven't finished initial connection handshake yet
// (so that we don't decide we're over our desired connection limit, and then
// evict some peer that has finished the handshake)
-int CConnman::GetExtraFullOutboundCount()
+int CConnman::GetExtraFullOutboundCount() const
{
int full_outbound_peers = 0;
{
@@ -1712,7 +1756,7 @@ int CConnman::GetExtraFullOutboundCount()
return std::max(full_outbound_peers - m_max_outbound_full_relay, 0);
}
-int CConnman::GetExtraBlockRelayCount()
+int CConnman::GetExtraBlockRelayCount() const
{
int block_relay_peers = 0;
{
@@ -1750,12 +1794,11 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
}
// Initiate network connections
- auto start = GetTime<std::chrono::seconds>();
+ auto start = GetTime<std::chrono::microseconds>();
// Minimum time before next feeler connection (in microseconds).
-
- int64_t nNextFeeler = PoissonNextSend(count_microseconds(start), FEELER_INTERVAL);
- int64_t nNextExtraBlockRelay = PoissonNextSend(count_microseconds(start), EXTRA_BLOCK_RELAY_ONLY_PEER_INTERVAL);
+ auto next_feeler = PoissonNextSend(start, FEELER_INTERVAL);
+ auto next_extra_block_relay = PoissonNextSend(start, EXTRA_BLOCK_RELAY_ONLY_PEER_INTERVAL);
const bool dnsseed = gArgs.GetBoolArg("-dnsseed", DEFAULT_DNSSEED);
bool add_fixed_seeds = gArgs.GetBoolArg("-fixedseeds", DEFAULT_FIXEDSEEDS);
@@ -1798,7 +1841,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
if (add_fixed_seeds_now) {
CNetAddr local;
local.SetInternal("fixedseeds");
- addrman.Add(convertSeed6(Params().FixedSeeds()), local);
+ addrman.Add(ConvertSeeds(Params().FixedSeeds()), local);
add_fixed_seeds = false;
}
}
@@ -1838,7 +1881,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
}
ConnectionType conn_type = ConnectionType::OUTBOUND_FULL_RELAY;
- int64_t nTime = GetTimeMicros();
+ auto now = GetTime<std::chrono::microseconds>();
bool anchor = false;
bool fFeeler = false;
@@ -1850,7 +1893,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
// GetTryNewOutboundPeer() gets set when a stale tip is detected, so we
// try opening an additional OUTBOUND_FULL_RELAY connection. If none of
// these conditions are met, check to see if it's time to try an extra
- // block-relay-only peer (to confirm our tip is current, see below) or the nNextFeeler
+ // block-relay-only peer (to confirm our tip is current, see below) or the next_feeler
// timer to decide if we should open a FEELER.
if (!m_anchors.empty() && (nOutboundBlockRelay < m_max_outbound_block_relay)) {
@@ -1862,7 +1905,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
conn_type = ConnectionType::BLOCK_RELAY;
} else if (GetTryNewOutboundPeer()) {
// OUTBOUND_FULL_RELAY
- } else if (nTime > nNextExtraBlockRelay && m_start_extra_block_relay_peers) {
+ } else if (now > next_extra_block_relay && m_start_extra_block_relay_peers) {
// Periodically connect to a peer (using regular outbound selection
// methodology from addrman) and stay connected long enough to sync
// headers, but not much else.
@@ -1884,10 +1927,10 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
// Because we can promote these connections to block-relay-only
// connections, they do not get their own ConnectionType enum
// (similar to how we deal with extra outbound peers).
- nNextExtraBlockRelay = PoissonNextSend(nTime, EXTRA_BLOCK_RELAY_ONLY_PEER_INTERVAL);
+ next_extra_block_relay = PoissonNextSend(now, EXTRA_BLOCK_RELAY_ONLY_PEER_INTERVAL);
conn_type = ConnectionType::BLOCK_RELAY;
- } else if (nTime > nNextFeeler) {
- nNextFeeler = PoissonNextSend(nTime, FEELER_INTERVAL);
+ } else if (now > next_feeler) {
+ next_feeler = PoissonNextSend(now, FEELER_INTERVAL);
conn_type = ConnectionType::FEELER;
fFeeler = true;
} else {
@@ -2011,7 +2054,7 @@ std::vector<CAddress> CConnman::GetCurrentBlockRelayOnlyConns() const
return ret;
}
-std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo()
+std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo() const
{
std::vector<AddedNodeInfo> ret;
@@ -2177,6 +2220,45 @@ void CConnman::ThreadMessageHandler()
}
}
+void CConnman::ThreadI2PAcceptIncoming()
+{
+ static constexpr auto err_wait_begin = 1s;
+ static constexpr auto err_wait_cap = 5min;
+ auto err_wait = err_wait_begin;
+
+ bool advertising_listen_addr = false;
+ i2p::Connection conn;
+
+ while (!interruptNet) {
+
+ if (!m_i2p_sam_session->Listen(conn)) {
+ if (advertising_listen_addr && conn.me.IsValid()) {
+ RemoveLocal(conn.me);
+ advertising_listen_addr = false;
+ }
+
+ interruptNet.sleep_for(err_wait);
+ if (err_wait < err_wait_cap) {
+ err_wait *= 2;
+ }
+
+ continue;
+ }
+
+ if (!advertising_listen_addr) {
+ AddLocal(conn.me, LOCAL_MANUAL);
+ advertising_listen_addr = true;
+ }
+
+ if (!m_i2p_sam_session->Accept(conn)) {
+ continue;
+ }
+
+ CreateNodeFromAcceptedSocket(conn.sock->Release(), NetPermissionFlags::None,
+ CAddress{conn.me, NODE_NONE}, CAddress{conn.peer, NODE_NONE});
+ }
+}
+
bool CConnman::BindListenPort(const CService& addrBind, bilingual_str& strError, NetPermissionFlags permissions)
{
int nOne = 1;
@@ -2302,8 +2384,8 @@ void CConnman::SetNetworkActive(bool active)
uiInterface.NotifyNetworkActiveChanged(fNetworkActive);
}
-CConnman::CConnman(uint64_t nSeed0In, uint64_t nSeed1In, bool network_active)
- : nSeed0(nSeed0In), nSeed1(nSeed1In)
+CConnman::CConnman(uint64_t nSeed0In, uint64_t nSeed1In, CAddrMan& addrman_in, bool network_active)
+ : addrman(addrman_in), nSeed0(nSeed0In), nSeed1(nSeed1In)
{
SetTryNewOutboundPeer(false);
@@ -2319,8 +2401,9 @@ NodeId CConnman::GetNewNodeId()
bool CConnman::Bind(const CService &addr, unsigned int flags, NetPermissionFlags permissions) {
- if (!(flags & BF_EXPLICIT) && !IsReachable(addr))
+ if (!(flags & BF_EXPLICIT) && !IsReachable(addr)) {
return false;
+ }
bilingual_str strError;
if (!BindListenPort(addr, strError, permissions)) {
if ((flags & BF_REPORT_ERROR) && clientInterface) {
@@ -2329,7 +2412,7 @@ bool CConnman::Bind(const CService &addr, unsigned int flags, NetPermissionFlags
return false;
}
- if (addr.IsRoutable() && fDiscover && !(flags & BF_DONT_ADVERTISE) && !(permissions & PF_NOBAN)) {
+ if (addr.IsRoutable() && fDiscover && !(flags & BF_DONT_ADVERTISE) && !NetPermissions::HasFlag(permissions, NetPermissionFlags::NoBan)) {
AddLocal(addr, LOCAL_BIND);
}
@@ -2343,7 +2426,7 @@ bool CConnman::InitBinds(
{
bool fBound = false;
for (const auto& addrBind : binds) {
- fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR), NetPermissionFlags::PF_NONE);
+ fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR), NetPermissionFlags::None);
}
for (const auto& addrBind : whiteBinds) {
fBound |= Bind(addrBind.m_service, (BF_EXPLICIT | BF_REPORT_ERROR), addrBind.m_flags);
@@ -2352,12 +2435,12 @@ bool CConnman::InitBinds(
struct in_addr inaddr_any;
inaddr_any.s_addr = htonl(INADDR_ANY);
struct in6_addr inaddr6_any = IN6ADDR_ANY_INIT;
- fBound |= Bind(CService(inaddr6_any, GetListenPort()), BF_NONE, NetPermissionFlags::PF_NONE);
- fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE, NetPermissionFlags::PF_NONE);
+ fBound |= Bind(CService(inaddr6_any, GetListenPort()), BF_NONE, NetPermissionFlags::None);
+ fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE, NetPermissionFlags::None);
}
for (const auto& addr_bind : onion_binds) {
- fBound |= Bind(addr_bind, BF_EXPLICIT | BF_DONT_ADVERTISE, NetPermissionFlags::PF_NONE);
+ fBound |= Bind(addr_bind, BF_EXPLICIT | BF_DONT_ADVERTISE, NetPermissionFlags::None);
}
return fBound;
@@ -2376,12 +2459,18 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
return false;
}
+ proxyType i2p_sam;
+ if (GetProxy(NET_I2P, i2p_sam)) {
+ m_i2p_sam_session = std::make_unique<i2p::sam::Session>(gArgs.GetDataDirNet() / "i2p_private_key",
+ i2p_sam.proxy, &interruptNet);
+ }
+
for (const auto& strDest : connOptions.vSeedNodes) {
AddAddrFetch(strDest);
}
if (clientInterface) {
- clientInterface->InitMessage(_("Loading P2P addresses...").translated);
+ clientInterface->InitMessage(_("Loading P2P addresses…").translated);
}
// Load addresses from peers.dat
int64_t nStart = GetTimeMillis();
@@ -2391,31 +2480,31 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
LogPrintf("Loaded %i addresses from peers.dat %dms\n", addrman.size(), GetTimeMillis() - nStart);
else {
addrman.Clear(); // Addrman can be in an inconsistent state after failure, reset it
- LogPrintf("Invalid or missing peers.dat; recreating\n");
+ LogPrintf("Recreating peers.dat\n");
DumpAddresses();
}
}
if (m_use_addrman_outgoing) {
// Load addresses from anchors.dat
- m_anchors = ReadAnchors(GetDataDir() / ANCHORS_DATABASE_FILENAME);
+ m_anchors = ReadAnchors(gArgs.GetDataDirNet() / ANCHORS_DATABASE_FILENAME);
if (m_anchors.size() > MAX_BLOCK_RELAY_ONLY_ANCHORS) {
m_anchors.resize(MAX_BLOCK_RELAY_ONLY_ANCHORS);
}
LogPrintf("%i block-relay-only anchors will be tried for connections.\n", m_anchors.size());
}
- uiInterface.InitMessage(_("Starting network threads...").translated);
+ uiInterface.InitMessage(_("Starting network threads…").translated);
fAddressesInitialized = true;
if (semOutbound == nullptr) {
// initialize semaphore
- semOutbound = MakeUnique<CSemaphore>(std::min(m_max_outbound, nMaxConnections));
+ semOutbound = std::make_unique<CSemaphore>(std::min(m_max_outbound, nMaxConnections));
}
if (semAddnode == nullptr) {
// initialize semaphore
- semAddnode = MakeUnique<CSemaphore>(nMaxAddnode);
+ semAddnode = std::make_unique<CSemaphore>(nMaxAddnode);
}
//
@@ -2432,15 +2521,15 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
}
// Send and receive from sockets, accept connections
- threadSocketHandler = std::thread(&TraceThread<std::function<void()> >, "net", std::function<void()>(std::bind(&CConnman::ThreadSocketHandler, this)));
+ threadSocketHandler = std::thread(&util::TraceThread, "net", [this] { ThreadSocketHandler(); });
if (!gArgs.GetBoolArg("-dnsseed", DEFAULT_DNSSEED))
LogPrintf("DNS seeding disabled\n");
else
- threadDNSAddressSeed = std::thread(&TraceThread<std::function<void()> >, "dnsseed", std::function<void()>(std::bind(&CConnman::ThreadDNSAddressSeed, this)));
+ threadDNSAddressSeed = std::thread(&util::TraceThread, "dnsseed", [this] { ThreadDNSAddressSeed(); });
// Initiate manual connections
- threadOpenAddedConnections = std::thread(&TraceThread<std::function<void()> >, "addcon", std::function<void()>(std::bind(&CConnman::ThreadOpenAddedConnections, this)));
+ threadOpenAddedConnections = std::thread(&util::TraceThread, "addcon", [this] { ThreadOpenAddedConnections(); });
if (connOptions.m_use_addrman_outgoing && !connOptions.m_specified_outgoing.empty()) {
if (clientInterface) {
@@ -2450,11 +2539,19 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
}
return false;
}
- if (connOptions.m_use_addrman_outgoing || !connOptions.m_specified_outgoing.empty())
- threadOpenConnections = std::thread(&TraceThread<std::function<void()> >, "opencon", std::function<void()>(std::bind(&CConnman::ThreadOpenConnections, this, connOptions.m_specified_outgoing)));
+ if (connOptions.m_use_addrman_outgoing || !connOptions.m_specified_outgoing.empty()) {
+ threadOpenConnections = std::thread(
+ &util::TraceThread, "opencon",
+ [this, connect = connOptions.m_specified_outgoing] { ThreadOpenConnections(connect); });
+ }
// Process messages
- threadMessageHandler = std::thread(&TraceThread<std::function<void()> >, "msghand", std::function<void()>(std::bind(&CConnman::ThreadMessageHandler, this)));
+ threadMessageHandler = std::thread(&util::TraceThread, "msghand", [this] { ThreadMessageHandler(); });
+
+ if (connOptions.m_i2p_accept_incoming && m_i2p_sam_session.get() != nullptr) {
+ threadI2PAcceptIncoming =
+ std::thread(&util::TraceThread, "i2paccept", [this] { ThreadI2PAcceptIncoming(); });
+ }
// Dump network addresses
scheduler.scheduleEvery([this] { DumpAddresses(); }, DUMP_PEERS_INTERVAL);
@@ -2503,6 +2600,9 @@ void CConnman::Interrupt()
void CConnman::StopThreads()
{
+ if (threadI2PAcceptIncoming.joinable()) {
+ threadI2PAcceptIncoming.join();
+ }
if (threadMessageHandler.joinable())
threadMessageHandler.join();
if (threadOpenConnections.joinable())
@@ -2527,27 +2627,30 @@ void CConnman::StopNodes()
if (anchors_to_dump.size() > MAX_BLOCK_RELAY_ONLY_ANCHORS) {
anchors_to_dump.resize(MAX_BLOCK_RELAY_ONLY_ANCHORS);
}
- DumpAnchors(GetDataDir() / ANCHORS_DATABASE_FILENAME, anchors_to_dump);
+ DumpAnchors(gArgs.GetDataDirNet() / ANCHORS_DATABASE_FILENAME, anchors_to_dump);
}
}
- // Close sockets
- LOCK(cs_vNodes);
- for (CNode* pnode : vNodes)
+ // Delete peer connections.
+ std::vector<CNode*> nodes;
+ WITH_LOCK(cs_vNodes, nodes.swap(vNodes));
+ for (CNode* pnode : nodes) {
pnode->CloseSocketDisconnect();
- for (ListenSocket& hListenSocket : vhListenSocket)
- if (hListenSocket.socket != INVALID_SOCKET)
- if (!CloseSocket(hListenSocket.socket))
- LogPrintf("CloseSocket(hListenSocket) failed with error %s\n", NetworkErrorString(WSAGetLastError()));
-
- // clean up some globals (to help leak detection)
- for (CNode* pnode : vNodes) {
DeleteNode(pnode);
}
+
+ // Close listening sockets.
+ for (ListenSocket& hListenSocket : vhListenSocket) {
+ if (hListenSocket.socket != INVALID_SOCKET) {
+ if (!CloseSocket(hListenSocket.socket)) {
+ LogPrintf("CloseSocket(hListenSocket) failed with error %s\n", NetworkErrorString(WSAGetLastError()));
+ }
+ }
+ }
+
for (CNode* pnode : vNodesDisconnected) {
DeleteNode(pnode);
}
- vNodes.clear();
vNodesDisconnected.clear();
vhListenSocket.clear();
semOutbound.reset();
@@ -2557,11 +2660,7 @@ void CConnman::StopNodes()
void CConnman::DeleteNode(CNode* pnode)
{
assert(pnode);
- bool fUpdateConnectionTime = false;
- m_msgproc->FinalizeNode(*pnode, fUpdateConnectionTime);
- if (fUpdateConnectionTime) {
- addrman.Connected(pnode->addr);
- }
+ m_msgproc->FinalizeNode(*pnode);
delete pnode;
}
@@ -2571,24 +2670,9 @@ CConnman::~CConnman()
Stop();
}
-void CConnman::SetServices(const CService &addr, ServiceFlags nServices)
-{
- addrman.SetServices(addr, nServices);
-}
-
-void CConnman::MarkAddressGood(const CAddress& addr)
-{
- addrman.Good(addr);
-}
-
-bool CConnman::AddNewAddresses(const std::vector<CAddress>& vAddr, const CAddress& addrFrom, int64_t nTimePenalty)
+std::vector<CAddress> CConnman::GetAddresses(size_t max_addresses, size_t max_pct, std::optional<Network> network) const
{
- return addrman.Add(vAddr, addrFrom, nTimePenalty);
-}
-
-std::vector<CAddress> CConnman::GetAddresses(size_t max_addresses, size_t max_pct)
-{
- std::vector<CAddress> addresses = addrman.GetAddr(max_addresses, max_pct);
+ std::vector<CAddress> addresses = addrman.GetAddr(max_addresses, max_pct, network);
if (m_banman) {
addresses.erase(std::remove_if(addresses.begin(), addresses.end(),
[this](const CAddress& addr){return m_banman->IsDiscouraged(addr) || m_banman->IsBanned(addr);}),
@@ -2599,9 +2683,7 @@ std::vector<CAddress> CConnman::GetAddresses(size_t max_addresses, size_t max_pc
std::vector<CAddress> CConnman::GetAddresses(CNode& requestor, size_t max_addresses, size_t max_pct)
{
- SOCKET socket;
- WITH_LOCK(requestor.cs_hSocket, socket = requestor.hSocket);
- auto local_socket_bytes = GetBindAddress(socket).GetAddrBytes();
+ auto local_socket_bytes = requestor.addrBind.GetAddrBytes();
uint64_t cache_id = GetDeterministicRandomizer(RANDOMIZER_ID_ADDRCACHE)
.Write(requestor.addr.GetNetwork())
.Write(local_socket_bytes.data(), local_socket_bytes.size())
@@ -2610,7 +2692,7 @@ std::vector<CAddress> CConnman::GetAddresses(CNode& requestor, size_t max_addres
auto r = m_addr_response_caches.emplace(cache_id, CachedAddrResponse{});
CachedAddrResponse& cache_entry = r.first->second;
if (cache_entry.m_cache_entry_expiration < current_time) { // If emplace() added new one it has expiration 0.
- cache_entry.m_addrs_response_cache = GetAddresses(max_addresses, max_pct);
+ cache_entry.m_addrs_response_cache = GetAddresses(max_addresses, max_pct, /* network */ std::nullopt);
// Choosing a proper cache lifetime is a trade-off between the privacy leak minimization
// and the usefulness of ADDR responses to honest users.
//
@@ -2663,15 +2745,15 @@ bool CConnman::RemoveAddedNode(const std::string& strNode)
return false;
}
-size_t CConnman::GetNodeCount(NumConnections flags)
+size_t CConnman::GetNodeCount(ConnectionDirection flags) const
{
LOCK(cs_vNodes);
- if (flags == CConnman::CONNECTIONS_ALL) // Shortcut if we want total
+ if (flags == ConnectionDirection::Both) // Shortcut if we want total
return vNodes.size();
int nNum = 0;
for (const auto& pnode : vNodes) {
- if (flags & (pnode->IsInboundConn() ? CONNECTIONS_IN : CONNECTIONS_OUT)) {
+ if (flags & (pnode->IsInboundConn() ? ConnectionDirection::In : ConnectionDirection::Out)) {
nNum++;
}
}
@@ -2679,7 +2761,7 @@ size_t CConnman::GetNodeCount(NumConnections flags)
return nNum;
}
-void CConnman::GetNodeStats(std::vector<CNodeStats>& vstats)
+void CConnman::GetNodeStats(std::vector<CNodeStats>& vstats) const
{
vstats.clear();
LOCK(cs_vNodes);
@@ -2756,18 +2838,18 @@ void CConnman::RecordBytesSent(uint64_t bytes)
nMaxOutboundTotalBytesSentInCycle += bytes;
}
-uint64_t CConnman::GetMaxOutboundTarget()
+uint64_t CConnman::GetMaxOutboundTarget() const
{
LOCK(cs_totalBytesSent);
return nMaxOutboundLimit;
}
-std::chrono::seconds CConnman::GetMaxOutboundTimeframe()
+std::chrono::seconds CConnman::GetMaxOutboundTimeframe() const
{
return MAX_UPLOAD_TIMEFRAME;
}
-std::chrono::seconds CConnman::GetMaxOutboundTimeLeftInCycle()
+std::chrono::seconds CConnman::GetMaxOutboundTimeLeftInCycle() const
{
LOCK(cs_totalBytesSent);
if (nMaxOutboundLimit == 0)
@@ -2781,7 +2863,7 @@ std::chrono::seconds CConnman::GetMaxOutboundTimeLeftInCycle()
return (cycleEndTime < now) ? 0s : cycleEndTime - now;
}
-bool CConnman::OutboundTargetReached(bool historicalBlockServingLimit)
+bool CConnman::OutboundTargetReached(bool historicalBlockServingLimit) const
{
LOCK(cs_totalBytesSent);
if (nMaxOutboundLimit == 0)
@@ -2801,7 +2883,7 @@ bool CConnman::OutboundTargetReached(bool historicalBlockServingLimit)
return false;
}
-uint64_t CConnman::GetOutboundTargetBytesLeft()
+uint64_t CConnman::GetOutboundTargetBytesLeft() const
{
LOCK(cs_totalBytesSent);
if (nMaxOutboundLimit == 0)
@@ -2810,13 +2892,13 @@ uint64_t CConnman::GetOutboundTargetBytesLeft()
return (nMaxOutboundTotalBytesSentInCycle >= nMaxOutboundLimit) ? 0 : nMaxOutboundLimit - nMaxOutboundTotalBytesSentInCycle;
}
-uint64_t CConnman::GetTotalBytesRecv()
+uint64_t CConnman::GetTotalBytesRecv() const
{
LOCK(cs_totalBytesRecv);
return nTotalBytesRecv;
}
-uint64_t CConnman::GetTotalBytesSent()
+uint64_t CConnman::GetTotalBytesSent() const
{
LOCK(cs_totalBytesSent);
return nTotalBytesSent;
@@ -2830,7 +2912,7 @@ ServiceFlags CConnman::GetLocalServices() const
unsigned int CConnman::GetReceiveFloodSize() const { return nReceiveFloodSize; }
CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, SOCKET hSocketIn, const CAddress& addrIn, uint64_t nKeyedNetGroupIn, uint64_t nLocalHostNonceIn, const CAddress& addrBindIn, const std::string& addrNameIn, ConnectionType conn_type_in, bool inbound_onion)
- : nTimeConnected(GetSystemTimeInSeconds()),
+ : nTimeConnected(GetTimeSeconds()),
addr(addrIn),
addrBind(addrBindIn),
m_inbound_onion(inbound_onion),
@@ -2844,11 +2926,7 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, SOCKET hSocketIn, const
hSocket = hSocketIn;
addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn;
if (conn_type_in != ConnectionType::BLOCK_RELAY) {
- m_tx_relay = MakeUnique<TxRelay>();
- }
-
- if (RelayAddrsWithConn()) {
- m_addr_known = MakeUnique<CRollingBloomFilter>(5000, 0.001);
+ m_tx_relay = std::make_unique<TxRelay>();
}
for (const std::string &msg : getAllNetMessageTypes())
@@ -2861,8 +2939,8 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, SOCKET hSocketIn, const
LogPrint(BCLog::NET, "Added connection peer=%d\n", id);
}
- m_deserializer = MakeUnique<V1TransportDeserializer>(V1TransportDeserializer(Params(), GetId(), SER_NETWORK, INIT_PROTO_VERSION));
- m_serializer = MakeUnique<V1TransportSerializer>(V1TransportSerializer());
+ m_deserializer = std::make_unique<V1TransportDeserializer>(V1TransportDeserializer(Params(), GetId(), SER_NETWORK, INIT_PROTO_VERSION));
+ m_serializer = std::make_unique<V1TransportSerializer>(V1TransportSerializer());
}
CNode::~CNode()
@@ -2920,20 +2998,21 @@ bool CConnman::ForNode(NodeId id, std::function<bool(CNode* pnode)> func)
return found != nullptr && NodeFullyConnected(found) && func(found);
}
-int64_t CConnman::PoissonNextSendInbound(int64_t now, int average_interval_seconds)
+std::chrono::microseconds CConnman::PoissonNextSendInbound(std::chrono::microseconds now, std::chrono::seconds average_interval)
{
- if (m_next_send_inv_to_incoming < now) {
+ if (m_next_send_inv_to_incoming.load() < now) {
// If this function were called from multiple threads simultaneously
// it would possible that both update the next send variable, and return a different result to their caller.
// This is not possible in practice as only the net processing thread invokes this function.
- m_next_send_inv_to_incoming = PoissonNextSend(now, average_interval_seconds);
+ m_next_send_inv_to_incoming = PoissonNextSend(now, average_interval);
}
return m_next_send_inv_to_incoming;
}
-int64_t PoissonNextSend(int64_t now, int average_interval_seconds)
+std::chrono::microseconds PoissonNextSend(std::chrono::microseconds now, std::chrono::seconds average_interval)
{
- return now + (int64_t)(log1p(GetRand(1ULL << 48) * -0.0000000000000035527136788 /* -1/2^48 */) * average_interval_seconds * -1000000.0 + 0.5);
+ double unscaled = -log1p(GetRand(1ULL << 48) * -0.0000000000000035527136788 /* -1/2^48 */);
+ return now + std::chrono::duration_cast<std::chrono::microseconds>(unscaled * average_interval + 0.5us);
}
CSipHasher CConnman::GetDeterministicRandomizer(uint64_t id) const
@@ -2960,7 +3039,7 @@ void CaptureMessage(const CAddress& addr, const std::string& msg_type, const Spa
std::string clean_addr = addr.ToString();
std::replace(clean_addr.begin(), clean_addr.end(), ':', '_');
- fs::path base_path = GetDataDir() / "message_capture" / clean_addr;
+ fs::path base_path = gArgs.GetDataDirNet() / "message_capture" / clean_addr;
fs::create_directories(base_path);
fs::path path = base_path / (is_incoming ? "msgs_recv.dat" : "msgs_sent.dat");
@@ -2969,7 +3048,7 @@ void CaptureMessage(const CAddress& addr, const std::string& msg_type, const Spa
ser_writedata64(f, now.count());
f.write(msg_type.data(), msg_type.length());
for (auto i = msg_type.length(); i < CMessageHeader::COMMAND_SIZE; ++i) {
- f << '\0';
+ f << uint8_t{'\0'};
}
uint32_t size = data.size();
ser_writedata32(f, size);