Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

backport: merge bitcoin#21879, #23604, #24357, #25426, #24378 (sockets backports) #6054

Merged
merged 5 commits into from
Jun 12, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 21 additions & 26 deletions src/net.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1220,9 +1220,10 @@ bool CConnman::AttemptToEvictConnection()
void CConnman::AcceptConnection(const ListenSocket& hListenSocket, CMasternodeSync& mn_sync) {
struct sockaddr_storage sockaddr;
socklen_t len = sizeof(sockaddr);
SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr*)&sockaddr, &len);
auto sock = hListenSocket.sock->Accept((struct sockaddr*)&sockaddr, &len);
CAddress addr;
if (hSocket == INVALID_SOCKET) {

if (!sock) {
const int nErr = WSAGetLastError();
if (nErr != WSAEWOULDBLOCK) {
LogPrintf("socket error accept failed: %s\n", NetworkErrorString(nErr));
Expand All @@ -1236,15 +1237,15 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket, CMasternodeSy
addr = CAddress{MaybeFlipIPv6toCJDNS(addr), NODE_NONE};
}

const CAddress addr_bind{MaybeFlipIPv6toCJDNS(GetBindAddress(hSocket)), NODE_NONE};
const CAddress addr_bind{MaybeFlipIPv6toCJDNS(GetBindAddress(sock->Get())), NODE_NONE};

NetPermissionFlags permissionFlags = NetPermissionFlags::None;
hListenSocket.AddSocketPermissionFlags(permissionFlags);

CreateNodeFromAcceptedSocket(hSocket, permissionFlags, addr_bind, addr, mn_sync);
CreateNodeFromAcceptedSocket(std::move(sock), permissionFlags, addr_bind, addr, mn_sync);
}

void CConnman::CreateNodeFromAcceptedSocket(SOCKET hSocket,
void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
NetPermissionFlags permissionFlags,
const CAddress& addr_bind,
const CAddress& addr,
Expand Down Expand Up @@ -1287,27 +1288,24 @@ void CConnman::CreateNodeFromAcceptedSocket(SOCKET hSocket,

if (!fNetworkActive) {
LogPrint(BCLog::NET_NETCONN, "%s: not accepting new connections\n", strDropped);
CloseSocket(hSocket);
return;
}

if (!IsSelectableSocket(hSocket))
if (!IsSelectableSocket(sock->Get()))
{
LogPrintf("%s: non-selectable socket\n", strDropped);
CloseSocket(hSocket);
return;
}

// According to the internet TCP_NODELAY is not carried into accepted sockets
// on all platforms. Set it again here just to be sure.
SetSocketNoDelay(hSocket);
SetSocketNoDelay(sock->Get());

// Don't accept connections from banned peers.
bool banned = m_banman && m_banman->IsBanned(addr);
if (!NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::NoBan) && banned)
{
LogPrint(BCLog::NET, "%s (banned)\n", strDropped);
CloseSocket(hSocket);
return;
}

Expand All @@ -1316,7 +1314,6 @@ void CConnman::CreateNodeFromAcceptedSocket(SOCKET hSocket,
if (!NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::NoBan) && nInbound + 1 >= nMaxInbound && discouraged)
{
LogPrint(BCLog::NET, "connection from %s dropped (discouraged)\n", addr.ToString());
CloseSocket(hSocket);
return;
}

Expand All @@ -1330,7 +1327,6 @@ void CConnman::CreateNodeFromAcceptedSocket(SOCKET hSocket,
if (!AttemptToEvictConnection()) {
// No connection to evict, disconnect the new connection
LogPrint(BCLog::NET, "failed to find an eviction candidate - connection dropped (full)\n");
CloseSocket(hSocket);
return;
}
nInbound--;
Expand All @@ -1339,7 +1335,6 @@ void CConnman::CreateNodeFromAcceptedSocket(SOCKET hSocket,
// don't accept incoming connections until blockchain is synced
if (fMasternodeMode && !mn_sync.IsBlockchainSynced()) {
LogPrint(BCLog::NET, "AcceptConnection -- blockchain is not synced yet, skipping inbound connection attempt\n");
CloseSocket(hSocket);
return;
}

Expand All @@ -1352,7 +1347,7 @@ void CConnman::CreateNodeFromAcceptedSocket(SOCKET hSocket,
}

const bool inbound_onion = std::find(m_onion_binds.begin(), m_onion_binds.end(), addr_bind) != m_onion_binds.end();
CNode* pnode = new CNode(id, nodeServices, hSocket, addr, CalculateKeyedNetGroup(addr), nonce, addr_bind, "", ConnectionType::INBOUND, inbound_onion);
CNode* pnode = new CNode(id, nodeServices, sock->Release(), addr, CalculateKeyedNetGroup(addr), nonce, addr_bind, "", ConnectionType::INBOUND, inbound_onion);
pnode->AddRef();
pnode->m_permissionFlags = permissionFlags;
// If this flag is present, the user probably expect that RPC and QT report it as whitelisted (backward compatibility)
Expand All @@ -1361,17 +1356,19 @@ void CConnman::CreateNodeFromAcceptedSocket(SOCKET hSocket,
m_msgproc->InitializeNode(pnode);

if (fLogIPs) {
LogPrint(BCLog::NET_NETCONN, "connection from %s accepted, sock=%d, peer=%d\n", addr.ToString(), hSocket, pnode->GetId());
LogPrint(BCLog::NET_NETCONN, "connection from %s accepted, sock=%d, peer=%d\n", addr.ToString(), sock->Get(), pnode->GetId());
} else {
LogPrint(BCLog::NET_NETCONN, "connection accepted, sock=%d, peer=%d\n", hSocket, pnode->GetId());
LogPrint(BCLog::NET_NETCONN, "connection accepted, sock=%d, peer=%d\n", sock->Get(), pnode->GetId());
}

{
LOCK(m_nodes_mutex);
m_nodes.push_back(pnode);
WITH_LOCK(cs_mapSocketToNode, mapSocketToNode.emplace(hSocket, pnode));
}
{
LOCK(pnode->cs_hSocket);
WITH_LOCK(cs_mapSocketToNode, mapSocketToNode.emplace(pnode->hSocket, pnode));
if (m_edge_trig_events) {
LOCK(pnode->cs_hSocket);
if (!m_edge_trig_events->RegisterEvents(pnode->hSocket)) {
LogPrint(BCLog::NET, "EdgeTriggeredEvents::RegisterEvents() failed\n");
}
Expand Down Expand Up @@ -1656,7 +1653,7 @@ bool CConnman::GenerateSelectSet(const std::vector<CNode*>& nodes,
std::set<SOCKET>& error_set)
{
for (const ListenSocket& hListenSocket : vhListenSocket) {
recv_set.insert(hListenSocket.socket);
recv_set.insert(hListenSocket.sock->Get());
}

for (CNode* pnode : nodes)
Expand Down Expand Up @@ -2128,7 +2125,7 @@ void CConnman::SocketHandlerListening(const std::set<SOCKET>& recv_set, CMastern
if (interruptNet) {
return;
}
if (recv_set.count(listen_socket.socket) > 0) {
if (recv_set.count(listen_socket.sock->Get()) > 0) {
AcceptConnection(listen_socket, mn_sync);
}
}
Expand Down Expand Up @@ -3168,7 +3165,7 @@ void CConnman::ThreadI2PAcceptIncoming(CMasternodeSync& mn_sync)
continue;
}

CreateNodeFromAcceptedSocket(conn.sock->Release(), NetPermissionFlags::None,
CreateNodeFromAcceptedSocket(std::move(conn.sock), NetPermissionFlags::None,
CAddress{conn.me, NODE_NONE}, CAddress{conn.peer, NODE_NONE}, mn_sync);
}
}
Expand Down Expand Up @@ -3235,7 +3232,7 @@ bool CConnman::BindListenPort(const CService& addrBind, bilingual_str& strError,
return false;
}

vhListenSocket.push_back(ListenSocket(sock->Release(), permissions));
vhListenSocket.emplace_back(std::move(sock), permissions);

return true;
}
Expand Down Expand Up @@ -3582,12 +3579,10 @@ void CConnman::StopNodes()
pnode->CloseSocketDisconnect(this);
}
for (ListenSocket& hListenSocket : vhListenSocket) {
if (hListenSocket.socket != INVALID_SOCKET) {
if (m_edge_trig_events && !m_edge_trig_events->RemoveSocket(hListenSocket.socket)) {
if (hListenSocket.sock->Get() != INVALID_SOCKET) {
Copy link
Collaborator

@knst knst Sep 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why these lines are not removed? I am going to prepare PR with fix

Copy link
Collaborator Author

@kwvg kwvg Sep 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because we iterate through them to remove them from the ETE list, the kernel needs to be notified before the socket gets destroyed.

dash/src/net.cpp

Lines 3763 to 3769 in 0c243ab

for (ListenSocket& hListenSocket : vhListenSocket) {
if (hListenSocket.sock) {
if (m_edge_trig_events && !m_edge_trig_events->RemoveSocket(hListenSocket.sock->Get())) {
LogPrintf("EdgeTriggeredEvents::RemoveSocket() failed\n");
}
}
}

if (m_edge_trig_events && !m_edge_trig_events->RemoveSocket(hListenSocket.sock->Get())) {
LogPrintf("EdgeTriggeredEvents::RemoveSocket() failed\n");
}
if (!CloseSocket(hListenSocket.socket))
LogPrintf("CloseSocket(hListenSocket) failed with error %s\n", NetworkErrorString(WSAGetLastError()));
}
}

Expand Down
13 changes: 9 additions & 4 deletions src/net.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include <uint256.h>
#include <util/check.h>
#include <util/edge.h>
#include <util/sock.h>
#include <util/system.h>
#include <util/wpipe.h>
#include <consensus/params.h>
Expand Down Expand Up @@ -1221,9 +1222,13 @@ friend class CNode;
private:
struct ListenSocket {
public:
SOCKET socket;
std::shared_ptr<Sock> sock;
inline void AddSocketPermissionFlags(NetPermissionFlags& flags) const { NetPermissions::AddFlag(flags, m_permissions); }
ListenSocket(SOCKET socket_, NetPermissionFlags permissions_) : socket(socket_), m_permissions(permissions_) {}
ListenSocket(std::shared_ptr<Sock> sock_, NetPermissionFlags permissions_)
: sock{sock_}, m_permissions{permissions_}
{
}

private:
NetPermissionFlags m_permissions;
};
Expand Down Expand Up @@ -1251,12 +1256,12 @@ friend class CNode;
/**
* Create a `CNode` object from a socket that has just been accepted and add the node to
* the `m_nodes` member.
* @param[in] hSocket Connected socket to communicate with the peer.
* @param[in] sock Connected socket to communicate with the peer.
* @param[in] permissionFlags The peer's permissions.
* @param[in] addr_bind The address and port at our side of the connection.
* @param[in] addr The address and port at the peer's side of the connection.
*/
void CreateNodeFromAcceptedSocket(SOCKET hSocket,
void CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
NetPermissionFlags permissionFlags,
const CAddress& addr_bind,
const CAddress& addr,
Expand Down
16 changes: 16 additions & 0 deletions src/test/fuzz/util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#include <util/time.h>
#include <version.h>

#include <memory>

FuzzedSock::FuzzedSock(FuzzedDataProvider& fuzzed_data_provider)
: m_fuzzed_data_provider{fuzzed_data_provider}
{
Expand Down Expand Up @@ -155,6 +157,20 @@ int FuzzedSock::Connect(const sockaddr*, socklen_t) const
return 0;
}

std::unique_ptr<Sock> FuzzedSock::Accept(sockaddr* addr, socklen_t* addr_len) const
{
constexpr std::array accept_errnos{
ECONNABORTED,
EINTR,
ENOMEM,
};
if (m_fuzzed_data_provider.ConsumeBool()) {
SetFuzzedErrNo(m_fuzzed_data_provider, accept_errnos);
return std::unique_ptr<FuzzedSock>();
}
return std::make_unique<FuzzedSock>(m_fuzzed_data_provider);
}

int FuzzedSock::GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const
{
constexpr std::array getsockopt_errnos{
Expand Down
2 changes: 2 additions & 0 deletions src/test/fuzz/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,8 @@ class FuzzedSock : public Sock

int Connect(const sockaddr*, socklen_t) const override;

std::unique_ptr<Sock> Accept(sockaddr* addr, socklen_t* addr_len) const override;

int GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const override;

bool Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred = nullptr) const override;
Expand Down
18 changes: 18 additions & 0 deletions src/test/util/net.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <array>
#include <cassert>
#include <cstring>
#include <memory>
#include <string>

struct ConnmanTestMsg : public CConnman {
Expand Down Expand Up @@ -126,6 +127,23 @@ class StaticContentsSock : public Sock

int Connect(const sockaddr*, socklen_t) const override { return 0; }

std::unique_ptr<Sock> Accept(sockaddr* addr, socklen_t* addr_len) const override
{
if (addr != nullptr) {
// Pretend all connections come from 5.5.5.5:6789
memset(addr, 0x00, *addr_len);
const socklen_t write_len = static_cast<socklen_t>(sizeof(sockaddr_in));
if (*addr_len >= write_len) {
*addr_len = write_len;
sockaddr_in* addr_in = reinterpret_cast<sockaddr_in*>(addr);
addr_in->sin_family = AF_INET;
memset(&addr_in->sin_addr, 0x05, sizeof(addr_in->sin_addr));
addr_in->sin_port = htons(6789);
}
}
return std::make_unique<StaticContentsSock>("");
};

int GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const override
{
std::memset(opt_val, 0x0, *opt_len);
Expand Down
27 changes: 27 additions & 0 deletions src/util/sock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <util/system.h>
#include <util/time.h>

#include <memory>
#include <stdexcept>
#include <string>

Expand Down Expand Up @@ -73,6 +74,32 @@ int Sock::Connect(const sockaddr* addr, socklen_t addr_len) const
return connect(m_socket, addr, addr_len);
}

std::unique_ptr<Sock> Sock::Accept(sockaddr* addr, socklen_t* addr_len) const
{
#ifdef WIN32
static constexpr auto ERR = INVALID_SOCKET;
#else
static constexpr auto ERR = SOCKET_ERROR;
#endif

std::unique_ptr<Sock> sock;

const auto socket = accept(m_socket, addr, addr_len);
if (socket != ERR) {
try {
sock = std::make_unique<Sock>(socket);
} catch (const std::exception&) {
#ifdef WIN32
closesocket(socket);
#else
close(socket);
#endif
}
}

return sock;
}

int Sock::GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const
{
return getsockopt(m_socket, level, opt_name, static_cast<char*>(opt_val), opt_len);
Expand Down
9 changes: 9 additions & 0 deletions src/util/sock.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <util/time.h>

#include <chrono>
#include <memory>
#include <string>

/**
Expand Down Expand Up @@ -144,6 +145,14 @@ class Sock
*/
[[nodiscard]] virtual int Connect(const sockaddr* addr, socklen_t addr_len) const;

/**
* accept(2) wrapper. Equivalent to `std::make_unique<Sock>(accept(this->Get(), addr, addr_len))`.
* Code that uses this wrapper can be unit tested if this method is overridden by a mock Sock
* implementation.
* The returned unique_ptr is empty if `accept()` failed in which case errno will be set.
*/
[[nodiscard]] virtual std::unique_ptr<Sock> Accept(sockaddr* addr, socklen_t* addr_len) const;

/**
* getsockopt(2) wrapper. Equivalent to
* `getsockopt(this->Get(), level, opt_name, opt_val, opt_len)`. Code that uses this
Expand Down