Skip to content

Commit 55c9e2d

Browse files
committed
Merge bitcoin#24378: refactor: make bind() and listen() mockable/testable
b2733ab net: add new method Sock::Listen() that wraps listen() (Vasil Dimov) 3ad7de2 net: add new method Sock::Bind() that wraps bind() (Vasil Dimov) Pull request description: _This is a piece of bitcoin#21878, chopped off to ease review._ Add new methods `Sock::Bind()` and `Sock::Listen()` that wrap `bind()` and `listen()`. This will help to increase `Sock` usage and make more code mockable. ACKs for top commit: pk-b2: ACK b2733ab laanwj: Code review ACK b2733ab Tree-SHA512: c6e737606703e2106fe60cc000cfbbae3a7f43deadb25f70531e2cac0457e0b0581440279d14c76c492eb85c12af4adde52c30baf74542c41597e419817488e8
2 parents ba29911 + b2733ab commit 55c9e2d

File tree

6 files changed

+71
-3
lines changed

6 files changed

+71
-3
lines changed

src/net.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2323,8 +2323,7 @@ bool CConnman::BindListenPort(const CService& addrBind, bilingual_str& strError,
23232323
#endif
23242324
}
23252325

2326-
if (::bind(sock->Get(), (struct sockaddr*)&sockaddr, len) == SOCKET_ERROR)
2327-
{
2326+
if (sock->Bind(reinterpret_cast<struct sockaddr*>(&sockaddr), len) == SOCKET_ERROR) {
23282327
int nErr = WSAGetLastError();
23292328
if (nErr == WSAEADDRINUSE)
23302329
strError = strprintf(_("Unable to bind to %s on this computer. %s is probably already running."), addrBind.ToString(), PACKAGE_NAME);
@@ -2336,7 +2335,7 @@ bool CConnman::BindListenPort(const CService& addrBind, bilingual_str& strError,
23362335
LogPrintf("Bound to %s\n", addrBind.ToString());
23372336

23382337
// Listen for incoming connections
2339-
if (listen(sock->Get(), SOMAXCONN) == SOCKET_ERROR)
2338+
if (sock->Listen(SOMAXCONN) == SOCKET_ERROR)
23402339
{
23412340
strError = strprintf(_("Listening for incoming connections failed (listen returned error %s)"), NetworkErrorString(WSAGetLastError()));
23422341
LogPrintLevel(BCLog::NET, BCLog::Level::Error, "%s\n", strError.original);

src/test/fuzz/util.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,45 @@ int FuzzedSock::Connect(const sockaddr*, socklen_t) const
155155
return 0;
156156
}
157157

158+
int FuzzedSock::Bind(const sockaddr*, socklen_t) const
159+
{
160+
// Have a permanent error at bind_errnos[0] because when the fuzzed data is exhausted
161+
// SetFuzzedErrNo() will always set the global errno to bind_errnos[0]. We want to
162+
// avoid this method returning -1 and setting errno to a temporary error (like EAGAIN)
163+
// repeatedly because proper code should retry on temporary errors, leading to an
164+
// infinite loop.
165+
constexpr std::array bind_errnos{
166+
EACCES,
167+
EADDRINUSE,
168+
EADDRNOTAVAIL,
169+
EAGAIN,
170+
};
171+
if (m_fuzzed_data_provider.ConsumeBool()) {
172+
SetFuzzedErrNo(m_fuzzed_data_provider, bind_errnos);
173+
return -1;
174+
}
175+
return 0;
176+
}
177+
178+
int FuzzedSock::Listen(int) const
179+
{
180+
// Have a permanent error at listen_errnos[0] because when the fuzzed data is exhausted
181+
// SetFuzzedErrNo() will always set the global errno to listen_errnos[0]. We want to
182+
// avoid this method returning -1 and setting errno to a temporary error (like EAGAIN)
183+
// repeatedly because proper code should retry on temporary errors, leading to an
184+
// infinite loop.
185+
constexpr std::array listen_errnos{
186+
EADDRINUSE,
187+
EINVAL,
188+
EOPNOTSUPP,
189+
};
190+
if (m_fuzzed_data_provider.ConsumeBool()) {
191+
SetFuzzedErrNo(m_fuzzed_data_provider, listen_errnos);
192+
return -1;
193+
}
194+
return 0;
195+
}
196+
158197
std::unique_ptr<Sock> FuzzedSock::Accept(sockaddr* addr, socklen_t* addr_len) const
159198
{
160199
constexpr std::array accept_errnos{

src/test/fuzz/util.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ class FuzzedSock : public Sock
6161

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

64+
int Bind(const sockaddr*, socklen_t) const override;
65+
66+
int Listen(int backlog) const override;
67+
6468
std::unique_ptr<Sock> Accept(sockaddr* addr, socklen_t* addr_len) const override;
6569

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

src/test/util/net.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,10 @@ class StaticContentsSock : public Sock
122122

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

125+
int Bind(const sockaddr*, socklen_t) const override { return 0; }
126+
127+
int Listen(int) const override { return 0; }
128+
125129
std::unique_ptr<Sock> Accept(sockaddr* addr, socklen_t* addr_len) const override
126130
{
127131
if (addr != nullptr) {

src/util/sock.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,16 @@ int Sock::Connect(const sockaddr* addr, socklen_t addr_len) const
6666
return connect(m_socket, addr, addr_len);
6767
}
6868

69+
int Sock::Bind(const sockaddr* addr, socklen_t addr_len) const
70+
{
71+
return bind(m_socket, addr, addr_len);
72+
}
73+
74+
int Sock::Listen(int backlog) const
75+
{
76+
return listen(m_socket, backlog);
77+
}
78+
6979
std::unique_ptr<Sock> Sock::Accept(sockaddr* addr, socklen_t* addr_len) const
7080
{
7181
#ifdef WIN32

src/util/sock.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,18 @@ class Sock
8686
*/
8787
[[nodiscard]] virtual int Connect(const sockaddr* addr, socklen_t addr_len) const;
8888

89+
/**
90+
* bind(2) wrapper. Equivalent to `bind(this->Get(), addr, addr_len)`. Code that uses this
91+
* wrapper can be unit tested if this method is overridden by a mock Sock implementation.
92+
*/
93+
[[nodiscard]] virtual int Bind(const sockaddr* addr, socklen_t addr_len) const;
94+
95+
/**
96+
* listen(2) wrapper. Equivalent to `listen(this->Get(), backlog)`. Code that uses this
97+
* wrapper can be unit tested if this method is overridden by a mock Sock implementation.
98+
*/
99+
[[nodiscard]] virtual int Listen(int backlog) const;
100+
89101
/**
90102
* accept(2) wrapper. Equivalent to `std::make_unique<Sock>(accept(this->Get(), addr, addr_len))`.
91103
* Code that uses this wrapper can be unit tested if this method is overridden by a mock Sock

0 commit comments

Comments
 (0)