sock.h raw

   1  // Copyright (c) 2020-present 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  #ifndef BITCOIN_UTIL_SOCK_H
   6  #define BITCOIN_UTIL_SOCK_H
   7  
   8  #include <compat/compat.h>
   9  #include <util/time.h>
  10  
  11  #include <cstdint>
  12  #include <limits>
  13  #include <memory>
  14  #include <span>
  15  #include <string>
  16  #include <unordered_map>
  17  
  18  class CThreadInterrupt;
  19  
  20  /**
  21   * Maximum time to wait for I/O readiness.
  22   * It will take up until this time to break off in case of an interruption.
  23   */
  24  static constexpr auto MAX_WAIT_FOR_IO = 1s;
  25  
  26  inline bool IOErrorIsPermanent(int err)
  27  {
  28      return err != WSAEAGAIN && err != WSAEINTR && err != WSAEWOULDBLOCK && err != WSAEINPROGRESS;
  29  }
  30  
  31  /**
  32   * RAII helper class that manages a socket and closes it automatically when it goes out of scope.
  33   */
  34  class Sock
  35  {
  36  public:
  37      Sock() = delete;
  38  
  39      /**
  40       * Take ownership of an existent socket.
  41       */
  42      explicit Sock(SOCKET s);
  43  
  44      /**
  45       * Copy constructor, disabled because closing the same socket twice is undesirable.
  46       */
  47      Sock(const Sock&) = delete;
  48  
  49      /**
  50       * Move constructor, grab the socket from another object and close ours (if set).
  51       */
  52      Sock(Sock&& other);
  53  
  54      /**
  55       * Destructor, close the socket or do nothing if empty.
  56       */
  57      virtual ~Sock();
  58  
  59      /**
  60       * Copy assignment operator, disabled because closing the same socket twice is undesirable.
  61       */
  62      Sock& operator=(const Sock&) = delete;
  63  
  64      /**
  65       * Move assignment operator, grab the socket from another object and close ours (if set).
  66       */
  67      virtual Sock& operator=(Sock&& other);
  68  
  69      /**
  70       * send(2) wrapper. Equivalent to `send(m_socket, data, len, flags);`. Code that uses this
  71       * wrapper can be unit tested if this method is overridden by a mock Sock implementation.
  72       */
  73      [[nodiscard]] virtual ssize_t Send(const void* data, size_t len, int flags) const;
  74  
  75      /**
  76       * recv(2) wrapper. Equivalent to `recv(m_socket, buf, len, flags);`. Code that uses this
  77       * wrapper can be unit tested if this method is overridden by a mock Sock implementation.
  78       */
  79      [[nodiscard]] virtual ssize_t Recv(void* buf, size_t len, int flags) const;
  80  
  81      /**
  82       * connect(2) wrapper. Equivalent to `connect(m_socket, addr, addrlen)`. Code that uses this
  83       * wrapper can be unit tested if this method is overridden by a mock Sock implementation.
  84       */
  85      [[nodiscard]] virtual int Connect(const sockaddr* addr, socklen_t addr_len) const;
  86  
  87      /**
  88       * bind(2) wrapper. Equivalent to `bind(m_socket, addr, addr_len)`. Code that uses this
  89       * wrapper can be unit tested if this method is overridden by a mock Sock implementation.
  90       */
  91      [[nodiscard]] virtual int Bind(const sockaddr* addr, socklen_t addr_len) const;
  92  
  93      /**
  94       * listen(2) wrapper. Equivalent to `listen(m_socket, backlog)`. Code that uses this
  95       * wrapper can be unit tested if this method is overridden by a mock Sock implementation.
  96       */
  97      [[nodiscard]] virtual int Listen(int backlog) const;
  98  
  99      /**
 100       * accept(2) wrapper. Equivalent to `std::make_unique<Sock>(accept(m_socket, addr, addr_len))`.
 101       * Code that uses this wrapper can be unit tested if this method is overridden by a mock Sock
 102       * implementation.
 103       * The returned unique_ptr is empty if `accept()` failed in which case errno will be set.
 104       */
 105      [[nodiscard]] virtual std::unique_ptr<Sock> Accept(sockaddr* addr, socklen_t* addr_len) const;
 106  
 107      /**
 108       * getsockopt(2) wrapper. Equivalent to
 109       * `getsockopt(m_socket, level, opt_name, opt_val, opt_len)`. Code that uses this
 110       * wrapper can be unit tested if this method is overridden by a mock Sock implementation.
 111       */
 112      [[nodiscard]] virtual int GetSockOpt(int level,
 113                                           int opt_name,
 114                                           void* opt_val,
 115                                           socklen_t* opt_len) const;
 116  
 117      /**
 118       * setsockopt(2) wrapper. Equivalent to
 119       * `setsockopt(m_socket, level, opt_name, opt_val, opt_len)`. Code that uses this
 120       * wrapper can be unit tested if this method is overridden by a mock Sock implementation.
 121       */
 122      [[nodiscard]] virtual int SetSockOpt(int level,
 123                                           int opt_name,
 124                                           const void* opt_val,
 125                                           socklen_t opt_len) const;
 126  
 127      /**
 128       * getsockname(2) wrapper. Equivalent to
 129       * `getsockname(m_socket, name, name_len)`. Code that uses this
 130       * wrapper can be unit tested if this method is overridden by a mock Sock implementation.
 131       */
 132      [[nodiscard]] virtual int GetSockName(sockaddr* name, socklen_t* name_len) const;
 133  
 134      /**
 135       * Set the non-blocking option on the socket.
 136       * @return true if set successfully
 137       */
 138      [[nodiscard]] virtual bool SetNonBlocking() const;
 139  
 140      /**
 141       * Check if the underlying socket can be used for `select(2)` (or the `Wait()` method).
 142       * @return true if selectable
 143       */
 144      [[nodiscard]] virtual bool IsSelectable() const;
 145  
 146      using Event = uint8_t;
 147  
 148      /**
 149       * If passed to `Wait()`, then it will wait for readiness to read from the socket.
 150       */
 151      static constexpr Event RecvEvent = 0b001;
 152  
 153      /**
 154       * If passed to `Wait()`, then it will wait for readiness to send to the socket.
 155       */
 156      static constexpr Event SendEvent = 0b010;
 157  
 158      /**
 159       * Ignored if passed to `Wait()`, but could be set in the occurred events if an
 160       * exceptional condition has occurred on the socket or if it has been disconnected.
 161       */
 162      static constexpr Event ErrorEvent = 0b100;
 163  
 164      /**
 165       * Wait for readiness for input (recv) or output (send).
 166       * @param[in] timeout Wait this much for at least one of the requested events to occur.
 167       * @param[in] requested Wait for those events, bitwise-or of `RecvEvent` and `SendEvent`.
 168       * @param[out] occurred If not nullptr and the function returns `true`, then this
 169       * indicates which of the requested events occurred (`ErrorEvent` will be added, even if
 170       * not requested, if an exceptional event occurs on the socket).
 171       * A timeout is indicated by return value of `true` and `occurred` being set to 0.
 172       * @return true on success (or timeout, if `occurred` of 0 is returned), false otherwise
 173       */
 174      [[nodiscard]] virtual bool Wait(std::chrono::milliseconds timeout,
 175                                      Event requested,
 176                                      Event* occurred = nullptr) const;
 177  
 178      /**
 179       * Auxiliary requested/occurred events to wait for in `WaitMany()`.
 180       */
 181      struct Events {
 182          explicit Events(Event req) : requested{req} {}
 183          Event requested;
 184          Event occurred{0};
 185      };
 186  
 187      struct HashSharedPtrSock {
 188          size_t operator()(const std::shared_ptr<const Sock>& s) const
 189          {
 190              return s ? s->m_socket : std::numeric_limits<SOCKET>::max();
 191          }
 192      };
 193  
 194      struct EqualSharedPtrSock {
 195          bool operator()(const std::shared_ptr<const Sock>& lhs,
 196                          const std::shared_ptr<const Sock>& rhs) const
 197          {
 198              if (lhs && rhs) {
 199                  return lhs->m_socket == rhs->m_socket;
 200              }
 201              if (!lhs && !rhs) {
 202                  return true;
 203              }
 204              return false;
 205          }
 206      };
 207  
 208      /**
 209       * On which socket to wait for what events in `WaitMany()`.
 210       * The `shared_ptr` is copied into the map to ensure that the `Sock` object
 211       * is not destroyed (its destructor would close the underlying socket).
 212       * If this happens shortly before or after we call `poll(2)` and a new
 213       * socket gets created under the same file descriptor number then the report
 214       * from `WaitMany()` will be bogus.
 215       */
 216      using EventsPerSock = std::unordered_map<std::shared_ptr<const Sock>, Events, HashSharedPtrSock, EqualSharedPtrSock>;
 217  
 218      /**
 219       * Same as `Wait()`, but wait on many sockets within the same timeout.
 220       * @param[in] timeout Wait this long for at least one of the requested events to occur.
 221       * @param[in,out] events_per_sock Wait for the requested events on these sockets and set
 222       * `occurred` for the events that actually occurred.
 223       * @return true on success (or timeout, if all `what[].occurred` are returned as 0),
 224       * false otherwise
 225       */
 226      [[nodiscard]] virtual bool WaitMany(std::chrono::milliseconds timeout,
 227                                          EventsPerSock& events_per_sock) const;
 228  
 229      /* Higher level, convenience, methods. These may throw. */
 230  
 231      /**
 232       * Send the given data, retrying on transient errors.
 233       * @param[in] data Data to send.
 234       * @param[in] timeout Timeout for the entire operation.
 235       * @param[in] interrupt If this is signaled then the operation is canceled.
 236       * @throws std::runtime_error if the operation cannot be completed. In this case only some of
 237       * the data will be written to the socket.
 238       */
 239      virtual void SendComplete(std::span<const unsigned char> data,
 240                                std::chrono::milliseconds timeout,
 241                                CThreadInterrupt& interrupt) const;
 242  
 243      /**
 244       * Convenience method, equivalent to `SendComplete(MakeUCharSpan(data), timeout, interrupt)`.
 245       */
 246      virtual void SendComplete(std::span<const char> data,
 247                                std::chrono::milliseconds timeout,
 248                                CThreadInterrupt& interrupt) const;
 249  
 250      /**
 251       * Read from socket until a terminator character is encountered. Will never consume bytes past
 252       * the terminator from the socket.
 253       * @param[in] terminator Character up to which to read from the socket.
 254       * @param[in] timeout Timeout for the entire operation.
 255       * @param[in] interrupt If this is signaled then the operation is canceled.
 256       * @param[in] max_data The maximum amount of data (in bytes) to receive. If this many bytes
 257       * are received and there is still no terminator, then this method will throw an exception.
 258       * @return The data that has been read, without the terminating character.
 259       * @throws std::runtime_error if the operation cannot be completed. In this case some bytes may
 260       * have been consumed from the socket.
 261       */
 262      [[nodiscard]] virtual std::string RecvUntilTerminator(uint8_t terminator,
 263                                                            std::chrono::milliseconds timeout,
 264                                                            CThreadInterrupt& interrupt,
 265                                                            size_t max_data) const;
 266  
 267      /**
 268       * Check if still connected.
 269       * @param[out] errmsg The error string, if the socket has been disconnected.
 270       * @return true if connected
 271       */
 272      [[nodiscard]] virtual bool IsConnected(std::string& errmsg) const;
 273  
 274      /**
 275       * Check if the internal socket is equal to `s`. Use only in tests.
 276       */
 277      bool operator==(SOCKET s) const;
 278  
 279  protected:
 280      /**
 281       * Contained socket. `INVALID_SOCKET` designates the object is empty.
 282       */
 283      SOCKET m_socket;
 284  
 285  private:
 286      /**
 287       * Close `m_socket` if it is not `INVALID_SOCKET`.
 288       */
 289      void Close();
 290  };
 291  
 292  /** Return readable error string for a network error code */
 293  std::string NetworkErrorString(int err);
 294  
 295  #endif // BITCOIN_UTIL_SOCK_H
 296