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
25static inline bool IOErrorIsPermanent(int err)
26{
27 return err != WSAEAGAIN && err != WSAEINTR && err != WSAEWOULDBLOCK && err != WSAEINPROGRESS;
28}
29
30Sock::Sock() : m_socket(INVALID_SOCKET) {}
31
32Sock::Sock(SOCKET s) : m_socket(s) {}
33
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
50SOCKET Sock::Get() const { return m_socket; }
51
53{
54 const SOCKET s = m_socket;
56 return s;
57}
58
60
61ssize_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
66ssize_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
71int Sock::Connect(const sockaddr* addr, socklen_t addr_len) const
72{
73 return connect(m_socket, addr, addr_len);
74}
75
76int 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
81bool 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
147void 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
188std::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
271bool 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
297std::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
313std::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
331bool 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}
int flags
Definition: bitcoin-tx.cpp:525
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:61
static constexpr Event SEND
If passed to Wait(), then it will wait for readiness to send to the socket.
Definition: sock.h:119
SOCKET m_socket
Contained socket.
Definition: sock.h:176
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:147
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:40
uint8_t Event
Definition: sock.h:109
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:52
virtual bool IsConnected(std::string &errmsg) const
Check if still connected.
Definition: sock.cpp:271
static constexpr Event RECV
If passed to Wait(), then it will wait for readiness to read from the socket.
Definition: sock.h:114
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:71
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:66
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
#define INVALID_SOCKET
Definition: compat.h:53
#define WSAEWOULDBLOCK
Definition: compat.h:46
#define SOCKET_ERROR
Definition: compat.h:54
#define WSAGetLastError()
Definition: compat.h:43
static bool IsSelectableSocket(const SOCKET &s)
Definition: compat.h:100
#define MSG_NOSIGNAL
Definition: compat.h:110
unsigned int SOCKET
Definition: compat.h:41
#define WSAEINPROGRESS
Definition: compat.h:50
#define WSAEINTR
Definition: compat.h:49
#define WSAEAGAIN
Definition: compat.h:47
#define LogPrintf(...)
Definition: logging.h:187
static RPCHelpMan send()
Definition: rpcwallet.cpp:4258
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:313
bool CloseSocket(SOCKET &hSocket)
Close socket and set hSocket to INVALID_SOCKET.
Definition: sock.cpp:331
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:172
constexpr int64_t count_milliseconds(std::chrono::milliseconds t)
Definition: time.h:30
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1164