aboutsummaryrefslogtreecommitdiff
path: root/src/net.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/net.cpp')
-rw-r--r--src/net.cpp470
1 files changed, 213 insertions, 257 deletions
diff --git a/src/net.cpp b/src/net.cpp
index 363ae062d8..7f4e571c8d 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -21,10 +21,11 @@
#include <net_permissions.h>
#include <netaddress.h>
#include <netbase.h>
-#include <node/ui_interface.h>
+#include <node/interface_ui.h>
#include <protocol.h>
#include <random.h>
#include <scheduler.h>
+#include <util/designator.h>
#include <util/sock.h>
#include <util/strencodings.h>
#include <util/syscall_sandbox.h>
@@ -103,7 +104,7 @@ enum BindFlags {
// The sleep time needs to be small to avoid new sockets stalling
static const uint64_t SELECT_TIMEOUT_MILLISECONDS = 50;
-const std::string NET_MESSAGE_COMMAND_OTHER = "*other*";
+const std::string NET_MESSAGE_TYPE_OTHER = "*other*";
static const uint64_t RANDOMIZER_ID_NETGROUP = 0x6c0edd8036ef4036ULL; // SHA256("netgroup")[0:8]
static const uint64_t RANDOMIZER_ID_LOCALHOSTNONCE = 0xd93e69e2bbfa5735ULL; // SHA256("localhostnonce")[0:8]
@@ -113,7 +114,7 @@ static const uint64_t RANDOMIZER_ID_ADDRCACHE = 0x1cf2e4ddd306dda9ULL; // SHA256
//
bool fDiscover = true;
bool fListen = true;
-Mutex g_maplocalhost_mutex;
+GlobalMutex g_maplocalhost_mutex;
std::map<CNetAddr, LocalServiceInfo> mapLocalHost GUARDED_BY(g_maplocalhost_mutex);
static bool vfLimited[NET_MAX] GUARDED_BY(g_maplocalhost_mutex) = {};
std::string strSubVersion;
@@ -126,6 +127,31 @@ void CConnman::AddAddrFetch(const std::string& strDest)
uint16_t GetListenPort()
{
+ // If -bind= is provided with ":port" part, use that (first one if multiple are provided).
+ for (const std::string& bind_arg : gArgs.GetArgs("-bind")) {
+ CService bind_addr;
+ constexpr uint16_t dummy_port = 0;
+
+ if (Lookup(bind_arg, bind_addr, dummy_port, /*fAllowLookup=*/false)) {
+ if (bind_addr.GetPort() != dummy_port) {
+ return bind_addr.GetPort();
+ }
+ }
+ }
+
+ // Otherwise, if -whitebind= without NetPermissionFlags::NoBan is provided, use that
+ // (-whitebind= is required to have ":port").
+ for (const std::string& whitebind_arg : gArgs.GetArgs("-whitebind")) {
+ NetWhitebindPermissions whitebind;
+ bilingual_str error;
+ if (NetWhitebindPermissions::TryParse(whitebind_arg, whitebind, error)) {
+ if (!NetPermissions::HasFlag(whitebind.m_flags, NetPermissionFlags::NoBan)) {
+ return whitebind.m_service.GetPort();
+ }
+ }
+ }
+
+ // Otherwise, if -port= is provided, use that. Otherwise use the default port.
return static_cast<uint16_t>(gArgs.GetIntArg("-port", Params().GetDefaultPort()));
}
@@ -221,7 +247,17 @@ std::optional<CAddress> GetLocalAddrForPeer(CNode *pnode)
if (IsPeerAddrLocalGood(pnode) && (!addrLocal.IsRoutable() ||
rng.randbits((GetnScore(addrLocal) > LOCAL_MANUAL) ? 3 : 1) == 0))
{
- addrLocal.SetIP(pnode->GetAddrLocal());
+ if (pnode->IsInboundConn()) {
+ // For inbound connections, assume both the address and the port
+ // as seen from the peer.
+ addrLocal = CAddress{pnode->GetAddrLocal(), addrLocal.nServices, addrLocal.nTime};
+ } else {
+ // For outbound connections, assume just the address as seen from
+ // the peer and leave the port in `addrLocal` as returned by
+ // `GetLocalAddress()` above. The peer has no way to observe our
+ // listening port when we have initiated the connection.
+ addrLocal.SetIP(pnode->GetAddrLocal());
+ }
}
if (addrLocal.IsRoutable() || gArgs.GetBoolArg("-addrmantest", false))
{
@@ -386,16 +422,16 @@ bool CConnman::CheckIncomingNonce(uint64_t nonce)
}
/** Get the bind address for a socket as CAddress */
-static CAddress GetBindAddress(SOCKET sock)
+static CAddress GetBindAddress(const Sock& sock)
{
CAddress addr_bind;
struct sockaddr_storage sockaddr_bind;
socklen_t sockaddr_bind_len = sizeof(sockaddr_bind);
- if (sock != INVALID_SOCKET) {
- if (!getsockname(sock, (struct sockaddr*)&sockaddr_bind, &sockaddr_bind_len)) {
+ if (sock.Get() != INVALID_SOCKET) {
+ if (!sock.GetSockName((struct sockaddr*)&sockaddr_bind, &sockaddr_bind_len)) {
addr_bind.SetSockAddr((const struct sockaddr*)&sockaddr_bind);
} else {
- LogPrint(BCLog::NET, "Warning: getsockname failed\n");
+ LogPrintLevel(BCLog::NET, BCLog::Level::Warning, "getsockname failed\n");
}
}
return addr_bind;
@@ -419,9 +455,9 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
}
/// debug print
- LogPrint(BCLog::NET, "trying connection %s lastseen=%.1fhrs\n",
- pszDest ? pszDest : addrConnect.ToString(),
- pszDest ? 0.0 : (double)(GetAdjustedTime() - addrConnect.nTime)/3600.0);
+ LogPrintLevel(BCLog::NET, BCLog::Level::Debug, "trying connection %s lastseen=%.1fhrs\n",
+ pszDest ? pszDest : addrConnect.ToString(),
+ pszDest ? 0.0 : (double)(GetAdjustedTime() - addrConnect.nTime) / 3600.0);
// Resolve
const uint16_t default_port{pszDest != nullptr ? Params().GetDefaultPort(pszDest) :
@@ -449,7 +485,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
// Connect
bool connected = false;
std::unique_ptr<Sock> sock;
- proxyType proxy;
+ Proxy proxy;
CAddress addr_bind;
assert(!addr_bind.IsValid());
@@ -504,7 +540,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
NodeId id = GetNewNodeId();
uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();
if (!addr_bind.IsValid()) {
- addr_bind = GetBindAddress(sock->Get());
+ addr_bind = GetBindAddress(*sock);
}
CNode* pnode = new CNode(id,
nLocalServices,
@@ -591,12 +627,6 @@ void CNode::CopyStats(CNodeStats& stats)
X(addr);
X(addrBind);
stats.m_network = ConnectedThroughNetwork();
- if (m_tx_relay != nullptr) {
- LOCK(m_tx_relay->cs_filter);
- stats.fRelayTxes = m_tx_relay->fRelayTxes;
- } else {
- stats.fRelayTxes = false;
- }
X(m_last_send);
X(m_last_recv);
X(m_last_tx_time);
@@ -614,20 +644,15 @@ void CNode::CopyStats(CNodeStats& stats)
X(m_bip152_highbandwidth_from);
{
LOCK(cs_vSend);
- X(mapSendBytesPerMsgCmd);
+ X(mapSendBytesPerMsgType);
X(nSendBytes);
}
{
LOCK(cs_vRecv);
- X(mapRecvBytesPerMsgCmd);
+ X(mapRecvBytesPerMsgType);
X(nRecvBytes);
}
X(m_permissionFlags);
- if (m_tx_relay != nullptr) {
- stats.minFeeFilter = m_tx_relay->minFeeFilter;
- } else {
- stats.minFeeFilter = 0;
- }
X(m_last_ping_time);
X(m_min_ping_time);
@@ -660,19 +685,19 @@ bool CNode::ReceiveMsgBytes(Span<const uint8_t> msg_bytes, bool& complete)
bool reject_message{false};
CNetMessage msg = m_deserializer->GetMessage(time, reject_message);
if (reject_message) {
- // Message deserialization failed. Drop the message but don't disconnect the peer.
+ // Message deserialization failed. Drop the message but don't disconnect the peer.
// store the size of the corrupt message
- mapRecvBytesPerMsgCmd.at(NET_MESSAGE_COMMAND_OTHER) += msg.m_raw_message_size;
+ mapRecvBytesPerMsgType.at(NET_MESSAGE_TYPE_OTHER) += msg.m_raw_message_size;
continue;
}
- // Store received bytes per message command
- // to prevent a memory DOS, only allow valid commands
- auto i = mapRecvBytesPerMsgCmd.find(msg.m_type);
- if (i == mapRecvBytesPerMsgCmd.end()) {
- i = mapRecvBytesPerMsgCmd.find(NET_MESSAGE_COMMAND_OTHER);
+ // Store received bytes per message type.
+ // To prevent a memory DOS, only allow known message types.
+ auto i = mapRecvBytesPerMsgType.find(msg.m_type);
+ if (i == mapRecvBytesPerMsgType.end()) {
+ i = mapRecvBytesPerMsgType.find(NET_MESSAGE_TYPE_OTHER);
}
- assert(i != mapRecvBytesPerMsgCmd.end());
+ assert(i != mapRecvBytesPerMsgType.end());
i->second += msg.m_raw_message_size;
// push the message to the process queue,
@@ -757,7 +782,7 @@ CNetMessage V1TransportDeserializer::GetMessage(const std::chrono::microseconds
// decompose a single CNetMessage from the TransportDeserializer
CNetMessage msg(std::move(vRecv));
- // store command string, time, and sizes
+ // store message type string, time, and sizes
msg.m_type = hdr.GetCommand();
msg.m_time = time;
msg.m_message_size = hdr.nMessageSize;
@@ -768,7 +793,7 @@ CNetMessage V1TransportDeserializer::GetMessage(const std::chrono::microseconds
// We just received a message off the wire, harvest entropy from the time (and the message checksum)
RandAddEvent(ReadLE32(hash.begin()));
- // Check checksum and header command string
+ // Check checksum and header message type string
if (memcmp(hash.begin(), hdr.pchChecksum, CMessageHeader::CHECKSUM_SIZE) != 0) {
LogPrint(BCLog::NET, "Header error: Wrong checksum (%s, %u bytes), expected %s was %s, peer=%d\n",
SanitizeString(msg.m_type), msg.m_message_size,
@@ -878,7 +903,7 @@ static bool CompareNodeTXTime(const NodeEvictionCandidate &a, const NodeEviction
{
// There is a fall-through here because it is common for a node to have more than a few peers that have not yet relayed txn.
if (a.m_last_tx_time != b.m_last_tx_time) return a.m_last_tx_time < b.m_last_tx_time;
- if (a.fRelayTxes != b.fRelayTxes) return b.fRelayTxes;
+ if (a.m_relay_txs != b.m_relay_txs) return b.m_relay_txs;
if (a.fBloomFilter != b.fBloomFilter) return a.fBloomFilter;
return a.m_connected > b.m_connected;
}
@@ -886,7 +911,7 @@ static bool CompareNodeTXTime(const NodeEvictionCandidate &a, const NodeEviction
// Pick out the potential block-relay only peers, and sort them by last block time.
static bool CompareNodeBlockRelayOnlyTime(const NodeEvictionCandidate &a, const NodeEvictionCandidate &b)
{
- if (a.fRelayTxes != b.fRelayTxes) return a.fRelayTxes;
+ if (a.m_relay_txs != b.m_relay_txs) return a.m_relay_txs;
if (a.m_last_block_time != b.m_last_block_time) return a.m_last_block_time < b.m_last_block_time;
if (a.fRelevantServices != b.fRelevantServices) return b.fRelevantServices;
return a.m_connected > b.m_connected;
@@ -928,17 +953,17 @@ void ProtectEvictionCandidatesByRatio(std::vector<NodeEvictionCandidate>& evicti
// 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 of these protected
- // spots for Tor/onion, localhost and I2P peers, even if they're not longest uptime overall.
- // This helps protect these higher-latency peers that tend to be otherwise
+ // spots for Tor/onion, localhost, I2P, and CJDNS peers, even if they're not longest uptime
+ // overall. This helps protect these higher-latency peers that tend to be otherwise
// disadvantaged under our eviction criteria.
const size_t initial_size = eviction_candidates.size();
const size_t total_protect_size{initial_size / 2};
- // Disadvantaged networks to protect: I2P, localhost, Tor/onion. In case of equal counts, earlier
- // array members have first opportunity to recover unused slots from the previous iteration.
+ // Disadvantaged networks to protect. In the case of equal counts, earlier array members
+ // have the first opportunity to recover unused slots from the previous iteration.
struct Net { bool is_local; Network id; size_t count; };
- std::array<Net, 3> networks{
- {{false, NET_I2P, 0}, {/* localhost */ true, NET_MAX, 0}, {false, NET_ONION, 0}}};
+ std::array<Net, 4> networks{
+ {{false, NET_CJDNS, 0}, {false, NET_I2P, 0}, {/*localhost=*/true, NET_MAX, 0}, {false, NET_ONION, 0}}};
// Count and store the number of eviction candidates per network.
for (Net& n : networks) {
@@ -1011,7 +1036,7 @@ void ProtectEvictionCandidatesByRatio(std::vector<NodeEvictionCandidate>& evicti
EraseLastKElements(vEvictionCandidates, CompareNodeTXTime, 4);
// Protect up to 8 non-tx-relay peers that have sent us novel blocks.
EraseLastKElements(vEvictionCandidates, CompareNodeBlockRelayOnlyTime, 8,
- [](const NodeEvictionCandidate& n) { return !n.fRelayTxes && n.fRelevantServices; });
+ [](const NodeEvictionCandidate& n) { return !n.m_relay_txs && n.fRelevantServices; });
// Protect 4 nodes that most recently sent us novel blocks.
// An attacker cannot manipulate this metric without performing useful work.
@@ -1077,19 +1102,20 @@ bool CConnman::AttemptToEvictConnection()
continue;
if (node->fDisconnect)
continue;
- bool peer_relay_txes = false;
- bool peer_filter_not_null = false;
- if (node->m_tx_relay != nullptr) {
- LOCK(node->m_tx_relay->cs_filter);
- peer_relay_txes = node->m_tx_relay->fRelayTxes;
- peer_filter_not_null = node->m_tx_relay->pfilter != nullptr;
- }
- NodeEvictionCandidate candidate = {node->GetId(), node->m_connected, node->m_min_ping_time,
- node->m_last_block_time, node->m_last_tx_time,
- HasAllDesirableServiceFlags(node->nServices),
- peer_relay_txes, peer_filter_not_null, node->nKeyedNetGroup,
- node->m_prefer_evict, node->addr.IsLocal(),
- node->ConnectedThroughNetwork()};
+ NodeEvictionCandidate candidate{
+ Desig(id) node->GetId(),
+ Desig(m_connected) node->m_connected,
+ Desig(m_min_ping_time) node->m_min_ping_time,
+ Desig(m_last_block_time) node->m_last_block_time,
+ Desig(m_last_tx_time) node->m_last_tx_time,
+ Desig(fRelevantServices) HasAllDesirableServiceFlags(node->nServices),
+ Desig(m_relay_txs) node->m_relays_txs.load(),
+ Desig(fBloomFilter) node->m_bloom_filter_loaded.load(),
+ Desig(nKeyedNetGroup) node->nKeyedNetGroup,
+ Desig(prefer_evict) node->m_prefer_evict,
+ Desig(m_is_local) node->addr.IsLocal(),
+ Desig(m_network) node->ConnectedThroughNetwork(),
+ };
vEvictionCandidates.push_back(candidate);
}
}
@@ -1123,12 +1149,12 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
}
if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr)) {
- LogPrintf("Warning: Unknown socket family\n");
+ LogPrintLevel(BCLog::NET, BCLog::Level::Warning, "Unknown socket family\n");
} else {
addr = CAddress{MaybeFlipIPv6toCJDNS(addr), NODE_NONE};
}
- const CAddress addr_bind{MaybeFlipIPv6toCJDNS(GetBindAddress(sock->Get())), NODE_NONE};
+ const CAddress addr_bind{MaybeFlipIPv6toCJDNS(GetBindAddress(*sock)), NODE_NONE};
NetPermissionFlags permissionFlags = NetPermissionFlags::None;
hListenSocket.AddSocketPermissionFlags(permissionFlags);
@@ -1173,7 +1199,11 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
// According to the internet TCP_NODELAY is not carried into accepted sockets
// on all platforms. Set it again here just to be sure.
- SetSocketNoDelay(sock->Get());
+ const int on{1};
+ if (sock->SetSockOpt(IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) == SOCKET_ERROR) {
+ LogPrint(BCLog::NET, "connection from %s: unable to set TCP_NODELAY, continuing anyway\n",
+ addr.ToString());
+ }
// Don't accept connections from banned peers.
bool banned = m_banman && m_banman->IsBanned(addr);
@@ -1374,13 +1404,12 @@ bool CConnman::InactivityCheck(const CNode& node) const
return false;
}
-bool CConnman::GenerateSelectSet(const std::vector<CNode*>& nodes,
- std::set<SOCKET>& recv_set,
- std::set<SOCKET>& send_set,
- std::set<SOCKET>& error_set)
+Sock::EventsPerSock CConnman::GenerateWaitSockets(Span<CNode* const> nodes)
{
+ Sock::EventsPerSock events_per_sock;
+
for (const ListenSocket& hListenSocket : vhListenSocket) {
- recv_set.insert(hListenSocket.sock->Get());
+ events_per_sock.emplace(hListenSocket.sock, Sock::Events{Sock::RECV});
}
for (CNode* pnode : nodes) {
@@ -1407,171 +1436,52 @@ bool CConnman::GenerateSelectSet(const std::vector<CNode*>& nodes,
continue;
}
- error_set.insert(pnode->m_sock->Get());
+ Sock::Event requested{0};
if (select_send) {
- send_set.insert(pnode->m_sock->Get());
- continue;
- }
- if (select_recv) {
- recv_set.insert(pnode->m_sock->Get());
- }
- }
-
- return !recv_set.empty() || !send_set.empty() || !error_set.empty();
-}
-
-#ifdef USE_POLL
-void CConnman::SocketEvents(const std::vector<CNode*>& nodes,
- std::set<SOCKET>& recv_set,
- std::set<SOCKET>& send_set,
- std::set<SOCKET>& error_set)
-{
- std::set<SOCKET> recv_select_set, send_select_set, error_select_set;
- if (!GenerateSelectSet(nodes, recv_select_set, send_select_set, error_select_set)) {
- interruptNet.sleep_for(std::chrono::milliseconds(SELECT_TIMEOUT_MILLISECONDS));
- return;
- }
-
- std::unordered_map<SOCKET, struct pollfd> pollfds;
- for (SOCKET socket_id : recv_select_set) {
- pollfds[socket_id].fd = socket_id;
- pollfds[socket_id].events |= POLLIN;
- }
-
- for (SOCKET socket_id : send_select_set) {
- pollfds[socket_id].fd = socket_id;
- pollfds[socket_id].events |= POLLOUT;
- }
-
- for (SOCKET socket_id : error_select_set) {
- pollfds[socket_id].fd = socket_id;
- // These flags are ignored, but we set them for clarity
- pollfds[socket_id].events |= POLLERR|POLLHUP;
- }
-
- std::vector<struct pollfd> vpollfds;
- vpollfds.reserve(pollfds.size());
- for (auto it : pollfds) {
- vpollfds.push_back(std::move(it.second));
- }
-
- if (poll(vpollfds.data(), vpollfds.size(), SELECT_TIMEOUT_MILLISECONDS) < 0) return;
-
- if (interruptNet) return;
-
- for (struct pollfd pollfd_entry : vpollfds) {
- if (pollfd_entry.revents & POLLIN) recv_set.insert(pollfd_entry.fd);
- if (pollfd_entry.revents & POLLOUT) send_set.insert(pollfd_entry.fd);
- if (pollfd_entry.revents & (POLLERR|POLLHUP)) error_set.insert(pollfd_entry.fd);
- }
-}
-#else
-void CConnman::SocketEvents(const std::vector<CNode*>& nodes,
- std::set<SOCKET>& recv_set,
- std::set<SOCKET>& send_set,
- std::set<SOCKET>& error_set)
-{
- std::set<SOCKET> recv_select_set, send_select_set, error_select_set;
- if (!GenerateSelectSet(nodes, recv_select_set, send_select_set, error_select_set)) {
- interruptNet.sleep_for(std::chrono::milliseconds(SELECT_TIMEOUT_MILLISECONDS));
- return;
- }
-
- //
- // Find which sockets have data to receive
- //
- struct timeval timeout;
- timeout.tv_sec = 0;
- timeout.tv_usec = SELECT_TIMEOUT_MILLISECONDS * 1000; // frequency to poll pnode->vSend
-
- fd_set fdsetRecv;
- fd_set fdsetSend;
- fd_set fdsetError;
- FD_ZERO(&fdsetRecv);
- FD_ZERO(&fdsetSend);
- FD_ZERO(&fdsetError);
- SOCKET hSocketMax = 0;
-
- for (SOCKET hSocket : recv_select_set) {
- FD_SET(hSocket, &fdsetRecv);
- hSocketMax = std::max(hSocketMax, hSocket);
- }
-
- for (SOCKET hSocket : send_select_set) {
- FD_SET(hSocket, &fdsetSend);
- hSocketMax = std::max(hSocketMax, hSocket);
- }
-
- for (SOCKET hSocket : error_select_set) {
- FD_SET(hSocket, &fdsetError);
- hSocketMax = std::max(hSocketMax, hSocket);
- }
-
- int nSelect = select(hSocketMax + 1, &fdsetRecv, &fdsetSend, &fdsetError, &timeout);
-
- if (interruptNet)
- return;
-
- if (nSelect == SOCKET_ERROR)
- {
- int nErr = WSAGetLastError();
- LogPrintf("socket select error %s\n", NetworkErrorString(nErr));
- for (unsigned int i = 0; i <= hSocketMax; i++)
- FD_SET(i, &fdsetRecv);
- FD_ZERO(&fdsetSend);
- FD_ZERO(&fdsetError);
- if (!interruptNet.sleep_for(std::chrono::milliseconds(SELECT_TIMEOUT_MILLISECONDS)))
- return;
- }
-
- for (SOCKET hSocket : recv_select_set) {
- if (FD_ISSET(hSocket, &fdsetRecv)) {
- recv_set.insert(hSocket);
+ requested = Sock::SEND;
+ } else if (select_recv) {
+ requested = Sock::RECV;
}
- }
- for (SOCKET hSocket : send_select_set) {
- if (FD_ISSET(hSocket, &fdsetSend)) {
- send_set.insert(hSocket);
- }
+ events_per_sock.emplace(pnode->m_sock, Sock::Events{requested});
}
- for (SOCKET hSocket : error_select_set) {
- if (FD_ISSET(hSocket, &fdsetError)) {
- error_set.insert(hSocket);
- }
- }
+ return events_per_sock;
}
-#endif
void CConnman::SocketHandler()
{
- std::set<SOCKET> recv_set;
- std::set<SOCKET> send_set;
- std::set<SOCKET> error_set;
+ AssertLockNotHeld(m_total_bytes_sent_mutex);
+
+ Sock::EventsPerSock events_per_sock;
{
const NodesSnapshot snap{*this, /*shuffle=*/false};
+ const auto timeout = std::chrono::milliseconds(SELECT_TIMEOUT_MILLISECONDS);
+
// Check for the readiness of the already connected sockets and the
// listening sockets in one call ("readiness" as in poll(2) or
// select(2)). If none are ready, wait for a short while and return
// empty sets.
- SocketEvents(snap.Nodes(), recv_set, send_set, error_set);
+ events_per_sock = GenerateWaitSockets(snap.Nodes());
+ if (events_per_sock.empty() || !events_per_sock.begin()->first->WaitMany(timeout, events_per_sock)) {
+ interruptNet.sleep_for(timeout);
+ }
// Service (send/receive) each of the already connected nodes.
- SocketHandlerConnected(snap.Nodes(), recv_set, send_set, error_set);
+ SocketHandlerConnected(snap.Nodes(), events_per_sock);
}
// Accept new connections from listening sockets.
- SocketHandlerListening(recv_set);
+ SocketHandlerListening(events_per_sock);
}
void CConnman::SocketHandlerConnected(const std::vector<CNode*>& nodes,
- const std::set<SOCKET>& recv_set,
- const std::set<SOCKET>& send_set,
- const std::set<SOCKET>& error_set)
+ const Sock::EventsPerSock& events_per_sock)
{
+ AssertLockNotHeld(m_total_bytes_sent_mutex);
+
for (CNode* pnode : nodes) {
if (interruptNet)
return;
@@ -1587,9 +1497,12 @@ void CConnman::SocketHandlerConnected(const std::vector<CNode*>& nodes,
if (!pnode->m_sock) {
continue;
}
- recvSet = recv_set.count(pnode->m_sock->Get()) > 0;
- sendSet = send_set.count(pnode->m_sock->Get()) > 0;
- errorSet = error_set.count(pnode->m_sock->Get()) > 0;
+ const auto it = events_per_sock.find(pnode->m_sock);
+ if (it != events_per_sock.end()) {
+ recvSet = it->second.occurred & Sock::RECV;
+ sendSet = it->second.occurred & Sock::SEND;
+ errorSet = it->second.occurred & Sock::ERR;
+ }
}
if (recvSet || errorSet)
{
@@ -1659,13 +1572,14 @@ void CConnman::SocketHandlerConnected(const std::vector<CNode*>& nodes,
}
}
-void CConnman::SocketHandlerListening(const std::set<SOCKET>& recv_set)
+void CConnman::SocketHandlerListening(const Sock::EventsPerSock& events_per_sock)
{
for (const ListenSocket& listen_socket : vhListenSocket) {
if (interruptNet) {
return;
}
- if (recv_set.count(listen_socket.sock->Get()) > 0) {
+ const auto it = events_per_sock.find(listen_socket.sock);
+ if (it != events_per_sock.end() && it->second.occurred & Sock::RECV) {
AcceptConnection(listen_socket);
}
}
@@ -1673,6 +1587,8 @@ void CConnman::SocketHandlerListening(const std::set<SOCKET>& recv_set)
void CConnman::ThreadSocketHandler()
{
+ AssertLockNotHeld(m_total_bytes_sent_mutex);
+
SetSyscallSandboxPolicy(SyscallSandboxPolicy::NET);
while (!interruptNet)
{
@@ -1837,7 +1753,13 @@ bool CConnman::GetTryNewOutboundPeer() const
void CConnman::SetTryNewOutboundPeer(bool flag)
{
m_try_another_outbound_peer = flag;
- LogPrint(BCLog::NET, "net: setting try another outbound peer=%s\n", flag ? "true" : "false");
+ LogPrint(BCLog::NET, "setting try another outbound peer=%s\n", flag ? "true" : "false");
+}
+
+void CConnman::StartExtraBlockRelayPeers()
+{
+ LogPrint(BCLog::NET, "enabling extra block-relay-only peers\n");
+ m_start_extra_block_relay_peers = true;
}
// Return the number of peers we have over our outbound connection limit
@@ -1980,7 +1902,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
case ConnectionType::BLOCK_RELAY:
case ConnectionType::ADDR_FETCH:
case ConnectionType::FEELER:
- setConnected.insert(pnode->addr.GetGroup(addrman.GetAsmap()));
+ setConnected.insert(m_netgroupman.GetGroup(pnode->addr));
} // no default case, so the compiler can warn about missing cases
}
}
@@ -2054,7 +1976,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
m_anchors.pop_back();
if (!addr.IsValid() || IsLocal(addr) || !IsReachable(addr) ||
!HasAllDesirableServiceFlags(addr.nServices) ||
- setConnected.count(addr.GetGroup(addrman.GetAsmap()))) continue;
+ setConnected.count(m_netgroupman.GetGroup(addr))) continue;
addrConnect = addr;
LogPrint(BCLog::NET, "Trying to make an anchor connection to %s\n", addrConnect.ToString());
break;
@@ -2095,7 +2017,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
}
// Require outbound connections, other than feelers, to be to distinct network groups
- if (!fFeeler && setConnected.count(addr.GetGroup(addrman.GetAsmap()))) {
+ if (!fFeeler && setConnected.count(m_netgroupman.GetGroup(addr))) {
break;
}
@@ -2120,12 +2042,8 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
continue;
}
- // Do not allow non-default ports, unless after 50 invalid
- // addresses selected already. This is to prevent malicious peers
- // from advertising themselves as a service on another host and
- // port, causing a DoS attack as nodes around the network attempt
- // to connect to it fruitlessly.
- if (addr.GetPort() != Params().GetDefaultPort(addr.GetNetwork()) && nTries < 50) {
+ // Do not connect to bad ports, unless 50 invalid addresses have been selected already.
+ if (nTries < 50 && (addr.IsIPv4() || addr.IsIPv6()) && IsBadPort(addr.GetPort())) {
continue;
}
@@ -2137,7 +2055,7 @@ void CConnman::ThreadOpenConnections(const std::vector<std::string> connect)
if (fFeeler) {
// Add small amount of random noise before connection to avoid synchronization.
- int randsleep = GetRandInt(FEELER_SLEEP_WINDOW * 1000);
+ int randsleep = GetRand<int>(FEELER_SLEEP_WINDOW * 1000);
if (!interruptNet.sleep_for(std::chrono::milliseconds(randsleep)))
return;
LogPrint(BCLog::NET, "Making feeler connection to %s\n", addrConnect.ToString());
@@ -2368,31 +2286,40 @@ bool CConnman::BindListenPort(const CService& addrBind, bilingual_str& strError,
socklen_t len = sizeof(sockaddr);
if (!addrBind.GetSockAddr((struct sockaddr*)&sockaddr, &len))
{
- strError = strprintf(Untranslated("Error: Bind address family for %s not supported"), addrBind.ToString());
- LogPrintf("%s\n", strError.original);
+ strError = strprintf(Untranslated("Bind address family for %s not supported"), addrBind.ToString());
+ LogPrintLevel(BCLog::NET, BCLog::Level::Error, "%s\n", strError.original);
return false;
}
std::unique_ptr<Sock> sock = CreateSock(addrBind);
if (!sock) {
- strError = strprintf(Untranslated("Error: Couldn't open socket for incoming connections (socket returned error %s)"), NetworkErrorString(WSAGetLastError()));
- LogPrintf("%s\n", strError.original);
+ strError = strprintf(Untranslated("Couldn't open socket for incoming connections (socket returned error %s)"), NetworkErrorString(WSAGetLastError()));
+ LogPrintLevel(BCLog::NET, BCLog::Level::Error, "%s\n", strError.original);
return false;
}
// Allow binding if the port is still in TIME_WAIT state after
// the program was closed and restarted.
- setsockopt(sock->Get(), SOL_SOCKET, SO_REUSEADDR, (sockopt_arg_type)&nOne, sizeof(int));
+ if (sock->SetSockOpt(SOL_SOCKET, SO_REUSEADDR, (sockopt_arg_type)&nOne, sizeof(int)) == SOCKET_ERROR) {
+ strError = strprintf(Untranslated("Error setting SO_REUSEADDR on socket: %s, continuing anyway"), NetworkErrorString(WSAGetLastError()));
+ LogPrintf("%s\n", strError.original);
+ }
// some systems don't have IPV6_V6ONLY but are always v6only; others do have the option
// and enable it by default or not. Try to enable it, if possible.
if (addrBind.IsIPv6()) {
#ifdef IPV6_V6ONLY
- setsockopt(sock->Get(), IPPROTO_IPV6, IPV6_V6ONLY, (sockopt_arg_type)&nOne, sizeof(int));
+ if (sock->SetSockOpt(IPPROTO_IPV6, IPV6_V6ONLY, (sockopt_arg_type)&nOne, sizeof(int)) == SOCKET_ERROR) {
+ strError = strprintf(Untranslated("Error setting IPV6_V6ONLY on socket: %s, continuing anyway"), NetworkErrorString(WSAGetLastError()));
+ LogPrintf("%s\n", strError.original);
+ }
#endif
#ifdef WIN32
int nProtLevel = PROTECTION_LEVEL_UNRESTRICTED;
- setsockopt(sock->Get(), IPPROTO_IPV6, IPV6_PROTECTION_LEVEL, (const char*)&nProtLevel, sizeof(int));
+ if (sock->SetSockOpt(IPPROTO_IPV6, IPV6_PROTECTION_LEVEL, (const char*)&nProtLevel, sizeof(int)) == SOCKET_ERROR) {
+ strError = strprintf(Untranslated("Error setting IPV6_PROTECTION_LEVEL on socket: %s, continuing anyway"), NetworkErrorString(WSAGetLastError()));
+ LogPrintf("%s\n", strError.original);
+ }
#endif
}
@@ -2402,7 +2329,7 @@ bool CConnman::BindListenPort(const CService& addrBind, bilingual_str& strError,
strError = strprintf(_("Unable to bind to %s on this computer. %s is probably already running."), addrBind.ToString(), PACKAGE_NAME);
else
strError = strprintf(_("Unable to bind to %s on this computer (bind returned error %s)"), addrBind.ToString(), NetworkErrorString(nErr));
- LogPrintf("%s\n", strError.original);
+ LogPrintLevel(BCLog::NET, BCLog::Level::Error, "%s\n", strError.original);
return false;
}
LogPrintf("Bound to %s\n", addrBind.ToString());
@@ -2410,8 +2337,8 @@ bool CConnman::BindListenPort(const CService& addrBind, bilingual_str& strError,
// Listen for incoming connections
if (sock->Listen(SOMAXCONN) == SOCKET_ERROR)
{
- strError = strprintf(_("Error: Listening for incoming connections failed (listen returned error %s)"), NetworkErrorString(WSAGetLastError()));
- LogPrintf("%s\n", strError.original);
+ strError = strprintf(_("Listening for incoming connections failed (listen returned error %s)"), NetworkErrorString(WSAGetLastError()));
+ LogPrintLevel(BCLog::NET, BCLog::Level::Error, "%s\n", strError.original);
return false;
}
@@ -2485,8 +2412,12 @@ void CConnman::SetNetworkActive(bool active)
}
}
-CConnman::CConnman(uint64_t nSeed0In, uint64_t nSeed1In, AddrMan& addrman_in, bool network_active)
- : addrman(addrman_in), nSeed0(nSeed0In), nSeed1(nSeed1In)
+CConnman::CConnman(uint64_t nSeed0In, uint64_t nSeed1In, AddrMan& addrman_in,
+ const NetGroupManager& netgroupman, bool network_active)
+ : addrman(addrman_in)
+ , m_netgroupman{netgroupman}
+ , nSeed0(nSeed0In)
+ , nSeed1(nSeed1In)
{
SetTryNewOutboundPeer(false);
@@ -2547,6 +2478,7 @@ bool CConnman::InitBinds(const Options& options)
bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
{
+ AssertLockNotHeld(m_total_bytes_sent_mutex);
Init(connOptions);
if (fListen && !InitBinds(connOptions)) {
@@ -2558,7 +2490,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
return false;
}
- proxyType i2p_sam;
+ Proxy 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);
@@ -2619,7 +2551,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
if (connOptions.m_use_addrman_outgoing && !connOptions.m_specified_outgoing.empty()) {
if (m_client_interface) {
m_client_interface->ThreadSafeMessageBox(
- _("Cannot provide specific connections and have addrman find outgoing connections at the same."),
+ _("Cannot provide specific connections and have addrman find outgoing connections at the same time."),
"", CClientUIInterface::MSG_ERROR);
}
return false;
@@ -2647,7 +2579,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
class CNetCleanup
{
public:
- CNetCleanup() {}
+ CNetCleanup() = default;
~CNetCleanup()
{
@@ -2761,14 +2693,17 @@ std::vector<CAddress> CConnman::GetAddresses(CNode& requestor, size_t max_addres
{
auto local_socket_bytes = requestor.addrBind.GetAddrBytes();
uint64_t cache_id = GetDeterministicRandomizer(RANDOMIZER_ID_ADDRCACHE)
- .Write(requestor.addr.GetNetwork())
+ .Write(requestor.ConnectedThroughNetwork())
.Write(local_socket_bytes.data(), local_socket_bytes.size())
+ // For outbound connections, the port of the bound address is randomly
+ // assigned by the OS and would therefore not be useful for seeding.
+ .Write(requestor.IsInboundConn() ? requestor.addrBind.GetPort() : 0)
.Finalize();
const auto current_time = GetTime<std::chrono::microseconds>();
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, /* network */ std::nullopt);
+ 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.
//
@@ -2845,7 +2780,7 @@ void CConnman::GetNodeStats(std::vector<CNodeStats>& vstats) const
for (CNode* pnode : m_nodes) {
vstats.emplace_back();
pnode->CopyStats(vstats.back());
- vstats.back().m_mapped_as = pnode->addr.GetMappedAS(addrman.GetAsmap());
+ vstats.back().m_mapped_as = m_netgroupman.GetMappedAS(pnode->addr);
}
}
@@ -2899,7 +2834,9 @@ void CConnman::RecordBytesRecv(uint64_t bytes)
void CConnman::RecordBytesSent(uint64_t bytes)
{
- LOCK(cs_totalBytesSent);
+ AssertLockNotHeld(m_total_bytes_sent_mutex);
+ LOCK(m_total_bytes_sent_mutex);
+
nTotalBytesSent += bytes;
const auto now = GetTime<std::chrono::seconds>();
@@ -2915,7 +2852,8 @@ void CConnman::RecordBytesSent(uint64_t bytes)
uint64_t CConnman::GetMaxOutboundTarget() const
{
- LOCK(cs_totalBytesSent);
+ AssertLockNotHeld(m_total_bytes_sent_mutex);
+ LOCK(m_total_bytes_sent_mutex);
return nMaxOutboundLimit;
}
@@ -2926,7 +2864,15 @@ std::chrono::seconds CConnman::GetMaxOutboundTimeframe() const
std::chrono::seconds CConnman::GetMaxOutboundTimeLeftInCycle() const
{
- LOCK(cs_totalBytesSent);
+ AssertLockNotHeld(m_total_bytes_sent_mutex);
+ LOCK(m_total_bytes_sent_mutex);
+ return GetMaxOutboundTimeLeftInCycle_();
+}
+
+std::chrono::seconds CConnman::GetMaxOutboundTimeLeftInCycle_() const
+{
+ AssertLockHeld(m_total_bytes_sent_mutex);
+
if (nMaxOutboundLimit == 0)
return 0s;
@@ -2940,14 +2886,15 @@ std::chrono::seconds CConnman::GetMaxOutboundTimeLeftInCycle() const
bool CConnman::OutboundTargetReached(bool historicalBlockServingLimit) const
{
- LOCK(cs_totalBytesSent);
+ AssertLockNotHeld(m_total_bytes_sent_mutex);
+ LOCK(m_total_bytes_sent_mutex);
if (nMaxOutboundLimit == 0)
return false;
if (historicalBlockServingLimit)
{
// keep a large enough buffer to at least relay each block once
- const std::chrono::seconds timeLeftInCycle = GetMaxOutboundTimeLeftInCycle();
+ const std::chrono::seconds timeLeftInCycle = GetMaxOutboundTimeLeftInCycle_();
const uint64_t buffer = timeLeftInCycle / std::chrono::minutes{10} * MAX_BLOCK_SERIALIZED_SIZE;
if (buffer >= nMaxOutboundLimit || nMaxOutboundTotalBytesSentInCycle >= nMaxOutboundLimit - buffer)
return true;
@@ -2960,7 +2907,8 @@ bool CConnman::OutboundTargetReached(bool historicalBlockServingLimit) const
uint64_t CConnman::GetOutboundTargetBytesLeft() const
{
- LOCK(cs_totalBytesSent);
+ AssertLockNotHeld(m_total_bytes_sent_mutex);
+ LOCK(m_total_bytes_sent_mutex);
if (nMaxOutboundLimit == 0)
return 0;
@@ -2974,7 +2922,8 @@ uint64_t CConnman::GetTotalBytesRecv() const
uint64_t CConnman::GetTotalBytesSent() const
{
- LOCK(cs_totalBytesSent);
+ AssertLockNotHeld(m_total_bytes_sent_mutex);
+ LOCK(m_total_bytes_sent_mutex);
return nTotalBytesSent;
}
@@ -2999,13 +2948,10 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, std::shared_ptr<Sock> s
nLocalServices(nLocalServicesIn)
{
if (inbound_onion) assert(conn_type_in == ConnectionType::INBOUND);
- if (conn_type_in != ConnectionType::BLOCK_RELAY) {
- m_tx_relay = std::make_unique<TxRelay>();
- }
for (const std::string &msg : getAllNetMessageTypes())
- mapRecvBytesPerMsgCmd[msg] = 0;
- mapRecvBytesPerMsgCmd[NET_MESSAGE_COMMAND_OTHER] = 0;
+ mapRecvBytesPerMsgType[msg] = 0;
+ mapRecvBytesPerMsgType[NET_MESSAGE_TYPE_OTHER] = 0;
if (fLogIPs) {
LogPrint(BCLog::NET, "Added connection to %s peer=%d\n", m_addr_name, id);
@@ -3024,6 +2970,7 @@ bool CConnman::NodeFullyConnected(const CNode* pnode)
void CConnman::PushMessage(CNode* pnode, CSerializedNetMsg&& msg)
{
+ AssertLockNotHeld(m_total_bytes_sent_mutex);
size_t nMessageSize = msg.data.size();
LogPrint(BCLog::NET, "sending %s (%d bytes) peer=%d\n", msg.m_type, nMessageSize, pnode->GetId());
if (gArgs.GetBoolArg("-capturemessages", false)) {
@@ -3050,7 +2997,7 @@ void CConnman::PushMessage(CNode* pnode, CSerializedNetMsg&& msg)
bool optimisticSend(pnode->vSendMsg.empty());
//log total amount of bytes per message type
- pnode->mapSendBytesPerMsgCmd[msg.m_type] += nTotalSize;
+ pnode->mapSendBytesPerMsgType[msg.m_type] += nTotalSize;
pnode->nSendSize += nTotalSize;
if (pnode->nSendSize > nSendBufferMaxSize) pnode->fPauseSend = true;
@@ -3081,14 +3028,17 @@ CSipHasher CConnman::GetDeterministicRandomizer(uint64_t id) const
return CSipHasher(nSeed0, nSeed1).Write(id);
}
-uint64_t CConnman::CalculateKeyedNetGroup(const CAddress& ad) const
+uint64_t CConnman::CalculateKeyedNetGroup(const CAddress& address) const
{
- std::vector<unsigned char> vchNetGroup(ad.GetGroup(addrman.GetAsmap()));
+ std::vector<unsigned char> vchNetGroup(m_netgroupman.GetGroup(address));
return GetDeterministicRandomizer(RANDOMIZER_ID_NETGROUP).Write(vchNetGroup.data(), vchNetGroup.size()).Finalize();
}
-void CaptureMessage(const CAddress& addr, const std::string& msg_type, const Span<const unsigned char>& data, bool is_incoming)
+void CaptureMessageToFile(const CAddress& addr,
+ const std::string& msg_type,
+ Span<const unsigned char> data,
+ bool is_incoming)
{
// Note: This function captures the message at the time of processing,
// not at socket receive/send time.
@@ -3096,11 +3046,11 @@ void CaptureMessage(const CAddress& addr, const std::string& msg_type, const Spa
// layer (processing) perspective.
auto now = GetTime<std::chrono::microseconds>();
- // Windows folder names can not include a colon
+ // Windows folder names cannot include a colon
std::string clean_addr = addr.ToString();
std::replace(clean_addr.begin(), clean_addr.end(), ':', '_');
- fs::path base_path = gArgs.GetDataDirNet() / "message_capture" / clean_addr;
+ fs::path base_path = gArgs.GetDataDirNet() / "message_capture" / fs::u8path(clean_addr);
fs::create_directories(base_path);
fs::path path = base_path / (is_incoming ? "msgs_recv.dat" : "msgs_sent.dat");
@@ -3115,3 +3065,9 @@ void CaptureMessage(const CAddress& addr, const std::string& msg_type, const Spa
ser_writedata32(f, size);
f.write(AsBytes(data));
}
+
+std::function<void(const CAddress& addr,
+ const std::string& msg_type,
+ Span<const unsigned char> data,
+ bool is_incoming)>
+ CaptureMessage = CaptureMessageToFile;