/*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | * / __| | | | |_) | | * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * * Copyright (C) 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. * * SPDX-License-Identifier: curl * ***************************************************************************/ #include "curl_setup.h" #include "socketpair.h" #include "urldata.h" #include "rand.h" #include "curlx/nonblock.h" #ifndef CURL_DISABLE_SOCKETPAIR /* choose implementation */ #ifdef USE_EVENTFD #include static int wakeup_eventfd(curl_socket_t socks[2], bool nonblocking) { int efd = eventfd(0, nonblocking ? EFD_CLOEXEC | EFD_NONBLOCK : EFD_CLOEXEC); if(efd == -1) { socks[0] = socks[1] = CURL_SOCKET_BAD; return -1; } socks[0] = socks[1] = efd; return 0; } #elif defined(HAVE_PIPE) #ifdef HAVE_FCNTL #include #endif static int wakeup_pipe(curl_socket_t socks[2], bool nonblocking) { #ifdef HAVE_PIPE2 int flags = nonblocking ? O_NONBLOCK | O_CLOEXEC : O_CLOEXEC; if(pipe2(socks, flags)) return -1; #else if(pipe(socks)) return -1; #ifdef HAVE_FCNTL if(fcntl(socks[0], F_SETFD, FD_CLOEXEC) || fcntl(socks[1], F_SETFD, FD_CLOEXEC)) { sclose(socks[0]); sclose(socks[1]); socks[0] = socks[1] = CURL_SOCKET_BAD; return -1; } #endif if(nonblocking) { if(curlx_nonblock(socks[0], TRUE) < 0 || curlx_nonblock(socks[1], TRUE) < 0) { sclose(socks[0]); sclose(socks[1]); socks[0] = socks[1] = CURL_SOCKET_BAD; return -1; } } #endif return 0; } #elif defined(HAVE_SOCKETPAIR) /* !USE_EVENTFD && !HAVE_PIPE */ #ifndef USE_UNIX_SOCKETS #error "unsupported Unix domain and socketpair build combo" #endif static int wakeup_socketpair(curl_socket_t socks[2], bool nonblocking) { int type = SOCK_STREAM; #ifdef SOCK_CLOEXEC type |= SOCK_CLOEXEC; #endif #ifdef SOCK_NONBLOCK if(nonblocking) type |= SOCK_NONBLOCK; #endif if(CURL_SOCKETPAIR(AF_UNIX, type, 0, socks)) return -1; #ifndef SOCK_NONBLOCK if(nonblocking) { if(curlx_nonblock(socks[0], TRUE) < 0 || curlx_nonblock(socks[1], TRUE) < 0) { sclose(socks[0]); sclose(socks[1]); socks[0] = socks[1] = CURL_SOCKET_BAD; return -1; } } #endif #ifdef USE_SO_NOSIGPIPE if(Curl_sock_nosigpipe(socks[1]) < 0) { sclose(socks[0]); sclose(socks[1]); socks[0] = socks[1] = CURL_SOCKET_BAD; return -1; } #endif /* USE_SO_NOSIGPIPE */ return 0; } #else /* !USE_EVENTFD && !HAVE_PIPE && !HAVE_SOCKETPAIR */ #ifdef HAVE_NETDB_H #include #endif #ifdef HAVE_NETINET_IN_H #include /* for IPPROTO_TCP */ #endif #ifdef HAVE_ARPA_INET_H #include #endif #ifndef INADDR_LOOPBACK #define INADDR_LOOPBACK 0x7f000001 #endif #include "select.h" /* for Curl_poll */ static int wakeup_inet(curl_socket_t socks[2], bool nonblocking) { union { struct sockaddr_in inaddr; struct sockaddr addr; } a; curl_socket_t listener; curl_socklen_t addrlen = sizeof(a.inaddr); int reuse = 1; struct pollfd pfd[1]; listener = CURL_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 defined(_WIN32) || defined(__CYGWIN__) /* do not set SO_REUSEADDR on Windows */ (void)reuse; #ifdef SO_EXCLUSIVEADDRUSE { int exclusive = 1; if(setsockopt(listener, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char *)&exclusive, (curl_socklen_t)sizeof(exclusive)) == -1) goto error; } #endif #else if(setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, (curl_socklen_t)sizeof(reuse)) == -1) goto error; #endif 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] = CURL_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 do not 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, 1000); /* one second */ socks[1] = CURL_ACCEPT(listener, NULL, NULL); if(socks[1] == CURL_SOCKET_BAD) goto error; else { struct curltime start = curlx_now(); char rnd[9]; char check[sizeof(rnd)]; char *p = &check[0]; size_t s = sizeof(check); if(Curl_rand(NULL, (unsigned char *)rnd, sizeof(rnd))) goto error; /* write data to the socket */ swrite(socks[0], rnd, sizeof(rnd)); /* verify that we read the correct data */ do { ssize_t nread; pfd[0].fd = socks[1]; pfd[0].events = POLLIN; pfd[0].revents = 0; (void)Curl_poll(pfd, 1, 1000); /* one second */ nread = sread(socks[1], p, s); if(nread == -1) { int sockerr = SOCKERRNO; /* Do not block forever */ if(curlx_timediff_ms(curlx_now(), start) > (60 * 1000)) goto error; if(SOCK_EAGAIN(sockerr) #ifndef USE_WINSOCK || (sockerr == SOCKEINTR) || (sockerr == SOCKEINPROGRESS) #endif ) { continue; } goto error; } s -= nread; if(s) { p += nread; continue; } if(memcmp(rnd, check, sizeof(check))) goto error; break; } while(1); } if(nonblocking) if(curlx_nonblock(socks[0], TRUE) < 0 || curlx_nonblock(socks[1], TRUE) < 0) goto error; #ifdef USE_SO_NOSIGPIPE if(Curl_sock_nosigpipe(socks[1]) < 0) goto error; #endif sclose(listener); return 0; error: sclose(listener); sclose(socks[0]); sclose(socks[1]); socks[0] = socks[1] = CURL_SOCKET_BAD; return -1; } #endif /* choose implementation */ int Curl_wakeup_init(curl_socket_t socks[2], bool nonblocking) { #ifdef USE_EVENTFD return wakeup_eventfd(socks, nonblocking); #elif defined(HAVE_PIPE) return wakeup_pipe(socks, nonblocking); #elif defined(HAVE_SOCKETPAIR) return wakeup_socketpair(socks, nonblocking); #else return wakeup_inet(socks, nonblocking); #endif } #if defined(USE_EVENTFD) || defined(HAVE_PIPE) #define wakeup_write write #define wakeup_read read #define wakeup_close close #else /* !USE_EVENTFD && !HAVE_PIPE */ #define wakeup_write swrite #define wakeup_read sread #define wakeup_close sclose #endif int Curl_wakeup_signal(curl_socket_t socks[2]) { int err = 0; #ifdef USE_EVENTFD const uint64_t buf[1] = { 1 }; #else const char buf[1] = { 1 }; #endif while(1) { err = 0; if(wakeup_write(socks[1], buf, sizeof(buf)) < 0) { err = SOCKERRNO; #ifndef USE_WINSOCK if(err == SOCKEINTR) continue; #endif if(SOCK_EAGAIN(err)) err = 0; /* wakeup is already ongoing */ } break; } return err; } CURLcode Curl_wakeup_consume(curl_socket_t socks[2], bool all) { char buf[64]; ssize_t rc; CURLcode result = CURLE_OK; do { rc = wakeup_read(socks[0], buf, sizeof(buf)); if(!rc) break; else if(rc < 0) { int sockerr = SOCKERRNO; #ifndef USE_WINSOCK if(sockerr == SOCKEINTR) continue; #endif if(SOCK_EAGAIN(sockerr)) break; result = CURLE_READ_ERROR; break; } } while(all); return result; } void Curl_wakeup_destroy(curl_socket_t socks[2]) { #ifndef USE_EVENTFD if(socks[1] != CURL_SOCKET_BAD) wakeup_close(socks[1]); #endif if(socks[0] != CURL_SOCKET_BAD) wakeup_close(socks[0]); socks[0] = socks[1] = CURL_SOCKET_BAD; } #endif /* !CURL_DISABLE_SOCKETPAIR */