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