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