113498266Sopenharmony_ci/*************************************************************************** 213498266Sopenharmony_ci * _ _ ____ _ 313498266Sopenharmony_ci * Project ___| | | | _ \| | 413498266Sopenharmony_ci * / __| | | | |_) | | 513498266Sopenharmony_ci * | (__| |_| | _ <| |___ 613498266Sopenharmony_ci * \___|\___/|_| \_\_____| 713498266Sopenharmony_ci * 813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 913498266Sopenharmony_ci * 1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which 1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms 1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html. 1313498266Sopenharmony_ci * 1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell 1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is 1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file. 1713498266Sopenharmony_ci * 1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 1913498266Sopenharmony_ci * KIND, either express or implied. 2013498266Sopenharmony_ci * 2113498266Sopenharmony_ci * SPDX-License-Identifier: curl 2213498266Sopenharmony_ci * 2313498266Sopenharmony_ci ***************************************************************************/ 2413498266Sopenharmony_ci 2513498266Sopenharmony_ci#include "curl_setup.h" 2613498266Sopenharmony_ci 2713498266Sopenharmony_ci#ifdef HAVE_NETINET_IN_H 2813498266Sopenharmony_ci#include <netinet/in.h> /* <netinet/tcp.h> may need it */ 2913498266Sopenharmony_ci#endif 3013498266Sopenharmony_ci#ifdef HAVE_SYS_UN_H 3113498266Sopenharmony_ci#include <sys/un.h> /* for sockaddr_un */ 3213498266Sopenharmony_ci#endif 3313498266Sopenharmony_ci#ifdef HAVE_LINUX_TCP_H 3413498266Sopenharmony_ci#include <linux/tcp.h> 3513498266Sopenharmony_ci#elif defined(HAVE_NETINET_TCP_H) 3613498266Sopenharmony_ci#include <netinet/tcp.h> 3713498266Sopenharmony_ci#endif 3813498266Sopenharmony_ci#ifdef HAVE_SYS_IOCTL_H 3913498266Sopenharmony_ci#include <sys/ioctl.h> 4013498266Sopenharmony_ci#endif 4113498266Sopenharmony_ci#ifdef HAVE_NETDB_H 4213498266Sopenharmony_ci#include <netdb.h> 4313498266Sopenharmony_ci#endif 4413498266Sopenharmony_ci#ifdef HAVE_FCNTL_H 4513498266Sopenharmony_ci#include <fcntl.h> 4613498266Sopenharmony_ci#endif 4713498266Sopenharmony_ci#ifdef HAVE_ARPA_INET_H 4813498266Sopenharmony_ci#include <arpa/inet.h> 4913498266Sopenharmony_ci#endif 5013498266Sopenharmony_ci 5113498266Sopenharmony_ci#ifdef __VMS 5213498266Sopenharmony_ci#include <in.h> 5313498266Sopenharmony_ci#include <inet.h> 5413498266Sopenharmony_ci#endif 5513498266Sopenharmony_ci 5613498266Sopenharmony_ci#include "urldata.h" 5713498266Sopenharmony_ci#include "bufq.h" 5813498266Sopenharmony_ci#include "sendf.h" 5913498266Sopenharmony_ci#include "if2ip.h" 6013498266Sopenharmony_ci#include "strerror.h" 6113498266Sopenharmony_ci#include "cfilters.h" 6213498266Sopenharmony_ci#include "cf-socket.h" 6313498266Sopenharmony_ci#include "connect.h" 6413498266Sopenharmony_ci#include "select.h" 6513498266Sopenharmony_ci#include "url.h" /* for Curl_safefree() */ 6613498266Sopenharmony_ci#include "multiif.h" 6713498266Sopenharmony_ci#include "sockaddr.h" /* required for Curl_sockaddr_storage */ 6813498266Sopenharmony_ci#include "inet_ntop.h" 6913498266Sopenharmony_ci#include "inet_pton.h" 7013498266Sopenharmony_ci#include "progress.h" 7113498266Sopenharmony_ci#include "warnless.h" 7213498266Sopenharmony_ci#include "conncache.h" 7313498266Sopenharmony_ci#include "multihandle.h" 7413498266Sopenharmony_ci#include "rand.h" 7513498266Sopenharmony_ci#include "share.h" 7613498266Sopenharmony_ci#include "version_win32.h" 7713498266Sopenharmony_ci 7813498266Sopenharmony_ci/* The last 3 #include files should be in this order */ 7913498266Sopenharmony_ci#include "curl_printf.h" 8013498266Sopenharmony_ci#include "curl_memory.h" 8113498266Sopenharmony_ci#include "memdebug.h" 8213498266Sopenharmony_ci 8313498266Sopenharmony_ci 8413498266Sopenharmony_ci#if defined(ENABLE_IPV6) && defined(IPV6_V6ONLY) && defined(_WIN32) 8513498266Sopenharmony_ci/* It makes support for IPv4-mapped IPv6 addresses. 8613498266Sopenharmony_ci * Linux kernel, NetBSD, FreeBSD and Darwin: default is off; 8713498266Sopenharmony_ci * Windows Vista and later: default is on; 8813498266Sopenharmony_ci * DragonFly BSD: acts like off, and dummy setting; 8913498266Sopenharmony_ci * OpenBSD and earlier Windows: unsupported. 9013498266Sopenharmony_ci * Linux: controlled by /proc/sys/net/ipv6/bindv6only. 9113498266Sopenharmony_ci */ 9213498266Sopenharmony_cistatic void set_ipv6_v6only(curl_socket_t sockfd, int on) 9313498266Sopenharmony_ci{ 9413498266Sopenharmony_ci (void)setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&on, sizeof(on)); 9513498266Sopenharmony_ci} 9613498266Sopenharmony_ci#else 9713498266Sopenharmony_ci#define set_ipv6_v6only(x,y) 9813498266Sopenharmony_ci#endif 9913498266Sopenharmony_ci 10013498266Sopenharmony_cistatic void tcpnodelay(struct Curl_easy *data, curl_socket_t sockfd) 10113498266Sopenharmony_ci{ 10213498266Sopenharmony_ci#if defined(TCP_NODELAY) 10313498266Sopenharmony_ci curl_socklen_t onoff = (curl_socklen_t) 1; 10413498266Sopenharmony_ci int level = IPPROTO_TCP; 10513498266Sopenharmony_ci char buffer[STRERROR_LEN]; 10613498266Sopenharmony_ci 10713498266Sopenharmony_ci if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff, 10813498266Sopenharmony_ci sizeof(onoff)) < 0) 10913498266Sopenharmony_ci infof(data, "Could not set TCP_NODELAY: %s", 11013498266Sopenharmony_ci Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); 11113498266Sopenharmony_ci#else 11213498266Sopenharmony_ci (void)data; 11313498266Sopenharmony_ci (void)sockfd; 11413498266Sopenharmony_ci#endif 11513498266Sopenharmony_ci} 11613498266Sopenharmony_ci 11713498266Sopenharmony_ci#ifdef SO_NOSIGPIPE 11813498266Sopenharmony_ci/* The preferred method on Mac OS X (10.2 and later) to prevent SIGPIPEs when 11913498266Sopenharmony_ci sending data to a dead peer (instead of relying on the 4th argument to send 12013498266Sopenharmony_ci being MSG_NOSIGNAL). Possibly also existing and in use on other BSD 12113498266Sopenharmony_ci systems? */ 12213498266Sopenharmony_cistatic void nosigpipe(struct Curl_easy *data, 12313498266Sopenharmony_ci curl_socket_t sockfd) 12413498266Sopenharmony_ci{ 12513498266Sopenharmony_ci int onoff = 1; 12613498266Sopenharmony_ci (void)data; 12713498266Sopenharmony_ci if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&onoff, 12813498266Sopenharmony_ci sizeof(onoff)) < 0) { 12913498266Sopenharmony_ci#if !defined(CURL_DISABLE_VERBOSE_STRINGS) 13013498266Sopenharmony_ci char buffer[STRERROR_LEN]; 13113498266Sopenharmony_ci infof(data, "Could not set SO_NOSIGPIPE: %s", 13213498266Sopenharmony_ci Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); 13313498266Sopenharmony_ci#endif 13413498266Sopenharmony_ci } 13513498266Sopenharmony_ci} 13613498266Sopenharmony_ci#else 13713498266Sopenharmony_ci#define nosigpipe(x,y) Curl_nop_stmt 13813498266Sopenharmony_ci#endif 13913498266Sopenharmony_ci 14013498266Sopenharmony_ci#if defined(__DragonFly__) || defined(USE_WINSOCK) 14113498266Sopenharmony_ci/* DragonFlyBSD and Windows use millisecond units */ 14213498266Sopenharmony_ci#define KEEPALIVE_FACTOR(x) (x *= 1000) 14313498266Sopenharmony_ci#else 14413498266Sopenharmony_ci#define KEEPALIVE_FACTOR(x) 14513498266Sopenharmony_ci#endif 14613498266Sopenharmony_ci 14713498266Sopenharmony_ci#if defined(USE_WINSOCK) && !defined(SIO_KEEPALIVE_VALS) 14813498266Sopenharmony_ci#define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR,4) 14913498266Sopenharmony_ci 15013498266Sopenharmony_cistruct tcp_keepalive { 15113498266Sopenharmony_ci u_long onoff; 15213498266Sopenharmony_ci u_long keepalivetime; 15313498266Sopenharmony_ci u_long keepaliveinterval; 15413498266Sopenharmony_ci}; 15513498266Sopenharmony_ci#endif 15613498266Sopenharmony_ci 15713498266Sopenharmony_cistatic void 15813498266Sopenharmony_citcpkeepalive(struct Curl_easy *data, 15913498266Sopenharmony_ci curl_socket_t sockfd) 16013498266Sopenharmony_ci{ 16113498266Sopenharmony_ci int optval = data->set.tcp_keepalive?1:0; 16213498266Sopenharmony_ci 16313498266Sopenharmony_ci /* only set IDLE and INTVL if setting KEEPALIVE is successful */ 16413498266Sopenharmony_ci if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, 16513498266Sopenharmony_ci (void *)&optval, sizeof(optval)) < 0) { 16613498266Sopenharmony_ci infof(data, "Failed to set SO_KEEPALIVE on fd " 16713498266Sopenharmony_ci "%" CURL_FORMAT_SOCKET_T ": errno %d", 16813498266Sopenharmony_ci sockfd, SOCKERRNO); 16913498266Sopenharmony_ci } 17013498266Sopenharmony_ci else { 17113498266Sopenharmony_ci#if defined(SIO_KEEPALIVE_VALS) 17213498266Sopenharmony_ci struct tcp_keepalive vals; 17313498266Sopenharmony_ci DWORD dummy; 17413498266Sopenharmony_ci vals.onoff = 1; 17513498266Sopenharmony_ci optval = curlx_sltosi(data->set.tcp_keepidle); 17613498266Sopenharmony_ci KEEPALIVE_FACTOR(optval); 17713498266Sopenharmony_ci vals.keepalivetime = optval; 17813498266Sopenharmony_ci optval = curlx_sltosi(data->set.tcp_keepintvl); 17913498266Sopenharmony_ci KEEPALIVE_FACTOR(optval); 18013498266Sopenharmony_ci vals.keepaliveinterval = optval; 18113498266Sopenharmony_ci if(WSAIoctl(sockfd, SIO_KEEPALIVE_VALS, (LPVOID) &vals, sizeof(vals), 18213498266Sopenharmony_ci NULL, 0, &dummy, NULL, NULL) != 0) { 18313498266Sopenharmony_ci infof(data, "Failed to set SIO_KEEPALIVE_VALS on fd " 18413498266Sopenharmony_ci "%" CURL_FORMAT_SOCKET_T ": errno %d", 18513498266Sopenharmony_ci sockfd, SOCKERRNO); 18613498266Sopenharmony_ci } 18713498266Sopenharmony_ci#else 18813498266Sopenharmony_ci#ifdef TCP_KEEPIDLE 18913498266Sopenharmony_ci optval = curlx_sltosi(data->set.tcp_keepidle); 19013498266Sopenharmony_ci KEEPALIVE_FACTOR(optval); 19113498266Sopenharmony_ci if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, 19213498266Sopenharmony_ci (void *)&optval, sizeof(optval)) < 0) { 19313498266Sopenharmony_ci infof(data, "Failed to set TCP_KEEPIDLE on fd " 19413498266Sopenharmony_ci "%" CURL_FORMAT_SOCKET_T ": errno %d", 19513498266Sopenharmony_ci sockfd, SOCKERRNO); 19613498266Sopenharmony_ci } 19713498266Sopenharmony_ci#elif defined(TCP_KEEPALIVE) 19813498266Sopenharmony_ci /* Mac OS X style */ 19913498266Sopenharmony_ci optval = curlx_sltosi(data->set.tcp_keepidle); 20013498266Sopenharmony_ci KEEPALIVE_FACTOR(optval); 20113498266Sopenharmony_ci if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE, 20213498266Sopenharmony_ci (void *)&optval, sizeof(optval)) < 0) { 20313498266Sopenharmony_ci infof(data, "Failed to set TCP_KEEPALIVE on fd " 20413498266Sopenharmony_ci "%" CURL_FORMAT_SOCKET_T ": errno %d", 20513498266Sopenharmony_ci sockfd, SOCKERRNO); 20613498266Sopenharmony_ci } 20713498266Sopenharmony_ci#endif 20813498266Sopenharmony_ci#ifdef TCP_KEEPINTVL 20913498266Sopenharmony_ci optval = curlx_sltosi(data->set.tcp_keepintvl); 21013498266Sopenharmony_ci KEEPALIVE_FACTOR(optval); 21113498266Sopenharmony_ci if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL, 21213498266Sopenharmony_ci (void *)&optval, sizeof(optval)) < 0) { 21313498266Sopenharmony_ci infof(data, "Failed to set TCP_KEEPINTVL on fd " 21413498266Sopenharmony_ci "%" CURL_FORMAT_SOCKET_T ": errno %d", 21513498266Sopenharmony_ci sockfd, SOCKERRNO); 21613498266Sopenharmony_ci } 21713498266Sopenharmony_ci#endif 21813498266Sopenharmony_ci#endif 21913498266Sopenharmony_ci } 22013498266Sopenharmony_ci} 22113498266Sopenharmony_ci 22213498266Sopenharmony_ci/** 22313498266Sopenharmony_ci * Assign the address `ai` to the Curl_sockaddr_ex `dest` and 22413498266Sopenharmony_ci * set the transport used. 22513498266Sopenharmony_ci */ 22613498266Sopenharmony_civoid Curl_sock_assign_addr(struct Curl_sockaddr_ex *dest, 22713498266Sopenharmony_ci const struct Curl_addrinfo *ai, 22813498266Sopenharmony_ci int transport) 22913498266Sopenharmony_ci{ 23013498266Sopenharmony_ci /* 23113498266Sopenharmony_ci * The Curl_sockaddr_ex structure is basically libcurl's external API 23213498266Sopenharmony_ci * curl_sockaddr structure with enough space available to directly hold 23313498266Sopenharmony_ci * any protocol-specific address structures. The variable declared here 23413498266Sopenharmony_ci * will be used to pass / receive data to/from the fopensocket callback 23513498266Sopenharmony_ci * if this has been set, before that, it is initialized from parameters. 23613498266Sopenharmony_ci */ 23713498266Sopenharmony_ci dest->family = ai->ai_family; 23813498266Sopenharmony_ci switch(transport) { 23913498266Sopenharmony_ci case TRNSPRT_TCP: 24013498266Sopenharmony_ci dest->socktype = SOCK_STREAM; 24113498266Sopenharmony_ci dest->protocol = IPPROTO_TCP; 24213498266Sopenharmony_ci break; 24313498266Sopenharmony_ci case TRNSPRT_UNIX: 24413498266Sopenharmony_ci dest->socktype = SOCK_STREAM; 24513498266Sopenharmony_ci dest->protocol = IPPROTO_IP; 24613498266Sopenharmony_ci break; 24713498266Sopenharmony_ci default: /* UDP and QUIC */ 24813498266Sopenharmony_ci dest->socktype = SOCK_DGRAM; 24913498266Sopenharmony_ci dest->protocol = IPPROTO_UDP; 25013498266Sopenharmony_ci break; 25113498266Sopenharmony_ci } 25213498266Sopenharmony_ci dest->addrlen = ai->ai_addrlen; 25313498266Sopenharmony_ci 25413498266Sopenharmony_ci if(dest->addrlen > sizeof(struct Curl_sockaddr_storage)) 25513498266Sopenharmony_ci dest->addrlen = sizeof(struct Curl_sockaddr_storage); 25613498266Sopenharmony_ci memcpy(&dest->sa_addr, ai->ai_addr, dest->addrlen); 25713498266Sopenharmony_ci} 25813498266Sopenharmony_ci 25913498266Sopenharmony_cistatic CURLcode socket_open(struct Curl_easy *data, 26013498266Sopenharmony_ci struct Curl_sockaddr_ex *addr, 26113498266Sopenharmony_ci curl_socket_t *sockfd) 26213498266Sopenharmony_ci{ 26313498266Sopenharmony_ci DEBUGASSERT(data); 26413498266Sopenharmony_ci DEBUGASSERT(data->conn); 26513498266Sopenharmony_ci if(data->set.fopensocket) { 26613498266Sopenharmony_ci /* 26713498266Sopenharmony_ci * If the opensocket callback is set, all the destination address 26813498266Sopenharmony_ci * information is passed to the callback. Depending on this information the 26913498266Sopenharmony_ci * callback may opt to abort the connection, this is indicated returning 27013498266Sopenharmony_ci * CURL_SOCKET_BAD; otherwise it will return a not-connected socket. When 27113498266Sopenharmony_ci * the callback returns a valid socket the destination address information 27213498266Sopenharmony_ci * might have been changed and this 'new' address will actually be used 27313498266Sopenharmony_ci * here to connect. 27413498266Sopenharmony_ci */ 27513498266Sopenharmony_ci Curl_set_in_callback(data, true); 27613498266Sopenharmony_ci *sockfd = data->set.fopensocket(data->set.opensocket_client, 27713498266Sopenharmony_ci CURLSOCKTYPE_IPCXN, 27813498266Sopenharmony_ci (struct curl_sockaddr *)addr); 27913498266Sopenharmony_ci Curl_set_in_callback(data, false); 28013498266Sopenharmony_ci } 28113498266Sopenharmony_ci else { 28213498266Sopenharmony_ci /* opensocket callback not set, so simply create the socket now */ 28313498266Sopenharmony_ci *sockfd = socket(addr->family, addr->socktype, addr->protocol); 28413498266Sopenharmony_ci } 28513498266Sopenharmony_ci 28613498266Sopenharmony_ci if(*sockfd == CURL_SOCKET_BAD) 28713498266Sopenharmony_ci /* no socket, no connection */ 28813498266Sopenharmony_ci return CURLE_COULDNT_CONNECT; 28913498266Sopenharmony_ci 29013498266Sopenharmony_ci#if defined(ENABLE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID) 29113498266Sopenharmony_ci if(data->conn->scope_id && (addr->family == AF_INET6)) { 29213498266Sopenharmony_ci struct sockaddr_in6 * const sa6 = (void *)&addr->sa_addr; 29313498266Sopenharmony_ci sa6->sin6_scope_id = data->conn->scope_id; 29413498266Sopenharmony_ci } 29513498266Sopenharmony_ci#endif 29613498266Sopenharmony_ci return CURLE_OK; 29713498266Sopenharmony_ci} 29813498266Sopenharmony_ci 29913498266Sopenharmony_ci/* 30013498266Sopenharmony_ci * Create a socket based on info from 'conn' and 'ai'. 30113498266Sopenharmony_ci * 30213498266Sopenharmony_ci * 'addr' should be a pointer to the correct struct to get data back, or NULL. 30313498266Sopenharmony_ci * 'sockfd' must be a pointer to a socket descriptor. 30413498266Sopenharmony_ci * 30513498266Sopenharmony_ci * If the open socket callback is set, used that! 30613498266Sopenharmony_ci * 30713498266Sopenharmony_ci */ 30813498266Sopenharmony_ciCURLcode Curl_socket_open(struct Curl_easy *data, 30913498266Sopenharmony_ci const struct Curl_addrinfo *ai, 31013498266Sopenharmony_ci struct Curl_sockaddr_ex *addr, 31113498266Sopenharmony_ci int transport, 31213498266Sopenharmony_ci curl_socket_t *sockfd) 31313498266Sopenharmony_ci{ 31413498266Sopenharmony_ci struct Curl_sockaddr_ex dummy; 31513498266Sopenharmony_ci 31613498266Sopenharmony_ci if(!addr) 31713498266Sopenharmony_ci /* if the caller doesn't want info back, use a local temp copy */ 31813498266Sopenharmony_ci addr = &dummy; 31913498266Sopenharmony_ci 32013498266Sopenharmony_ci Curl_sock_assign_addr(addr, ai, transport); 32113498266Sopenharmony_ci return socket_open(data, addr, sockfd); 32213498266Sopenharmony_ci} 32313498266Sopenharmony_ci 32413498266Sopenharmony_cistatic int socket_close(struct Curl_easy *data, struct connectdata *conn, 32513498266Sopenharmony_ci int use_callback, curl_socket_t sock) 32613498266Sopenharmony_ci{ 32713498266Sopenharmony_ci if(use_callback && conn && conn->fclosesocket) { 32813498266Sopenharmony_ci int rc; 32913498266Sopenharmony_ci Curl_multi_closed(data, sock); 33013498266Sopenharmony_ci Curl_set_in_callback(data, true); 33113498266Sopenharmony_ci rc = conn->fclosesocket(conn->closesocket_client, sock); 33213498266Sopenharmony_ci Curl_set_in_callback(data, false); 33313498266Sopenharmony_ci return rc; 33413498266Sopenharmony_ci } 33513498266Sopenharmony_ci 33613498266Sopenharmony_ci if(conn) 33713498266Sopenharmony_ci /* tell the multi-socket code about this */ 33813498266Sopenharmony_ci Curl_multi_closed(data, sock); 33913498266Sopenharmony_ci 34013498266Sopenharmony_ci sclose(sock); 34113498266Sopenharmony_ci 34213498266Sopenharmony_ci return 0; 34313498266Sopenharmony_ci} 34413498266Sopenharmony_ci 34513498266Sopenharmony_ci/* 34613498266Sopenharmony_ci * Close a socket. 34713498266Sopenharmony_ci * 34813498266Sopenharmony_ci * 'conn' can be NULL, beware! 34913498266Sopenharmony_ci */ 35013498266Sopenharmony_ciint Curl_socket_close(struct Curl_easy *data, struct connectdata *conn, 35113498266Sopenharmony_ci curl_socket_t sock) 35213498266Sopenharmony_ci{ 35313498266Sopenharmony_ci return socket_close(data, conn, FALSE, sock); 35413498266Sopenharmony_ci} 35513498266Sopenharmony_ci 35613498266Sopenharmony_ci#ifdef USE_WINSOCK 35713498266Sopenharmony_ci/* When you run a program that uses the Windows Sockets API, you may 35813498266Sopenharmony_ci experience slow performance when you copy data to a TCP server. 35913498266Sopenharmony_ci 36013498266Sopenharmony_ci https://support.microsoft.com/kb/823764 36113498266Sopenharmony_ci 36213498266Sopenharmony_ci Work-around: Make the Socket Send Buffer Size Larger Than the Program Send 36313498266Sopenharmony_ci Buffer Size 36413498266Sopenharmony_ci 36513498266Sopenharmony_ci The problem described in this knowledge-base is applied only to pre-Vista 36613498266Sopenharmony_ci Windows. Following function trying to detect OS version and skips 36713498266Sopenharmony_ci SO_SNDBUF adjustment for Windows Vista and above. 36813498266Sopenharmony_ci*/ 36913498266Sopenharmony_ci#define DETECT_OS_NONE 0 37013498266Sopenharmony_ci#define DETECT_OS_PREVISTA 1 37113498266Sopenharmony_ci#define DETECT_OS_VISTA_OR_LATER 2 37213498266Sopenharmony_ci 37313498266Sopenharmony_civoid Curl_sndbufset(curl_socket_t sockfd) 37413498266Sopenharmony_ci{ 37513498266Sopenharmony_ci int val = CURL_MAX_WRITE_SIZE + 32; 37613498266Sopenharmony_ci int curval = 0; 37713498266Sopenharmony_ci int curlen = sizeof(curval); 37813498266Sopenharmony_ci 37913498266Sopenharmony_ci static int detectOsState = DETECT_OS_NONE; 38013498266Sopenharmony_ci 38113498266Sopenharmony_ci if(detectOsState == DETECT_OS_NONE) { 38213498266Sopenharmony_ci if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT, 38313498266Sopenharmony_ci VERSION_GREATER_THAN_EQUAL)) 38413498266Sopenharmony_ci detectOsState = DETECT_OS_VISTA_OR_LATER; 38513498266Sopenharmony_ci else 38613498266Sopenharmony_ci detectOsState = DETECT_OS_PREVISTA; 38713498266Sopenharmony_ci } 38813498266Sopenharmony_ci 38913498266Sopenharmony_ci if(detectOsState == DETECT_OS_VISTA_OR_LATER) 39013498266Sopenharmony_ci return; 39113498266Sopenharmony_ci 39213498266Sopenharmony_ci if(getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&curval, &curlen) == 0) 39313498266Sopenharmony_ci if(curval > val) 39413498266Sopenharmony_ci return; 39513498266Sopenharmony_ci 39613498266Sopenharmony_ci setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&val, sizeof(val)); 39713498266Sopenharmony_ci} 39813498266Sopenharmony_ci#endif 39913498266Sopenharmony_ci 40013498266Sopenharmony_ci#ifndef CURL_DISABLE_BINDLOCAL 40113498266Sopenharmony_cistatic CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn, 40213498266Sopenharmony_ci curl_socket_t sockfd, int af, unsigned int scope) 40313498266Sopenharmony_ci{ 40413498266Sopenharmony_ci struct Curl_sockaddr_storage sa; 40513498266Sopenharmony_ci struct sockaddr *sock = (struct sockaddr *)&sa; /* bind to this address */ 40613498266Sopenharmony_ci curl_socklen_t sizeof_sa = 0; /* size of the data sock points to */ 40713498266Sopenharmony_ci struct sockaddr_in *si4 = (struct sockaddr_in *)&sa; 40813498266Sopenharmony_ci#ifdef ENABLE_IPV6 40913498266Sopenharmony_ci struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&sa; 41013498266Sopenharmony_ci#endif 41113498266Sopenharmony_ci 41213498266Sopenharmony_ci struct Curl_dns_entry *h = NULL; 41313498266Sopenharmony_ci unsigned short port = data->set.localport; /* use this port number, 0 for 41413498266Sopenharmony_ci "random" */ 41513498266Sopenharmony_ci /* how many port numbers to try to bind to, increasing one at a time */ 41613498266Sopenharmony_ci int portnum = data->set.localportrange; 41713498266Sopenharmony_ci const char *dev = data->set.str[STRING_DEVICE]; 41813498266Sopenharmony_ci int error; 41913498266Sopenharmony_ci#ifdef IP_BIND_ADDRESS_NO_PORT 42013498266Sopenharmony_ci int on = 1; 42113498266Sopenharmony_ci#endif 42213498266Sopenharmony_ci#ifndef ENABLE_IPV6 42313498266Sopenharmony_ci (void)scope; 42413498266Sopenharmony_ci#endif 42513498266Sopenharmony_ci 42613498266Sopenharmony_ci /************************************************************* 42713498266Sopenharmony_ci * Select device to bind socket to 42813498266Sopenharmony_ci *************************************************************/ 42913498266Sopenharmony_ci if(!dev && !port) 43013498266Sopenharmony_ci /* no local kind of binding was requested */ 43113498266Sopenharmony_ci return CURLE_OK; 43213498266Sopenharmony_ci 43313498266Sopenharmony_ci memset(&sa, 0, sizeof(struct Curl_sockaddr_storage)); 43413498266Sopenharmony_ci 43513498266Sopenharmony_ci if(dev && (strlen(dev)<255) ) { 43613498266Sopenharmony_ci char myhost[256] = ""; 43713498266Sopenharmony_ci int done = 0; /* -1 for error, 1 for address found */ 43813498266Sopenharmony_ci bool is_interface = FALSE; 43913498266Sopenharmony_ci bool is_host = FALSE; 44013498266Sopenharmony_ci static const char *if_prefix = "if!"; 44113498266Sopenharmony_ci static const char *host_prefix = "host!"; 44213498266Sopenharmony_ci 44313498266Sopenharmony_ci if(strncmp(if_prefix, dev, strlen(if_prefix)) == 0) { 44413498266Sopenharmony_ci dev += strlen(if_prefix); 44513498266Sopenharmony_ci is_interface = TRUE; 44613498266Sopenharmony_ci } 44713498266Sopenharmony_ci else if(strncmp(host_prefix, dev, strlen(host_prefix)) == 0) { 44813498266Sopenharmony_ci dev += strlen(host_prefix); 44913498266Sopenharmony_ci is_host = TRUE; 45013498266Sopenharmony_ci } 45113498266Sopenharmony_ci 45213498266Sopenharmony_ci /* interface */ 45313498266Sopenharmony_ci if(!is_host) { 45413498266Sopenharmony_ci#ifdef SO_BINDTODEVICE 45513498266Sopenharmony_ci /* 45613498266Sopenharmony_ci * This binds the local socket to a particular interface. This will 45713498266Sopenharmony_ci * force even requests to other local interfaces to go out the external 45813498266Sopenharmony_ci * interface. Only bind to the interface when specified as interface, 45913498266Sopenharmony_ci * not just as a hostname or ip address. 46013498266Sopenharmony_ci * 46113498266Sopenharmony_ci * The interface might be a VRF, eg: vrf-blue, which means it cannot be 46213498266Sopenharmony_ci * converted to an IP address and would fail Curl_if2ip. Simply try to 46313498266Sopenharmony_ci * use it straight away. 46413498266Sopenharmony_ci */ 46513498266Sopenharmony_ci if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, 46613498266Sopenharmony_ci dev, (curl_socklen_t)strlen(dev) + 1) == 0) { 46713498266Sopenharmony_ci /* This is often "errno 1, error: Operation not permitted" if you're 46813498266Sopenharmony_ci * not running as root or another suitable privileged user. If it 46913498266Sopenharmony_ci * succeeds it means the parameter was a valid interface and not an IP 47013498266Sopenharmony_ci * address. Return immediately. 47113498266Sopenharmony_ci */ 47213498266Sopenharmony_ci infof(data, "socket successfully bound to interface '%s'", dev); 47313498266Sopenharmony_ci return CURLE_OK; 47413498266Sopenharmony_ci } 47513498266Sopenharmony_ci#endif 47613498266Sopenharmony_ci 47713498266Sopenharmony_ci switch(Curl_if2ip(af, 47813498266Sopenharmony_ci#ifdef ENABLE_IPV6 47913498266Sopenharmony_ci scope, conn->scope_id, 48013498266Sopenharmony_ci#endif 48113498266Sopenharmony_ci dev, myhost, sizeof(myhost))) { 48213498266Sopenharmony_ci case IF2IP_NOT_FOUND: 48313498266Sopenharmony_ci if(is_interface) { 48413498266Sopenharmony_ci /* Do not fall back to treating it as a host name */ 48513498266Sopenharmony_ci failf(data, "Couldn't bind to interface '%s'", dev); 48613498266Sopenharmony_ci return CURLE_INTERFACE_FAILED; 48713498266Sopenharmony_ci } 48813498266Sopenharmony_ci break; 48913498266Sopenharmony_ci case IF2IP_AF_NOT_SUPPORTED: 49013498266Sopenharmony_ci /* Signal the caller to try another address family if available */ 49113498266Sopenharmony_ci return CURLE_UNSUPPORTED_PROTOCOL; 49213498266Sopenharmony_ci case IF2IP_FOUND: 49313498266Sopenharmony_ci is_interface = TRUE; 49413498266Sopenharmony_ci /* 49513498266Sopenharmony_ci * We now have the numerical IP address in the 'myhost' buffer 49613498266Sopenharmony_ci */ 49713498266Sopenharmony_ci infof(data, "Local Interface %s is ip %s using address family %i", 49813498266Sopenharmony_ci dev, myhost, af); 49913498266Sopenharmony_ci done = 1; 50013498266Sopenharmony_ci break; 50113498266Sopenharmony_ci } 50213498266Sopenharmony_ci } 50313498266Sopenharmony_ci if(!is_interface) { 50413498266Sopenharmony_ci /* 50513498266Sopenharmony_ci * This was not an interface, resolve the name as a host name 50613498266Sopenharmony_ci * or IP number 50713498266Sopenharmony_ci * 50813498266Sopenharmony_ci * Temporarily force name resolution to use only the address type 50913498266Sopenharmony_ci * of the connection. The resolve functions should really be changed 51013498266Sopenharmony_ci * to take a type parameter instead. 51113498266Sopenharmony_ci */ 51213498266Sopenharmony_ci unsigned char ipver = conn->ip_version; 51313498266Sopenharmony_ci int rc; 51413498266Sopenharmony_ci 51513498266Sopenharmony_ci if(af == AF_INET) 51613498266Sopenharmony_ci conn->ip_version = CURL_IPRESOLVE_V4; 51713498266Sopenharmony_ci#ifdef ENABLE_IPV6 51813498266Sopenharmony_ci else if(af == AF_INET6) 51913498266Sopenharmony_ci conn->ip_version = CURL_IPRESOLVE_V6; 52013498266Sopenharmony_ci#endif 52113498266Sopenharmony_ci 52213498266Sopenharmony_ci rc = Curl_resolv(data, dev, 80, FALSE, &h); 52313498266Sopenharmony_ci if(rc == CURLRESOLV_PENDING) 52413498266Sopenharmony_ci (void)Curl_resolver_wait_resolv(data, &h); 52513498266Sopenharmony_ci conn->ip_version = ipver; 52613498266Sopenharmony_ci 52713498266Sopenharmony_ci if(h) { 52813498266Sopenharmony_ci /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */ 52913498266Sopenharmony_ci Curl_printable_address(h->addr, myhost, sizeof(myhost)); 53013498266Sopenharmony_ci infof(data, "Name '%s' family %i resolved to '%s' family %i", 53113498266Sopenharmony_ci dev, af, myhost, h->addr->ai_family); 53213498266Sopenharmony_ci Curl_resolv_unlock(data, h); 53313498266Sopenharmony_ci if(af != h->addr->ai_family) { 53413498266Sopenharmony_ci /* bad IP version combo, signal the caller to try another address 53513498266Sopenharmony_ci family if available */ 53613498266Sopenharmony_ci return CURLE_UNSUPPORTED_PROTOCOL; 53713498266Sopenharmony_ci } 53813498266Sopenharmony_ci done = 1; 53913498266Sopenharmony_ci } 54013498266Sopenharmony_ci else { 54113498266Sopenharmony_ci /* 54213498266Sopenharmony_ci * provided dev was no interface (or interfaces are not supported 54313498266Sopenharmony_ci * e.g. solaris) no ip address and no domain we fail here 54413498266Sopenharmony_ci */ 54513498266Sopenharmony_ci done = -1; 54613498266Sopenharmony_ci } 54713498266Sopenharmony_ci } 54813498266Sopenharmony_ci 54913498266Sopenharmony_ci if(done > 0) { 55013498266Sopenharmony_ci#ifdef ENABLE_IPV6 55113498266Sopenharmony_ci /* IPv6 address */ 55213498266Sopenharmony_ci if(af == AF_INET6) { 55313498266Sopenharmony_ci#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 55413498266Sopenharmony_ci char *scope_ptr = strchr(myhost, '%'); 55513498266Sopenharmony_ci if(scope_ptr) 55613498266Sopenharmony_ci *(scope_ptr++) = '\0'; 55713498266Sopenharmony_ci#endif 55813498266Sopenharmony_ci if(Curl_inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0) { 55913498266Sopenharmony_ci si6->sin6_family = AF_INET6; 56013498266Sopenharmony_ci si6->sin6_port = htons(port); 56113498266Sopenharmony_ci#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 56213498266Sopenharmony_ci if(scope_ptr) { 56313498266Sopenharmony_ci /* The "myhost" string either comes from Curl_if2ip or from 56413498266Sopenharmony_ci Curl_printable_address. The latter returns only numeric scope 56513498266Sopenharmony_ci IDs and the former returns none at all. So the scope ID, if 56613498266Sopenharmony_ci present, is known to be numeric */ 56713498266Sopenharmony_ci unsigned long scope_id = strtoul(scope_ptr, NULL, 10); 56813498266Sopenharmony_ci if(scope_id > UINT_MAX) 56913498266Sopenharmony_ci return CURLE_UNSUPPORTED_PROTOCOL; 57013498266Sopenharmony_ci 57113498266Sopenharmony_ci si6->sin6_scope_id = (unsigned int)scope_id; 57213498266Sopenharmony_ci } 57313498266Sopenharmony_ci#endif 57413498266Sopenharmony_ci } 57513498266Sopenharmony_ci sizeof_sa = sizeof(struct sockaddr_in6); 57613498266Sopenharmony_ci } 57713498266Sopenharmony_ci else 57813498266Sopenharmony_ci#endif 57913498266Sopenharmony_ci /* IPv4 address */ 58013498266Sopenharmony_ci if((af == AF_INET) && 58113498266Sopenharmony_ci (Curl_inet_pton(AF_INET, myhost, &si4->sin_addr) > 0)) { 58213498266Sopenharmony_ci si4->sin_family = AF_INET; 58313498266Sopenharmony_ci si4->sin_port = htons(port); 58413498266Sopenharmony_ci sizeof_sa = sizeof(struct sockaddr_in); 58513498266Sopenharmony_ci } 58613498266Sopenharmony_ci } 58713498266Sopenharmony_ci 58813498266Sopenharmony_ci if(done < 1) { 58913498266Sopenharmony_ci /* errorbuf is set false so failf will overwrite any message already in 59013498266Sopenharmony_ci the error buffer, so the user receives this error message instead of a 59113498266Sopenharmony_ci generic resolve error. */ 59213498266Sopenharmony_ci data->state.errorbuf = FALSE; 59313498266Sopenharmony_ci failf(data, "Couldn't bind to '%s'", dev); 59413498266Sopenharmony_ci return CURLE_INTERFACE_FAILED; 59513498266Sopenharmony_ci } 59613498266Sopenharmony_ci } 59713498266Sopenharmony_ci else { 59813498266Sopenharmony_ci /* no device was given, prepare sa to match af's needs */ 59913498266Sopenharmony_ci#ifdef ENABLE_IPV6 60013498266Sopenharmony_ci if(af == AF_INET6) { 60113498266Sopenharmony_ci si6->sin6_family = AF_INET6; 60213498266Sopenharmony_ci si6->sin6_port = htons(port); 60313498266Sopenharmony_ci sizeof_sa = sizeof(struct sockaddr_in6); 60413498266Sopenharmony_ci } 60513498266Sopenharmony_ci else 60613498266Sopenharmony_ci#endif 60713498266Sopenharmony_ci if(af == AF_INET) { 60813498266Sopenharmony_ci si4->sin_family = AF_INET; 60913498266Sopenharmony_ci si4->sin_port = htons(port); 61013498266Sopenharmony_ci sizeof_sa = sizeof(struct sockaddr_in); 61113498266Sopenharmony_ci } 61213498266Sopenharmony_ci } 61313498266Sopenharmony_ci#ifdef IP_BIND_ADDRESS_NO_PORT 61413498266Sopenharmony_ci (void)setsockopt(sockfd, SOL_IP, IP_BIND_ADDRESS_NO_PORT, &on, sizeof(on)); 61513498266Sopenharmony_ci#endif 61613498266Sopenharmony_ci for(;;) { 61713498266Sopenharmony_ci if(bind(sockfd, sock, sizeof_sa) >= 0) { 61813498266Sopenharmony_ci /* we succeeded to bind */ 61913498266Sopenharmony_ci struct Curl_sockaddr_storage add; 62013498266Sopenharmony_ci curl_socklen_t size = sizeof(add); 62113498266Sopenharmony_ci memset(&add, 0, sizeof(struct Curl_sockaddr_storage)); 62213498266Sopenharmony_ci if(getsockname(sockfd, (struct sockaddr *) &add, &size) < 0) { 62313498266Sopenharmony_ci char buffer[STRERROR_LEN]; 62413498266Sopenharmony_ci data->state.os_errno = error = SOCKERRNO; 62513498266Sopenharmony_ci failf(data, "getsockname() failed with errno %d: %s", 62613498266Sopenharmony_ci error, Curl_strerror(error, buffer, sizeof(buffer))); 62713498266Sopenharmony_ci return CURLE_INTERFACE_FAILED; 62813498266Sopenharmony_ci } 62913498266Sopenharmony_ci infof(data, "Local port: %hu", port); 63013498266Sopenharmony_ci conn->bits.bound = TRUE; 63113498266Sopenharmony_ci return CURLE_OK; 63213498266Sopenharmony_ci } 63313498266Sopenharmony_ci 63413498266Sopenharmony_ci if(--portnum > 0) { 63513498266Sopenharmony_ci port++; /* try next port */ 63613498266Sopenharmony_ci if(port == 0) 63713498266Sopenharmony_ci break; 63813498266Sopenharmony_ci infof(data, "Bind to local port %d failed, trying next", port - 1); 63913498266Sopenharmony_ci /* We reuse/clobber the port variable here below */ 64013498266Sopenharmony_ci if(sock->sa_family == AF_INET) 64113498266Sopenharmony_ci si4->sin_port = ntohs(port); 64213498266Sopenharmony_ci#ifdef ENABLE_IPV6 64313498266Sopenharmony_ci else 64413498266Sopenharmony_ci si6->sin6_port = ntohs(port); 64513498266Sopenharmony_ci#endif 64613498266Sopenharmony_ci } 64713498266Sopenharmony_ci else 64813498266Sopenharmony_ci break; 64913498266Sopenharmony_ci } 65013498266Sopenharmony_ci { 65113498266Sopenharmony_ci char buffer[STRERROR_LEN]; 65213498266Sopenharmony_ci data->state.os_errno = error = SOCKERRNO; 65313498266Sopenharmony_ci failf(data, "bind failed with errno %d: %s", 65413498266Sopenharmony_ci error, Curl_strerror(error, buffer, sizeof(buffer))); 65513498266Sopenharmony_ci } 65613498266Sopenharmony_ci 65713498266Sopenharmony_ci return CURLE_INTERFACE_FAILED; 65813498266Sopenharmony_ci} 65913498266Sopenharmony_ci#endif 66013498266Sopenharmony_ci 66113498266Sopenharmony_ci/* 66213498266Sopenharmony_ci * verifyconnect() returns TRUE if the connect really has happened. 66313498266Sopenharmony_ci */ 66413498266Sopenharmony_cistatic bool verifyconnect(curl_socket_t sockfd, int *error) 66513498266Sopenharmony_ci{ 66613498266Sopenharmony_ci bool rc = TRUE; 66713498266Sopenharmony_ci#ifdef SO_ERROR 66813498266Sopenharmony_ci int err = 0; 66913498266Sopenharmony_ci curl_socklen_t errSize = sizeof(err); 67013498266Sopenharmony_ci 67113498266Sopenharmony_ci#ifdef _WIN32 67213498266Sopenharmony_ci /* 67313498266Sopenharmony_ci * In October 2003 we effectively nullified this function on Windows due to 67413498266Sopenharmony_ci * problems with it using all CPU in multi-threaded cases. 67513498266Sopenharmony_ci * 67613498266Sopenharmony_ci * In May 2004, we bring it back to offer more info back on connect failures. 67713498266Sopenharmony_ci * Gisle Vanem could reproduce the former problems with this function, but 67813498266Sopenharmony_ci * could avoid them by adding this SleepEx() call below: 67913498266Sopenharmony_ci * 68013498266Sopenharmony_ci * "I don't have Rational Quantify, but the hint from his post was 68113498266Sopenharmony_ci * ntdll::NtRemoveIoCompletion(). So I'd assume the SleepEx (or maybe 68213498266Sopenharmony_ci * just Sleep(0) would be enough?) would release whatever 68313498266Sopenharmony_ci * mutex/critical-section the ntdll call is waiting on. 68413498266Sopenharmony_ci * 68513498266Sopenharmony_ci * Someone got to verify this on Win-NT 4.0, 2000." 68613498266Sopenharmony_ci */ 68713498266Sopenharmony_ci 68813498266Sopenharmony_ci#ifdef _WIN32_WCE 68913498266Sopenharmony_ci Sleep(0); 69013498266Sopenharmony_ci#else 69113498266Sopenharmony_ci SleepEx(0, FALSE); 69213498266Sopenharmony_ci#endif 69313498266Sopenharmony_ci 69413498266Sopenharmony_ci#endif 69513498266Sopenharmony_ci 69613498266Sopenharmony_ci if(0 != getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&err, &errSize)) 69713498266Sopenharmony_ci err = SOCKERRNO; 69813498266Sopenharmony_ci#ifdef _WIN32_WCE 69913498266Sopenharmony_ci /* Old WinCE versions don't support SO_ERROR */ 70013498266Sopenharmony_ci if(WSAENOPROTOOPT == err) { 70113498266Sopenharmony_ci SET_SOCKERRNO(0); 70213498266Sopenharmony_ci err = 0; 70313498266Sopenharmony_ci } 70413498266Sopenharmony_ci#endif 70513498266Sopenharmony_ci#if defined(EBADIOCTL) && defined(__minix) 70613498266Sopenharmony_ci /* Minix 3.1.x doesn't support getsockopt on UDP sockets */ 70713498266Sopenharmony_ci if(EBADIOCTL == err) { 70813498266Sopenharmony_ci SET_SOCKERRNO(0); 70913498266Sopenharmony_ci err = 0; 71013498266Sopenharmony_ci } 71113498266Sopenharmony_ci#endif 71213498266Sopenharmony_ci if((0 == err) || (EISCONN == err)) 71313498266Sopenharmony_ci /* we are connected, awesome! */ 71413498266Sopenharmony_ci rc = TRUE; 71513498266Sopenharmony_ci else 71613498266Sopenharmony_ci /* This wasn't a successful connect */ 71713498266Sopenharmony_ci rc = FALSE; 71813498266Sopenharmony_ci if(error) 71913498266Sopenharmony_ci *error = err; 72013498266Sopenharmony_ci#else 72113498266Sopenharmony_ci (void)sockfd; 72213498266Sopenharmony_ci if(error) 72313498266Sopenharmony_ci *error = SOCKERRNO; 72413498266Sopenharmony_ci#endif 72513498266Sopenharmony_ci return rc; 72613498266Sopenharmony_ci} 72713498266Sopenharmony_ci 72813498266Sopenharmony_ci/** 72913498266Sopenharmony_ci * Determine the curl code for a socket connect() == -1 with errno. 73013498266Sopenharmony_ci */ 73113498266Sopenharmony_cistatic CURLcode socket_connect_result(struct Curl_easy *data, 73213498266Sopenharmony_ci const char *ipaddress, int error) 73313498266Sopenharmony_ci{ 73413498266Sopenharmony_ci switch(error) { 73513498266Sopenharmony_ci case EINPROGRESS: 73613498266Sopenharmony_ci case EWOULDBLOCK: 73713498266Sopenharmony_ci#if defined(EAGAIN) 73813498266Sopenharmony_ci#if (EAGAIN) != (EWOULDBLOCK) 73913498266Sopenharmony_ci /* On some platforms EAGAIN and EWOULDBLOCK are the 74013498266Sopenharmony_ci * same value, and on others they are different, hence 74113498266Sopenharmony_ci * the odd #if 74213498266Sopenharmony_ci */ 74313498266Sopenharmony_ci case EAGAIN: 74413498266Sopenharmony_ci#endif 74513498266Sopenharmony_ci#endif 74613498266Sopenharmony_ci return CURLE_OK; 74713498266Sopenharmony_ci 74813498266Sopenharmony_ci default: 74913498266Sopenharmony_ci /* unknown error, fallthrough and try another address! */ 75013498266Sopenharmony_ci#ifdef CURL_DISABLE_VERBOSE_STRINGS 75113498266Sopenharmony_ci (void)ipaddress; 75213498266Sopenharmony_ci#else 75313498266Sopenharmony_ci { 75413498266Sopenharmony_ci char buffer[STRERROR_LEN]; 75513498266Sopenharmony_ci infof(data, "Immediate connect fail for %s: %s", 75613498266Sopenharmony_ci ipaddress, Curl_strerror(error, buffer, sizeof(buffer))); 75713498266Sopenharmony_ci } 75813498266Sopenharmony_ci#endif 75913498266Sopenharmony_ci data->state.os_errno = error; 76013498266Sopenharmony_ci /* connect failed */ 76113498266Sopenharmony_ci return CURLE_COULDNT_CONNECT; 76213498266Sopenharmony_ci } 76313498266Sopenharmony_ci} 76413498266Sopenharmony_ci 76513498266Sopenharmony_ci/* We have a recv buffer to enhance reads with len < NW_SMALL_READS. 76613498266Sopenharmony_ci * This happens often on TLS connections where the TLS implementation 76713498266Sopenharmony_ci * tries to read the head of a TLS record, determine the length of the 76813498266Sopenharmony_ci * full record and then make a subsequent read for that. 76913498266Sopenharmony_ci * On large reads, we will not fill the buffer to avoid the double copy. */ 77013498266Sopenharmony_ci#define NW_RECV_CHUNK_SIZE (64 * 1024) 77113498266Sopenharmony_ci#define NW_RECV_CHUNKS 1 77213498266Sopenharmony_ci#define NW_SMALL_READS (1024) 77313498266Sopenharmony_ci 77413498266Sopenharmony_cistruct cf_socket_ctx { 77513498266Sopenharmony_ci int transport; 77613498266Sopenharmony_ci struct Curl_sockaddr_ex addr; /* address to connect to */ 77713498266Sopenharmony_ci curl_socket_t sock; /* current attempt socket */ 77813498266Sopenharmony_ci struct bufq recvbuf; /* used when `buffer_recv` is set */ 77913498266Sopenharmony_ci char r_ip[MAX_IPADR_LEN]; /* remote IP as string */ 78013498266Sopenharmony_ci int r_port; /* remote port number */ 78113498266Sopenharmony_ci char l_ip[MAX_IPADR_LEN]; /* local IP as string */ 78213498266Sopenharmony_ci int l_port; /* local port number */ 78313498266Sopenharmony_ci struct curltime started_at; /* when socket was created */ 78413498266Sopenharmony_ci struct curltime connected_at; /* when socket connected/got first byte */ 78513498266Sopenharmony_ci struct curltime first_byte_at; /* when first byte was recvd */ 78613498266Sopenharmony_ci int error; /* errno of last failure or 0 */ 78713498266Sopenharmony_ci#ifdef DEBUGBUILD 78813498266Sopenharmony_ci int wblock_percent; /* percent of writes doing EAGAIN */ 78913498266Sopenharmony_ci int wpartial_percent; /* percent of bytes written in send */ 79013498266Sopenharmony_ci int rblock_percent; /* percent of reads doing EAGAIN */ 79113498266Sopenharmony_ci size_t recv_max; /* max enforced read size */ 79213498266Sopenharmony_ci#endif 79313498266Sopenharmony_ci BIT(got_first_byte); /* if first byte was received */ 79413498266Sopenharmony_ci BIT(accepted); /* socket was accepted, not connected */ 79513498266Sopenharmony_ci BIT(sock_connected); /* socket is "connected", e.g. in UDP */ 79613498266Sopenharmony_ci BIT(active); 79713498266Sopenharmony_ci BIT(buffer_recv); 79813498266Sopenharmony_ci}; 79913498266Sopenharmony_ci 80013498266Sopenharmony_cistatic void cf_socket_ctx_init(struct cf_socket_ctx *ctx, 80113498266Sopenharmony_ci const struct Curl_addrinfo *ai, 80213498266Sopenharmony_ci int transport) 80313498266Sopenharmony_ci{ 80413498266Sopenharmony_ci memset(ctx, 0, sizeof(*ctx)); 80513498266Sopenharmony_ci ctx->sock = CURL_SOCKET_BAD; 80613498266Sopenharmony_ci ctx->transport = transport; 80713498266Sopenharmony_ci Curl_sock_assign_addr(&ctx->addr, ai, transport); 80813498266Sopenharmony_ci Curl_bufq_init(&ctx->recvbuf, NW_RECV_CHUNK_SIZE, NW_RECV_CHUNKS); 80913498266Sopenharmony_ci#ifdef DEBUGBUILD 81013498266Sopenharmony_ci { 81113498266Sopenharmony_ci char *p = getenv("CURL_DBG_SOCK_WBLOCK"); 81213498266Sopenharmony_ci if(p) { 81313498266Sopenharmony_ci long l = strtol(p, NULL, 10); 81413498266Sopenharmony_ci if(l >= 0 && l <= 100) 81513498266Sopenharmony_ci ctx->wblock_percent = (int)l; 81613498266Sopenharmony_ci } 81713498266Sopenharmony_ci p = getenv("CURL_DBG_SOCK_WPARTIAL"); 81813498266Sopenharmony_ci if(p) { 81913498266Sopenharmony_ci long l = strtol(p, NULL, 10); 82013498266Sopenharmony_ci if(l >= 0 && l <= 100) 82113498266Sopenharmony_ci ctx->wpartial_percent = (int)l; 82213498266Sopenharmony_ci } 82313498266Sopenharmony_ci p = getenv("CURL_DBG_SOCK_RBLOCK"); 82413498266Sopenharmony_ci if(p) { 82513498266Sopenharmony_ci long l = strtol(p, NULL, 10); 82613498266Sopenharmony_ci if(l >= 0 && l <= 100) 82713498266Sopenharmony_ci ctx->rblock_percent = (int)l; 82813498266Sopenharmony_ci } 82913498266Sopenharmony_ci p = getenv("CURL_DBG_SOCK_RMAX"); 83013498266Sopenharmony_ci if(p) { 83113498266Sopenharmony_ci long l = strtol(p, NULL, 10); 83213498266Sopenharmony_ci if(l >= 0) 83313498266Sopenharmony_ci ctx->recv_max = (size_t)l; 83413498266Sopenharmony_ci } 83513498266Sopenharmony_ci } 83613498266Sopenharmony_ci#endif 83713498266Sopenharmony_ci} 83813498266Sopenharmony_ci 83913498266Sopenharmony_cistruct reader_ctx { 84013498266Sopenharmony_ci struct Curl_cfilter *cf; 84113498266Sopenharmony_ci struct Curl_easy *data; 84213498266Sopenharmony_ci}; 84313498266Sopenharmony_ci 84413498266Sopenharmony_cistatic ssize_t nw_in_read(void *reader_ctx, 84513498266Sopenharmony_ci unsigned char *buf, size_t len, 84613498266Sopenharmony_ci CURLcode *err) 84713498266Sopenharmony_ci{ 84813498266Sopenharmony_ci struct reader_ctx *rctx = reader_ctx; 84913498266Sopenharmony_ci struct cf_socket_ctx *ctx = rctx->cf->ctx; 85013498266Sopenharmony_ci ssize_t nread; 85113498266Sopenharmony_ci 85213498266Sopenharmony_ci *err = CURLE_OK; 85313498266Sopenharmony_ci nread = sread(ctx->sock, buf, len); 85413498266Sopenharmony_ci 85513498266Sopenharmony_ci if(-1 == nread) { 85613498266Sopenharmony_ci int sockerr = SOCKERRNO; 85713498266Sopenharmony_ci 85813498266Sopenharmony_ci if( 85913498266Sopenharmony_ci#ifdef WSAEWOULDBLOCK 86013498266Sopenharmony_ci /* This is how Windows does it */ 86113498266Sopenharmony_ci (WSAEWOULDBLOCK == sockerr) 86213498266Sopenharmony_ci#else 86313498266Sopenharmony_ci /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned 86413498266Sopenharmony_ci due to its inability to send off data without blocking. We therefore 86513498266Sopenharmony_ci treat both error codes the same here */ 86613498266Sopenharmony_ci (EWOULDBLOCK == sockerr) || (EAGAIN == sockerr) || (EINTR == sockerr) 86713498266Sopenharmony_ci#endif 86813498266Sopenharmony_ci ) { 86913498266Sopenharmony_ci /* this is just a case of EWOULDBLOCK */ 87013498266Sopenharmony_ci *err = CURLE_AGAIN; 87113498266Sopenharmony_ci nread = -1; 87213498266Sopenharmony_ci } 87313498266Sopenharmony_ci else { 87413498266Sopenharmony_ci char buffer[STRERROR_LEN]; 87513498266Sopenharmony_ci 87613498266Sopenharmony_ci failf(rctx->data, "Recv failure: %s", 87713498266Sopenharmony_ci Curl_strerror(sockerr, buffer, sizeof(buffer))); 87813498266Sopenharmony_ci rctx->data->state.os_errno = sockerr; 87913498266Sopenharmony_ci *err = CURLE_RECV_ERROR; 88013498266Sopenharmony_ci nread = -1; 88113498266Sopenharmony_ci } 88213498266Sopenharmony_ci } 88313498266Sopenharmony_ci CURL_TRC_CF(rctx->data, rctx->cf, "nw_in_read(len=%zu) -> %d, err=%d", 88413498266Sopenharmony_ci len, (int)nread, *err); 88513498266Sopenharmony_ci return nread; 88613498266Sopenharmony_ci} 88713498266Sopenharmony_ci 88813498266Sopenharmony_cistatic void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data) 88913498266Sopenharmony_ci{ 89013498266Sopenharmony_ci struct cf_socket_ctx *ctx = cf->ctx; 89113498266Sopenharmony_ci 89213498266Sopenharmony_ci if(ctx && CURL_SOCKET_BAD != ctx->sock) { 89313498266Sopenharmony_ci CURL_TRC_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T 89413498266Sopenharmony_ci ")", ctx->sock); 89513498266Sopenharmony_ci if(ctx->sock == cf->conn->sock[cf->sockindex]) 89613498266Sopenharmony_ci cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD; 89713498266Sopenharmony_ci socket_close(data, cf->conn, !ctx->accepted, ctx->sock); 89813498266Sopenharmony_ci ctx->sock = CURL_SOCKET_BAD; 89913498266Sopenharmony_ci if(ctx->active && cf->sockindex == FIRSTSOCKET) 90013498266Sopenharmony_ci cf->conn->remote_addr = NULL; 90113498266Sopenharmony_ci Curl_bufq_reset(&ctx->recvbuf); 90213498266Sopenharmony_ci ctx->active = FALSE; 90313498266Sopenharmony_ci ctx->buffer_recv = FALSE; 90413498266Sopenharmony_ci memset(&ctx->started_at, 0, sizeof(ctx->started_at)); 90513498266Sopenharmony_ci memset(&ctx->connected_at, 0, sizeof(ctx->connected_at)); 90613498266Sopenharmony_ci } 90713498266Sopenharmony_ci 90813498266Sopenharmony_ci cf->connected = FALSE; 90913498266Sopenharmony_ci} 91013498266Sopenharmony_ci 91113498266Sopenharmony_cistatic void cf_socket_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) 91213498266Sopenharmony_ci{ 91313498266Sopenharmony_ci struct cf_socket_ctx *ctx = cf->ctx; 91413498266Sopenharmony_ci 91513498266Sopenharmony_ci cf_socket_close(cf, data); 91613498266Sopenharmony_ci CURL_TRC_CF(data, cf, "destroy"); 91713498266Sopenharmony_ci Curl_bufq_free(&ctx->recvbuf); 91813498266Sopenharmony_ci free(ctx); 91913498266Sopenharmony_ci cf->ctx = NULL; 92013498266Sopenharmony_ci} 92113498266Sopenharmony_ci 92213498266Sopenharmony_cistatic CURLcode set_local_ip(struct Curl_cfilter *cf, 92313498266Sopenharmony_ci struct Curl_easy *data) 92413498266Sopenharmony_ci{ 92513498266Sopenharmony_ci struct cf_socket_ctx *ctx = cf->ctx; 92613498266Sopenharmony_ci 92713498266Sopenharmony_ci#ifdef HAVE_GETSOCKNAME 92813498266Sopenharmony_ci if(!(data->conn->handler->protocol & CURLPROTO_TFTP)) { 92913498266Sopenharmony_ci /* TFTP does not connect, so it cannot get the IP like this */ 93013498266Sopenharmony_ci 93113498266Sopenharmony_ci char buffer[STRERROR_LEN]; 93213498266Sopenharmony_ci struct Curl_sockaddr_storage ssloc; 93313498266Sopenharmony_ci curl_socklen_t slen = sizeof(struct Curl_sockaddr_storage); 93413498266Sopenharmony_ci 93513498266Sopenharmony_ci memset(&ssloc, 0, sizeof(ssloc)); 93613498266Sopenharmony_ci if(getsockname(ctx->sock, (struct sockaddr*) &ssloc, &slen)) { 93713498266Sopenharmony_ci int error = SOCKERRNO; 93813498266Sopenharmony_ci failf(data, "getsockname() failed with errno %d: %s", 93913498266Sopenharmony_ci error, Curl_strerror(error, buffer, sizeof(buffer))); 94013498266Sopenharmony_ci return CURLE_FAILED_INIT; 94113498266Sopenharmony_ci } 94213498266Sopenharmony_ci if(!Curl_addr2string((struct sockaddr*)&ssloc, slen, 94313498266Sopenharmony_ci ctx->l_ip, &ctx->l_port)) { 94413498266Sopenharmony_ci failf(data, "ssloc inet_ntop() failed with errno %d: %s", 94513498266Sopenharmony_ci errno, Curl_strerror(errno, buffer, sizeof(buffer))); 94613498266Sopenharmony_ci return CURLE_FAILED_INIT; 94713498266Sopenharmony_ci } 94813498266Sopenharmony_ci } 94913498266Sopenharmony_ci#else 95013498266Sopenharmony_ci (void)data; 95113498266Sopenharmony_ci ctx->l_ip[0] = 0; 95213498266Sopenharmony_ci ctx->l_port = -1; 95313498266Sopenharmony_ci#endif 95413498266Sopenharmony_ci return CURLE_OK; 95513498266Sopenharmony_ci} 95613498266Sopenharmony_ci 95713498266Sopenharmony_cistatic CURLcode set_remote_ip(struct Curl_cfilter *cf, 95813498266Sopenharmony_ci struct Curl_easy *data) 95913498266Sopenharmony_ci{ 96013498266Sopenharmony_ci struct cf_socket_ctx *ctx = cf->ctx; 96113498266Sopenharmony_ci 96213498266Sopenharmony_ci /* store remote address and port used in this connection attempt */ 96313498266Sopenharmony_ci if(!Curl_addr2string(&ctx->addr.sa_addr, ctx->addr.addrlen, 96413498266Sopenharmony_ci ctx->r_ip, &ctx->r_port)) { 96513498266Sopenharmony_ci char buffer[STRERROR_LEN]; 96613498266Sopenharmony_ci 96713498266Sopenharmony_ci ctx->error = errno; 96813498266Sopenharmony_ci /* malformed address or bug in inet_ntop, try next address */ 96913498266Sopenharmony_ci failf(data, "sa_addr inet_ntop() failed with errno %d: %s", 97013498266Sopenharmony_ci errno, Curl_strerror(errno, buffer, sizeof(buffer))); 97113498266Sopenharmony_ci return CURLE_FAILED_INIT; 97213498266Sopenharmony_ci } 97313498266Sopenharmony_ci return CURLE_OK; 97413498266Sopenharmony_ci} 97513498266Sopenharmony_ci 97613498266Sopenharmony_cistatic CURLcode cf_socket_open(struct Curl_cfilter *cf, 97713498266Sopenharmony_ci struct Curl_easy *data) 97813498266Sopenharmony_ci{ 97913498266Sopenharmony_ci struct cf_socket_ctx *ctx = cf->ctx; 98013498266Sopenharmony_ci int error = 0; 98113498266Sopenharmony_ci bool isconnected = FALSE; 98213498266Sopenharmony_ci CURLcode result = CURLE_COULDNT_CONNECT; 98313498266Sopenharmony_ci bool is_tcp; 98413498266Sopenharmony_ci 98513498266Sopenharmony_ci (void)data; 98613498266Sopenharmony_ci DEBUGASSERT(ctx->sock == CURL_SOCKET_BAD); 98713498266Sopenharmony_ci ctx->started_at = Curl_now(); 98813498266Sopenharmony_ci result = socket_open(data, &ctx->addr, &ctx->sock); 98913498266Sopenharmony_ci if(result) 99013498266Sopenharmony_ci goto out; 99113498266Sopenharmony_ci 99213498266Sopenharmony_ci result = set_remote_ip(cf, data); 99313498266Sopenharmony_ci if(result) 99413498266Sopenharmony_ci goto out; 99513498266Sopenharmony_ci 99613498266Sopenharmony_ci#ifdef ENABLE_IPV6 99713498266Sopenharmony_ci if(ctx->addr.family == AF_INET6) { 99813498266Sopenharmony_ci set_ipv6_v6only(ctx->sock, 0); 99913498266Sopenharmony_ci infof(data, " Trying [%s]:%d...", ctx->r_ip, ctx->r_port); 100013498266Sopenharmony_ci } 100113498266Sopenharmony_ci else 100213498266Sopenharmony_ci#endif 100313498266Sopenharmony_ci infof(data, " Trying %s:%d...", ctx->r_ip, ctx->r_port); 100413498266Sopenharmony_ci 100513498266Sopenharmony_ci#ifdef ENABLE_IPV6 100613498266Sopenharmony_ci is_tcp = (ctx->addr.family == AF_INET 100713498266Sopenharmony_ci || ctx->addr.family == AF_INET6) && 100813498266Sopenharmony_ci ctx->addr.socktype == SOCK_STREAM; 100913498266Sopenharmony_ci#else 101013498266Sopenharmony_ci is_tcp = (ctx->addr.family == AF_INET) && 101113498266Sopenharmony_ci ctx->addr.socktype == SOCK_STREAM; 101213498266Sopenharmony_ci#endif 101313498266Sopenharmony_ci if(is_tcp && data->set.tcp_nodelay) 101413498266Sopenharmony_ci tcpnodelay(data, ctx->sock); 101513498266Sopenharmony_ci 101613498266Sopenharmony_ci nosigpipe(data, ctx->sock); 101713498266Sopenharmony_ci 101813498266Sopenharmony_ci Curl_sndbufset(ctx->sock); 101913498266Sopenharmony_ci 102013498266Sopenharmony_ci if(is_tcp && data->set.tcp_keepalive) 102113498266Sopenharmony_ci tcpkeepalive(data, ctx->sock); 102213498266Sopenharmony_ci 102313498266Sopenharmony_ci if(data->set.fsockopt) { 102413498266Sopenharmony_ci /* activate callback for setting socket options */ 102513498266Sopenharmony_ci Curl_set_in_callback(data, true); 102613498266Sopenharmony_ci error = data->set.fsockopt(data->set.sockopt_client, 102713498266Sopenharmony_ci ctx->sock, 102813498266Sopenharmony_ci CURLSOCKTYPE_IPCXN); 102913498266Sopenharmony_ci Curl_set_in_callback(data, false); 103013498266Sopenharmony_ci 103113498266Sopenharmony_ci if(error == CURL_SOCKOPT_ALREADY_CONNECTED) 103213498266Sopenharmony_ci isconnected = TRUE; 103313498266Sopenharmony_ci else if(error) { 103413498266Sopenharmony_ci result = CURLE_ABORTED_BY_CALLBACK; 103513498266Sopenharmony_ci goto out; 103613498266Sopenharmony_ci } 103713498266Sopenharmony_ci } 103813498266Sopenharmony_ci 103913498266Sopenharmony_ci#ifndef CURL_DISABLE_BINDLOCAL 104013498266Sopenharmony_ci /* possibly bind the local end to an IP, interface or port */ 104113498266Sopenharmony_ci if(ctx->addr.family == AF_INET 104213498266Sopenharmony_ci#ifdef ENABLE_IPV6 104313498266Sopenharmony_ci || ctx->addr.family == AF_INET6 104413498266Sopenharmony_ci#endif 104513498266Sopenharmony_ci ) { 104613498266Sopenharmony_ci result = bindlocal(data, cf->conn, ctx->sock, ctx->addr.family, 104713498266Sopenharmony_ci Curl_ipv6_scope(&ctx->addr.sa_addr)); 104813498266Sopenharmony_ci if(result) { 104913498266Sopenharmony_ci if(result == CURLE_UNSUPPORTED_PROTOCOL) { 105013498266Sopenharmony_ci /* The address family is not supported on this interface. 105113498266Sopenharmony_ci We can continue trying addresses */ 105213498266Sopenharmony_ci result = CURLE_COULDNT_CONNECT; 105313498266Sopenharmony_ci } 105413498266Sopenharmony_ci goto out; 105513498266Sopenharmony_ci } 105613498266Sopenharmony_ci } 105713498266Sopenharmony_ci#endif 105813498266Sopenharmony_ci 105913498266Sopenharmony_ci /* set socket non-blocking */ 106013498266Sopenharmony_ci (void)curlx_nonblock(ctx->sock, TRUE); 106113498266Sopenharmony_ci ctx->sock_connected = (ctx->addr.socktype != SOCK_DGRAM); 106213498266Sopenharmony_ciout: 106313498266Sopenharmony_ci if(result) { 106413498266Sopenharmony_ci if(ctx->sock != CURL_SOCKET_BAD) { 106513498266Sopenharmony_ci socket_close(data, cf->conn, TRUE, ctx->sock); 106613498266Sopenharmony_ci ctx->sock = CURL_SOCKET_BAD; 106713498266Sopenharmony_ci } 106813498266Sopenharmony_ci } 106913498266Sopenharmony_ci else if(isconnected) { 107013498266Sopenharmony_ci set_local_ip(cf, data); 107113498266Sopenharmony_ci ctx->connected_at = Curl_now(); 107213498266Sopenharmony_ci cf->connected = TRUE; 107313498266Sopenharmony_ci } 107413498266Sopenharmony_ci CURL_TRC_CF(data, cf, "cf_socket_open() -> %d, fd=%" CURL_FORMAT_SOCKET_T, 107513498266Sopenharmony_ci result, ctx->sock); 107613498266Sopenharmony_ci return result; 107713498266Sopenharmony_ci} 107813498266Sopenharmony_ci 107913498266Sopenharmony_cistatic int do_connect(struct Curl_cfilter *cf, struct Curl_easy *data, 108013498266Sopenharmony_ci bool is_tcp_fastopen) 108113498266Sopenharmony_ci{ 108213498266Sopenharmony_ci struct cf_socket_ctx *ctx = cf->ctx; 108313498266Sopenharmony_ci#ifdef TCP_FASTOPEN_CONNECT 108413498266Sopenharmony_ci int optval = 1; 108513498266Sopenharmony_ci#endif 108613498266Sopenharmony_ci int rc = -1; 108713498266Sopenharmony_ci 108813498266Sopenharmony_ci (void)data; 108913498266Sopenharmony_ci if(is_tcp_fastopen) { 109013498266Sopenharmony_ci#if defined(CONNECT_DATA_IDEMPOTENT) /* Darwin */ 109113498266Sopenharmony_ci# if defined(HAVE_BUILTIN_AVAILABLE) 109213498266Sopenharmony_ci /* while connectx function is available since macOS 10.11 / iOS 9, 109313498266Sopenharmony_ci it did not have the interface declared correctly until 109413498266Sopenharmony_ci Xcode 9 / macOS SDK 10.13 */ 109513498266Sopenharmony_ci if(__builtin_available(macOS 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *)) { 109613498266Sopenharmony_ci sa_endpoints_t endpoints; 109713498266Sopenharmony_ci endpoints.sae_srcif = 0; 109813498266Sopenharmony_ci endpoints.sae_srcaddr = NULL; 109913498266Sopenharmony_ci endpoints.sae_srcaddrlen = 0; 110013498266Sopenharmony_ci endpoints.sae_dstaddr = &ctx->addr.sa_addr; 110113498266Sopenharmony_ci endpoints.sae_dstaddrlen = ctx->addr.addrlen; 110213498266Sopenharmony_ci 110313498266Sopenharmony_ci rc = connectx(ctx->sock, &endpoints, SAE_ASSOCID_ANY, 110413498266Sopenharmony_ci CONNECT_RESUME_ON_READ_WRITE | CONNECT_DATA_IDEMPOTENT, 110513498266Sopenharmony_ci NULL, 0, NULL, NULL); 110613498266Sopenharmony_ci } 110713498266Sopenharmony_ci else { 110813498266Sopenharmony_ci rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen); 110913498266Sopenharmony_ci } 111013498266Sopenharmony_ci# else 111113498266Sopenharmony_ci rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen); 111213498266Sopenharmony_ci# endif /* HAVE_BUILTIN_AVAILABLE */ 111313498266Sopenharmony_ci#elif defined(TCP_FASTOPEN_CONNECT) /* Linux >= 4.11 */ 111413498266Sopenharmony_ci if(setsockopt(ctx->sock, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, 111513498266Sopenharmony_ci (void *)&optval, sizeof(optval)) < 0) 111613498266Sopenharmony_ci infof(data, "Failed to enable TCP Fast Open on fd %" 111713498266Sopenharmony_ci CURL_FORMAT_SOCKET_T, ctx->sock); 111813498266Sopenharmony_ci 111913498266Sopenharmony_ci rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen); 112013498266Sopenharmony_ci#elif defined(MSG_FASTOPEN) /* old Linux */ 112113498266Sopenharmony_ci if(cf->conn->given->flags & PROTOPT_SSL) 112213498266Sopenharmony_ci rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen); 112313498266Sopenharmony_ci else 112413498266Sopenharmony_ci rc = 0; /* Do nothing */ 112513498266Sopenharmony_ci#endif 112613498266Sopenharmony_ci } 112713498266Sopenharmony_ci else { 112813498266Sopenharmony_ci rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen); 112913498266Sopenharmony_ci } 113013498266Sopenharmony_ci return rc; 113113498266Sopenharmony_ci} 113213498266Sopenharmony_ci 113313498266Sopenharmony_cistatic CURLcode cf_tcp_connect(struct Curl_cfilter *cf, 113413498266Sopenharmony_ci struct Curl_easy *data, 113513498266Sopenharmony_ci bool blocking, bool *done) 113613498266Sopenharmony_ci{ 113713498266Sopenharmony_ci struct cf_socket_ctx *ctx = cf->ctx; 113813498266Sopenharmony_ci CURLcode result = CURLE_COULDNT_CONNECT; 113913498266Sopenharmony_ci int rc = 0; 114013498266Sopenharmony_ci 114113498266Sopenharmony_ci (void)data; 114213498266Sopenharmony_ci if(cf->connected) { 114313498266Sopenharmony_ci *done = TRUE; 114413498266Sopenharmony_ci return CURLE_OK; 114513498266Sopenharmony_ci } 114613498266Sopenharmony_ci 114713498266Sopenharmony_ci /* TODO: need to support blocking connect? */ 114813498266Sopenharmony_ci if(blocking) 114913498266Sopenharmony_ci return CURLE_UNSUPPORTED_PROTOCOL; 115013498266Sopenharmony_ci 115113498266Sopenharmony_ci *done = FALSE; /* a very negative world view is best */ 115213498266Sopenharmony_ci if(ctx->sock == CURL_SOCKET_BAD) { 115313498266Sopenharmony_ci int error; 115413498266Sopenharmony_ci 115513498266Sopenharmony_ci result = cf_socket_open(cf, data); 115613498266Sopenharmony_ci if(result) 115713498266Sopenharmony_ci goto out; 115813498266Sopenharmony_ci 115913498266Sopenharmony_ci if(cf->connected) { 116013498266Sopenharmony_ci *done = TRUE; 116113498266Sopenharmony_ci return CURLE_OK; 116213498266Sopenharmony_ci } 116313498266Sopenharmony_ci 116413498266Sopenharmony_ci /* Connect TCP socket */ 116513498266Sopenharmony_ci rc = do_connect(cf, data, cf->conn->bits.tcp_fastopen); 116613498266Sopenharmony_ci error = SOCKERRNO; 116713498266Sopenharmony_ci set_local_ip(cf, data); 116813498266Sopenharmony_ci CURL_TRC_CF(data, cf, "local address %s port %d...", 116913498266Sopenharmony_ci ctx->l_ip, ctx->l_port); 117013498266Sopenharmony_ci if(-1 == rc) { 117113498266Sopenharmony_ci result = socket_connect_result(data, ctx->r_ip, error); 117213498266Sopenharmony_ci goto out; 117313498266Sopenharmony_ci } 117413498266Sopenharmony_ci } 117513498266Sopenharmony_ci 117613498266Sopenharmony_ci#ifdef mpeix 117713498266Sopenharmony_ci /* Call this function once now, and ignore the results. We do this to 117813498266Sopenharmony_ci "clear" the error state on the socket so that we can later read it 117913498266Sopenharmony_ci reliably. This is reported necessary on the MPE/iX operating 118013498266Sopenharmony_ci system. */ 118113498266Sopenharmony_ci (void)verifyconnect(ctx->sock, NULL); 118213498266Sopenharmony_ci#endif 118313498266Sopenharmony_ci /* check socket for connect */ 118413498266Sopenharmony_ci rc = SOCKET_WRITABLE(ctx->sock, 0); 118513498266Sopenharmony_ci 118613498266Sopenharmony_ci if(rc == 0) { /* no connection yet */ 118713498266Sopenharmony_ci CURL_TRC_CF(data, cf, "not connected yet"); 118813498266Sopenharmony_ci return CURLE_OK; 118913498266Sopenharmony_ci } 119013498266Sopenharmony_ci else if(rc == CURL_CSELECT_OUT || cf->conn->bits.tcp_fastopen) { 119113498266Sopenharmony_ci if(verifyconnect(ctx->sock, &ctx->error)) { 119213498266Sopenharmony_ci /* we are connected with TCP, awesome! */ 119313498266Sopenharmony_ci ctx->connected_at = Curl_now(); 119413498266Sopenharmony_ci set_local_ip(cf, data); 119513498266Sopenharmony_ci *done = TRUE; 119613498266Sopenharmony_ci cf->connected = TRUE; 119713498266Sopenharmony_ci CURL_TRC_CF(data, cf, "connected"); 119813498266Sopenharmony_ci return CURLE_OK; 119913498266Sopenharmony_ci } 120013498266Sopenharmony_ci } 120113498266Sopenharmony_ci else if(rc & CURL_CSELECT_ERR) { 120213498266Sopenharmony_ci (void)verifyconnect(ctx->sock, &ctx->error); 120313498266Sopenharmony_ci result = CURLE_COULDNT_CONNECT; 120413498266Sopenharmony_ci } 120513498266Sopenharmony_ci 120613498266Sopenharmony_ciout: 120713498266Sopenharmony_ci if(result) { 120813498266Sopenharmony_ci if(ctx->error) { 120913498266Sopenharmony_ci set_local_ip(cf, data); 121013498266Sopenharmony_ci data->state.os_errno = ctx->error; 121113498266Sopenharmony_ci SET_SOCKERRNO(ctx->error); 121213498266Sopenharmony_ci#ifndef CURL_DISABLE_VERBOSE_STRINGS 121313498266Sopenharmony_ci { 121413498266Sopenharmony_ci char buffer[STRERROR_LEN]; 121513498266Sopenharmony_ci infof(data, "connect to %s port %u from %s port %d failed: %s", 121613498266Sopenharmony_ci ctx->r_ip, ctx->r_port, ctx->l_ip, ctx->l_port, 121713498266Sopenharmony_ci Curl_strerror(ctx->error, buffer, sizeof(buffer))); 121813498266Sopenharmony_ci } 121913498266Sopenharmony_ci#endif 122013498266Sopenharmony_ci } 122113498266Sopenharmony_ci if(ctx->sock != CURL_SOCKET_BAD) { 122213498266Sopenharmony_ci socket_close(data, cf->conn, TRUE, ctx->sock); 122313498266Sopenharmony_ci ctx->sock = CURL_SOCKET_BAD; 122413498266Sopenharmony_ci } 122513498266Sopenharmony_ci *done = FALSE; 122613498266Sopenharmony_ci } 122713498266Sopenharmony_ci return result; 122813498266Sopenharmony_ci} 122913498266Sopenharmony_ci 123013498266Sopenharmony_cistatic void cf_socket_get_host(struct Curl_cfilter *cf, 123113498266Sopenharmony_ci struct Curl_easy *data, 123213498266Sopenharmony_ci const char **phost, 123313498266Sopenharmony_ci const char **pdisplay_host, 123413498266Sopenharmony_ci int *pport) 123513498266Sopenharmony_ci{ 123613498266Sopenharmony_ci (void)data; 123713498266Sopenharmony_ci *phost = cf->conn->host.name; 123813498266Sopenharmony_ci *pdisplay_host = cf->conn->host.dispname; 123913498266Sopenharmony_ci *pport = cf->conn->port; 124013498266Sopenharmony_ci} 124113498266Sopenharmony_ci 124213498266Sopenharmony_cistatic void cf_socket_adjust_pollset(struct Curl_cfilter *cf, 124313498266Sopenharmony_ci struct Curl_easy *data, 124413498266Sopenharmony_ci struct easy_pollset *ps) 124513498266Sopenharmony_ci{ 124613498266Sopenharmony_ci struct cf_socket_ctx *ctx = cf->ctx; 124713498266Sopenharmony_ci 124813498266Sopenharmony_ci if(ctx->sock != CURL_SOCKET_BAD) { 124913498266Sopenharmony_ci if(!cf->connected) { 125013498266Sopenharmony_ci Curl_pollset_set_out_only(data, ps, ctx->sock); 125113498266Sopenharmony_ci CURL_TRC_CF(data, cf, "adjust_pollset(!connected) -> %d socks", ps->num); 125213498266Sopenharmony_ci } 125313498266Sopenharmony_ci else if(!ctx->active) { 125413498266Sopenharmony_ci Curl_pollset_add_in(data, ps, ctx->sock); 125513498266Sopenharmony_ci CURL_TRC_CF(data, cf, "adjust_pollset(!active) -> %d socks", ps->num); 125613498266Sopenharmony_ci } 125713498266Sopenharmony_ci } 125813498266Sopenharmony_ci} 125913498266Sopenharmony_ci 126013498266Sopenharmony_cistatic bool cf_socket_data_pending(struct Curl_cfilter *cf, 126113498266Sopenharmony_ci const struct Curl_easy *data) 126213498266Sopenharmony_ci{ 126313498266Sopenharmony_ci struct cf_socket_ctx *ctx = cf->ctx; 126413498266Sopenharmony_ci int readable; 126513498266Sopenharmony_ci 126613498266Sopenharmony_ci (void)data; 126713498266Sopenharmony_ci if(!Curl_bufq_is_empty(&ctx->recvbuf)) 126813498266Sopenharmony_ci return TRUE; 126913498266Sopenharmony_ci 127013498266Sopenharmony_ci readable = SOCKET_READABLE(ctx->sock, 0); 127113498266Sopenharmony_ci return (readable > 0 && (readable & CURL_CSELECT_IN)); 127213498266Sopenharmony_ci} 127313498266Sopenharmony_ci 127413498266Sopenharmony_cistatic ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data, 127513498266Sopenharmony_ci const void *buf, size_t len, CURLcode *err) 127613498266Sopenharmony_ci{ 127713498266Sopenharmony_ci struct cf_socket_ctx *ctx = cf->ctx; 127813498266Sopenharmony_ci curl_socket_t fdsave; 127913498266Sopenharmony_ci ssize_t nwritten; 128013498266Sopenharmony_ci size_t orig_len = len; 128113498266Sopenharmony_ci 128213498266Sopenharmony_ci *err = CURLE_OK; 128313498266Sopenharmony_ci fdsave = cf->conn->sock[cf->sockindex]; 128413498266Sopenharmony_ci cf->conn->sock[cf->sockindex] = ctx->sock; 128513498266Sopenharmony_ci 128613498266Sopenharmony_ci#ifdef DEBUGBUILD 128713498266Sopenharmony_ci /* simulate network blocking/partial writes */ 128813498266Sopenharmony_ci if(ctx->wblock_percent > 0) { 128913498266Sopenharmony_ci unsigned char c; 129013498266Sopenharmony_ci Curl_rand(data, &c, 1); 129113498266Sopenharmony_ci if(c >= ((100-ctx->wblock_percent)*256/100)) { 129213498266Sopenharmony_ci CURL_TRC_CF(data, cf, "send(len=%zu) SIMULATE EWOULDBLOCK", orig_len); 129313498266Sopenharmony_ci *err = CURLE_AGAIN; 129413498266Sopenharmony_ci nwritten = -1; 129513498266Sopenharmony_ci cf->conn->sock[cf->sockindex] = fdsave; 129613498266Sopenharmony_ci return nwritten; 129713498266Sopenharmony_ci } 129813498266Sopenharmony_ci } 129913498266Sopenharmony_ci if(cf->cft != &Curl_cft_udp && ctx->wpartial_percent > 0 && len > 8) { 130013498266Sopenharmony_ci len = len * ctx->wpartial_percent / 100; 130113498266Sopenharmony_ci if(!len) 130213498266Sopenharmony_ci len = 1; 130313498266Sopenharmony_ci CURL_TRC_CF(data, cf, "send(len=%zu) SIMULATE partial write of %zu bytes", 130413498266Sopenharmony_ci orig_len, len); 130513498266Sopenharmony_ci } 130613498266Sopenharmony_ci#endif 130713498266Sopenharmony_ci 130813498266Sopenharmony_ci#if defined(MSG_FASTOPEN) && !defined(TCP_FASTOPEN_CONNECT) /* Linux */ 130913498266Sopenharmony_ci if(cf->conn->bits.tcp_fastopen) { 131013498266Sopenharmony_ci nwritten = sendto(ctx->sock, buf, len, MSG_FASTOPEN, 131113498266Sopenharmony_ci &cf->conn->remote_addr->sa_addr, 131213498266Sopenharmony_ci cf->conn->remote_addr->addrlen); 131313498266Sopenharmony_ci cf->conn->bits.tcp_fastopen = FALSE; 131413498266Sopenharmony_ci } 131513498266Sopenharmony_ci else 131613498266Sopenharmony_ci#endif 131713498266Sopenharmony_ci nwritten = swrite(ctx->sock, buf, len); 131813498266Sopenharmony_ci 131913498266Sopenharmony_ci if(-1 == nwritten) { 132013498266Sopenharmony_ci int sockerr = SOCKERRNO; 132113498266Sopenharmony_ci 132213498266Sopenharmony_ci if( 132313498266Sopenharmony_ci#ifdef WSAEWOULDBLOCK 132413498266Sopenharmony_ci /* This is how Windows does it */ 132513498266Sopenharmony_ci (WSAEWOULDBLOCK == sockerr) 132613498266Sopenharmony_ci#else 132713498266Sopenharmony_ci /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned 132813498266Sopenharmony_ci due to its inability to send off data without blocking. We therefore 132913498266Sopenharmony_ci treat both error codes the same here */ 133013498266Sopenharmony_ci (EWOULDBLOCK == sockerr) || (EAGAIN == sockerr) || (EINTR == sockerr) || 133113498266Sopenharmony_ci (EINPROGRESS == sockerr) 133213498266Sopenharmony_ci#endif 133313498266Sopenharmony_ci ) { 133413498266Sopenharmony_ci /* this is just a case of EWOULDBLOCK */ 133513498266Sopenharmony_ci *err = CURLE_AGAIN; 133613498266Sopenharmony_ci } 133713498266Sopenharmony_ci else { 133813498266Sopenharmony_ci char buffer[STRERROR_LEN]; 133913498266Sopenharmony_ci failf(data, "Send failure: %s", 134013498266Sopenharmony_ci Curl_strerror(sockerr, buffer, sizeof(buffer))); 134113498266Sopenharmony_ci data->state.os_errno = sockerr; 134213498266Sopenharmony_ci *err = CURLE_SEND_ERROR; 134313498266Sopenharmony_ci } 134413498266Sopenharmony_ci } 134513498266Sopenharmony_ci 134613498266Sopenharmony_ci CURL_TRC_CF(data, cf, "send(len=%zu) -> %d, err=%d", 134713498266Sopenharmony_ci orig_len, (int)nwritten, *err); 134813498266Sopenharmony_ci cf->conn->sock[cf->sockindex] = fdsave; 134913498266Sopenharmony_ci return nwritten; 135013498266Sopenharmony_ci} 135113498266Sopenharmony_ci 135213498266Sopenharmony_cistatic ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data, 135313498266Sopenharmony_ci char *buf, size_t len, CURLcode *err) 135413498266Sopenharmony_ci{ 135513498266Sopenharmony_ci struct cf_socket_ctx *ctx = cf->ctx; 135613498266Sopenharmony_ci curl_socket_t fdsave; 135713498266Sopenharmony_ci ssize_t nread; 135813498266Sopenharmony_ci 135913498266Sopenharmony_ci *err = CURLE_OK; 136013498266Sopenharmony_ci 136113498266Sopenharmony_ci fdsave = cf->conn->sock[cf->sockindex]; 136213498266Sopenharmony_ci cf->conn->sock[cf->sockindex] = ctx->sock; 136313498266Sopenharmony_ci 136413498266Sopenharmony_ci#ifdef DEBUGBUILD 136513498266Sopenharmony_ci /* simulate network blocking/partial reads */ 136613498266Sopenharmony_ci if(cf->cft != &Curl_cft_udp && ctx->rblock_percent > 0) { 136713498266Sopenharmony_ci unsigned char c; 136813498266Sopenharmony_ci Curl_rand(data, &c, 1); 136913498266Sopenharmony_ci if(c >= ((100-ctx->rblock_percent)*256/100)) { 137013498266Sopenharmony_ci CURL_TRC_CF(data, cf, "recv(len=%zu) SIMULATE EWOULDBLOCK", len); 137113498266Sopenharmony_ci *err = CURLE_AGAIN; 137213498266Sopenharmony_ci nread = -1; 137313498266Sopenharmony_ci cf->conn->sock[cf->sockindex] = fdsave; 137413498266Sopenharmony_ci return nread; 137513498266Sopenharmony_ci } 137613498266Sopenharmony_ci } 137713498266Sopenharmony_ci if(cf->cft != &Curl_cft_udp && ctx->recv_max && ctx->recv_max < len) { 137813498266Sopenharmony_ci size_t orig_len = len; 137913498266Sopenharmony_ci len = ctx->recv_max; 138013498266Sopenharmony_ci CURL_TRC_CF(data, cf, "recv(len=%zu) SIMULATE max read of %zu bytes", 138113498266Sopenharmony_ci orig_len, len); 138213498266Sopenharmony_ci } 138313498266Sopenharmony_ci#endif 138413498266Sopenharmony_ci 138513498266Sopenharmony_ci if(ctx->buffer_recv && !Curl_bufq_is_empty(&ctx->recvbuf)) { 138613498266Sopenharmony_ci CURL_TRC_CF(data, cf, "recv from buffer"); 138713498266Sopenharmony_ci nread = Curl_bufq_read(&ctx->recvbuf, (unsigned char *)buf, len, err); 138813498266Sopenharmony_ci } 138913498266Sopenharmony_ci else { 139013498266Sopenharmony_ci struct reader_ctx rctx; 139113498266Sopenharmony_ci 139213498266Sopenharmony_ci rctx.cf = cf; 139313498266Sopenharmony_ci rctx.data = data; 139413498266Sopenharmony_ci 139513498266Sopenharmony_ci /* "small" reads may trigger filling our buffer, "large" reads 139613498266Sopenharmony_ci * are probably not worth the additional copy */ 139713498266Sopenharmony_ci if(ctx->buffer_recv && len < NW_SMALL_READS) { 139813498266Sopenharmony_ci ssize_t nwritten; 139913498266Sopenharmony_ci nwritten = Curl_bufq_slurp(&ctx->recvbuf, nw_in_read, &rctx, err); 140013498266Sopenharmony_ci if(nwritten < 0 && !Curl_bufq_is_empty(&ctx->recvbuf)) { 140113498266Sopenharmony_ci /* we have a partial read with an error. need to deliver 140213498266Sopenharmony_ci * what we got, return the error later. */ 140313498266Sopenharmony_ci CURL_TRC_CF(data, cf, "partial read: empty buffer first"); 140413498266Sopenharmony_ci nread = Curl_bufq_read(&ctx->recvbuf, (unsigned char *)buf, len, err); 140513498266Sopenharmony_ci } 140613498266Sopenharmony_ci else if(nwritten < 0) { 140713498266Sopenharmony_ci nread = -1; 140813498266Sopenharmony_ci goto out; 140913498266Sopenharmony_ci } 141013498266Sopenharmony_ci else if(nwritten == 0) { 141113498266Sopenharmony_ci /* eof */ 141213498266Sopenharmony_ci *err = CURLE_OK; 141313498266Sopenharmony_ci nread = 0; 141413498266Sopenharmony_ci } 141513498266Sopenharmony_ci else { 141613498266Sopenharmony_ci CURL_TRC_CF(data, cf, "buffered %zd additional bytes", nwritten); 141713498266Sopenharmony_ci nread = Curl_bufq_read(&ctx->recvbuf, (unsigned char *)buf, len, err); 141813498266Sopenharmony_ci } 141913498266Sopenharmony_ci } 142013498266Sopenharmony_ci else { 142113498266Sopenharmony_ci nread = nw_in_read(&rctx, (unsigned char *)buf, len, err); 142213498266Sopenharmony_ci } 142313498266Sopenharmony_ci } 142413498266Sopenharmony_ci 142513498266Sopenharmony_ciout: 142613498266Sopenharmony_ci CURL_TRC_CF(data, cf, "recv(len=%zu) -> %d, err=%d", len, (int)nread, 142713498266Sopenharmony_ci *err); 142813498266Sopenharmony_ci if(nread > 0 && !ctx->got_first_byte) { 142913498266Sopenharmony_ci ctx->first_byte_at = Curl_now(); 143013498266Sopenharmony_ci ctx->got_first_byte = TRUE; 143113498266Sopenharmony_ci } 143213498266Sopenharmony_ci cf->conn->sock[cf->sockindex] = fdsave; 143313498266Sopenharmony_ci return nread; 143413498266Sopenharmony_ci} 143513498266Sopenharmony_ci 143613498266Sopenharmony_cistatic void conn_set_primary_ip(struct Curl_cfilter *cf, 143713498266Sopenharmony_ci struct Curl_easy *data) 143813498266Sopenharmony_ci{ 143913498266Sopenharmony_ci struct cf_socket_ctx *ctx = cf->ctx; 144013498266Sopenharmony_ci 144113498266Sopenharmony_ci (void)data; 144213498266Sopenharmony_ci DEBUGASSERT(sizeof(ctx->r_ip) == sizeof(cf->conn->primary_ip)); 144313498266Sopenharmony_ci memcpy(cf->conn->primary_ip, ctx->r_ip, sizeof(cf->conn->primary_ip)); 144413498266Sopenharmony_ci} 144513498266Sopenharmony_ci 144613498266Sopenharmony_cistatic void cf_socket_active(struct Curl_cfilter *cf, struct Curl_easy *data) 144713498266Sopenharmony_ci{ 144813498266Sopenharmony_ci struct cf_socket_ctx *ctx = cf->ctx; 144913498266Sopenharmony_ci 145013498266Sopenharmony_ci /* use this socket from now on */ 145113498266Sopenharmony_ci cf->conn->sock[cf->sockindex] = ctx->sock; 145213498266Sopenharmony_ci /* the first socket info gets set at conn and data */ 145313498266Sopenharmony_ci if(cf->sockindex == FIRSTSOCKET) { 145413498266Sopenharmony_ci cf->conn->remote_addr = &ctx->addr; 145513498266Sopenharmony_ci #ifdef ENABLE_IPV6 145613498266Sopenharmony_ci cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6)? TRUE : FALSE; 145713498266Sopenharmony_ci #endif 145813498266Sopenharmony_ci conn_set_primary_ip(cf, data); 145913498266Sopenharmony_ci set_local_ip(cf, data); 146013498266Sopenharmony_ci Curl_persistconninfo(data, cf->conn, ctx->l_ip, ctx->l_port); 146113498266Sopenharmony_ci /* buffering is currently disabled by default because we have stalls 146213498266Sopenharmony_ci * in parallel transfers where not all buffered data is consumed and no 146313498266Sopenharmony_ci * socket events happen. 146413498266Sopenharmony_ci */ 146513498266Sopenharmony_ci ctx->buffer_recv = FALSE; 146613498266Sopenharmony_ci } 146713498266Sopenharmony_ci ctx->active = TRUE; 146813498266Sopenharmony_ci} 146913498266Sopenharmony_ci 147013498266Sopenharmony_cistatic CURLcode cf_socket_cntrl(struct Curl_cfilter *cf, 147113498266Sopenharmony_ci struct Curl_easy *data, 147213498266Sopenharmony_ci int event, int arg1, void *arg2) 147313498266Sopenharmony_ci{ 147413498266Sopenharmony_ci struct cf_socket_ctx *ctx = cf->ctx; 147513498266Sopenharmony_ci 147613498266Sopenharmony_ci (void)arg1; 147713498266Sopenharmony_ci (void)arg2; 147813498266Sopenharmony_ci switch(event) { 147913498266Sopenharmony_ci case CF_CTRL_CONN_INFO_UPDATE: 148013498266Sopenharmony_ci cf_socket_active(cf, data); 148113498266Sopenharmony_ci break; 148213498266Sopenharmony_ci case CF_CTRL_DATA_SETUP: 148313498266Sopenharmony_ci Curl_persistconninfo(data, cf->conn, ctx->l_ip, ctx->l_port); 148413498266Sopenharmony_ci break; 148513498266Sopenharmony_ci case CF_CTRL_FORGET_SOCKET: 148613498266Sopenharmony_ci ctx->sock = CURL_SOCKET_BAD; 148713498266Sopenharmony_ci break; 148813498266Sopenharmony_ci } 148913498266Sopenharmony_ci return CURLE_OK; 149013498266Sopenharmony_ci} 149113498266Sopenharmony_ci 149213498266Sopenharmony_cistatic bool cf_socket_conn_is_alive(struct Curl_cfilter *cf, 149313498266Sopenharmony_ci struct Curl_easy *data, 149413498266Sopenharmony_ci bool *input_pending) 149513498266Sopenharmony_ci{ 149613498266Sopenharmony_ci struct cf_socket_ctx *ctx = cf->ctx; 149713498266Sopenharmony_ci struct pollfd pfd[1]; 149813498266Sopenharmony_ci int r; 149913498266Sopenharmony_ci 150013498266Sopenharmony_ci *input_pending = FALSE; 150113498266Sopenharmony_ci (void)data; 150213498266Sopenharmony_ci if(!ctx || ctx->sock == CURL_SOCKET_BAD) 150313498266Sopenharmony_ci return FALSE; 150413498266Sopenharmony_ci 150513498266Sopenharmony_ci /* Check with 0 timeout if there are any events pending on the socket */ 150613498266Sopenharmony_ci pfd[0].fd = ctx->sock; 150713498266Sopenharmony_ci pfd[0].events = POLLRDNORM|POLLIN|POLLRDBAND|POLLPRI; 150813498266Sopenharmony_ci pfd[0].revents = 0; 150913498266Sopenharmony_ci 151013498266Sopenharmony_ci r = Curl_poll(pfd, 1, 0); 151113498266Sopenharmony_ci if(r < 0) { 151213498266Sopenharmony_ci CURL_TRC_CF(data, cf, "is_alive: poll error, assume dead"); 151313498266Sopenharmony_ci return FALSE; 151413498266Sopenharmony_ci } 151513498266Sopenharmony_ci else if(r == 0) { 151613498266Sopenharmony_ci CURL_TRC_CF(data, cf, "is_alive: poll timeout, assume alive"); 151713498266Sopenharmony_ci return TRUE; 151813498266Sopenharmony_ci } 151913498266Sopenharmony_ci else if(pfd[0].revents & (POLLERR|POLLHUP|POLLPRI|POLLNVAL)) { 152013498266Sopenharmony_ci CURL_TRC_CF(data, cf, "is_alive: err/hup/etc events, assume dead"); 152113498266Sopenharmony_ci return FALSE; 152213498266Sopenharmony_ci } 152313498266Sopenharmony_ci 152413498266Sopenharmony_ci CURL_TRC_CF(data, cf, "is_alive: valid events, looks alive"); 152513498266Sopenharmony_ci *input_pending = TRUE; 152613498266Sopenharmony_ci return TRUE; 152713498266Sopenharmony_ci} 152813498266Sopenharmony_ci 152913498266Sopenharmony_cistatic CURLcode cf_socket_query(struct Curl_cfilter *cf, 153013498266Sopenharmony_ci struct Curl_easy *data, 153113498266Sopenharmony_ci int query, int *pres1, void *pres2) 153213498266Sopenharmony_ci{ 153313498266Sopenharmony_ci struct cf_socket_ctx *ctx = cf->ctx; 153413498266Sopenharmony_ci 153513498266Sopenharmony_ci switch(query) { 153613498266Sopenharmony_ci case CF_QUERY_SOCKET: 153713498266Sopenharmony_ci DEBUGASSERT(pres2); 153813498266Sopenharmony_ci *((curl_socket_t *)pres2) = ctx->sock; 153913498266Sopenharmony_ci return CURLE_OK; 154013498266Sopenharmony_ci case CF_QUERY_CONNECT_REPLY_MS: 154113498266Sopenharmony_ci if(ctx->got_first_byte) { 154213498266Sopenharmony_ci timediff_t ms = Curl_timediff(ctx->first_byte_at, ctx->started_at); 154313498266Sopenharmony_ci *pres1 = (ms < INT_MAX)? (int)ms : INT_MAX; 154413498266Sopenharmony_ci } 154513498266Sopenharmony_ci else 154613498266Sopenharmony_ci *pres1 = -1; 154713498266Sopenharmony_ci return CURLE_OK; 154813498266Sopenharmony_ci case CF_QUERY_TIMER_CONNECT: { 154913498266Sopenharmony_ci struct curltime *when = pres2; 155013498266Sopenharmony_ci switch(ctx->transport) { 155113498266Sopenharmony_ci case TRNSPRT_UDP: 155213498266Sopenharmony_ci case TRNSPRT_QUIC: 155313498266Sopenharmony_ci /* Since UDP connected sockets work different from TCP, we use the 155413498266Sopenharmony_ci * time of the first byte from the peer as the "connect" time. */ 155513498266Sopenharmony_ci if(ctx->got_first_byte) { 155613498266Sopenharmony_ci *when = ctx->first_byte_at; 155713498266Sopenharmony_ci break; 155813498266Sopenharmony_ci } 155913498266Sopenharmony_ci FALLTHROUGH(); 156013498266Sopenharmony_ci default: 156113498266Sopenharmony_ci *when = ctx->connected_at; 156213498266Sopenharmony_ci break; 156313498266Sopenharmony_ci } 156413498266Sopenharmony_ci return CURLE_OK; 156513498266Sopenharmony_ci } 156613498266Sopenharmony_ci default: 156713498266Sopenharmony_ci break; 156813498266Sopenharmony_ci } 156913498266Sopenharmony_ci return cf->next? 157013498266Sopenharmony_ci cf->next->cft->query(cf->next, data, query, pres1, pres2) : 157113498266Sopenharmony_ci CURLE_UNKNOWN_OPTION; 157213498266Sopenharmony_ci} 157313498266Sopenharmony_ci 157413498266Sopenharmony_cistruct Curl_cftype Curl_cft_tcp = { 157513498266Sopenharmony_ci "TCP", 157613498266Sopenharmony_ci CF_TYPE_IP_CONNECT, 157713498266Sopenharmony_ci CURL_LOG_LVL_NONE, 157813498266Sopenharmony_ci cf_socket_destroy, 157913498266Sopenharmony_ci cf_tcp_connect, 158013498266Sopenharmony_ci cf_socket_close, 158113498266Sopenharmony_ci cf_socket_get_host, 158213498266Sopenharmony_ci cf_socket_adjust_pollset, 158313498266Sopenharmony_ci cf_socket_data_pending, 158413498266Sopenharmony_ci cf_socket_send, 158513498266Sopenharmony_ci cf_socket_recv, 158613498266Sopenharmony_ci cf_socket_cntrl, 158713498266Sopenharmony_ci cf_socket_conn_is_alive, 158813498266Sopenharmony_ci Curl_cf_def_conn_keep_alive, 158913498266Sopenharmony_ci cf_socket_query, 159013498266Sopenharmony_ci}; 159113498266Sopenharmony_ci 159213498266Sopenharmony_ciCURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf, 159313498266Sopenharmony_ci struct Curl_easy *data, 159413498266Sopenharmony_ci struct connectdata *conn, 159513498266Sopenharmony_ci const struct Curl_addrinfo *ai, 159613498266Sopenharmony_ci int transport) 159713498266Sopenharmony_ci{ 159813498266Sopenharmony_ci struct cf_socket_ctx *ctx = NULL; 159913498266Sopenharmony_ci struct Curl_cfilter *cf = NULL; 160013498266Sopenharmony_ci CURLcode result; 160113498266Sopenharmony_ci 160213498266Sopenharmony_ci (void)data; 160313498266Sopenharmony_ci (void)conn; 160413498266Sopenharmony_ci DEBUGASSERT(transport == TRNSPRT_TCP); 160513498266Sopenharmony_ci ctx = calloc(1, sizeof(*ctx)); 160613498266Sopenharmony_ci if(!ctx) { 160713498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 160813498266Sopenharmony_ci goto out; 160913498266Sopenharmony_ci } 161013498266Sopenharmony_ci cf_socket_ctx_init(ctx, ai, transport); 161113498266Sopenharmony_ci 161213498266Sopenharmony_ci result = Curl_cf_create(&cf, &Curl_cft_tcp, ctx); 161313498266Sopenharmony_ci 161413498266Sopenharmony_ciout: 161513498266Sopenharmony_ci *pcf = (!result)? cf : NULL; 161613498266Sopenharmony_ci if(result) { 161713498266Sopenharmony_ci Curl_safefree(cf); 161813498266Sopenharmony_ci Curl_safefree(ctx); 161913498266Sopenharmony_ci } 162013498266Sopenharmony_ci 162113498266Sopenharmony_ci return result; 162213498266Sopenharmony_ci} 162313498266Sopenharmony_ci 162413498266Sopenharmony_cistatic CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf, 162513498266Sopenharmony_ci struct Curl_easy *data) 162613498266Sopenharmony_ci{ 162713498266Sopenharmony_ci struct cf_socket_ctx *ctx = cf->ctx; 162813498266Sopenharmony_ci int rc; 162913498266Sopenharmony_ci 163013498266Sopenharmony_ci /* QUIC needs a connected socket, nonblocking */ 163113498266Sopenharmony_ci DEBUGASSERT(ctx->sock != CURL_SOCKET_BAD); 163213498266Sopenharmony_ci 163313498266Sopenharmony_ci#if defined(__APPLE__) && defined(USE_OPENSSL_QUIC) 163413498266Sopenharmony_ci (void)rc; 163513498266Sopenharmony_ci /* On macOS OpenSSL QUIC fails on connected sockets. 163613498266Sopenharmony_ci * see: <https://github.com/openssl/openssl/issues/23251> */ 163713498266Sopenharmony_ci#else 163813498266Sopenharmony_ci rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen); 163913498266Sopenharmony_ci if(-1 == rc) { 164013498266Sopenharmony_ci return socket_connect_result(data, ctx->r_ip, SOCKERRNO); 164113498266Sopenharmony_ci } 164213498266Sopenharmony_ci ctx->sock_connected = TRUE; 164313498266Sopenharmony_ci#endif 164413498266Sopenharmony_ci set_local_ip(cf, data); 164513498266Sopenharmony_ci CURL_TRC_CF(data, cf, "%s socket %" CURL_FORMAT_SOCKET_T 164613498266Sopenharmony_ci " connected: [%s:%d] -> [%s:%d]", 164713498266Sopenharmony_ci (ctx->transport == TRNSPRT_QUIC)? "QUIC" : "UDP", 164813498266Sopenharmony_ci ctx->sock, ctx->l_ip, ctx->l_port, ctx->r_ip, ctx->r_port); 164913498266Sopenharmony_ci 165013498266Sopenharmony_ci (void)curlx_nonblock(ctx->sock, TRUE); 165113498266Sopenharmony_ci switch(ctx->addr.family) { 165213498266Sopenharmony_ci#if defined(__linux__) && defined(IP_MTU_DISCOVER) 165313498266Sopenharmony_ci case AF_INET: { 165413498266Sopenharmony_ci int val = IP_PMTUDISC_DO; 165513498266Sopenharmony_ci (void)setsockopt(ctx->sock, IPPROTO_IP, IP_MTU_DISCOVER, &val, 165613498266Sopenharmony_ci sizeof(val)); 165713498266Sopenharmony_ci break; 165813498266Sopenharmony_ci } 165913498266Sopenharmony_ci#endif 166013498266Sopenharmony_ci#if defined(__linux__) && defined(IPV6_MTU_DISCOVER) 166113498266Sopenharmony_ci case AF_INET6: { 166213498266Sopenharmony_ci int val = IPV6_PMTUDISC_DO; 166313498266Sopenharmony_ci (void)setsockopt(ctx->sock, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val, 166413498266Sopenharmony_ci sizeof(val)); 166513498266Sopenharmony_ci break; 166613498266Sopenharmony_ci } 166713498266Sopenharmony_ci#endif 166813498266Sopenharmony_ci } 166913498266Sopenharmony_ci return CURLE_OK; 167013498266Sopenharmony_ci} 167113498266Sopenharmony_ci 167213498266Sopenharmony_cistatic CURLcode cf_udp_connect(struct Curl_cfilter *cf, 167313498266Sopenharmony_ci struct Curl_easy *data, 167413498266Sopenharmony_ci bool blocking, bool *done) 167513498266Sopenharmony_ci{ 167613498266Sopenharmony_ci struct cf_socket_ctx *ctx = cf->ctx; 167713498266Sopenharmony_ci CURLcode result = CURLE_COULDNT_CONNECT; 167813498266Sopenharmony_ci 167913498266Sopenharmony_ci (void)blocking; 168013498266Sopenharmony_ci if(cf->connected) { 168113498266Sopenharmony_ci *done = TRUE; 168213498266Sopenharmony_ci return CURLE_OK; 168313498266Sopenharmony_ci } 168413498266Sopenharmony_ci *done = FALSE; 168513498266Sopenharmony_ci if(ctx->sock == CURL_SOCKET_BAD) { 168613498266Sopenharmony_ci result = cf_socket_open(cf, data); 168713498266Sopenharmony_ci if(result) { 168813498266Sopenharmony_ci CURL_TRC_CF(data, cf, "cf_udp_connect(), open failed -> %d", result); 168913498266Sopenharmony_ci goto out; 169013498266Sopenharmony_ci } 169113498266Sopenharmony_ci 169213498266Sopenharmony_ci if(ctx->transport == TRNSPRT_QUIC) { 169313498266Sopenharmony_ci result = cf_udp_setup_quic(cf, data); 169413498266Sopenharmony_ci if(result) 169513498266Sopenharmony_ci goto out; 169613498266Sopenharmony_ci CURL_TRC_CF(data, cf, "cf_udp_connect(), opened socket=%" 169713498266Sopenharmony_ci CURL_FORMAT_SOCKET_T " (%s:%d)", 169813498266Sopenharmony_ci ctx->sock, ctx->l_ip, ctx->l_port); 169913498266Sopenharmony_ci } 170013498266Sopenharmony_ci else { 170113498266Sopenharmony_ci CURL_TRC_CF(data, cf, "cf_udp_connect(), opened socket=%" 170213498266Sopenharmony_ci CURL_FORMAT_SOCKET_T " (unconnected)", ctx->sock); 170313498266Sopenharmony_ci } 170413498266Sopenharmony_ci *done = TRUE; 170513498266Sopenharmony_ci cf->connected = TRUE; 170613498266Sopenharmony_ci } 170713498266Sopenharmony_ciout: 170813498266Sopenharmony_ci return result; 170913498266Sopenharmony_ci} 171013498266Sopenharmony_ci 171113498266Sopenharmony_cistruct Curl_cftype Curl_cft_udp = { 171213498266Sopenharmony_ci "UDP", 171313498266Sopenharmony_ci CF_TYPE_IP_CONNECT, 171413498266Sopenharmony_ci CURL_LOG_LVL_NONE, 171513498266Sopenharmony_ci cf_socket_destroy, 171613498266Sopenharmony_ci cf_udp_connect, 171713498266Sopenharmony_ci cf_socket_close, 171813498266Sopenharmony_ci cf_socket_get_host, 171913498266Sopenharmony_ci cf_socket_adjust_pollset, 172013498266Sopenharmony_ci cf_socket_data_pending, 172113498266Sopenharmony_ci cf_socket_send, 172213498266Sopenharmony_ci cf_socket_recv, 172313498266Sopenharmony_ci cf_socket_cntrl, 172413498266Sopenharmony_ci cf_socket_conn_is_alive, 172513498266Sopenharmony_ci Curl_cf_def_conn_keep_alive, 172613498266Sopenharmony_ci cf_socket_query, 172713498266Sopenharmony_ci}; 172813498266Sopenharmony_ci 172913498266Sopenharmony_ciCURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf, 173013498266Sopenharmony_ci struct Curl_easy *data, 173113498266Sopenharmony_ci struct connectdata *conn, 173213498266Sopenharmony_ci const struct Curl_addrinfo *ai, 173313498266Sopenharmony_ci int transport) 173413498266Sopenharmony_ci{ 173513498266Sopenharmony_ci struct cf_socket_ctx *ctx = NULL; 173613498266Sopenharmony_ci struct Curl_cfilter *cf = NULL; 173713498266Sopenharmony_ci CURLcode result; 173813498266Sopenharmony_ci 173913498266Sopenharmony_ci (void)data; 174013498266Sopenharmony_ci (void)conn; 174113498266Sopenharmony_ci DEBUGASSERT(transport == TRNSPRT_UDP || transport == TRNSPRT_QUIC); 174213498266Sopenharmony_ci ctx = calloc(1, sizeof(*ctx)); 174313498266Sopenharmony_ci if(!ctx) { 174413498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 174513498266Sopenharmony_ci goto out; 174613498266Sopenharmony_ci } 174713498266Sopenharmony_ci cf_socket_ctx_init(ctx, ai, transport); 174813498266Sopenharmony_ci 174913498266Sopenharmony_ci result = Curl_cf_create(&cf, &Curl_cft_udp, ctx); 175013498266Sopenharmony_ci 175113498266Sopenharmony_ciout: 175213498266Sopenharmony_ci *pcf = (!result)? cf : NULL; 175313498266Sopenharmony_ci if(result) { 175413498266Sopenharmony_ci Curl_safefree(cf); 175513498266Sopenharmony_ci Curl_safefree(ctx); 175613498266Sopenharmony_ci } 175713498266Sopenharmony_ci 175813498266Sopenharmony_ci return result; 175913498266Sopenharmony_ci} 176013498266Sopenharmony_ci 176113498266Sopenharmony_ci/* this is the TCP filter which can also handle this case */ 176213498266Sopenharmony_cistruct Curl_cftype Curl_cft_unix = { 176313498266Sopenharmony_ci "UNIX", 176413498266Sopenharmony_ci CF_TYPE_IP_CONNECT, 176513498266Sopenharmony_ci CURL_LOG_LVL_NONE, 176613498266Sopenharmony_ci cf_socket_destroy, 176713498266Sopenharmony_ci cf_tcp_connect, 176813498266Sopenharmony_ci cf_socket_close, 176913498266Sopenharmony_ci cf_socket_get_host, 177013498266Sopenharmony_ci cf_socket_adjust_pollset, 177113498266Sopenharmony_ci cf_socket_data_pending, 177213498266Sopenharmony_ci cf_socket_send, 177313498266Sopenharmony_ci cf_socket_recv, 177413498266Sopenharmony_ci cf_socket_cntrl, 177513498266Sopenharmony_ci cf_socket_conn_is_alive, 177613498266Sopenharmony_ci Curl_cf_def_conn_keep_alive, 177713498266Sopenharmony_ci cf_socket_query, 177813498266Sopenharmony_ci}; 177913498266Sopenharmony_ci 178013498266Sopenharmony_ciCURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf, 178113498266Sopenharmony_ci struct Curl_easy *data, 178213498266Sopenharmony_ci struct connectdata *conn, 178313498266Sopenharmony_ci const struct Curl_addrinfo *ai, 178413498266Sopenharmony_ci int transport) 178513498266Sopenharmony_ci{ 178613498266Sopenharmony_ci struct cf_socket_ctx *ctx = NULL; 178713498266Sopenharmony_ci struct Curl_cfilter *cf = NULL; 178813498266Sopenharmony_ci CURLcode result; 178913498266Sopenharmony_ci 179013498266Sopenharmony_ci (void)data; 179113498266Sopenharmony_ci (void)conn; 179213498266Sopenharmony_ci DEBUGASSERT(transport == TRNSPRT_UNIX); 179313498266Sopenharmony_ci ctx = calloc(1, sizeof(*ctx)); 179413498266Sopenharmony_ci if(!ctx) { 179513498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 179613498266Sopenharmony_ci goto out; 179713498266Sopenharmony_ci } 179813498266Sopenharmony_ci cf_socket_ctx_init(ctx, ai, transport); 179913498266Sopenharmony_ci 180013498266Sopenharmony_ci result = Curl_cf_create(&cf, &Curl_cft_unix, ctx); 180113498266Sopenharmony_ci 180213498266Sopenharmony_ciout: 180313498266Sopenharmony_ci *pcf = (!result)? cf : NULL; 180413498266Sopenharmony_ci if(result) { 180513498266Sopenharmony_ci Curl_safefree(cf); 180613498266Sopenharmony_ci Curl_safefree(ctx); 180713498266Sopenharmony_ci } 180813498266Sopenharmony_ci 180913498266Sopenharmony_ci return result; 181013498266Sopenharmony_ci} 181113498266Sopenharmony_ci 181213498266Sopenharmony_cistatic CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf, 181313498266Sopenharmony_ci struct Curl_easy *data, 181413498266Sopenharmony_ci bool blocking, bool *done) 181513498266Sopenharmony_ci{ 181613498266Sopenharmony_ci /* we start accepted, if we ever close, we cannot go on */ 181713498266Sopenharmony_ci (void)data; 181813498266Sopenharmony_ci (void)blocking; 181913498266Sopenharmony_ci if(cf->connected) { 182013498266Sopenharmony_ci *done = TRUE; 182113498266Sopenharmony_ci return CURLE_OK; 182213498266Sopenharmony_ci } 182313498266Sopenharmony_ci return CURLE_FAILED_INIT; 182413498266Sopenharmony_ci} 182513498266Sopenharmony_ci 182613498266Sopenharmony_cistruct Curl_cftype Curl_cft_tcp_accept = { 182713498266Sopenharmony_ci "TCP-ACCEPT", 182813498266Sopenharmony_ci CF_TYPE_IP_CONNECT, 182913498266Sopenharmony_ci CURL_LOG_LVL_NONE, 183013498266Sopenharmony_ci cf_socket_destroy, 183113498266Sopenharmony_ci cf_tcp_accept_connect, 183213498266Sopenharmony_ci cf_socket_close, 183313498266Sopenharmony_ci cf_socket_get_host, /* TODO: not accurate */ 183413498266Sopenharmony_ci cf_socket_adjust_pollset, 183513498266Sopenharmony_ci cf_socket_data_pending, 183613498266Sopenharmony_ci cf_socket_send, 183713498266Sopenharmony_ci cf_socket_recv, 183813498266Sopenharmony_ci cf_socket_cntrl, 183913498266Sopenharmony_ci cf_socket_conn_is_alive, 184013498266Sopenharmony_ci Curl_cf_def_conn_keep_alive, 184113498266Sopenharmony_ci cf_socket_query, 184213498266Sopenharmony_ci}; 184313498266Sopenharmony_ci 184413498266Sopenharmony_ciCURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data, 184513498266Sopenharmony_ci struct connectdata *conn, 184613498266Sopenharmony_ci int sockindex, curl_socket_t *s) 184713498266Sopenharmony_ci{ 184813498266Sopenharmony_ci CURLcode result; 184913498266Sopenharmony_ci struct Curl_cfilter *cf = NULL; 185013498266Sopenharmony_ci struct cf_socket_ctx *ctx = NULL; 185113498266Sopenharmony_ci 185213498266Sopenharmony_ci /* replace any existing */ 185313498266Sopenharmony_ci Curl_conn_cf_discard_all(data, conn, sockindex); 185413498266Sopenharmony_ci DEBUGASSERT(conn->sock[sockindex] == CURL_SOCKET_BAD); 185513498266Sopenharmony_ci 185613498266Sopenharmony_ci ctx = calloc(1, sizeof(*ctx)); 185713498266Sopenharmony_ci if(!ctx) { 185813498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 185913498266Sopenharmony_ci goto out; 186013498266Sopenharmony_ci } 186113498266Sopenharmony_ci ctx->transport = conn->transport; 186213498266Sopenharmony_ci ctx->sock = *s; 186313498266Sopenharmony_ci ctx->accepted = FALSE; 186413498266Sopenharmony_ci result = Curl_cf_create(&cf, &Curl_cft_tcp_accept, ctx); 186513498266Sopenharmony_ci if(result) 186613498266Sopenharmony_ci goto out; 186713498266Sopenharmony_ci Curl_conn_cf_add(data, conn, sockindex, cf); 186813498266Sopenharmony_ci 186913498266Sopenharmony_ci conn->sock[sockindex] = ctx->sock; 187013498266Sopenharmony_ci set_local_ip(cf, data); 187113498266Sopenharmony_ci ctx->active = TRUE; 187213498266Sopenharmony_ci ctx->connected_at = Curl_now(); 187313498266Sopenharmony_ci cf->connected = TRUE; 187413498266Sopenharmony_ci CURL_TRC_CF(data, cf, "Curl_conn_tcp_listen_set(%" 187513498266Sopenharmony_ci CURL_FORMAT_SOCKET_T ")", ctx->sock); 187613498266Sopenharmony_ci 187713498266Sopenharmony_ciout: 187813498266Sopenharmony_ci if(result) { 187913498266Sopenharmony_ci Curl_safefree(cf); 188013498266Sopenharmony_ci Curl_safefree(ctx); 188113498266Sopenharmony_ci } 188213498266Sopenharmony_ci return result; 188313498266Sopenharmony_ci} 188413498266Sopenharmony_ci 188513498266Sopenharmony_cistatic void set_accepted_remote_ip(struct Curl_cfilter *cf, 188613498266Sopenharmony_ci struct Curl_easy *data) 188713498266Sopenharmony_ci{ 188813498266Sopenharmony_ci struct cf_socket_ctx *ctx = cf->ctx; 188913498266Sopenharmony_ci#ifdef HAVE_GETPEERNAME 189013498266Sopenharmony_ci char buffer[STRERROR_LEN]; 189113498266Sopenharmony_ci struct Curl_sockaddr_storage ssrem; 189213498266Sopenharmony_ci curl_socklen_t plen; 189313498266Sopenharmony_ci 189413498266Sopenharmony_ci ctx->r_ip[0] = 0; 189513498266Sopenharmony_ci ctx->r_port = 0; 189613498266Sopenharmony_ci plen = sizeof(ssrem); 189713498266Sopenharmony_ci memset(&ssrem, 0, plen); 189813498266Sopenharmony_ci if(getpeername(ctx->sock, (struct sockaddr*) &ssrem, &plen)) { 189913498266Sopenharmony_ci int error = SOCKERRNO; 190013498266Sopenharmony_ci failf(data, "getpeername() failed with errno %d: %s", 190113498266Sopenharmony_ci error, Curl_strerror(error, buffer, sizeof(buffer))); 190213498266Sopenharmony_ci return; 190313498266Sopenharmony_ci } 190413498266Sopenharmony_ci if(!Curl_addr2string((struct sockaddr*)&ssrem, plen, 190513498266Sopenharmony_ci ctx->r_ip, &ctx->r_port)) { 190613498266Sopenharmony_ci failf(data, "ssrem inet_ntop() failed with errno %d: %s", 190713498266Sopenharmony_ci errno, Curl_strerror(errno, buffer, sizeof(buffer))); 190813498266Sopenharmony_ci return; 190913498266Sopenharmony_ci } 191013498266Sopenharmony_ci#else 191113498266Sopenharmony_ci ctx->r_ip[0] = 0; 191213498266Sopenharmony_ci ctx->r_port = 0; 191313498266Sopenharmony_ci (void)data; 191413498266Sopenharmony_ci#endif 191513498266Sopenharmony_ci} 191613498266Sopenharmony_ci 191713498266Sopenharmony_ciCURLcode Curl_conn_tcp_accepted_set(struct Curl_easy *data, 191813498266Sopenharmony_ci struct connectdata *conn, 191913498266Sopenharmony_ci int sockindex, curl_socket_t *s) 192013498266Sopenharmony_ci{ 192113498266Sopenharmony_ci struct Curl_cfilter *cf = NULL; 192213498266Sopenharmony_ci struct cf_socket_ctx *ctx = NULL; 192313498266Sopenharmony_ci 192413498266Sopenharmony_ci cf = conn->cfilter[sockindex]; 192513498266Sopenharmony_ci if(!cf || cf->cft != &Curl_cft_tcp_accept) 192613498266Sopenharmony_ci return CURLE_FAILED_INIT; 192713498266Sopenharmony_ci 192813498266Sopenharmony_ci ctx = cf->ctx; 192913498266Sopenharmony_ci /* discard the listen socket */ 193013498266Sopenharmony_ci socket_close(data, conn, TRUE, ctx->sock); 193113498266Sopenharmony_ci ctx->sock = *s; 193213498266Sopenharmony_ci conn->sock[sockindex] = ctx->sock; 193313498266Sopenharmony_ci set_accepted_remote_ip(cf, data); 193413498266Sopenharmony_ci set_local_ip(cf, data); 193513498266Sopenharmony_ci ctx->active = TRUE; 193613498266Sopenharmony_ci ctx->accepted = TRUE; 193713498266Sopenharmony_ci ctx->connected_at = Curl_now(); 193813498266Sopenharmony_ci cf->connected = TRUE; 193913498266Sopenharmony_ci CURL_TRC_CF(data, cf, "accepted_set(sock=%" CURL_FORMAT_SOCKET_T 194013498266Sopenharmony_ci ", remote=%s port=%d)", 194113498266Sopenharmony_ci ctx->sock, ctx->r_ip, ctx->r_port); 194213498266Sopenharmony_ci 194313498266Sopenharmony_ci return CURLE_OK; 194413498266Sopenharmony_ci} 194513498266Sopenharmony_ci 194613498266Sopenharmony_ci/** 194713498266Sopenharmony_ci * Return TRUE iff `cf` is a socket filter. 194813498266Sopenharmony_ci */ 194913498266Sopenharmony_cistatic bool cf_is_socket(struct Curl_cfilter *cf) 195013498266Sopenharmony_ci{ 195113498266Sopenharmony_ci return cf && (cf->cft == &Curl_cft_tcp || 195213498266Sopenharmony_ci cf->cft == &Curl_cft_udp || 195313498266Sopenharmony_ci cf->cft == &Curl_cft_unix || 195413498266Sopenharmony_ci cf->cft == &Curl_cft_tcp_accept); 195513498266Sopenharmony_ci} 195613498266Sopenharmony_ci 195713498266Sopenharmony_ciCURLcode Curl_cf_socket_peek(struct Curl_cfilter *cf, 195813498266Sopenharmony_ci struct Curl_easy *data, 195913498266Sopenharmony_ci curl_socket_t *psock, 196013498266Sopenharmony_ci const struct Curl_sockaddr_ex **paddr, 196113498266Sopenharmony_ci const char **pr_ip_str, int *pr_port, 196213498266Sopenharmony_ci const char **pl_ip_str, int *pl_port) 196313498266Sopenharmony_ci{ 196413498266Sopenharmony_ci if(cf_is_socket(cf) && cf->ctx) { 196513498266Sopenharmony_ci struct cf_socket_ctx *ctx = cf->ctx; 196613498266Sopenharmony_ci 196713498266Sopenharmony_ci if(psock) 196813498266Sopenharmony_ci *psock = ctx->sock; 196913498266Sopenharmony_ci if(paddr) 197013498266Sopenharmony_ci *paddr = &ctx->addr; 197113498266Sopenharmony_ci if(pr_ip_str) 197213498266Sopenharmony_ci *pr_ip_str = ctx->r_ip; 197313498266Sopenharmony_ci if(pr_port) 197413498266Sopenharmony_ci *pr_port = ctx->r_port; 197513498266Sopenharmony_ci if(pl_port ||pl_ip_str) { 197613498266Sopenharmony_ci set_local_ip(cf, data); 197713498266Sopenharmony_ci if(pl_ip_str) 197813498266Sopenharmony_ci *pl_ip_str = ctx->l_ip; 197913498266Sopenharmony_ci if(pl_port) 198013498266Sopenharmony_ci *pl_port = ctx->l_port; 198113498266Sopenharmony_ci } 198213498266Sopenharmony_ci return CURLE_OK; 198313498266Sopenharmony_ci } 198413498266Sopenharmony_ci return CURLE_FAILED_INIT; 198513498266Sopenharmony_ci} 1986