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