Skip to content

Commit 6b159f1

Browse files
committed
merge bitcoin#24357: make setsockopt() and SetSocketNoDelay() mockable/testable
1 parent 9c751ef commit 6b159f1

File tree

8 files changed

+62
-20
lines changed

8 files changed

+62
-20
lines changed

src/net.cpp

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1310,7 +1310,11 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
13101310

13111311
// According to the internet TCP_NODELAY is not carried into accepted sockets
13121312
// on all platforms. Set it again here just to be sure.
1313-
SetSocketNoDelay(sock->Get());
1313+
const int on{1};
1314+
if (sock->SetSockOpt(IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) == SOCKET_ERROR) {
1315+
LogPrint(BCLog::NET, "connection from %s: unable to set TCP_NODELAY, continuing anyway\n",
1316+
addr.ToString());
1317+
}
13141318

13151319
// Don't accept connections from banned peers.
13161320
bool banned = m_banman && m_banman->IsBanned(addr);
@@ -3219,17 +3223,26 @@ bool CConnman::BindListenPort(const CService& addrBind, bilingual_str& strError,
32193223

32203224
// Allow binding if the port is still in TIME_WAIT state after
32213225
// the program was closed and restarted.
3222-
setsockopt(sock->Get(), SOL_SOCKET, SO_REUSEADDR, (sockopt_arg_type)&nOne, sizeof(int));
3226+
if (sock->SetSockOpt(SOL_SOCKET, SO_REUSEADDR, (sockopt_arg_type)&nOne, sizeof(int)) == SOCKET_ERROR) {
3227+
strError = strprintf(Untranslated("Error setting SO_REUSEADDR on socket: %s, continuing anyway"), NetworkErrorString(WSAGetLastError()));
3228+
LogPrintf("%s\n", strError.original);
3229+
}
32233230

32243231
// some systems don't have IPV6_V6ONLY but are always v6only; others do have the option
32253232
// and enable it by default or not. Try to enable it, if possible.
32263233
if (addrBind.IsIPv6()) {
32273234
#ifdef IPV6_V6ONLY
3228-
setsockopt(sock->Get(), IPPROTO_IPV6, IPV6_V6ONLY, (sockopt_arg_type)&nOne, sizeof(int));
3235+
if (sock->SetSockOpt(IPPROTO_IPV6, IPV6_V6ONLY, (sockopt_arg_type)&nOne, sizeof(int)) == SOCKET_ERROR) {
3236+
strError = strprintf(Untranslated("Error setting IPV6_V6ONLY on socket: %s, continuing anyway"), NetworkErrorString(WSAGetLastError()));
3237+
LogPrintf("%s\n", strError.original);
3238+
}
32293239
#endif
32303240
#ifdef WIN32
32313241
int nProtLevel = PROTECTION_LEVEL_UNRESTRICTED;
3232-
setsockopt(sock->Get(), IPPROTO_IPV6, IPV6_PROTECTION_LEVEL, (const char*)&nProtLevel, sizeof(int));
3242+
if (sock->SetSockOpt(IPPROTO_IPV6, IPV6_PROTECTION_LEVEL, (const char*)&nProtLevel, sizeof(int)) == SOCKET_ERROR) {
3243+
strError = strprintf(Untranslated("Error setting IPV6_PROTECTION_LEVEL on socket: %s, continuing anyway"), NetworkErrorString(WSAGetLastError()));
3244+
LogPrintf("%s\n", strError.original);
3245+
}
32333246
#endif
32343247
}
32353248

src/netbase.cpp

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -498,10 +498,11 @@ std::unique_ptr<Sock> CreateSockTCP(const CService& address_family)
498498
return nullptr;
499499
}
500500

501+
auto sock = std::make_unique<Sock>(hSocket);
502+
501503
// Ensure that waiting for I/O on this socket won't result in undefined
502504
// behavior.
503-
if (!IsSelectableSocket(hSocket)) {
504-
CloseSocket(hSocket);
505+
if (!IsSelectableSocket(sock->Get())) {
505506
LogPrintf("Cannot create connection: non-selectable socket created (fd >= FD_SETSIZE ?)\n");
506507
return nullptr;
507508
}
@@ -510,19 +511,24 @@ std::unique_ptr<Sock> CreateSockTCP(const CService& address_family)
510511
int set = 1;
511512
// Set the no-sigpipe option on the socket for BSD systems, other UNIXes
512513
// should use the MSG_NOSIGNAL flag for every send.
513-
setsockopt(hSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&set, sizeof(int));
514+
if (sock->SetSockOpt(SOL_SOCKET, SO_NOSIGPIPE, (void*)&set, sizeof(int)) == SOCKET_ERROR) {
515+
LogPrintf("Error setting SO_NOSIGPIPE on socket: %s, continuing anyway\n",
516+
NetworkErrorString(WSAGetLastError()));
517+
}
514518
#endif
515519

516520
// Set the no-delay option (disable Nagle's algorithm) on the TCP socket.
517-
SetSocketNoDelay(hSocket);
521+
const int on{1};
522+
if (sock->SetSockOpt(IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) == SOCKET_ERROR) {
523+
LogPrint(BCLog::NET, "Unable to set TCP_NODELAY on a newly created socket, continuing anyway\n");
524+
}
518525

519526
// Set the non-blocking option on the socket.
520-
if (!SetSocketNonBlocking(hSocket)) {
521-
CloseSocket(hSocket);
527+
if (!SetSocketNonBlocking(sock->Get())) {
522528
LogPrintf("Error setting socket to non-blocking: %s\n", NetworkErrorString(WSAGetLastError()));
523529
return nullptr;
524530
}
525-
return std::make_unique<Sock>(hSocket);
531+
return sock;
526532
}
527533

528534
std::function<std::unique_ptr<Sock>(const CService&)> CreateSock = CreateSockTCP;
@@ -729,13 +735,6 @@ bool SetSocketNonBlocking(const SOCKET& hSocket)
729735
return true;
730736
}
731737

732-
bool SetSocketNoDelay(const SOCKET& hSocket)
733-
{
734-
int set = 1;
735-
int rc = setsockopt(hSocket, IPPROTO_TCP, TCP_NODELAY, (const char*)&set, sizeof(int));
736-
return rc == 0;
737-
}
738-
739738
void InterruptSocks5(bool interrupt)
740739
{
741740
interruptSocks5Recv = interrupt;

src/netbase.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,8 +227,6 @@ bool ConnectThroughProxy(const Proxy& proxy, const std::string& strDest, uint16_
227227

228228
/** Enable non-blocking mode for a socket */
229229
bool SetSocketNonBlocking(const SOCKET& hSocket);
230-
/** Set the TCP_NODELAY flag on a socket */
231-
bool SetSocketNoDelay(const SOCKET& hSocket);
232230
void InterruptSocks5(bool interrupt);
233231

234232
/**

src/test/fuzz/util.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,19 @@ int FuzzedSock::GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* op
190190
return 0;
191191
}
192192

193+
int FuzzedSock::SetSockOpt(int, int, const void*, socklen_t) const
194+
{
195+
constexpr std::array setsockopt_errnos{
196+
ENOMEM,
197+
ENOBUFS,
198+
};
199+
if (m_fuzzed_data_provider.ConsumeBool()) {
200+
SetFuzzedErrNo(m_fuzzed_data_provider, setsockopt_errnos);
201+
return -1;
202+
}
203+
return 0;
204+
}
205+
193206
bool FuzzedSock::Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred) const
194207
{
195208
constexpr std::array wait_errnos{

src/test/fuzz/util.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ class FuzzedSock : public Sock
7070

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

73+
int SetSockOpt(int level, int opt_name, const void* opt_val, socklen_t opt_len) const override;
74+
7375
bool Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred = nullptr) const override;
7476

7577
bool IsConnected(std::string& errmsg) const override;

src/test/util/net.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,8 @@ class StaticContentsSock : public Sock
150150
return 0;
151151
}
152152

153+
int SetSockOpt(int, int, const void*, socklen_t) const override { return 0; }
154+
153155
bool Wait(std::chrono::milliseconds timeout,
154156
Event requested,
155157
Event* occurred = nullptr) const override

src/util/sock.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,11 @@ int Sock::GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len)
105105
return getsockopt(m_socket, level, opt_name, static_cast<char*>(opt_val), opt_len);
106106
}
107107

108+
int Sock::SetSockOpt(int level, int opt_name, const void* opt_val, socklen_t opt_len) const
109+
{
110+
return setsockopt(m_socket, level, opt_name, static_cast<const char*>(opt_val), opt_len);
111+
}
112+
108113
bool Sock::Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred) const
109114
{
110115
#ifdef USE_POLL

src/util/sock.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,16 @@ class Sock
163163
void* opt_val,
164164
socklen_t* opt_len) const;
165165

166+
/**
167+
* setsockopt(2) wrapper. Equivalent to
168+
* `setsockopt(this->Get(), level, opt_name, opt_val, opt_len)`. Code that uses this
169+
* wrapper can be unit tested if this method is overridden by a mock Sock implementation.
170+
*/
171+
[[nodiscard]] virtual int SetSockOpt(int level,
172+
int opt_name,
173+
const void* opt_val,
174+
socklen_t opt_len) const;
175+
166176
using Event = uint8_t;
167177

168178
/**

0 commit comments

Comments
 (0)