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