Bitcoin Core  22.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 <stdexcept>
14 #include <string>
15 
16 #ifdef WIN32
17 #include <codecvt>
18 #include <locale>
19 #endif
20 
21 #ifdef USE_POLL
22 #include <poll.h>
23 #endif
24 
25 static inline bool IOErrorIsPermanent(int err)
26 {
27  return err != WSAEAGAIN && err != WSAEINTR && err != WSAEWOULDBLOCK && 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 {
36  m_socket = other.m_socket;
37  other.m_socket = INVALID_SOCKET;
38 }
39 
41 
43 {
44  Reset();
45  m_socket = other.m_socket;
46  other.m_socket = INVALID_SOCKET;
47  return *this;
48 }
49 
50 SOCKET Sock::Get() const { return m_socket; }
51 
53 {
54  const SOCKET s = m_socket;
56  return s;
57 }
58 
60 
61 ssize_t Sock::Send(const void* data, size_t len, int flags) const
62 {
63  return send(m_socket, static_cast<const char*>(data), len, flags);
64 }
65 
66 ssize_t Sock::Recv(void* buf, size_t len, int flags) const
67 {
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 {
73  return connect(m_socket, addr, addr_len);
74 }
75 
76 int Sock::GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const
77 {
78  return getsockopt(m_socket, level, opt_name, static_cast<char*>(opt_val), opt_len);
79 }
80 
81 bool Sock::Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred) const
82 {
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, &timeout_struct) == SOCKET_ERROR) {
130  return false;
131  }
132 
133  if (occurred != nullptr) {
134  *occurred = 0;
135  if (FD_ISSET(m_socket, &fdset_recv)) {
136  *occurred |= RECV;
137  }
138  if (FD_ISSET(m_socket, &fdset_send)) {
139  *occurred |= SEND;
140  }
141  }
142 
143  return true;
144 #endif /* USE_POLL */
145 }
146 
147 void Sock::SendComplete(const std::string& data,
148  std::chrono::milliseconds timeout,
149  CThreadInterrupt& interrupt) const
150 {
151  const auto deadline = GetTime<std::chrono::milliseconds>() + timeout;
152  size_t sent{0};
153 
154  for (;;) {
155  const ssize_t ret{Send(data.data() + sent, data.size() - sent, MSG_NOSIGNAL)};
156 
157  if (ret > 0) {
158  sent += static_cast<size_t>(ret);
159  if (sent == data.size()) {
160  break;
161  }
162  } else {
163  const int err{WSAGetLastError()};
164  if (IOErrorIsPermanent(err)) {
165  throw std::runtime_error(strprintf("send(): %s", NetworkErrorString(err)));
166  }
167  }
168 
169  const auto now = GetTime<std::chrono::milliseconds>();
170 
171  if (now >= deadline) {
172  throw std::runtime_error(strprintf(
173  "Send timeout (sent only %u of %u bytes before that)", sent, data.size()));
174  }
175 
176  if (interrupt) {
177  throw std::runtime_error(strprintf(
178  "Send interrupted (sent only %u of %u bytes before that)", sent, data.size()));
179  }
180 
181  // Wait for a short while (or the socket to become ready for sending) before retrying
182  // if nothing was sent.
183  const auto wait_time = std::min(deadline - now, std::chrono::milliseconds{MAX_WAIT_FOR_IO});
184  (void)Wait(wait_time, SEND);
185  }
186 }
187 
188 std::string Sock::RecvUntilTerminator(uint8_t terminator,
189  std::chrono::milliseconds timeout,
190  CThreadInterrupt& interrupt,
191  size_t max_data) const
192 {
193  const auto deadline = GetTime<std::chrono::milliseconds>() + timeout;
194  std::string data;
195  bool terminator_found{false};
196 
197  // We must not consume any bytes past the terminator from the socket.
198  // One option is to read one byte at a time and check if we have read a terminator.
199  // However that is very slow. Instead, we peek at what is in the socket and only read
200  // as many bytes as possible without crossing the terminator.
201  // Reading 64 MiB of random data with 262526 terminator chars takes 37 seconds to read
202  // one byte at a time VS 0.71 seconds with the "peek" solution below. Reading one byte
203  // at a time is about 50 times slower.
204 
205  for (;;) {
206  if (data.size() >= max_data) {
207  throw std::runtime_error(
208  strprintf("Received too many bytes without a terminator (%u)", data.size()));
209  }
210 
211  char buf[512];
212 
213  const ssize_t peek_ret{Recv(buf, std::min(sizeof(buf), max_data - data.size()), MSG_PEEK)};
214 
215  switch (peek_ret) {
216  case -1: {
217  const int err{WSAGetLastError()};
218  if (IOErrorIsPermanent(err)) {
219  throw std::runtime_error(strprintf("recv(): %s", NetworkErrorString(err)));
220  }
221  break;
222  }
223  case 0:
224  throw std::runtime_error("Connection unexpectedly closed by peer");
225  default:
226  auto end = buf + peek_ret;
227  auto terminator_pos = std::find(buf, end, terminator);
228  terminator_found = terminator_pos != end;
229 
230  const size_t try_len{terminator_found ? terminator_pos - buf + 1 :
231  static_cast<size_t>(peek_ret)};
232 
233  const ssize_t read_ret{Recv(buf, try_len, 0)};
234 
235  if (read_ret < 0 || static_cast<size_t>(read_ret) != try_len) {
236  throw std::runtime_error(
237  strprintf("recv() returned %u bytes on attempt to read %u bytes but previous "
238  "peek claimed %u bytes are available",
239  read_ret, try_len, peek_ret));
240  }
241 
242  // Don't include the terminator in the output.
243  const size_t append_len{terminator_found ? try_len - 1 : try_len};
244 
245  data.append(buf, buf + append_len);
246 
247  if (terminator_found) {
248  return data;
249  }
250  }
251 
252  const auto now = GetTime<std::chrono::milliseconds>();
253 
254  if (now >= deadline) {
255  throw std::runtime_error(strprintf(
256  "Receive timeout (received %u bytes without terminator before that)", data.size()));
257  }
258 
259  if (interrupt) {
260  throw std::runtime_error(strprintf(
261  "Receive interrupted (received %u bytes without terminator before that)",
262  data.size()));
263  }
264 
265  // Wait for a short while (or the socket to become ready for reading) before retrying.
266  const auto wait_time = std::min(deadline - now, std::chrono::milliseconds{MAX_WAIT_FOR_IO});
267  (void)Wait(wait_time, RECV);
268  }
269 }
270 
271 bool Sock::IsConnected(std::string& errmsg) const
272 {
273  if (m_socket == INVALID_SOCKET) {
274  errmsg = "not connected";
275  return false;
276  }
277 
278  char c;
279  switch (Recv(&c, sizeof(c), MSG_PEEK)) {
280  case -1: {
281  const int err = WSAGetLastError();
282  if (IOErrorIsPermanent(err)) {
283  errmsg = NetworkErrorString(err);
284  return false;
285  }
286  return true;
287  }
288  case 0:
289  errmsg = "closed";
290  return false;
291  default:
292  return true;
293  }
294 }
295 
296 #ifdef WIN32
297 std::string NetworkErrorString(int err)
298 {
299  wchar_t buf[256];
300  buf[0] = 0;
301  if(FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
302  nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
303  buf, ARRAYSIZE(buf), nullptr))
304  {
305  return strprintf("%s (%d)", std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>,wchar_t>().to_bytes(buf), err);
306  }
307  else
308  {
309  return strprintf("Unknown error (%d)", err);
310  }
311 }
312 #else
313 std::string NetworkErrorString(int err)
314 {
315  char buf[256];
316  buf[0] = 0;
317  /* Too bad there are two incompatible implementations of the
318  * thread-safe strerror. */
319  const char *s;
320 #ifdef STRERROR_R_CHAR_P /* GNU variant can return a pointer outside the passed buffer */
321  s = strerror_r(err, buf, sizeof(buf));
322 #else /* POSIX variant always returns message in buffer */
323  s = buf;
324  if (strerror_r(err, buf, sizeof(buf)))
325  buf[0] = 0;
326 #endif
327  return strprintf("%s (%d)", s, err);
328 }
329 #endif
330 
331 bool CloseSocket(SOCKET& hSocket)
332 {
333  if (hSocket == INVALID_SOCKET)
334  return false;
335 #ifdef WIN32
336  int ret = closesocket(hSocket);
337 #else
338  int ret = close(hSocket);
339 #endif
340  if (ret) {
341  LogPrintf("Socket close failed: %d. Error: %s\n", hSocket, NetworkErrorString(WSAGetLastError()));
342  }
343  hSocket = INVALID_SOCKET;
344  return ret != SOCKET_ERROR;
345 }
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:71
flags
int flags
Definition: bitcoin-tx.cpp:513
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:147
WSAGetLastError
#define WSAGetLastError()
Definition: compat.h:43
Sock::Reset
virtual void Reset()
Close if non-empty.
Definition: sock.cpp:59
tinyformat.h
CloseSocket
bool CloseSocket(SOCKET &hSocket)
Close socket and set hSocket to INVALID_SOCKET.
Definition: sock.cpp:331
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:188
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:66
send
static RPCHelpMan send()
Definition: rpcwallet.cpp:4143
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:81
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:185
Sock::GetSockOpt
virtual int GetSockOpt(int level, int opt_name, void *opt_val, socklen_t *opt_len) const
getsockopt(2) wrapper.
Definition: sock.cpp:76
NetworkErrorString
std::string NetworkErrorString(int err)
Return readable error string for a network error code.
Definition: sock.cpp:313
Sock::Send
virtual ssize_t Send(const void *data, size_t len, int flags) const
send(2) wrapper.
Definition: sock.cpp:61
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:30
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:50
Sock::IsConnected
virtual bool IsConnected(std::string &errmsg) const
Check if still connected.
Definition: sock.cpp:271
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
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:52
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:25
Sock::~Sock
virtual ~Sock()
Destructor, close the socket or do nothing if empty.
Definition: sock.cpp:40
CThreadInterrupt
Definition: threadinterrupt.h:19
sock.h
WSAEAGAIN
#define WSAEAGAIN
Definition: compat.h:47
SOCKET
unsigned int SOCKET
Definition: compat.h:41