Coverage Report

Created: 2026-07-14 07:09

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(SOCK_EAGAIN(sockerr)
236
#ifndef USE_WINSOCK
237
           || (sockerr == SOCKEINTR) || (sockerr == SOCKEINPROGRESS)
238
#endif
239
          ) {
240
          continue;
241
        }
242
        goto error;
243
      }
244
      s -= nread;
245
      if(s) {
246
        p += nread;
247
        continue;
248
      }
249
      if(memcmp(rnd, check, sizeof(check)))
250
        goto error;
251
      break;
252
    } while(1);
253
  }
254
255
  if(nonblocking)
256
    if(curlx_nonblock(socks[0], TRUE) < 0 ||
257
       curlx_nonblock(socks[1], TRUE) < 0)
258
      goto error;
259
#ifdef USE_SO_NOSIGPIPE
260
  if(Curl_sock_nosigpipe(socks[1]) < 0)
261
    goto error;
262
#endif
263
  sclose(listener);
264
  return 0;
265
266
error:
267
  sclose(listener);
268
  sclose(socks[0]);
269
  sclose(socks[1]);
270
  socks[0] = socks[1] = CURL_SOCKET_BAD;
271
  return -1;
272
}
273
274
#endif /* choose implementation */
275
276
int Curl_wakeup_init(curl_socket_t socks[2], bool nonblocking)
277
0
{
278
0
#ifdef USE_EVENTFD
279
0
  return wakeup_eventfd(socks, nonblocking);
280
#elif defined(HAVE_PIPE)
281
  return wakeup_pipe(socks, nonblocking);
282
#elif defined(HAVE_SOCKETPAIR)
283
  return wakeup_socketpair(socks, nonblocking);
284
#else
285
  return wakeup_inet(socks, nonblocking);
286
#endif
287
0
}
288
289
#if defined(USE_EVENTFD) || defined(HAVE_PIPE)
290
291
0
#define wakeup_write        write
292
0
#define wakeup_read         read
293
0
#define wakeup_close        close
294
295
#else /* !USE_EVENTFD && !HAVE_PIPE */
296
297
#define wakeup_write        swrite
298
#define wakeup_read         sread
299
#define wakeup_close        sclose
300
301
#endif
302
303
int Curl_wakeup_signal(curl_socket_t socks[2])
304
0
{
305
0
  int sockerr = 0;
306
0
#ifdef USE_EVENTFD
307
0
  const uint64_t buf[1] = { 1 };
308
#else
309
  const char buf[1] = { 1 };
310
#endif
311
312
0
  while(1) {
313
0
    sockerr = 0;
314
0
    if(wakeup_write(socks[1], buf, sizeof(buf)) < 0) {
315
0
      sockerr = SOCKERRNO;
316
0
#ifndef USE_WINSOCK
317
0
      if(sockerr == SOCKEINTR)
318
0
        continue;
319
0
#endif
320
0
      if(SOCK_EAGAIN(sockerr))
321
0
        sockerr = 0; /* wakeup is already ongoing */
322
0
    }
323
0
    break;
324
0
  }
325
0
  return sockerr;
326
0
}
327
328
CURLcode Curl_wakeup_consume(curl_socket_t socks[2], bool all)
329
0
{
330
0
  char buf[64];
331
0
  ssize_t rc;
332
0
  CURLcode result = CURLE_OK;
333
334
0
  do {
335
0
    rc = wakeup_read(socks[0], buf, sizeof(buf));
336
0
    if(!rc)
337
0
      break;
338
0
    else if(rc < 0) {
339
0
      int sockerr = SOCKERRNO;
340
0
#ifndef USE_WINSOCK
341
0
      if(sockerr == SOCKEINTR)
342
0
        continue;
343
0
#endif
344
0
      if(SOCK_EAGAIN(sockerr))
345
0
        break;
346
0
      result = CURLE_READ_ERROR;
347
0
      break;
348
0
    }
349
0
  } while(all);
350
0
  return result;
351
0
}
352
353
void Curl_wakeup_destroy(curl_socket_t socks[2])
354
0
{
355
#ifndef USE_EVENTFD
356
  if(socks[1] != CURL_SOCKET_BAD)
357
    wakeup_close(socks[1]);
358
#endif
359
0
  if(socks[0] != CURL_SOCKET_BAD)
360
0
    wakeup_close(socks[0]);
361
0
  socks[0] = socks[1] = CURL_SOCKET_BAD;
362
0
}
363
364
#endif /* !CURL_DISABLE_SOCKETPAIR */