Coverage Report

Created: 2025-08-26 07:08

/src/PROJ/curl/lib/socketpair.c
Line
Count
Source (jump to first uncovered line)
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(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
/* The last 3 #include files should be in this order */
138
#include "curl_printf.h"
139
#include "curl_memory.h"
140
#include "memdebug.h"
141
142
int Curl_socketpair(int domain, int type, int protocol,
143
                    curl_socket_t socks[2], bool nonblocking)
144
{
145
  union {
146
    struct sockaddr_in inaddr;
147
    struct sockaddr addr;
148
  } a;
149
  curl_socket_t listener;
150
  curl_socklen_t addrlen = sizeof(a.inaddr);
151
  int reuse = 1;
152
  struct pollfd pfd[1];
153
  (void)domain;
154
  (void)type;
155
  (void)protocol;
156
157
  listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
158
  if(listener == CURL_SOCKET_BAD)
159
    return -1;
160
161
  memset(&a, 0, sizeof(a));
162
  a.inaddr.sin_family = AF_INET;
163
  a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
164
  a.inaddr.sin_port = 0;
165
166
  socks[0] = socks[1] = CURL_SOCKET_BAD;
167
168
#if defined(_WIN32) || defined(__CYGWIN__)
169
  /* do not set SO_REUSEADDR on Windows */
170
  (void)reuse;
171
#ifdef SO_EXCLUSIVEADDRUSE
172
  {
173
    int exclusive = 1;
174
    if(setsockopt(listener, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
175
                  (char *)&exclusive, (curl_socklen_t)sizeof(exclusive)) == -1)
176
      goto error;
177
  }
178
#endif
179
#else
180
  if(setsockopt(listener, SOL_SOCKET, SO_REUSEADDR,
181
                (char *)&reuse, (curl_socklen_t)sizeof(reuse)) == -1)
182
    goto error;
183
#endif
184
  if(bind(listener, &a.addr, sizeof(a.inaddr)) == -1)
185
    goto error;
186
  if(getsockname(listener, &a.addr, &addrlen) == -1 ||
187
     addrlen < (int)sizeof(a.inaddr))
188
    goto error;
189
  if(listen(listener, 1) == -1)
190
    goto error;
191
  socks[0] = socket(AF_INET, SOCK_STREAM, 0);
192
  if(socks[0] == CURL_SOCKET_BAD)
193
    goto error;
194
  if(connect(socks[0], &a.addr, sizeof(a.inaddr)) == -1)
195
    goto error;
196
197
  /* use non-blocking accept to make sure we do not block forever */
198
  if(curlx_nonblock(listener, TRUE) < 0)
199
    goto error;
200
  pfd[0].fd = listener;
201
  pfd[0].events = POLLIN;
202
  pfd[0].revents = 0;
203
  (void)Curl_poll(pfd, 1, 1000); /* one second */
204
  socks[1] = accept(listener, NULL, NULL);
205
  if(socks[1] == CURL_SOCKET_BAD)
206
    goto error;
207
  else {
208
    struct curltime start = curlx_now();
209
    char rnd[9];
210
    char check[sizeof(rnd)];
211
    char *p = &check[0];
212
    size_t s = sizeof(check);
213
214
    if(Curl_rand(NULL, (unsigned char *)rnd, sizeof(rnd)))
215
      goto error;
216
217
    /* write data to the socket */
218
    swrite(socks[0], rnd, sizeof(rnd));
219
    /* verify that we read the correct data */
220
    do {
221
      ssize_t nread;
222
223
      pfd[0].fd = socks[1];
224
      pfd[0].events = POLLIN;
225
      pfd[0].revents = 0;
226
      (void)Curl_poll(pfd, 1, 1000); /* one second */
227
228
      nread = sread(socks[1], p, s);
229
      if(nread == -1) {
230
        int sockerr = SOCKERRNO;
231
        /* Do not block forever */
232
        if(curlx_timediff(curlx_now(), start) > (60 * 1000))
233
          goto error;
234
        if(
235
#ifdef USE_WINSOCK
236
          /* This is how Windows does it */
237
          (SOCKEWOULDBLOCK == sockerr)
238
#else
239
          /* errno may be EWOULDBLOCK or on some systems EAGAIN when it
240
             returned due to its inability to send off data without
241
             blocking. We therefore treat both error codes the same here */
242
          (SOCKEWOULDBLOCK == sockerr) || (EAGAIN == sockerr) ||
243
          (SOCKEINTR == sockerr) || (SOCKEINPROGRESS == sockerr)
244
#endif
245
          ) {
246
          continue;
247
        }
248
        goto error;
249
      }
250
      s -= nread;
251
      if(s) {
252
        p += nread;
253
        continue;
254
      }
255
      if(memcmp(rnd, check, sizeof(check)))
256
        goto error;
257
      break;
258
    } while(1);
259
  }
260
261
  if(nonblocking)
262
    if(curlx_nonblock(socks[0], TRUE) < 0 ||
263
       curlx_nonblock(socks[1], TRUE) < 0)
264
      goto error;
265
  sclose(listener);
266
  return 0;
267
268
error:
269
  sclose(listener);
270
  sclose(socks[0]);
271
  sclose(socks[1]);
272
  return -1;
273
}
274
#endif
275
#endif /* !CURL_DISABLE_SOCKETPAIR */