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