/root/bitcoin/src/util/sock.h
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) 2020-2022 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(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(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 |