net: split CConnman::BindListenPort() off CConnman

Introduce a new low-level socket managing class `SockMan`
and move the `CConnman::BindListenPort()` method to it.
This commit is contained in:
Vasil Dimov 2024-08-23 15:36:40 +02:00
parent 69ac6802ba
commit 98ba5c7965
No known key found for this signature in database
GPG Key ID: 54DF06F64B55CBBF
5 changed files with 133 additions and 78 deletions

View File

@ -123,6 +123,7 @@ add_library(bitcoin_common STATIC EXCLUDE_FROM_ALL
common/run_command.cpp
common/settings.cpp
common/signmessage.cpp
common/sockman.cpp
common/system.cpp
common/url.cpp
compressor.cpp

85
src/common/sockman.cpp Normal file
View File

@ -0,0 +1,85 @@
// Copyright (c) 2024-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://opensource.org/license/mit/.
#include <bitcoin-build-config.h> // IWYU pragma: keep
#include <common/sockman.h>
#include <logging.h>
#include <netbase.h>
#include <util/sock.h>
bool SockMan::BindListenPort(const CService& addrBind, bilingual_str& strError)
{
int nOne = 1;
// Create socket for listening for incoming connections
struct sockaddr_storage sockaddr;
socklen_t len = sizeof(sockaddr);
if (!addrBind.GetSockAddr((struct sockaddr*)&sockaddr, &len))
{
strError = Untranslated(strprintf("Bind address family for %s not supported", addrBind.ToStringAddrPort()));
LogPrintLevel(BCLog::NET, BCLog::Level::Error, "%s\n", strError.original);
return false;
}
std::unique_ptr<Sock> sock = CreateSock(addrBind.GetSAFamily(), SOCK_STREAM, IPPROTO_TCP);
if (!sock) {
strError = Untranslated(strprintf("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.
if (sock->SetSockOpt(SOL_SOCKET, SO_REUSEADDR, (sockopt_arg_type)&nOne, sizeof(int)) == SOCKET_ERROR) {
strError = Untranslated(strprintf("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
if (sock->SetSockOpt(IPPROTO_IPV6, IPV6_V6ONLY, (sockopt_arg_type)&nOne, sizeof(int)) == SOCKET_ERROR) {
strError = Untranslated(strprintf("Error setting IPV6_V6ONLY on socket: %s, continuing anyway", NetworkErrorString(WSAGetLastError())));
LogPrintf("%s\n", strError.original);
}
#endif
#ifdef WIN32
int nProtLevel = PROTECTION_LEVEL_UNRESTRICTED;
if (sock->SetSockOpt(IPPROTO_IPV6, IPV6_PROTECTION_LEVEL, (const char*)&nProtLevel, sizeof(int)) == SOCKET_ERROR) {
strError = Untranslated(strprintf("Error setting IPV6_PROTECTION_LEVEL on socket: %s, continuing anyway", NetworkErrorString(WSAGetLastError())));
LogPrintf("%s\n", strError.original);
}
#endif
}
if (sock->Bind(reinterpret_cast<struct sockaddr*>(&sockaddr), len) == SOCKET_ERROR) {
int nErr = WSAGetLastError();
if (nErr == WSAEADDRINUSE)
strError = strprintf(_("Unable to bind to %s on this computer. %s is probably already running."), addrBind.ToStringAddrPort(), CLIENT_NAME);
else
strError = strprintf(_("Unable to bind to %s on this computer (bind returned error %s)"), addrBind.ToStringAddrPort(), NetworkErrorString(nErr));
LogPrintLevel(BCLog::NET, BCLog::Level::Error, "%s\n", strError.original);
return false;
}
LogPrintf("Bound to %s\n", addrBind.ToStringAddrPort());
// Listen for incoming connections
if (sock->Listen(SOMAXCONN) == SOCKET_ERROR)
{
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;
}
m_listen.emplace_back(std::move(sock));
return true;
}
void SockMan::StopListening()
{
m_listen.clear();
}

44
src/common/sockman.h Normal file
View File

@ -0,0 +1,44 @@
// Copyright (c) 2024-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://opensource.org/license/mit/.
#ifndef BITCOIN_COMMON_SOCKMAN_H
#define BITCOIN_COMMON_SOCKMAN_H
#include <netaddress.h>
#include <util/sock.h>
#include <util/translation.h>
#include <memory>
#include <vector>
/**
* A socket manager class which handles socket operations.
* To use this class, inherit from it and implement the pure virtual methods.
* Handled operations:
* - binding and listening on sockets
*/
class SockMan
{
public:
/**
* Bind to a new address:port, start listening and add the listen socket to `m_listen`.
* @param[in] addrBind Where to bind.
* @param[out] strError Error string if an error occurs.
* @retval true Success.
* @retval false Failure, `strError` will be set.
*/
bool BindListenPort(const CService& addrBind, bilingual_str& strError);
/**
* Stop listening by closing all listening sockets.
*/
void StopListening();
/**
* List of listening sockets.
*/
std::vector<std::shared_ptr<Sock>> m_listen;
};
#endif // BITCOIN_COMMON_SOCKMAN_H

View File

@ -3114,76 +3114,6 @@ void CConnman::ThreadI2PAcceptIncoming()
}
}
bool CConnman::BindListenPort(const CService& addrBind, bilingual_str& strError)
{
int nOne = 1;
// Create socket for listening for incoming connections
struct sockaddr_storage sockaddr;
socklen_t len = sizeof(sockaddr);
if (!addrBind.GetSockAddr((struct sockaddr*)&sockaddr, &len))
{
strError = Untranslated(strprintf("Bind address family for %s not supported", addrBind.ToStringAddrPort()));
LogPrintLevel(BCLog::NET, BCLog::Level::Error, "%s\n", strError.original);
return false;
}
std::unique_ptr<Sock> sock = CreateSock(addrBind.GetSAFamily(), SOCK_STREAM, IPPROTO_TCP);
if (!sock) {
strError = Untranslated(strprintf("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.
if (sock->SetSockOpt(SOL_SOCKET, SO_REUSEADDR, (sockopt_arg_type)&nOne, sizeof(int)) == SOCKET_ERROR) {
strError = Untranslated(strprintf("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
if (sock->SetSockOpt(IPPROTO_IPV6, IPV6_V6ONLY, (sockopt_arg_type)&nOne, sizeof(int)) == SOCKET_ERROR) {
strError = Untranslated(strprintf("Error setting IPV6_V6ONLY on socket: %s, continuing anyway", NetworkErrorString(WSAGetLastError())));
LogPrintf("%s\n", strError.original);
}
#endif
#ifdef WIN32
int nProtLevel = PROTECTION_LEVEL_UNRESTRICTED;
if (sock->SetSockOpt(IPPROTO_IPV6, IPV6_PROTECTION_LEVEL, (const char*)&nProtLevel, sizeof(int)) == SOCKET_ERROR) {
strError = Untranslated(strprintf("Error setting IPV6_PROTECTION_LEVEL on socket: %s, continuing anyway", NetworkErrorString(WSAGetLastError())));
LogPrintf("%s\n", strError.original);
}
#endif
}
if (sock->Bind(reinterpret_cast<struct sockaddr*>(&sockaddr), len) == SOCKET_ERROR) {
int nErr = WSAGetLastError();
if (nErr == WSAEADDRINUSE)
strError = strprintf(_("Unable to bind to %s on this computer. %s is probably already running."), addrBind.ToStringAddrPort(), CLIENT_NAME);
else
strError = strprintf(_("Unable to bind to %s on this computer (bind returned error %s)"), addrBind.ToStringAddrPort(), NetworkErrorString(nErr));
LogPrintLevel(BCLog::NET, BCLog::Level::Error, "%s\n", strError.original);
return false;
}
LogPrintf("Bound to %s\n", addrBind.ToStringAddrPort());
// Listen for incoming connections
if (sock->Listen(SOMAXCONN) == SOCKET_ERROR)
{
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;
}
m_listen.emplace_back(std::move(sock));
return true;
}
void Discover()
{
if (!fDiscover)
@ -3489,7 +3419,7 @@ void CConnman::StopNodes()
}
m_nodes_disconnected.clear();
m_listen_permissions.clear();
m_listen.clear();
StopListening();
semOutbound.reset();
semAddnode.reset();
}

View File

@ -9,6 +9,7 @@
#include <bip324.h>
#include <chainparams.h>
#include <common/bloom.h>
#include <common/sockman.h>
#include <compat/compat.h>
#include <consensus/amount.h>
#include <crypto/siphash.h>
@ -1048,7 +1049,7 @@ protected:
~NetEventsInterface() = default;
};
class CConnman
class CConnman : private SockMan
{
public:
@ -1277,7 +1278,6 @@ private:
//! in case of no limit, it will always return 0
std::chrono::seconds GetMaxOutboundTimeLeftInCycle_() const EXCLUSIVE_LOCKS_REQUIRED(m_total_bytes_sent_mutex);
bool BindListenPort(const CService& bindAddr, bilingual_str& strError);
bool Bind(const CService& addr, unsigned int flags, NetPermissionFlags permissions);
bool InitBinds(const Options& options);
@ -1417,11 +1417,6 @@ private:
unsigned int nSendBufferMaxSize{0};
unsigned int nReceiveFloodSize{0};
/**
* List of listening sockets.
*/
std::vector<std::shared_ptr<Sock>> m_listen;
/**
* Permissions that incoming peers get based on our listening address they connected to.
*/