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