xref: /third_party/curl/lib/socketpair.c (revision 13498266)
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#if !defined(HAVE_SOCKETPAIR) && !defined(CURL_DISABLE_SOCKETPAIR)
31#ifdef _WIN32
32/*
33 * This is a socketpair() implementation for Windows.
34 */
35#include <string.h>
36#include <io.h>
37#else
38#ifdef HAVE_NETDB_H
39#include <netdb.h>
40#endif
41#ifdef HAVE_NETINET_IN_H
42#include <netinet/in.h> /* IPPROTO_TCP */
43#endif
44#ifdef HAVE_ARPA_INET_H
45#include <arpa/inet.h>
46#endif
47#ifndef INADDR_LOOPBACK
48#define INADDR_LOOPBACK 0x7f000001
49#endif /* !INADDR_LOOPBACK */
50#endif /* !_WIN32 */
51
52#include "nonblock.h" /* for curlx_nonblock */
53#include "timeval.h"  /* needed before select.h */
54#include "select.h"   /* for Curl_poll */
55
56/* The last 3 #include files should be in this order */
57#include "curl_printf.h"
58#include "curl_memory.h"
59#include "memdebug.h"
60
61int Curl_socketpair(int domain, int type, int protocol,
62                    curl_socket_t socks[2])
63{
64  union {
65    struct sockaddr_in inaddr;
66    struct sockaddr addr;
67  } a;
68  curl_socket_t listener;
69  curl_socklen_t addrlen = sizeof(a.inaddr);
70  int reuse = 1;
71  struct pollfd pfd[1];
72  (void)domain;
73  (void)type;
74  (void)protocol;
75
76  listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
77  if(listener == CURL_SOCKET_BAD)
78    return -1;
79
80  memset(&a, 0, sizeof(a));
81  a.inaddr.sin_family = AF_INET;
82  a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
83  a.inaddr.sin_port = 0;
84
85  socks[0] = socks[1] = CURL_SOCKET_BAD;
86
87#if defined(_WIN32) || defined(__CYGWIN__)
88  /* don't set SO_REUSEADDR on Windows */
89  (void)reuse;
90#ifdef SO_EXCLUSIVEADDRUSE
91  {
92    int exclusive = 1;
93    if(setsockopt(listener, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
94                  (char *)&exclusive, (curl_socklen_t)sizeof(exclusive)) == -1)
95      goto error;
96  }
97#endif
98#else
99  if(setsockopt(listener, SOL_SOCKET, SO_REUSEADDR,
100                (char *)&reuse, (curl_socklen_t)sizeof(reuse)) == -1)
101    goto error;
102#endif
103  if(bind(listener, &a.addr, sizeof(a.inaddr)) == -1)
104    goto error;
105  if(getsockname(listener, &a.addr, &addrlen) == -1 ||
106     addrlen < (int)sizeof(a.inaddr))
107    goto error;
108  if(listen(listener, 1) == -1)
109    goto error;
110  socks[0] = socket(AF_INET, SOCK_STREAM, 0);
111  if(socks[0] == CURL_SOCKET_BAD)
112    goto error;
113  if(connect(socks[0], &a.addr, sizeof(a.inaddr)) == -1)
114    goto error;
115
116  /* use non-blocking accept to make sure we don't block forever */
117  if(curlx_nonblock(listener, TRUE) < 0)
118    goto error;
119  pfd[0].fd = listener;
120  pfd[0].events = POLLIN;
121  pfd[0].revents = 0;
122  (void)Curl_poll(pfd, 1, 1000); /* one second */
123  socks[1] = accept(listener, NULL, NULL);
124  if(socks[1] == CURL_SOCKET_BAD)
125    goto error;
126  else {
127    struct curltime start = Curl_now();
128    char rnd[9];
129    char check[sizeof(rnd)];
130    char *p = &check[0];
131    size_t s = sizeof(check);
132
133    if(Curl_rand(NULL, (unsigned char *)rnd, sizeof(rnd)))
134      goto error;
135
136    /* write data to the socket */
137    swrite(socks[0], rnd, sizeof(rnd));
138    /* verify that we read the correct data */
139    do {
140      ssize_t nread;
141
142      pfd[0].fd = socks[1];
143      pfd[0].events = POLLIN;
144      pfd[0].revents = 0;
145      (void)Curl_poll(pfd, 1, 1000); /* one second */
146
147      nread = sread(socks[1], p, s);
148      if(nread == -1) {
149        int sockerr = SOCKERRNO;
150        /* Don't block forever */
151        if(Curl_timediff(Curl_now(), start) > (60 * 1000))
152          goto error;
153        if(
154#ifdef WSAEWOULDBLOCK
155          /* This is how Windows does it */
156          (WSAEWOULDBLOCK == sockerr)
157#else
158          /* errno may be EWOULDBLOCK or on some systems EAGAIN when it
159             returned due to its inability to send off data without
160             blocking. We therefore treat both error codes the same here */
161          (EWOULDBLOCK == sockerr) || (EAGAIN == sockerr) ||
162          (EINTR == sockerr) || (EINPROGRESS == sockerr)
163#endif
164          ) {
165          continue;
166        }
167        goto error;
168      }
169      s -= nread;
170      if(s) {
171        p += nread;
172        continue;
173      }
174      if(memcmp(rnd, check, sizeof(check)))
175        goto error;
176      break;
177    } while(1);
178  }
179
180  sclose(listener);
181  return 0;
182
183error:
184  sclose(listener);
185  sclose(socks[0]);
186  sclose(socks[1]);
187  return -1;
188}
189
190#endif /* ! HAVE_SOCKETPAIR */
191