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