Coverage Report

Created: 2026-01-10 07:08

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