/tmp/bitcoin/src/util/sock.h
Line | Count | Source |
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 | 19 | { |
28 | 19 | return err != WSAEAGAIN && err != WSAEINTR && err != WSAEWOULDBLOCK && err != WSAEINPROGRESS; |
29 | 19 | } |
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 RECV = 0b001; |
152 | | |
153 | | /** |
154 | | * If passed to `Wait()`, then it will wait for readiness to send to the socket. |
155 | | */ |
156 | | static constexpr Event SEND = 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 ERR = 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 `RECV` and `SEND`. |
168 | | * @param[out] occurred If not nullptr and the function returns `true`, then this |
169 | | * indicates which of the requested events occurred (`ERR` 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 | 815k | 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 | 1.62M | { |
190 | 1.62M | return s ? s->m_socket : std::numeric_limits<SOCKET>::max(); |
191 | 1.62M | } |
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 | 809k | { |
198 | 809k | if (lhs && rhs) { |
199 | 809k | return lhs->m_socket == rhs->m_socket; |
200 | 809k | } |
201 | 0 | if (!lhs && !rhs) { |
202 | 0 | return true; |
203 | 0 | } |
204 | 0 | return false; |
205 | 0 | } |
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 |