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