Bitcoin ABC 0.26.3
P2P Digital Currency
Loading...
Searching...
No Matches
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
25static inline bool IOErrorIsPermanent(int err) {
26 return err != WSAEAGAIN && err != WSAEINTR && err != WSAEWOULDBLOCK &&
28}
29
30Sock::Sock() : m_socket(INVALID_SOCKET) {}
31
32Sock::Sock(SOCKET s) : m_socket(s) {}
33
34Sock::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
51 return m_socket;
52}
53
55 const SOCKET s = m_socket;
57 return s;
58}
59
63
64ssize_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
68ssize_t Sock::Recv(void *buf, size_t len, int flags) const {
69 return recv(m_socket, static_cast<char *>(buf), len, flags);
70}
71
72int Sock::Connect(const sockaddr *addr, socklen_t addr_len) const {
73 return connect(m_socket, addr, addr_len);
74}
75
77 socklen_t *opt_len) const {
78 return getsockopt(m_socket, level, opt_name, static_cast<char *>(opt_val),
79 opt_len);
80}
81
82bool 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
119
120 if (requested & RECV) {
122 }
123
124 if (requested & SEND) {
126 }
127
129
130 if (select(m_socket + 1, &fdset_recv, &fdset_send, nullptr,
132 return false;
133 }
134
135 if (occurred != nullptr) {
136 *occurred = 0;
138 *occurred |= RECV;
139 }
141 *occurred |= SEND;
142 }
143 }
144
145 return true;
146#endif /* USE_POLL */
147}
148
149void Sock::SendComplete(const std::string &data,
150 std::chrono::milliseconds timeout,
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});
191 }
192}
193
195 std::chrono::milliseconds timeout,
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);
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",
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});
286 }
287}
288
289bool 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)) {
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
314std::string NetworkErrorString(int err) {
315 wchar_t buf[256];
316 buf[0] = 0;
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
332std::string NetworkErrorString(int err) {
333 // On BSD sockets implementations, NetworkErrorString is the same as
334 // SysErrorString.
335 return SysErrorString(err);
336}
337#endif
338
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 }
353 return ret != SOCKET_ERROR;
354}
int flags
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
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: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
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
T GetRand(T nMax=std::numeric_limits< T >::max()) noexcept
Generate a uniform random integer of type T in the range [0..nMax) nMax defaults to std::numeric_limi...
Definition random.h:85
static RPCHelpMan send()
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 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
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...