LCOV - code coverage report
Current view: top level - src/util - sock.cpp (source / functions) Hit Total Coverage
Test: coverage.lcov Lines: 134 206 65.0 %
Date: 2022-04-21 14:51:19 Functions: 19 20 95.0 %
Legend: Modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed

Not modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed
Branches: 27 70 38.6 %

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

Generated by: LCOV version 0-eol-96201-ge66f56f4af6a