/src/curl/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 | 49.4k | { |
40 | 49.4k | int efd = eventfd(0, nonblocking ? EFD_CLOEXEC | EFD_NONBLOCK : EFD_CLOEXEC); |
41 | 49.4k | if(efd == -1) { |
42 | 0 | socks[0] = socks[1] = CURL_SOCKET_BAD; |
43 | 0 | return -1; |
44 | 0 | } |
45 | 49.4k | socks[0] = socks[1] = efd; |
46 | 49.4k | return 0; |
47 | 49.4k | } |
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 | | return pipe2(socks, (nonblocking ? O_NONBLOCK : 0) | O_CLOEXEC) ? -1 : 0; |
59 | | #elif defined(__APPLE__) && defined(HAVE_BUILTIN_AVAILABLE) && \ |
60 | | ((defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \ |
61 | | __MAC_OS_X_VERSION_MAX_ALLOWED >= 270000) || \ |
62 | | (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && \ |
63 | | __IPHONE_OS_VERSION_MAX_ALLOWED >= 270000) || \ |
64 | | (defined(__TV_OS_VERSION_MAX_ALLOWED) && \ |
65 | | __TV_OS_VERSION_MAX_ALLOWED >= 270000) || \ |
66 | | (defined(__WATCH_OS_VERSION_MAX_ALLOWED) && \ |
67 | | __WATCH_OS_VERSION_MAX_ALLOWED >= 270000) || \ |
68 | | (defined(__VISION_OS_VERSION_MAX_ALLOWED) && \ |
69 | | __VISION_OS_VERSION_MAX_ALLOWED >= 270000)) |
70 | | if(__builtin_available(macOS 27, iOS 27, tvOS 27, watchOS 27, visionOS 27, |
71 | | macCatalyst 27, *)) |
72 | | return pipe2(socks, (nonblocking ? O_NONBLOCK : 0) | O_CLOEXEC) ? -1 : 0; |
73 | | else |
74 | | #endif |
75 | | if(pipe(socks)) |
76 | | return -1; |
77 | | #ifdef HAVE_FCNTL |
78 | | if(fcntl(socks[0], F_SETFD, FD_CLOEXEC) || |
79 | | fcntl(socks[1], F_SETFD, FD_CLOEXEC)) { |
80 | | sclose(socks[0]); |
81 | | sclose(socks[1]); |
82 | | socks[0] = socks[1] = CURL_SOCKET_BAD; |
83 | | return -1; |
84 | | } |
85 | | #endif |
86 | | if(nonblocking) { |
87 | | if(curlx_nonblock(socks[0], TRUE) < 0 || |
88 | | curlx_nonblock(socks[1], TRUE) < 0) { |
89 | | sclose(socks[0]); |
90 | | sclose(socks[1]); |
91 | | socks[0] = socks[1] = CURL_SOCKET_BAD; |
92 | | return -1; |
93 | | } |
94 | | } |
95 | | return 0; |
96 | | } |
97 | | |
98 | | #elif defined(HAVE_SOCKETPAIR) /* !USE_EVENTFD && !HAVE_PIPE */ |
99 | | |
100 | | #ifndef USE_UNIX_SOCKETS |
101 | | #error "unsupported Unix domain and socketpair build combo" |
102 | | #endif |
103 | | |
104 | | static int wakeup_socketpair(curl_socket_t socks[2], bool nonblocking) |
105 | | { |
106 | | int type = SOCK_STREAM; |
107 | | #ifdef SOCK_CLOEXEC |
108 | | type |= SOCK_CLOEXEC; |
109 | | #endif |
110 | | #ifdef CURL_USE_SOCK_NONBLOCK |
111 | | if(nonblocking) |
112 | | type |= SOCK_NONBLOCK; |
113 | | #endif |
114 | | |
115 | | if(CURL_SOCKETPAIR(AF_UNIX, type, 0, socks)) |
116 | | return -1; |
117 | | #ifndef CURL_USE_SOCK_NONBLOCK |
118 | | if(nonblocking) { |
119 | | if(curlx_nonblock(socks[0], TRUE) < 0 || |
120 | | curlx_nonblock(socks[1], TRUE) < 0) { |
121 | | sclose(socks[0]); |
122 | | sclose(socks[1]); |
123 | | socks[0] = socks[1] = CURL_SOCKET_BAD; |
124 | | return -1; |
125 | | } |
126 | | } |
127 | | #endif |
128 | | #ifdef USE_SO_NOSIGPIPE |
129 | | if(Curl_sock_nosigpipe(socks[1]) < 0) { |
130 | | sclose(socks[0]); |
131 | | sclose(socks[1]); |
132 | | socks[0] = socks[1] = CURL_SOCKET_BAD; |
133 | | return -1; |
134 | | } |
135 | | #endif /* USE_SO_NOSIGPIPE */ |
136 | | |
137 | | return 0; |
138 | | } |
139 | | |
140 | | #else /* !USE_EVENTFD && !HAVE_PIPE && !HAVE_SOCKETPAIR */ |
141 | | |
142 | | #ifdef HAVE_NETDB_H |
143 | | #include <netdb.h> |
144 | | #endif |
145 | | #ifdef HAVE_NETINET_IN_H |
146 | | #include <netinet/in.h> /* for IPPROTO_TCP */ |
147 | | #endif |
148 | | #ifdef HAVE_ARPA_INET_H |
149 | | #include <arpa/inet.h> |
150 | | #endif |
151 | | |
152 | | #ifndef INADDR_LOOPBACK |
153 | | #define INADDR_LOOPBACK 0x7f000001 |
154 | | #endif |
155 | | |
156 | | #include "select.h" /* for Curl_poll */ |
157 | | |
158 | | static int wakeup_inet(curl_socket_t socks[2], bool nonblocking) |
159 | | { |
160 | | union { |
161 | | struct sockaddr_in inaddr; |
162 | | struct sockaddr addr; |
163 | | } a; |
164 | | curl_socket_t listener; |
165 | | curl_socklen_t addrlen = sizeof(a.inaddr); |
166 | | int reuse = 1; |
167 | | struct pollfd pfd[1]; |
168 | | |
169 | | listener = CURL_SOCKET(AF_INET, SOCK_STREAM, IPPROTO_TCP); |
170 | | if(listener == CURL_SOCKET_BAD) |
171 | | return -1; |
172 | | |
173 | | memset(&a, 0, sizeof(a)); |
174 | | a.inaddr.sin_family = AF_INET; |
175 | | a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); |
176 | | a.inaddr.sin_port = 0; |
177 | | |
178 | | socks[0] = socks[1] = CURL_SOCKET_BAD; |
179 | | |
180 | | #if defined(_WIN32) || defined(__CYGWIN__) |
181 | | /* do not set SO_REUSEADDR on Windows */ |
182 | | (void)reuse; |
183 | | #ifdef SO_EXCLUSIVEADDRUSE |
184 | | { |
185 | | int exclusive = 1; |
186 | | if(setsockopt(listener, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, |
187 | | (char *)&exclusive, (curl_socklen_t)sizeof(exclusive)) == -1) |
188 | | goto error; |
189 | | } |
190 | | #endif |
191 | | #else |
192 | | if(setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, |
193 | | (char *)&reuse, (curl_socklen_t)sizeof(reuse)) == -1) |
194 | | goto error; |
195 | | #endif |
196 | | if(bind(listener, &a.addr, sizeof(a.inaddr)) == -1) |
197 | | goto error; |
198 | | if(getsockname(listener, &a.addr, &addrlen) == -1 || |
199 | | addrlen < (int)sizeof(a.inaddr)) |
200 | | goto error; |
201 | | if(listen(listener, 1) == -1) |
202 | | goto error; |
203 | | socks[0] = CURL_SOCKET(AF_INET, SOCK_STREAM, 0); |
204 | | if(socks[0] == CURL_SOCKET_BAD) |
205 | | goto error; |
206 | | if(connect(socks[0], &a.addr, sizeof(a.inaddr)) == -1) |
207 | | goto error; |
208 | | |
209 | | /* use non-blocking accept to make sure we do not block forever */ |
210 | | if(curlx_nonblock(listener, TRUE) < 0) |
211 | | goto error; |
212 | | pfd[0].fd = listener; |
213 | | pfd[0].events = POLLIN; |
214 | | pfd[0].revents = 0; |
215 | | (void)Curl_poll(pfd, 1, 1000); /* one second */ |
216 | | socks[1] = CURL_ACCEPT(listener, NULL, NULL); |
217 | | if(socks[1] == CURL_SOCKET_BAD) |
218 | | goto error; |
219 | | else { |
220 | | struct curltime start = curlx_now(); |
221 | | char rnd[9]; |
222 | | char check[sizeof(rnd)]; |
223 | | char *p = &check[0]; |
224 | | size_t s = sizeof(check); |
225 | | |
226 | | if(Curl_rand(NULL, (unsigned char *)rnd, sizeof(rnd))) |
227 | | goto error; |
228 | | |
229 | | /* write data to the socket */ |
230 | | swrite(socks[0], rnd, sizeof(rnd)); |
231 | | /* verify that we read the correct data */ |
232 | | do { |
233 | | ssize_t nread; |
234 | | |
235 | | /* Do not block forever */ |
236 | | if(curlx_timediff_ms(curlx_now(), start) > (60 * 1000)) |
237 | | goto error; |
238 | | |
239 | | pfd[0].fd = socks[1]; |
240 | | pfd[0].events = POLLIN; |
241 | | pfd[0].revents = 0; |
242 | | (void)Curl_poll(pfd, 1, 1000); /* one second */ |
243 | | |
244 | | nread = sread(socks[1], p, s); |
245 | | if(!nread) { /* unexpected close before getting complete `check` */ |
246 | | goto error; |
247 | | } |
248 | | else if(nread == -1) { /* error or close */ |
249 | | int sockerr = SOCKERRNO; |
250 | | if(SOCK_EAGAIN(sockerr) |
251 | | #ifndef USE_WINSOCK |
252 | | || (sockerr == SOCKEINTR) || (sockerr == SOCKEINPROGRESS) |
253 | | #endif |
254 | | ) { |
255 | | continue; |
256 | | } |
257 | | goto error; |
258 | | } |
259 | | s -= nread; |
260 | | if(s) { |
261 | | p += nread; |
262 | | continue; |
263 | | } |
264 | | if(memcmp(rnd, check, sizeof(check))) |
265 | | goto error; |
266 | | break; |
267 | | } while(1); |
268 | | } |
269 | | |
270 | | if(nonblocking) |
271 | | if(curlx_nonblock(socks[0], TRUE) < 0 || |
272 | | curlx_nonblock(socks[1], TRUE) < 0) |
273 | | goto error; |
274 | | #ifdef USE_SO_NOSIGPIPE |
275 | | if(Curl_sock_nosigpipe(socks[1]) < 0) |
276 | | goto error; |
277 | | #endif |
278 | | sclose(listener); |
279 | | return 0; |
280 | | |
281 | | error: |
282 | | sclose(listener); |
283 | | sclose(socks[0]); |
284 | | sclose(socks[1]); |
285 | | socks[0] = socks[1] = CURL_SOCKET_BAD; |
286 | | return -1; |
287 | | } |
288 | | |
289 | | #endif /* choose implementation */ |
290 | | |
291 | | int Curl_wakeup_init(curl_socket_t socks[2], bool nonblocking) |
292 | 49.4k | { |
293 | 49.4k | #ifdef USE_EVENTFD |
294 | 49.4k | return wakeup_eventfd(socks, nonblocking); |
295 | | #elif defined(HAVE_PIPE) |
296 | | return wakeup_pipe(socks, nonblocking); |
297 | | #elif defined(HAVE_SOCKETPAIR) |
298 | | return wakeup_socketpair(socks, nonblocking); |
299 | | #else |
300 | | return wakeup_inet(socks, nonblocking); |
301 | | #endif |
302 | 49.4k | } |
303 | | |
304 | | #if defined(USE_EVENTFD) || defined(HAVE_PIPE) |
305 | | |
306 | 0 | #define wakeup_write write |
307 | 154k | #define wakeup_read read |
308 | 49.4k | #define wakeup_close close |
309 | | |
310 | | #else /* !USE_EVENTFD && !HAVE_PIPE */ |
311 | | |
312 | | #define wakeup_write swrite |
313 | | #define wakeup_read sread |
314 | | #define wakeup_close sclose |
315 | | |
316 | | #endif |
317 | | |
318 | | int Curl_wakeup_signal(curl_socket_t socks[2]) |
319 | 0 | { |
320 | 0 | int sockerr = 0; |
321 | 0 | #ifdef USE_EVENTFD |
322 | 0 | const uint64_t buf[1] = { 1 }; |
323 | | #else |
324 | | const char buf[1] = { 1 }; |
325 | | #endif |
326 | |
|
327 | 0 | while(1) { |
328 | 0 | sockerr = 0; |
329 | 0 | if(wakeup_write(socks[1], buf, sizeof(buf)) < 0) { |
330 | 0 | sockerr = SOCKERRNO; |
331 | 0 | #ifndef USE_WINSOCK |
332 | 0 | if(sockerr == SOCKEINTR) |
333 | 0 | continue; |
334 | 0 | #endif |
335 | 0 | if(SOCK_EAGAIN(sockerr)) |
336 | 0 | sockerr = 0; /* wakeup is already ongoing */ |
337 | 0 | } |
338 | 0 | break; |
339 | 0 | } |
340 | 0 | return sockerr; |
341 | 0 | } |
342 | | |
343 | | CURLcode Curl_wakeup_consume(curl_socket_t socks[2], bool all) |
344 | 154k | { |
345 | 154k | char buf[64]; |
346 | 154k | ssize_t rc; |
347 | 154k | CURLcode result = CURLE_OK; |
348 | | |
349 | 154k | do { |
350 | 154k | rc = wakeup_read(socks[0], buf, sizeof(buf)); |
351 | 154k | if(!rc) |
352 | 0 | break; |
353 | 154k | else if(rc < 0) { |
354 | 154k | int sockerr = SOCKERRNO; |
355 | 154k | #ifndef USE_WINSOCK |
356 | 154k | if(sockerr == SOCKEINTR) |
357 | 0 | continue; |
358 | 154k | #endif |
359 | 154k | if(SOCK_EAGAIN(sockerr)) |
360 | 154k | break; |
361 | 0 | result = CURLE_READ_ERROR; |
362 | 0 | break; |
363 | 154k | } |
364 | 154k | } while(all); |
365 | 154k | return result; |
366 | 154k | } |
367 | | |
368 | | void Curl_wakeup_destroy(curl_socket_t socks[2]) |
369 | 49.4k | { |
370 | | #ifndef USE_EVENTFD |
371 | | if(socks[1] != CURL_SOCKET_BAD) |
372 | | wakeup_close(socks[1]); |
373 | | #endif |
374 | 49.4k | if(socks[0] != CURL_SOCKET_BAD) |
375 | 49.4k | wakeup_close(socks[0]); |
376 | 49.4k | socks[0] = socks[1] = CURL_SOCKET_BAD; |
377 | 49.4k | } |
378 | | |
379 | | #endif /* !CURL_DISABLE_SOCKETPAIR */ |