Coverage Report

Created: 2026-04-29 07:01

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