/src/CMake/Utilities/cmcurl/lib/socketpair.c
Line | Count | Source |
1 | | /*************************************************************************** |
2 | | * _ _ ____ _ |
3 | | * Project ___| | | | _ \| | |
4 | | * / __| | | | |_) | | |
5 | | * | (__| |_| | _ <| |___ |
6 | | * \___|\___/|_| \_\_____| |
7 | | * |
8 | | * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. |
9 | | * |
10 | | * This software is licensed as described in the file COPYING, which |
11 | | * you should have received as part of this distribution. The terms |
12 | | * are also available at https://curl.se/docs/copyright.html. |
13 | | * |
14 | | * You may opt to use, copy, modify, merge, publish, distribute and/or sell |
15 | | * copies of the Software, and permit persons to whom the Software is |
16 | | * furnished to do so, under the terms of the COPYING file. |
17 | | * |
18 | | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
19 | | * KIND, either express or implied. |
20 | | * |
21 | | * SPDX-License-Identifier: curl |
22 | | * |
23 | | ***************************************************************************/ |
24 | | #include "curl_setup.h" |
25 | | |
26 | | #include "socketpair.h" |
27 | | #include "urldata.h" |
28 | | #include "rand.h" |
29 | | #include "curlx/nonblock.h" |
30 | | |
31 | | #ifndef CURL_DISABLE_SOCKETPAIR |
32 | | |
33 | | /* choose implementation */ |
34 | | #ifdef USE_EVENTFD |
35 | | |
36 | | #include <sys/eventfd.h> |
37 | | |
38 | | static int wakeup_eventfd(curl_socket_t socks[2], bool nonblocking) |
39 | 0 | { |
40 | 0 | int efd = eventfd(0, nonblocking ? EFD_CLOEXEC | EFD_NONBLOCK : EFD_CLOEXEC); |
41 | 0 | if(efd == -1) { |
42 | 0 | socks[0] = socks[1] = CURL_SOCKET_BAD; |
43 | 0 | return -1; |
44 | 0 | } |
45 | 0 | socks[0] = socks[1] = efd; |
46 | 0 | return 0; |
47 | 0 | } |
48 | | |
49 | | #elif defined(HAVE_PIPE) |
50 | | |
51 | | #ifdef HAVE_FCNTL |
52 | | #include <fcntl.h> |
53 | | #endif |
54 | | |
55 | | static int wakeup_pipe(curl_socket_t socks[2], bool nonblocking) |
56 | | { |
57 | | #ifdef HAVE_PIPE2 |
58 | | int flags = nonblocking ? O_NONBLOCK | O_CLOEXEC : O_CLOEXEC; |
59 | | if(pipe2(socks, flags)) |
60 | | return -1; |
61 | | #else |
62 | | if(pipe(socks)) |
63 | | return -1; |
64 | | #ifdef HAVE_FCNTL |
65 | | if(fcntl(socks[0], F_SETFD, FD_CLOEXEC) || |
66 | | fcntl(socks[1], F_SETFD, FD_CLOEXEC)) { |
67 | | sclose(socks[0]); |
68 | | sclose(socks[1]); |
69 | | socks[0] = socks[1] = CURL_SOCKET_BAD; |
70 | | return -1; |
71 | | } |
72 | | #endif |
73 | | if(nonblocking) { |
74 | | if(curlx_nonblock(socks[0], TRUE) < 0 || |
75 | | curlx_nonblock(socks[1], TRUE) < 0) { |
76 | | sclose(socks[0]); |
77 | | sclose(socks[1]); |
78 | | socks[0] = socks[1] = CURL_SOCKET_BAD; |
79 | | return -1; |
80 | | } |
81 | | } |
82 | | #endif |
83 | | |
84 | | return 0; |
85 | | } |
86 | | |
87 | | #elif defined(HAVE_SOCKETPAIR) /* !USE_EVENTFD && !HAVE_PIPE */ |
88 | | |
89 | | #ifndef USE_UNIX_SOCKETS |
90 | | #error "unsupported Unix domain and socketpair build combo" |
91 | | #endif |
92 | | |
93 | | static int wakeup_socketpair(curl_socket_t socks[2], bool nonblocking) |
94 | | { |
95 | | int type = SOCK_STREAM; |
96 | | #ifdef SOCK_CLOEXEC |
97 | | type |= SOCK_CLOEXEC; |
98 | | #endif |
99 | | #ifdef SOCK_NONBLOCK |
100 | | if(nonblocking) |
101 | | type |= SOCK_NONBLOCK; |
102 | | #endif |
103 | | |
104 | | if(CURL_SOCKETPAIR(AF_UNIX, type, 0, socks)) |
105 | | return -1; |
106 | | #ifndef SOCK_NONBLOCK |
107 | | if(nonblocking) { |
108 | | if(curlx_nonblock(socks[0], TRUE) < 0 || |
109 | | curlx_nonblock(socks[1], TRUE) < 0) { |
110 | | sclose(socks[0]); |
111 | | sclose(socks[1]); |
112 | | socks[0] = socks[1] = CURL_SOCKET_BAD; |
113 | | return -1; |
114 | | } |
115 | | } |
116 | | #endif |
117 | | #ifdef USE_SO_NOSIGPIPE |
118 | | if(Curl_sock_nosigpipe(socks[1]) < 0) { |
119 | | sclose(socks[0]); |
120 | | sclose(socks[1]); |
121 | | socks[0] = socks[1] = CURL_SOCKET_BAD; |
122 | | return -1; |
123 | | } |
124 | | #endif /* USE_SO_NOSIGPIPE */ |
125 | | |
126 | | return 0; |
127 | | } |
128 | | |
129 | | #else /* !USE_EVENTFD && !HAVE_PIPE && !HAVE_SOCKETPAIR */ |
130 | | |
131 | | #ifdef HAVE_NETDB_H |
132 | | #include <netdb.h> |
133 | | #endif |
134 | | #ifdef HAVE_NETINET_IN_H |
135 | | #include <netinet/in.h> /* for IPPROTO_TCP */ |
136 | | #endif |
137 | | #ifdef HAVE_ARPA_INET_H |
138 | | #include <arpa/inet.h> |
139 | | #endif |
140 | | |
141 | | #ifndef INADDR_LOOPBACK |
142 | | #define INADDR_LOOPBACK 0x7f000001 |
143 | | #endif |
144 | | |
145 | | #include "select.h" /* for Curl_poll */ |
146 | | |
147 | | static int wakeup_inet(curl_socket_t socks[2], bool nonblocking) |
148 | | { |
149 | | union { |
150 | | struct sockaddr_in inaddr; |
151 | | struct sockaddr addr; |
152 | | } a; |
153 | | curl_socket_t listener; |
154 | | curl_socklen_t addrlen = sizeof(a.inaddr); |
155 | | int reuse = 1; |
156 | | struct pollfd pfd[1]; |
157 | | |
158 | | listener = CURL_SOCKET(AF_INET, SOCK_STREAM, IPPROTO_TCP); |
159 | | if(listener == CURL_SOCKET_BAD) |
160 | | return -1; |
161 | | |
162 | | memset(&a, 0, sizeof(a)); |
163 | | a.inaddr.sin_family = AF_INET; |
164 | | a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); |
165 | | a.inaddr.sin_port = 0; |
166 | | |
167 | | socks[0] = socks[1] = CURL_SOCKET_BAD; |
168 | | |
169 | | #if defined(_WIN32) || defined(__CYGWIN__) |
170 | | /* do not set SO_REUSEADDR on Windows */ |
171 | | (void)reuse; |
172 | | #ifdef SO_EXCLUSIVEADDRUSE |
173 | | { |
174 | | int exclusive = 1; |
175 | | if(setsockopt(listener, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, |
176 | | (char *)&exclusive, (curl_socklen_t)sizeof(exclusive)) == -1) |
177 | | goto error; |
178 | | } |
179 | | #endif |
180 | | #else |
181 | | if(setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, |
182 | | (char *)&reuse, (curl_socklen_t)sizeof(reuse)) == -1) |
183 | | goto error; |
184 | | #endif |
185 | | if(bind(listener, &a.addr, sizeof(a.inaddr)) == -1) |
186 | | goto error; |
187 | | if(getsockname(listener, &a.addr, &addrlen) == -1 || |
188 | | addrlen < (int)sizeof(a.inaddr)) |
189 | | goto error; |
190 | | if(listen(listener, 1) == -1) |
191 | | goto error; |
192 | | socks[0] = CURL_SOCKET(AF_INET, SOCK_STREAM, 0); |
193 | | if(socks[0] == CURL_SOCKET_BAD) |
194 | | goto error; |
195 | | if(connect(socks[0], &a.addr, sizeof(a.inaddr)) == -1) |
196 | | goto error; |
197 | | |
198 | | /* use non-blocking accept to make sure we do not block forever */ |
199 | | if(curlx_nonblock(listener, TRUE) < 0) |
200 | | goto error; |
201 | | pfd[0].fd = listener; |
202 | | pfd[0].events = POLLIN; |
203 | | pfd[0].revents = 0; |
204 | | (void)Curl_poll(pfd, 1, 1000); /* one second */ |
205 | | socks[1] = CURL_ACCEPT(listener, NULL, NULL); |
206 | | if(socks[1] == CURL_SOCKET_BAD) |
207 | | goto error; |
208 | | else { |
209 | | struct curltime start = curlx_now(); |
210 | | char rnd[9]; |
211 | | char check[sizeof(rnd)]; |
212 | | char *p = &check[0]; |
213 | | size_t s = sizeof(check); |
214 | | |
215 | | if(Curl_rand(NULL, (unsigned char *)rnd, sizeof(rnd))) |
216 | | goto error; |
217 | | |
218 | | /* write data to the socket */ |
219 | | swrite(socks[0], rnd, sizeof(rnd)); |
220 | | /* verify that we read the correct data */ |
221 | | do { |
222 | | ssize_t nread; |
223 | | |
224 | | pfd[0].fd = socks[1]; |
225 | | pfd[0].events = POLLIN; |
226 | | pfd[0].revents = 0; |
227 | | (void)Curl_poll(pfd, 1, 1000); /* one second */ |
228 | | |
229 | | nread = sread(socks[1], p, s); |
230 | | if(nread == -1) { |
231 | | int sockerr = SOCKERRNO; |
232 | | /* Do not block forever */ |
233 | | if(curlx_timediff_ms(curlx_now(), start) > (60 * 1000)) |
234 | | goto error; |
235 | | if( |
236 | | #ifdef USE_WINSOCK |
237 | | /* This is how Windows does it */ |
238 | | (SOCKEWOULDBLOCK == sockerr) |
239 | | #else |
240 | | /* errno may be EWOULDBLOCK or on some systems EAGAIN when it |
241 | | returned due to its inability to send off data without |
242 | | blocking. We therefore treat both error codes the same here */ |
243 | | (SOCKEWOULDBLOCK == sockerr) || (EAGAIN == sockerr) || |
244 | | (SOCKEINTR == sockerr) || (SOCKEINPROGRESS == sockerr) |
245 | | #endif |
246 | | ) { |
247 | | continue; |
248 | | } |
249 | | goto error; |
250 | | } |
251 | | s -= nread; |
252 | | if(s) { |
253 | | p += nread; |
254 | | continue; |
255 | | } |
256 | | if(memcmp(rnd, check, sizeof(check))) |
257 | | goto error; |
258 | | break; |
259 | | } while(1); |
260 | | } |
261 | | |
262 | | if(nonblocking) |
263 | | if(curlx_nonblock(socks[0], TRUE) < 0 || |
264 | | curlx_nonblock(socks[1], TRUE) < 0) |
265 | | goto error; |
266 | | #ifdef USE_SO_NOSIGPIPE |
267 | | if(Curl_sock_nosigpipe(socks[1]) < 0) |
268 | | goto error; |
269 | | #endif |
270 | | sclose(listener); |
271 | | return 0; |
272 | | |
273 | | error: |
274 | | sclose(listener); |
275 | | sclose(socks[0]); |
276 | | sclose(socks[1]); |
277 | | socks[0] = socks[1] = CURL_SOCKET_BAD; |
278 | | return -1; |
279 | | } |
280 | | |
281 | | #endif /* choose implementation */ |
282 | | |
283 | | int Curl_wakeup_init(curl_socket_t socks[2], bool nonblocking) |
284 | 0 | { |
285 | 0 | #ifdef USE_EVENTFD |
286 | 0 | return wakeup_eventfd(socks, nonblocking); |
287 | | #elif defined(HAVE_PIPE) |
288 | | return wakeup_pipe(socks, nonblocking); |
289 | | #elif defined(HAVE_SOCKETPAIR) |
290 | | return wakeup_socketpair(socks, nonblocking); |
291 | | #else |
292 | | return wakeup_inet(socks, nonblocking); |
293 | | #endif |
294 | 0 | } |
295 | | |
296 | | #if defined(USE_EVENTFD) || defined(HAVE_PIPE) |
297 | | |
298 | 0 | #define wakeup_write write |
299 | 0 | #define wakeup_read read |
300 | 0 | #define wakeup_close close |
301 | | |
302 | | #else /* !USE_EVENTFD && !HAVE_PIPE */ |
303 | | |
304 | | #define wakeup_write swrite |
305 | | #define wakeup_read sread |
306 | | #define wakeup_close sclose |
307 | | |
308 | | #endif |
309 | | |
310 | | int Curl_wakeup_signal(curl_socket_t socks[2]) |
311 | 0 | { |
312 | 0 | int err = 0; |
313 | 0 | #ifdef USE_EVENTFD |
314 | 0 | const uint64_t buf[1] = { 1 }; |
315 | | #else |
316 | | const char buf[1] = { 1 }; |
317 | | #endif |
318 | |
|
319 | 0 | while(1) { |
320 | 0 | err = 0; |
321 | 0 | if(wakeup_write(socks[1], buf, sizeof(buf)) < 0) { |
322 | 0 | err = SOCKERRNO; |
323 | | #ifdef USE_WINSOCK |
324 | | if(err == SOCKEWOULDBLOCK) |
325 | | err = 0; /* wakeup is already ongoing */ |
326 | | #else |
327 | 0 | if(SOCKEINTR == err) |
328 | 0 | continue; |
329 | 0 | if((err == SOCKEWOULDBLOCK) || (err == EAGAIN)) |
330 | 0 | err = 0; /* wakeup is already ongoing */ |
331 | 0 | #endif |
332 | 0 | } |
333 | 0 | break; |
334 | 0 | } |
335 | 0 | return err; |
336 | 0 | } |
337 | | |
338 | | CURLcode Curl_wakeup_consume(curl_socket_t socks[2], bool all) |
339 | 0 | { |
340 | 0 | char buf[64]; |
341 | 0 | ssize_t rc; |
342 | 0 | CURLcode result = CURLE_OK; |
343 | |
|
344 | 0 | do { |
345 | 0 | rc = wakeup_read(socks[0], buf, sizeof(buf)); |
346 | 0 | if(!rc) |
347 | 0 | break; |
348 | 0 | else if(rc < 0) { |
349 | | #ifdef USE_WINSOCK |
350 | | if(SOCKERRNO == SOCKEWOULDBLOCK) |
351 | | break; |
352 | | #else |
353 | 0 | if(SOCKEINTR == SOCKERRNO) |
354 | 0 | continue; |
355 | 0 | if((SOCKERRNO == SOCKEWOULDBLOCK) || (SOCKERRNO == EAGAIN)) |
356 | 0 | break; |
357 | 0 | #endif |
358 | 0 | result = CURLE_READ_ERROR; |
359 | 0 | break; |
360 | 0 | } |
361 | 0 | } while(all); |
362 | 0 | return result; |
363 | 0 | } |
364 | | |
365 | | void Curl_wakeup_destroy(curl_socket_t socks[2]) |
366 | 0 | { |
367 | | #ifndef USE_EVENTFD |
368 | | if(socks[1] != CURL_SOCKET_BAD) |
369 | | wakeup_close(socks[1]); |
370 | | #endif |
371 | 0 | if(socks[0] != CURL_SOCKET_BAD) |
372 | 0 | wakeup_close(socks[0]); |
373 | 0 | socks[0] = socks[1] = CURL_SOCKET_BAD; |
374 | 0 | } |
375 | | |
376 | | #endif /* !CURL_DISABLE_SOCKETPAIR */ |