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