xref: /curl/lib/socketpair.c (revision 03e7dff8)
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 
Curl_socketpair(int domain,int type,int protocol,curl_socket_t socks[2])61 int 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 
183 error:
184   sclose(listener);
185   sclose(socks[0]);
186   sclose(socks[1]);
187   return -1;
188 }
189 
190 #endif /* ! HAVE_SOCKETPAIR */
191