Coverage Report

Created: 2026-10-22 06:48

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
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 */