mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-11-11 14:38:29 +01:00
net: add RAII socket and use it instead of bare SOCKET
Introduce a class to manage the lifetime of a socket - when the object that contains the socket goes out of scope, the underlying socket will be closed. In addition, the new `Sock` class has a `Send()`, `Recv()` and `Wait()` methods that can be overridden by unit tests to mock the socket operations. The `Wait()` method also hides the `#ifdef USE_POLL poll() #else select() #endif` technique from higher level code.
This commit is contained in:
@@ -15,7 +15,9 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
|
||||
#ifndef WIN32
|
||||
#include <fcntl.h>
|
||||
@@ -559,34 +561,28 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to create a socket file descriptor with specific properties in the
|
||||
* communications domain (address family) of the specified service.
|
||||
*
|
||||
* For details on the desired properties, see the inline comments in the source
|
||||
* code.
|
||||
*/
|
||||
SOCKET CreateSocket(const CService &addrConnect)
|
||||
std::unique_ptr<Sock> CreateSockTCP(const CService& address_family)
|
||||
{
|
||||
// Create a sockaddr from the specified service.
|
||||
struct sockaddr_storage sockaddr;
|
||||
socklen_t len = sizeof(sockaddr);
|
||||
if (!addrConnect.GetSockAddr((struct sockaddr*)&sockaddr, &len)) {
|
||||
LogPrintf("Cannot create socket for %s: unsupported network\n", addrConnect.ToString());
|
||||
return INVALID_SOCKET;
|
||||
if (!address_family.GetSockAddr((struct sockaddr*)&sockaddr, &len)) {
|
||||
LogPrintf("Cannot create socket for %s: unsupported network\n", address_family.ToString());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create a TCP socket in the address family of the specified service.
|
||||
SOCKET hSocket = socket(((struct sockaddr*)&sockaddr)->sa_family, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (hSocket == INVALID_SOCKET)
|
||||
return INVALID_SOCKET;
|
||||
if (hSocket == INVALID_SOCKET) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Ensure that waiting for I/O on this socket won't result in undefined
|
||||
// behavior.
|
||||
if (!IsSelectableSocket(hSocket)) {
|
||||
CloseSocket(hSocket);
|
||||
LogPrintf("Cannot create connection: non-selectable socket created (fd >= FD_SETSIZE ?)\n");
|
||||
return INVALID_SOCKET;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifdef SO_NOSIGPIPE
|
||||
@@ -602,11 +598,14 @@ SOCKET CreateSocket(const CService &addrConnect)
|
||||
// Set the non-blocking option on the socket.
|
||||
if (!SetSocketNonBlocking(hSocket, true)) {
|
||||
CloseSocket(hSocket);
|
||||
LogPrintf("CreateSocket: Setting socket to non-blocking failed, error %s\n", NetworkErrorString(WSAGetLastError()));
|
||||
LogPrintf("Error setting socket to non-blocking: %s\n", NetworkErrorString(WSAGetLastError()));
|
||||
return nullptr;
|
||||
}
|
||||
return hSocket;
|
||||
return std::make_unique<Sock>(hSocket);
|
||||
}
|
||||
|
||||
std::function<std::unique_ptr<Sock>(const CService&)> CreateSock = CreateSockTCP;
|
||||
|
||||
template<typename... Args>
|
||||
static void LogConnectFailure(bool manual_connection, const char* fmt, const Args&... args) {
|
||||
std::string error_message = tfm::format(fmt, args...);
|
||||
|
||||
Reference in New Issue
Block a user