Branch data Line data Source code
# 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 : 0 : {
# 25 [ # # ][ # # ]: 0 : return err != WSAEAGAIN && err != WSAEINTR && err != WSAEWOULDBLOCK && err != WSAEINPROGRESS;
# [ # # ][ # # ]
# 26 : 0 : }
# 27 : :
# 28 : 6 : Sock::Sock() : m_socket(INVALID_SOCKET) {}
# 29 : :
# 30 : 1812 : Sock::Sock(SOCKET s) : m_socket(s) {}
# 31 : :
# 32 : : Sock::Sock(Sock&& other)
# 33 : 4 : {
# 34 : 4 : m_socket = other.m_socket;
# 35 : 4 : other.m_socket = INVALID_SOCKET;
# 36 : 4 : }
# 37 : :
# 38 : 1822 : Sock::~Sock() { Reset(); }
# 39 : :
# 40 : : Sock& Sock::operator=(Sock&& other)
# 41 : 4 : {
# 42 : 4 : Reset();
# 43 : 4 : m_socket = other.m_socket;
# 44 : 4 : other.m_socket = INVALID_SOCKET;
# 45 : 4 : return *this;
# 46 : 4 : }
# 47 : :
# 48 : 4155 : SOCKET Sock::Get() const { return m_socket; }
# 49 : :
# 50 : : SOCKET Sock::Release()
# 51 : 1117 : {
# 52 : 1117 : const SOCKET s = m_socket;
# 53 : 1117 : m_socket = INVALID_SOCKET;
# 54 : 1117 : return s;
# 55 : 1117 : }
# 56 : :
# 57 : 1836 : void Sock::Reset() { CloseSocket(m_socket); }
# 58 : :
# 59 : : ssize_t Sock::Send(const void* data, size_t len, int flags) const
# 60 : 44 : {
# 61 : 44 : return send(m_socket, static_cast<const char*>(data), len, flags);
# 62 : 44 : }
# 63 : :
# 64 : : ssize_t Sock::Recv(void* buf, size_t len, int flags) const
# 65 : 102 : {
# 66 : 102 : return recv(m_socket, static_cast<char*>(buf), len, flags);
# 67 : 102 : }
# 68 : :
# 69 : : int Sock::Connect(const sockaddr* addr, socklen_t addr_len) const
# 70 : 549 : {
# 71 : 549 : return connect(m_socket, addr, addr_len);
# 72 : 549 : }
# 73 : :
# 74 : : int Sock::GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const
# 75 : 351 : {
# 76 : 351 : return getsockopt(m_socket, level, opt_name, static_cast<char*>(opt_val), opt_len);
# 77 : 351 : }
# 78 : :
# 79 : : bool Sock::Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred) const
# 80 : 562 : {
# 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
# 108 [ - + ]: 562 : if (!IsSelectableSocket(m_socket)) {
# 109 : 0 : return false;
# 110 : 0 : }
# 111 : :
# 112 : 562 : fd_set fdset_recv;
# 113 : 562 : fd_set fdset_send;
# 114 : 562 : FD_ZERO(&fdset_recv);
# 115 : 562 : FD_ZERO(&fdset_send);
# 116 : :
# 117 [ + - ]: 562 : if (requested & RECV) {
# 118 : 562 : FD_SET(m_socket, &fdset_recv);
# 119 : 562 : }
# 120 : :
# 121 [ + + ]: 562 : if (requested & SEND) {
# 122 : 528 : FD_SET(m_socket, &fdset_send);
# 123 : 528 : }
# 124 : :
# 125 : 562 : timeval timeout_struct = MillisToTimeval(timeout);
# 126 : :
# 127 [ - + ]: 562 : if (select(m_socket + 1, &fdset_recv, &fdset_send, nullptr, &timeout_struct) == SOCKET_ERROR) {
# 128 : 0 : return false;
# 129 : 0 : }
# 130 : :
# 131 [ + + ]: 562 : if (occurred != nullptr) {
# 132 : 528 : *occurred = 0;
# 133 : 528 : if (FD_ISSET(m_socket, &fdset_recv)) {
# 134 : 8 : *occurred |= RECV;
# 135 : 8 : }
# 136 : 528 : if (FD_ISSET(m_socket, &fdset_send)) {
# 137 : 351 : *occurred |= SEND;
# 138 : 351 : }
# 139 : 528 : }
# 140 : :
# 141 : 562 : return true;
# 142 : 562 : #endif /* USE_POLL */
# 143 : 562 : }
# 144 : :
# 145 : : void Sock::SendComplete(const std::string& data,
# 146 : : std::chrono::milliseconds timeout,
# 147 : : CThreadInterrupt& interrupt) const
# 148 : 6 : {
# 149 : 6 : const auto deadline = GetTime<std::chrono::milliseconds>() + timeout;
# 150 : 6 : size_t sent{0};
# 151 : :
# 152 : 6 : for (;;) {
# 153 : 6 : const ssize_t ret{Send(data.data() + sent, data.size() - sent, MSG_NOSIGNAL)};
# 154 : :
# 155 [ + - ]: 6 : if (ret > 0) {
# 156 : 6 : sent += static_cast<size_t>(ret);
# 157 [ + - ]: 6 : if (sent == data.size()) {
# 158 : 6 : break;
# 159 : 6 : }
# 160 : 0 : } else {
# 161 : 0 : const int err{WSAGetLastError()};
# 162 [ # # ]: 0 : if (IOErrorIsPermanent(err)) {
# 163 : 0 : throw std::runtime_error(strprintf("send(): %s", NetworkErrorString(err)));
# 164 : 0 : }
# 165 : 0 : }
# 166 : :
# 167 : 0 : const auto now = GetTime<std::chrono::milliseconds>();
# 168 : :
# 169 [ # # ]: 0 : if (now >= deadline) {
# 170 : 0 : throw std::runtime_error(strprintf(
# 171 : 0 : "Send timeout (sent only %u of %u bytes before that)", sent, data.size()));
# 172 : 0 : }
# 173 : :
# 174 [ # # ]: 0 : if (interrupt) {
# 175 : 0 : throw std::runtime_error(strprintf(
# 176 : 0 : "Send interrupted (sent only %u of %u bytes before that)", sent, data.size()));
# 177 : 0 : }
# 178 : :
# 179 : : // Wait for a short while (or the socket to become ready for sending) before retrying
# 180 : : // if nothing was sent.
# 181 : 0 : const auto wait_time = std::min(deadline - now, std::chrono::milliseconds{MAX_WAIT_FOR_IO});
# 182 : 0 : (void)Wait(wait_time, SEND);
# 183 : 0 : }
# 184 : 6 : }
# 185 : :
# 186 : : std::string Sock::RecvUntilTerminator(uint8_t terminator,
# 187 : : std::chrono::milliseconds timeout,
# 188 : : CThreadInterrupt& interrupt,
# 189 : : size_t max_data) const
# 190 : 4 : {
# 191 : 4 : const auto deadline = GetTime<std::chrono::milliseconds>() + timeout;
# 192 : 4 : std::string data;
# 193 : 4 : 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 : 262 : for (;;) {
# 204 [ + + ]: 262 : if (data.size() >= max_data) {
# 205 : 4 : throw std::runtime_error(
# 206 : 4 : strprintf("Received too many bytes without a terminator (%u)", data.size()));
# 207 : 4 : }
# 208 : :
# 209 : 258 : char buf[512];
# 210 : :
# 211 : 258 : const ssize_t peek_ret{Recv(buf, std::min(sizeof(buf), max_data - data.size()), MSG_PEEK)};
# 212 : :
# 213 : 258 : switch (peek_ret) {
# 214 [ - + ]: 0 : case -1: {
# 215 : 0 : const int err{WSAGetLastError()};
# 216 [ # # ]: 0 : if (IOErrorIsPermanent(err)) {
# 217 : 0 : throw std::runtime_error(strprintf("recv(): %s", NetworkErrorString(err)));
# 218 : 0 : }
# 219 : 0 : break;
# 220 : 0 : }
# 221 [ - + ]: 0 : case 0:
# 222 : 0 : throw std::runtime_error("Connection unexpectedly closed by peer");
# 223 [ + - ]: 258 : default:
# 224 : 258 : auto end = buf + peek_ret;
# 225 : 258 : auto terminator_pos = std::find(buf, end, terminator);
# 226 : 258 : terminator_found = terminator_pos != end;
# 227 : :
# 228 [ - + ]: 258 : const size_t try_len{terminator_found ? terminator_pos - buf + 1 :
# 229 : 258 : static_cast<size_t>(peek_ret)};
# 230 : :
# 231 : 258 : const ssize_t read_ret{Recv(buf, try_len, 0)};
# 232 : :
# 233 [ - + ][ - + ]: 258 : if (read_ret < 0 || static_cast<size_t>(read_ret) != try_len) {
# 234 : 0 : throw std::runtime_error(
# 235 : 0 : strprintf("recv() returned %u bytes on attempt to read %u bytes but previous "
# 236 : 0 : "peek claimed %u bytes are available",
# 237 : 0 : read_ret, try_len, peek_ret));
# 238 : 0 : }
# 239 : :
# 240 : : // Don't include the terminator in the output.
# 241 [ - + ]: 258 : const size_t append_len{terminator_found ? try_len - 1 : try_len};
# 242 : :
# 243 : 258 : data.append(buf, buf + append_len);
# 244 : :
# 245 [ - + ]: 258 : if (terminator_found) {
# 246 : 0 : return data;
# 247 : 0 : }
# 248 : 258 : }
# 249 : :
# 250 : 258 : const auto now = GetTime<std::chrono::milliseconds>();
# 251 : :
# 252 [ - + ]: 258 : if (now >= deadline) {
# 253 : 0 : throw std::runtime_error(strprintf(
# 254 : 0 : "Receive timeout (received %u bytes without terminator before that)", data.size()));
# 255 : 0 : }
# 256 : :
# 257 [ - + ]: 258 : if (interrupt) {
# 258 : 0 : throw std::runtime_error(strprintf(
# 259 : 0 : "Receive interrupted (received %u bytes without terminator before that)",
# 260 : 0 : data.size()));
# 261 : 0 : }
# 262 : :
# 263 : : // Wait for a short while (or the socket to become ready for reading) before retrying.
# 264 : 258 : const auto wait_time = std::min(deadline - now, std::chrono::milliseconds{MAX_WAIT_FOR_IO});
# 265 : 258 : (void)Wait(wait_time, RECV);
# 266 : 258 : }
# 267 : 4 : }
# 268 : :
# 269 : : bool Sock::IsConnected(std::string& errmsg) const
# 270 : 8 : {
# 271 [ + - ]: 8 : if (m_socket == INVALID_SOCKET) {
# 272 : 8 : errmsg = "not connected";
# 273 : 8 : return false;
# 274 : 8 : }
# 275 : :
# 276 : 0 : char c;
# 277 : 0 : switch (Recv(&c, sizeof(c), MSG_PEEK)) {
# 278 [ # # ]: 0 : case -1: {
# 279 : 0 : const int err = WSAGetLastError();
# 280 [ # # ]: 0 : if (IOErrorIsPermanent(err)) {
# 281 : 0 : errmsg = NetworkErrorString(err);
# 282 : 0 : return false;
# 283 : 0 : }
# 284 : 0 : return true;
# 285 : 0 : }
# 286 [ # # ]: 0 : case 0:
# 287 : 0 : errmsg = "closed";
# 288 : 0 : return false;
# 289 [ # # ]: 0 : default:
# 290 : 0 : return true;
# 291 : 0 : }
# 292 : 0 : }
# 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 : 24 : {
# 313 : 24 : char buf[256];
# 314 : 24 : buf[0] = 0;
# 315 : : /* Too bad there are two incompatible implementations of the
# 316 : : * thread-safe strerror. */
# 317 : 24 : 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 : 24 : s = buf;
# 322 [ - + ]: 24 : if (strerror_r(err, buf, sizeof(buf)))
# 323 : 0 : buf[0] = 0;
# 324 : 24 : #endif
# 325 : 24 : return strprintf("%s (%d)", s, err);
# 326 : 24 : }
# 327 : : #endif
# 328 : :
# 329 : : bool CloseSocket(SOCKET& hSocket)
# 330 : 4580 : {
# 331 [ + + ]: 4580 : if (hSocket == INVALID_SOCKET)
# 332 : 4580 : return false;
# 333 : : #ifdef WIN32
# 334 : : int ret = closesocket(hSocket);
# 335 : : #else
# 336 : 2421 : int ret = close(hSocket);
# 337 : 2421 : #endif
# 338 [ - + ]: 2421 : if (ret) {
# 339 : 0 : LogPrintf("Socket close failed: %d. Error: %s\n", hSocket, NetworkErrorString(WSAGetLastError()));
# 340 : 0 : }
# 341 : 2421 : hSocket = INVALID_SOCKET;
# 342 : 2421 : return ret != SOCKET_ERROR;
# 343 : 2421 : }
|