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