Bitcoin ABC 0.26.3
P2P Digital Currency
Loading...
Searching...
No Matches
sock.cpp
Go to the documentation of this file.
1// Copyright (c) 2020-2021 The Bitcoin Core developers
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5#include <util/sock.h>
6
7#include <common/system.h>
8#include <compat.h>
9#include <logging.h>
10#include <threadinterrupt.h>
11#include <tinyformat.h>
12#include <util/syserror.h>
13#include <util/time.h>
14
15#include <codecvt>
16#include <cwchar>
17#include <locale>
18#include <memory>
19#include <stdexcept>
20#include <string>
21
22#ifdef USE_POLL
23#include <poll.h>
24#endif
25
26static inline bool IOErrorIsPermanent(int err) {
27 return err != WSAEAGAIN && err != WSAEINTR && err != WSAEWOULDBLOCK &&
29}
30
31Sock::Sock() : m_socket(INVALID_SOCKET) {}
32
33Sock::Sock(SOCKET s) : m_socket(s) {}
34
35Sock::Sock(Sock &&other) {
36 m_socket = other.m_socket;
37 other.m_socket = INVALID_SOCKET;
38}
39
41 Reset();
42}
43
45 Reset();
46 m_socket = other.m_socket;
47 other.m_socket = INVALID_SOCKET;
48 return *this;
49}
50
52 return m_socket;
53}
54
56 const SOCKET s = m_socket;
58 return s;
59}
60
64
65ssize_t Sock::Send(const void *data, size_t len, int flags) const {
66 return send(m_socket, static_cast<const char *>(data), len, flags);
67}
68
69ssize_t Sock::Recv(void *buf, size_t len, int flags) const {
70 return recv(m_socket, static_cast<char *>(buf), len, flags);
71}
72
73int Sock::Connect(const sockaddr *addr, socklen_t addr_len) const {
74 return connect(m_socket, addr, addr_len);
75}
76
77std::unique_ptr<Sock> Sock::Accept(sockaddr *addr, socklen_t *addr_len) const {
78#ifdef WIN32
79 static constexpr auto err = INVALID_SOCKET;
80#else
81 static constexpr auto err = SOCKET_ERROR;
82#endif
83
84 std::unique_ptr<Sock> sock;
85
86 const auto socket = accept(m_socket, addr, addr_len);
87 if (socket != err) {
88 try {
89 sock = std::make_unique<Sock>(socket);
90 } catch (const std::exception &) {
91#ifdef WIN32
93#else
95#endif
96 }
97 }
98
99 return sock;
100}
101
103 socklen_t *opt_len) const {
104 return getsockopt(m_socket, level, opt_name, static_cast<char *>(opt_val),
105 opt_len);
106}
107
108bool Sock::Wait(std::chrono::milliseconds timeout, Event requested,
109 Event *occurred) const {
110 // We need a `shared_ptr` owning `this` for `WaitMany()`, but don't want
111 // `this` to be destroyed when the `shared_ptr` goes out of scope at the
112 // end of this function. Create it with a custom noop deleter.
113 std::shared_ptr<const Sock> shared{this, [](const Sock *) {}};
114
115 EventsPerSock events_per_sock{std::make_pair(shared, Events{requested})};
116
117 if (!WaitMany(timeout, events_per_sock)) {
118 return false;
119 }
120
121 if (occurred != nullptr) {
122 *occurred = events_per_sock.begin()->second.occurred;
123 }
124
125 return true;
126}
127
128bool Sock::WaitMany(std::chrono::milliseconds timeout,
130#ifdef USE_POLL
131 std::vector<pollfd> pfds;
132 for (const auto &[sock, events] : events_per_sock) {
133 pfds.emplace_back();
134 auto &pfd = pfds.back();
135 pfd.fd = sock->m_socket;
136 if (events.requested & RECV) {
137 pfd.events |= POLLIN;
138 }
139 if (events.requested & SEND) {
140 pfd.events |= POLLOUT;
141 }
142 }
143
144 if (poll(pfds.data(), pfds.size(), count_milliseconds(timeout)) ==
145 SOCKET_ERROR) {
146 return false;
147 }
148
149 assert(pfds.size() == events_per_sock.size());
150 size_t i{0};
151 for (auto &[sock, events] : events_per_sock) {
152 assert(sock->m_socket == static_cast<SOCKET>(pfds[i].fd));
153 events.occurred = 0;
154 if (pfds[i].revents & POLLIN) {
155 events.occurred |= RECV;
156 }
157 if (pfds[i].revents & POLLOUT) {
158 events.occurred |= SEND;
159 }
160 if (pfds[i].revents & (POLLERR | POLLHUP)) {
161 events.occurred |= ERR;
162 }
163 ++i;
164 }
165
166 return true;
167#else
168 fd_set recv;
169 fd_set send;
170 fd_set err;
171 FD_ZERO(&recv);
172 FD_ZERO(&send);
173 FD_ZERO(&err);
175
176 for (const auto &[sock, events] : events_per_sock) {
177 const auto &s = sock->m_socket;
178 if (!IsSelectableSocket(s)) {
179 return false;
180 }
181 if (events.requested & RECV) {
182 FD_SET(s, &recv);
183 }
184 if (events.requested & SEND) {
185 FD_SET(s, &send);
186 }
187 FD_SET(s, &err);
188 socket_max = std::max(socket_max, s);
189 }
190
191 timeval tv = MillisToTimeval(timeout);
192
193 if (select(socket_max + 1, &recv, &send, &err, &tv) == SOCKET_ERROR) {
194 return false;
195 }
196
197 for (auto &[sock, events] : events_per_sock) {
198 const auto &s = sock->m_socket;
199 events.occurred = 0;
200 if (FD_ISSET(s, &recv)) {
201 events.occurred |= RECV;
202 }
203 if (FD_ISSET(s, &send)) {
204 events.occurred |= SEND;
205 }
206 if (FD_ISSET(s, &err)) {
207 events.occurred |= ERR;
208 }
209 }
210
211 return true;
212#endif /* USE_POLL */
213}
214
215void Sock::SendComplete(const std::string &data,
216 std::chrono::milliseconds timeout,
218 const auto deadline = GetTime<std::chrono::milliseconds>() + timeout;
219 size_t sent{0};
220
221 for (;;) {
222 const ssize_t ret{
223 Send(data.data() + sent, data.size() - sent, MSG_NOSIGNAL)};
224
225 if (ret > 0) {
226 sent += static_cast<size_t>(ret);
227 if (sent == data.size()) {
228 break;
229 }
230 } else {
231 const int err{WSAGetLastError()};
232 if (IOErrorIsPermanent(err)) {
233 throw std::runtime_error(
234 strprintf("send(): %s", NetworkErrorString(err)));
235 }
236 }
237
238 const auto now = GetTime<std::chrono::milliseconds>();
239
240 if (now >= deadline) {
241 throw std::runtime_error(
242 strprintf("Send timeout (sent only %u of %u bytes before that)",
243 sent, data.size()));
244 }
245
246 if (interrupt) {
247 throw std::runtime_error(strprintf(
248 "Send interrupted (sent only %u of %u bytes before that)", sent,
249 data.size()));
250 }
251
252 // Wait for a short while (or the socket to become ready for sending)
253 // before retrying if nothing was sent.
254 const auto wait_time = std::min(
255 deadline - now, std::chrono::milliseconds{MAX_WAIT_FOR_IO});
257 }
258}
259
261 std::chrono::milliseconds timeout,
263 size_t max_data) const {
264 const auto deadline = GetTime<std::chrono::milliseconds>() + timeout;
265 std::string data;
266 bool terminator_found{false};
267
268 // We must not consume any bytes past the terminator from the socket.
269 // One option is to read one byte at a time and check if we have read a
270 // terminator. However that is very slow. Instead, we peek at what is in the
271 // socket and only read as many bytes as possible without crossing the
272 // terminator. Reading 64 MiB of random data with 262526 terminator chars
273 // takes 37 seconds to read one byte at a time VS 0.71 seconds with the
274 // "peek" solution below. Reading one byte at a time is about 50 times
275 // slower.
276
277 for (;;) {
278 if (data.size() >= max_data) {
279 throw std::runtime_error(
280 strprintf("Received too many bytes without a terminator (%u)",
281 data.size()));
282 }
283
284 char buf[512];
285
286 const ssize_t peek_ret{
287 Recv(buf, std::min(sizeof(buf), max_data - data.size()), MSG_PEEK)};
288
289 switch (peek_ret) {
290 case -1: {
291 const int err{WSAGetLastError()};
292 if (IOErrorIsPermanent(err)) {
293 throw std::runtime_error(
294 strprintf("recv(): %s", NetworkErrorString(err)));
295 }
296 break;
297 }
298 case 0:
299 throw std::runtime_error(
300 "Connection unexpectedly closed by peer");
301 default:
302 auto end = buf + peek_ret;
303 auto terminator_pos = std::find(buf, end, terminator);
305
306 const size_t try_len{terminator_found
307 ? terminator_pos - buf + 1
308 : static_cast<size_t>(peek_ret)};
309
310 const ssize_t read_ret{Recv(buf, try_len, 0)};
311
312 if (read_ret < 0 || static_cast<size_t>(read_ret) != try_len) {
313 throw std::runtime_error(
314 strprintf("recv() returned %u bytes on attempt to read "
315 "%u bytes but previous "
316 "peek claimed %u bytes are available",
318 }
319
320 // Don't include the terminator in the output.
321 const size_t append_len{terminator_found ? try_len - 1
322 : try_len};
323
324 data.append(buf, buf + append_len);
325
326 if (terminator_found) {
327 return data;
328 }
329 }
330
331 const auto now = GetTime<std::chrono::milliseconds>();
332
333 if (now >= deadline) {
334 throw std::runtime_error(
335 strprintf("Receive timeout (received %u bytes without "
336 "terminator before that)",
337 data.size()));
338 }
339
340 if (interrupt) {
341 throw std::runtime_error(
342 strprintf("Receive interrupted (received %u bytes without "
343 "terminator before that)",
344 data.size()));
345 }
346
347 // Wait for a short while (or the socket to become ready for reading)
348 // before retrying.
349 const auto wait_time = std::min(
350 deadline - now, std::chrono::milliseconds{MAX_WAIT_FOR_IO});
352 }
353}
354
355bool Sock::IsConnected(std::string &errmsg) const {
356 if (m_socket == INVALID_SOCKET) {
357 errmsg = "not connected";
358 return false;
359 }
360
361 char c;
362 switch (Recv(&c, sizeof(c), MSG_PEEK)) {
363 case -1: {
364 const int err = WSAGetLastError();
365 if (IOErrorIsPermanent(err)) {
367 return false;
368 }
369 return true;
370 }
371 case 0:
372 errmsg = "closed";
373 return false;
374 default:
375 return true;
376 }
377}
378
379#ifdef WIN32
380std::string NetworkErrorString(int err) {
381 wchar_t buf[256];
382 buf[0] = 0;
387 buf, ARRAYSIZE(buf), nullptr)) {
388 return strprintf(
389 "%s (%d)",
390 std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t>()
391 .to_bytes(buf),
392 err);
393 } else {
394 return strprintf("Unknown error (%d)", err);
395 }
396}
397#else
398std::string NetworkErrorString(int err) {
399 // On BSD sockets implementations, NetworkErrorString is the same as
400 // SysErrorString.
401 return SysErrorString(err);
402}
403#endif
404
406 if (hSocket == INVALID_SOCKET) {
407 return false;
408 }
409#ifdef WIN32
410 int ret = closesocket(hSocket);
411#else
412 int ret = close(hSocket);
413#endif
414 if (ret) {
415 LogPrintf("Socket close failed: %d. Error: %s\n", hSocket,
417 }
419 return ret != SOCKET_ERROR;
420}
int flags
A helper class for interruptible sleeps.
RAII helper class that manages a socket.
Definition sock.h:28
virtual std::unique_ptr< Sock > Accept(sockaddr *addr, socklen_t *addr_len) const
accept(2) wrapper.
Definition sock.cpp:77
virtual ssize_t Send(const void *data, size_t len, int flags) const
send(2) wrapper.
Definition sock.cpp:65
static constexpr Event SEND
If passed to Wait(), then it will wait for readiness to send to the socket.
Definition sock.h:141
SOCKET m_socket
Contained socket.
Definition sock.h:262
Sock & operator=(const Sock &)=delete
Copy assignment operator, disabled because closing the same socket twice is undesirable.
virtual void SendComplete(const std::string &data, std::chrono::milliseconds timeout, CThreadInterrupt &interrupt) const
Send the given data, retrying on transient errors.
Definition sock.cpp:215
virtual bool Wait(std::chrono::milliseconds timeout, Event requested, Event *occurred=nullptr) const
Wait for readiness for input (recv) or output (send).
Definition sock.cpp:108
virtual ~Sock()
Destructor, close the socket or do nothing if empty.
Definition sock.cpp:40
uint8_t Event
Definition sock.h:129
Sock()
Default constructor, creates an empty object that does nothing when destroyed.
Definition sock.cpp:31
virtual bool WaitMany(std::chrono::milliseconds timeout, EventsPerSock &events_per_sock) const
Same as Wait(), but wait on many sockets within the same timeout.
Definition sock.cpp:128
virtual SOCKET Release()
Get the value of the contained socket and drop ownership.
Definition sock.cpp:55
static constexpr Event ERR
Ignored if passed to Wait(), but could be set in the occurred events if an exceptional condition has ...
Definition sock.h:148
virtual bool IsConnected(std::string &errmsg) const
Check if still connected.
Definition sock.cpp:355
static constexpr Event RECV
If passed to Wait(), then it will wait for readiness to read from the socket.
Definition sock.h:135
virtual SOCKET Get() const
Get the value of the contained socket.
Definition sock.cpp:51
virtual int GetSockOpt(int level, int opt_name, void *opt_val, socklen_t *opt_len) const
getsockopt(2) wrapper.
Definition sock.cpp:102
virtual int Connect(const sockaddr *addr, socklen_t addr_len) const
connect(2) wrapper.
Definition sock.cpp:73
virtual void Reset()
Close if non-empty.
Definition sock.cpp:61
virtual ssize_t Recv(void *buf, size_t len, int flags) const
recv(2) wrapper.
Definition sock.cpp:69
virtual std::string RecvUntilTerminator(uint8_t terminator, std::chrono::milliseconds timeout, CThreadInterrupt &interrupt, size_t max_data) const
Read from socket until a terminator character is encountered.
Definition sock.cpp:260
std::unordered_map< std::shared_ptr< const Sock >, Events, HashSharedPtrSock, EqualSharedPtrSock > EventsPerSock
On which socket to wait for what events in WaitMany().
Definition sock.h:205
#define INVALID_SOCKET
Definition compat.h:52
#define WSAEWOULDBLOCK
Definition compat.h:45
#define SOCKET_ERROR
Definition compat.h:53
#define WSAGetLastError()
Definition compat.h:42
static bool IsSelectableSocket(const SOCKET &s)
Definition compat.h:102
#define MSG_NOSIGNAL
Definition compat.h:113
unsigned int SOCKET
Definition compat.h:40
#define WSAEINPROGRESS
Definition compat.h:49
#define WSAEINTR
Definition compat.h:48
#define WSAEAGAIN
Definition compat.h:46
#define LogPrintf(...)
Definition logging.h:227
T GetRand(T nMax=std::numeric_limits< T >::max()) noexcept
Generate a uniform random integer of type T in the range [0..nMax) nMax defaults to std::numeric_limi...
Definition random.h:85
static RPCHelpMan send()
static bool IOErrorIsPermanent(int err)
Definition sock.cpp:26
std::string NetworkErrorString(int err)
Return readable error string for a network error code.
Definition sock.cpp:398
bool CloseSocket(SOCKET &hSocket)
Close socket and set hSocket to INVALID_SOCKET.
Definition sock.cpp:405
static constexpr auto MAX_WAIT_FOR_IO
Maximum time to wait for I/O readiness.
Definition sock.h:21
std::string NetworkErrorString(int err)
Return readable error string for a network error code.
Definition sock.cpp:398
bool CloseSocket(SOCKET &hSocket)
Close socket and set hSocket to INVALID_SOCKET.
Definition sock.cpp:405
Auxiliary requested/occurred events to wait for in WaitMany().
Definition sock.h:170
std::string SysErrorString(int err)
Return system error string from errno value.
Definition syserror.cpp:14
struct timeval MillisToTimeval(int64_t nTimeout)
Convert milliseconds to a struct timeval for e.g.
Definition time.cpp:158
constexpr int64_t count_milliseconds(std::chrono::milliseconds t)
Definition time.h:58
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
assert(!tx.IsCoinBase())