Bitcoin ABC  0.29.2
P2P Digital Currency
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 <compat.h>
8 #include <logging.h>
9 #include <threadinterrupt.h>
10 #include <tinyformat.h>
11 #include <util/system.h>
12 #include <util/time.h>
13 
14 #include <codecvt>
15 #include <cwchar>
16 #include <locale>
17 #include <stdexcept>
18 #include <string>
19 
20 #ifdef USE_POLL
21 #include <poll.h>
22 #endif
23 
24 static inline bool IOErrorIsPermanent(int err) {
25  return err != WSAEAGAIN && err != WSAEINTR && err != WSAEWOULDBLOCK &&
26  err != WSAEINPROGRESS;
27 }
28 
29 Sock::Sock() : m_socket(INVALID_SOCKET) {}
30 
31 Sock::Sock(SOCKET s) : m_socket(s) {}
32 
33 Sock::Sock(Sock &&other) {
34  m_socket = other.m_socket;
35  other.m_socket = INVALID_SOCKET;
36 }
37 
39  Reset();
40 }
41 
43  Reset();
44  m_socket = other.m_socket;
45  other.m_socket = INVALID_SOCKET;
46  return *this;
47 }
48 
49 SOCKET Sock::Get() const {
50  return m_socket;
51 }
52 
54  const SOCKET s = m_socket;
56  return s;
57 }
58 
59 void Sock::Reset() {
61 }
62 
63 ssize_t Sock::Send(const void *data, size_t len, int flags) const {
64  return send(m_socket, static_cast<const char *>(data), len, flags);
65 }
66 
67 ssize_t Sock::Recv(void *buf, size_t len, int flags) const {
68  return recv(m_socket, static_cast<char *>(buf), len, flags);
69 }
70 
71 int Sock::Connect(const sockaddr *addr, socklen_t addr_len) const {
72  return connect(m_socket, addr, addr_len);
73 }
74 
75 int Sock::GetSockOpt(int level, int opt_name, void *opt_val,
76  socklen_t *opt_len) const {
77  return getsockopt(m_socket, level, opt_name, static_cast<char *>(opt_val),
78  opt_len);
79 }
80 
81 bool Sock::Wait(std::chrono::milliseconds timeout, Event requested,
82  Event *occurred) const {
83 #ifdef USE_POLL
84  pollfd fd;
85  fd.fd = m_socket;
86  fd.events = 0;
87  if (requested & RECV) {
88  fd.events |= POLLIN;
89  }
90  if (requested & SEND) {
91  fd.events |= POLLOUT;
92  }
93 
94  if (poll(&fd, 1, count_milliseconds(timeout)) == SOCKET_ERROR) {
95  return false;
96  }
97 
98  if (occurred != nullptr) {
99  *occurred = 0;
100  if (fd.revents & POLLIN) {
101  *occurred |= RECV;
102  }
103  if (fd.revents & POLLOUT) {
104  *occurred |= SEND;
105  }
106  }
107 
108  return true;
109 #else
111  return false;
112  }
113 
114  fd_set fdset_recv;
115  fd_set fdset_send;
116  FD_ZERO(&fdset_recv);
117  FD_ZERO(&fdset_send);
118 
119  if (requested & RECV) {
120  FD_SET(m_socket, &fdset_recv);
121  }
122 
123  if (requested & SEND) {
124  FD_SET(m_socket, &fdset_send);
125  }
126 
127  timeval timeout_struct = MillisToTimeval(timeout);
128 
129  if (select(m_socket + 1, &fdset_recv, &fdset_send, nullptr,
130  &timeout_struct) == SOCKET_ERROR) {
131  return false;
132  }
133 
134  if (occurred != nullptr) {
135  *occurred = 0;
136  if (FD_ISSET(m_socket, &fdset_recv)) {
137  *occurred |= RECV;
138  }
139  if (FD_ISSET(m_socket, &fdset_send)) {
140  *occurred |= SEND;
141  }
142  }
143 
144  return true;
145 #endif /* USE_POLL */
146 }
147 
148 void Sock::SendComplete(const std::string &data,
149  std::chrono::milliseconds timeout,
150  CThreadInterrupt &interrupt) const {
151  const auto deadline = GetTime<std::chrono::milliseconds>() + timeout;
152  size_t sent{0};
153 
154  for (;;) {
155  const ssize_t ret{
156  Send(data.data() + sent, data.size() - sent, MSG_NOSIGNAL)};
157 
158  if (ret > 0) {
159  sent += static_cast<size_t>(ret);
160  if (sent == data.size()) {
161  break;
162  }
163  } else {
164  const int err{WSAGetLastError()};
165  if (IOErrorIsPermanent(err)) {
166  throw std::runtime_error(
167  strprintf("send(): %s", NetworkErrorString(err)));
168  }
169  }
170 
171  const auto now = GetTime<std::chrono::milliseconds>();
172 
173  if (now >= deadline) {
174  throw std::runtime_error(
175  strprintf("Send timeout (sent only %u of %u bytes before that)",
176  sent, data.size()));
177  }
178 
179  if (interrupt) {
180  throw std::runtime_error(strprintf(
181  "Send interrupted (sent only %u of %u bytes before that)", sent,
182  data.size()));
183  }
184 
185  // Wait for a short while (or the socket to become ready for sending)
186  // before retrying if nothing was sent.
187  const auto wait_time = std::min(
188  deadline - now, std::chrono::milliseconds{MAX_WAIT_FOR_IO});
189  Wait(wait_time, SEND);
190  }
191 }
192 
193 std::string Sock::RecvUntilTerminator(uint8_t terminator,
194  std::chrono::milliseconds timeout,
195  CThreadInterrupt &interrupt,
196  size_t max_data) const {
197  const auto deadline = GetTime<std::chrono::milliseconds>() + timeout;
198  std::string data;
199  bool terminator_found{false};
200 
201  // We must not consume any bytes past the terminator from the socket.
202  // One option is to read one byte at a time and check if we have read a
203  // terminator. However that is very slow. Instead, we peek at what is in the
204  // socket and only read as many bytes as possible without crossing the
205  // terminator. Reading 64 MiB of random data with 262526 terminator chars
206  // takes 37 seconds to read one byte at a time VS 0.71 seconds with the
207  // "peek" solution below. Reading one byte at a time is about 50 times
208  // slower.
209 
210  for (;;) {
211  if (data.size() >= max_data) {
212  throw std::runtime_error(
213  strprintf("Received too many bytes without a terminator (%u)",
214  data.size()));
215  }
216 
217  char buf[512];
218 
219  const ssize_t peek_ret{
220  Recv(buf, std::min(sizeof(buf), max_data - data.size()), MSG_PEEK)};
221 
222  switch (peek_ret) {
223  case -1: {
224  const int err{WSAGetLastError()};
225  if (IOErrorIsPermanent(err)) {
226  throw std::runtime_error(
227  strprintf("recv(): %s", NetworkErrorString(err)));
228  }
229  break;
230  }
231  case 0:
232  throw std::runtime_error(
233  "Connection unexpectedly closed by peer");
234  default:
235  auto end = buf + peek_ret;
236  auto terminator_pos = std::find(buf, end, terminator);
237  terminator_found = terminator_pos != end;
238 
239  const size_t try_len{terminator_found
240  ? terminator_pos - buf + 1
241  : static_cast<size_t>(peek_ret)};
242 
243  const ssize_t read_ret{Recv(buf, try_len, 0)};
244 
245  if (read_ret < 0 || static_cast<size_t>(read_ret) != try_len) {
246  throw std::runtime_error(
247  strprintf("recv() returned %u bytes on attempt to read "
248  "%u bytes but previous "
249  "peek claimed %u bytes are available",
250  read_ret, try_len, peek_ret));
251  }
252 
253  // Don't include the terminator in the output.
254  const size_t append_len{terminator_found ? try_len - 1
255  : try_len};
256 
257  data.append(buf, buf + append_len);
258 
259  if (terminator_found) {
260  return data;
261  }
262  }
263 
264  const auto now = GetTime<std::chrono::milliseconds>();
265 
266  if (now >= deadline) {
267  throw std::runtime_error(
268  strprintf("Receive timeout (received %u bytes without "
269  "terminator before that)",
270  data.size()));
271  }
272 
273  if (interrupt) {
274  throw std::runtime_error(
275  strprintf("Receive interrupted (received %u bytes without "
276  "terminator before that)",
277  data.size()));
278  }
279 
280  // Wait for a short while (or the socket to become ready for reading)
281  // before retrying.
282  const auto wait_time = std::min(
283  deadline - now, std::chrono::milliseconds{MAX_WAIT_FOR_IO});
284  Wait(wait_time, RECV);
285  }
286 }
287 
288 bool Sock::IsConnected(std::string &errmsg) const {
289  if (m_socket == INVALID_SOCKET) {
290  errmsg = "not connected";
291  return false;
292  }
293 
294  char c;
295  switch (Recv(&c, sizeof(c), MSG_PEEK)) {
296  case -1: {
297  const int err = WSAGetLastError();
298  if (IOErrorIsPermanent(err)) {
299  errmsg = NetworkErrorString(err);
300  return false;
301  }
302  return true;
303  }
304  case 0:
305  errmsg = "closed";
306  return false;
307  default:
308  return true;
309  }
310 }
311 
312 #ifdef WIN32
313 std::string NetworkErrorString(int err) {
314  wchar_t buf[256];
315  buf[0] = 0;
316  if (FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
317  FORMAT_MESSAGE_IGNORE_INSERTS |
318  FORMAT_MESSAGE_MAX_WIDTH_MASK,
319  nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
320  buf, ARRAYSIZE(buf), nullptr)) {
321  return strprintf(
322  "%s (%d)",
323  std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t>()
324  .to_bytes(buf),
325  err);
326  } else {
327  return strprintf("Unknown error (%d)", err);
328  }
329 }
330 #else
331 std::string NetworkErrorString(int err) {
332  char buf[256];
333  buf[0] = 0;
338  const char *s;
339 #ifdef STRERROR_R_CHAR_P
340  /* GNU variant can return a pointer outside the passed buffer */
341  s = strerror_r(err, buf, sizeof(buf));
342 #else
343  s = buf;
344  /* POSIX variant always returns message in buffer */
345  if (strerror_r(err, buf, sizeof(buf))) {
346  buf[0] = 0;
347  }
348 #endif
349  return strprintf("%s (%d)", s, err);
350 }
351 #endif
352 
353 bool CloseSocket(SOCKET &hSocket) {
354  if (hSocket == INVALID_SOCKET) {
355  return false;
356  }
357 #ifdef WIN32
358  int ret = closesocket(hSocket);
359 #else
360  int ret = close(hSocket);
361 #endif
362  if (ret) {
363  LogPrintf("Socket close failed: %d. Error: %s\n", hSocket,
365  }
366  hSocket = INVALID_SOCKET;
367  return ret != SOCKET_ERROR;
368 }
int flags
Definition: bitcoin-tx.cpp:533
A helper class for interruptible sleeps.
RAII helper class that manages a socket.
Definition: sock.h:26
virtual ssize_t Send(const void *data, size_t len, int flags) const
send(2) wrapper.
Definition: sock.cpp:63
static constexpr Event SEND
If passed to Wait(), then it will wait for readiness to send to the socket.
Definition: sock.h:128
SOCKET m_socket
Contained socket.
Definition: sock.h:188
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:148
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:81
virtual ~Sock()
Destructor, close the socket or do nothing if empty.
Definition: sock.cpp:38
uint8_t Event
Definition: sock.h:116
Sock()
Default constructor, creates an empty object that does nothing when destroyed.
Definition: sock.cpp:29
virtual SOCKET Release()
Get the value of the contained socket and drop ownership.
Definition: sock.cpp:53
virtual bool IsConnected(std::string &errmsg) const
Check if still connected.
Definition: sock.cpp:288
static constexpr Event RECV
If passed to Wait(), then it will wait for readiness to read from the socket.
Definition: sock.h:122
virtual SOCKET Get() const
Get the value of the contained socket.
Definition: sock.cpp:49
virtual int GetSockOpt(int level, int opt_name, void *opt_val, socklen_t *opt_len) const
getsockopt(2) wrapper.
Definition: sock.cpp:75
virtual int Connect(const sockaddr *addr, socklen_t addr_len) const
connect(2) wrapper.
Definition: sock.cpp:71
Sock & operator=(const Sock &)=delete
Copy assignment operator, disabled because closing the same socket twice is undesirable.
virtual void Reset()
Close if non-empty.
Definition: sock.cpp:59
virtual ssize_t Recv(void *buf, size_t len, int flags) const
recv(2) wrapper.
Definition: sock.cpp:67
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:193
#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:206
static RPCHelpMan send()
Definition: rpcwallet.cpp:4228
static bool IOErrorIsPermanent(int err)
Definition: sock.cpp:24
std::string NetworkErrorString(int err)
Return readable error string for a network error code.
Definition: sock.cpp:331
bool CloseSocket(SOCKET &hSocket)
Close socket and set hSocket to INVALID_SOCKET.
Definition: sock.cpp:353
static constexpr auto MAX_WAIT_FOR_IO
Maximum time to wait for I/O readiness.
Definition: sock.h:19
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...
Definition: tinyformat.h:1202