sock.h
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 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 { 182 return s ? s->m_socket : std::numeric_limits<SOCKET>::max(); 183 } 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 { 190 if (lhs && rhs) { 191 return lhs->m_socket == rhs->m_socket; 192 } 193 if (!lhs && !rhs) { 194 return true; 195 } 196 return false; 197 } 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