/*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | * / __| | | | |_) | | * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * * Copyright (C) 2019 - 2020, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at https://curl.se/docs/copyright.html. * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ***************************************************************************/ #include "curl_setup.h" #include "socketpair.h" #if !defined(HAVE_SOCKETPAIR) && !defined(CURL_DISABLE_SOCKETPAIR) #ifdef WIN32 /* * This is a socketpair() implementation for Windows. */ #include #include #include #include #include #else #ifdef HAVE_NETDB_H #include #endif #ifdef HAVE_NETINET_IN_H #include /* IPPROTO_TCP */ #endif #ifdef HAVE_ARPA_INET_H #include #endif #ifndef INADDR_LOOPBACK #define INADDR_LOOPBACK 0x7f000001 #endif /* !INADDR_LOOPBACK */ #endif /* !WIN32 */ #include "nonblock.h" /* for curlx_nonblock */ #include "timeval.h" /* needed before select.h */ #include "select.h" /* for Curl_poll */ /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" #include "memdebug.h" int Curl_socketpair(int domain, int type, int protocol, curl_socket_t socks[2]) { union { struct sockaddr_in inaddr; struct sockaddr addr; } a, a2; curl_socket_t listener; curl_socklen_t addrlen = sizeof(a.inaddr); int reuse = 1; struct pollfd pfd[1]; (void)domain; (void)type; (void)protocol; listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(listener == CURL_SOCKET_BAD) return -1; memset(&a, 0, sizeof(a)); a.inaddr.sin_family = AF_INET; a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); a.inaddr.sin_port = 0; socks[0] = socks[1] = CURL_SOCKET_BAD; if(setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, (curl_socklen_t)sizeof(reuse)) == -1) goto error; if(bind(listener, &a.addr, sizeof(a.inaddr)) == -1) goto error; if(getsockname(listener, &a.addr, &addrlen) == -1 || addrlen < (int)sizeof(a.inaddr)) goto error; if(listen(listener, 1) == -1) goto error; socks[0] = socket(AF_INET, SOCK_STREAM, 0); if(socks[0] == CURL_SOCKET_BAD) goto error; if(connect(socks[0], &a.addr, sizeof(a.inaddr)) == -1) goto error; /* use non-blocking accept to make sure we don't block forever */ if(curlx_nonblock(listener, TRUE) < 0) goto error; pfd[0].fd = listener; pfd[0].events = POLLIN; pfd[0].revents = 0; (void)Curl_poll(pfd, 1, 10*1000); /* 10 seconds */ socks[1] = accept(listener, NULL, NULL); if(socks[1] == CURL_SOCKET_BAD) goto error; /* verify that nothing else connected */ addrlen = sizeof(a.inaddr); if(getsockname(socks[0], &a.addr, &addrlen) == -1 || addrlen < (int)sizeof(a.inaddr)) goto error; addrlen = sizeof(a2.inaddr); if(getpeername(socks[1], &a2.addr, &addrlen) == -1 || addrlen < (int)sizeof(a2.inaddr)) goto error; if(a.inaddr.sin_family != a2.inaddr.sin_family || a.inaddr.sin_addr.s_addr != a2.inaddr.sin_addr.s_addr || a.inaddr.sin_port != a2.inaddr.sin_port) goto error; sclose(listener); return 0; error: sclose(listener); sclose(socks[0]); sclose(socks[1]); return -1; } #endif /* ! HAVE_SOCKETPAIR */