LCOV - code coverage report
Current view: top level - src/util - sock.cpp (source / functions) Hit Total Coverage
Test: coverage.lcov Lines: 123 191 64.4 %
Date: 2021-06-29 14:35:33 Functions: 18 19 94.7 %
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: 26 68 38.2 %

           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 : }

Generated by: LCOV version 1.14