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