Coverage Report

Created: 2025-12-03 07:02

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
25
#include "curl_setup.h"
26
#include "socketpair.h"
27
#include "urldata.h"
28
#include "rand.h"
29
30
#ifdef USE_EVENTFD
31
32
#include <sys/eventfd.h>
33
34
int Curl_eventfd(curl_socket_t socks[2], bool nonblocking)
35
0
{
36
0
  int efd = eventfd(0, nonblocking ? EFD_CLOEXEC | EFD_NONBLOCK : EFD_CLOEXEC);
37
0
  if(efd == -1) {
38
0
    socks[0] = socks[1] = CURL_SOCKET_BAD;
39
0
    return -1;
40
0
  }
41
0
  socks[0] = socks[1] = efd;
42
0
  return 0;
43
0
}
44
45
#elif defined(HAVE_PIPE)
46
47
#ifdef HAVE_FCNTL
48
#include <fcntl.h>
49
#endif
50
51
int Curl_pipe(curl_socket_t socks[2], bool nonblocking)
52
{
53
#ifdef HAVE_PIPE2
54
  int flags = nonblocking ? O_NONBLOCK | O_CLOEXEC : O_CLOEXEC;
55
  if(pipe2(socks, flags))
56
    return -1;
57
#else
58
  if(pipe(socks))
59
    return -1;
60
#ifdef HAVE_FCNTL
61
  if(fcntl(socks[0], F_SETFD, FD_CLOEXEC) ||
62
     fcntl(socks[1], F_SETFD, FD_CLOEXEC)) {
63
    close(socks[0]);
64
    close(socks[1]);
65
    socks[0] = socks[1] = CURL_SOCKET_BAD;
66
    return -1;
67
  }
68
#endif
69
  if(nonblocking) {
70
    if(curlx_nonblock(socks[0], TRUE) < 0 ||
71
       curlx_nonblock(socks[1], TRUE) < 0) {
72
      close(socks[0]);
73
      close(socks[1]);
74
      socks[0] = socks[1] = CURL_SOCKET_BAD;
75
      return -1;
76
    }
77
  }
78
#endif
79
80
  return 0;
81
}
82
83
#endif /* USE_EVENTFD */
84
85
#ifndef CURL_DISABLE_SOCKETPAIR
86
#ifdef HAVE_SOCKETPAIR
87
#ifdef USE_SOCKETPAIR
88
int Curl_socketpair(int domain, int type, int protocol,
89
                    curl_socket_t socks[2], bool nonblocking)
90
{
91
#ifdef SOCK_NONBLOCK
92
  type = nonblocking ? type | SOCK_NONBLOCK : type;
93
#endif
94
  if(CURL_SOCKETPAIR(domain, type, protocol, socks))
95
    return -1;
96
#ifndef SOCK_NONBLOCK
97
  if(nonblocking) {
98
    if(curlx_nonblock(socks[0], TRUE) < 0 ||
99
       curlx_nonblock(socks[1], TRUE) < 0) {
100
      close(socks[0]);
101
      close(socks[1]);
102
      return -1;
103
    }
104
  }
105
#endif
106
  return 0;
107
}
108
#endif /* USE_SOCKETPAIR */
109
#else /* !HAVE_SOCKETPAIR */
110
#ifdef _WIN32
111
/*
112
 * This is a socketpair() implementation for Windows.
113
 */
114
#include <string.h>
115
#ifdef HAVE_IO_H
116
#include <io.h>
117
#endif
118
#else
119
#ifdef HAVE_NETDB_H
120
#include <netdb.h>
121
#endif
122
#ifdef HAVE_NETINET_IN_H
123
#include <netinet/in.h> /* IPPROTO_TCP */
124
#endif
125
#ifdef HAVE_ARPA_INET_H
126
#include <arpa/inet.h>
127
#endif
128
#ifndef INADDR_LOOPBACK
129
#define INADDR_LOOPBACK 0x7f000001
130
#endif /* !INADDR_LOOPBACK */
131
#endif /* !_WIN32 */
132
133
#include "curlx/nonblock.h" /* for curlx_nonblock */
134
#include "curlx/timeval.h"  /* needed before select.h */
135
#include "select.h"   /* for Curl_poll */
136
137
int Curl_socketpair(int domain, int type, int protocol,
138
                    curl_socket_t socks[2], bool nonblocking)
139
{
140
  union {
141
    struct sockaddr_in inaddr;
142
    struct sockaddr addr;
143
  } a;
144
  curl_socket_t listener;
145
  curl_socklen_t addrlen = sizeof(a.inaddr);
146
  int reuse = 1;
147
  struct pollfd pfd[1];
148
  (void)domain;
149
  (void)type;
150
  (void)protocol;
151
152
  listener = CURL_SOCKET(AF_INET, SOCK_STREAM, IPPROTO_TCP);
153
  if(listener == CURL_SOCKET_BAD)
154
    return -1;
155
156
  memset(&a, 0, sizeof(a));
157
  a.inaddr.sin_family = AF_INET;
158
  a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
159
  a.inaddr.sin_port = 0;
160
161
  socks[0] = socks[1] = CURL_SOCKET_BAD;
162
163
#if defined(_WIN32) || defined(__CYGWIN__)
164
  /* do not set SO_REUSEADDR on Windows */
165
  (void)reuse;
166
#ifdef SO_EXCLUSIVEADDRUSE
167
  {
168
    int exclusive = 1;
169
    if(setsockopt(listener, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
170
                  (char *)&exclusive, (curl_socklen_t)sizeof(exclusive)) == -1)
171
      goto error;
172
  }
173
#endif
174
#else
175
  if(setsockopt(listener, SOL_SOCKET, SO_REUSEADDR,
176
                (char *)&reuse, (curl_socklen_t)sizeof(reuse)) == -1)
177
    goto error;
178
#endif
179
  if(bind(listener, &a.addr, sizeof(a.inaddr)) == -1)
180
    goto error;
181
  if(getsockname(listener, &a.addr, &addrlen) == -1 ||
182
     addrlen < (int)sizeof(a.inaddr))
183
    goto error;
184
  if(listen(listener, 1) == -1)
185
    goto error;
186
  socks[0] = CURL_SOCKET(AF_INET, SOCK_STREAM, 0);
187
  if(socks[0] == CURL_SOCKET_BAD)
188
    goto error;
189
  if(connect(socks[0], &a.addr, sizeof(a.inaddr)) == -1)
190
    goto error;
191
192
  /* use non-blocking accept to make sure we do not block forever */
193
  if(curlx_nonblock(listener, TRUE) < 0)
194
    goto error;
195
  pfd[0].fd = listener;
196
  pfd[0].events = POLLIN;
197
  pfd[0].revents = 0;
198
  (void)Curl_poll(pfd, 1, 1000); /* one second */
199
  socks[1] = CURL_ACCEPT(listener, NULL, NULL);
200
  if(socks[1] == CURL_SOCKET_BAD)
201
    goto error;
202
  else {
203
    struct curltime start = curlx_now();
204
    char rnd[9];
205
    char check[sizeof(rnd)];
206
    char *p = &check[0];
207
    size_t s = sizeof(check);
208
209
    if(Curl_rand(NULL, (unsigned char *)rnd, sizeof(rnd)))
210
      goto error;
211
212
    /* write data to the socket */
213
    swrite(socks[0], rnd, sizeof(rnd));
214
    /* verify that we read the correct data */
215
    do {
216
      ssize_t nread;
217
218
      pfd[0].fd = socks[1];
219
      pfd[0].events = POLLIN;
220
      pfd[0].revents = 0;
221
      (void)Curl_poll(pfd, 1, 1000); /* one second */
222
223
      nread = sread(socks[1], p, s);
224
      if(nread == -1) {
225
        int sockerr = SOCKERRNO;
226
        /* Do not block forever */
227
        if(curlx_timediff_ms(curlx_now(), start) > (60 * 1000))
228
          goto error;
229
        if(
230
#ifdef USE_WINSOCK
231
          /* This is how Windows does it */
232
          (SOCKEWOULDBLOCK == sockerr)
233
#else
234
          /* errno may be EWOULDBLOCK or on some systems EAGAIN when it
235
             returned due to its inability to send off data without
236
             blocking. We therefore treat both error codes the same here */
237
          (SOCKEWOULDBLOCK == sockerr) || (EAGAIN == sockerr) ||
238
          (SOCKEINTR == sockerr) || (SOCKEINPROGRESS == sockerr)
239
#endif
240
          ) {
241
          continue;
242
        }
243
        goto error;
244
      }
245
      s -= nread;
246
      if(s) {
247
        p += nread;
248
        continue;
249
      }
250
      if(memcmp(rnd, check, sizeof(check)))
251
        goto error;
252
      break;
253
    } while(1);
254
  }
255
256
  if(nonblocking)
257
    if(curlx_nonblock(socks[0], TRUE) < 0 ||
258
       curlx_nonblock(socks[1], TRUE) < 0)
259
      goto error;
260
  sclose(listener);
261
  return 0;
262
263
error:
264
  sclose(listener);
265
  sclose(socks[0]);
266
  sclose(socks[1]);
267
  return -1;
268
}
269
#endif
270
#endif /* !CURL_DISABLE_SOCKETPAIR */