Coverage Report

Created: 2026-10-15 06:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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 */