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 "sendf.h" 5813498266Sopenharmony_ci#include "if2ip.h" 5913498266Sopenharmony_ci#include "strerror.h" 6013498266Sopenharmony_ci#include "cfilters.h" 6113498266Sopenharmony_ci#include "connect.h" 6213498266Sopenharmony_ci#include "cf-haproxy.h" 6313498266Sopenharmony_ci#include "cf-https-connect.h" 6413498266Sopenharmony_ci#include "cf-socket.h" 6513498266Sopenharmony_ci#include "select.h" 6613498266Sopenharmony_ci#include "url.h" /* for Curl_safefree() */ 6713498266Sopenharmony_ci#include "multiif.h" 6813498266Sopenharmony_ci#include "sockaddr.h" /* required for Curl_sockaddr_storage */ 6913498266Sopenharmony_ci#include "inet_ntop.h" 7013498266Sopenharmony_ci#include "inet_pton.h" 7113498266Sopenharmony_ci#include "vtls/vtls.h" /* for vtsl cfilters */ 7213498266Sopenharmony_ci#include "progress.h" 7313498266Sopenharmony_ci#include "warnless.h" 7413498266Sopenharmony_ci#include "conncache.h" 7513498266Sopenharmony_ci#include "multihandle.h" 7613498266Sopenharmony_ci#include "share.h" 7713498266Sopenharmony_ci#include "version_win32.h" 7813498266Sopenharmony_ci#include "vquic/vquic.h" /* for quic cfilters */ 7913498266Sopenharmony_ci#include "http_proxy.h" 8013498266Sopenharmony_ci#include "socks.h" 8113498266Sopenharmony_ci 8213498266Sopenharmony_ci/* The last 3 #include files should be in this order */ 8313498266Sopenharmony_ci#include "curl_printf.h" 8413498266Sopenharmony_ci#include "curl_memory.h" 8513498266Sopenharmony_ci#include "memdebug.h" 8613498266Sopenharmony_ci 8713498266Sopenharmony_ci#ifndef ARRAYSIZE 8813498266Sopenharmony_ci#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) 8913498266Sopenharmony_ci#endif 9013498266Sopenharmony_ci 9113498266Sopenharmony_ci/* 9213498266Sopenharmony_ci * Curl_timeleft() returns the amount of milliseconds left allowed for the 9313498266Sopenharmony_ci * transfer/connection. If the value is 0, there's no timeout (ie there's 9413498266Sopenharmony_ci * infinite time left). If the value is negative, the timeout time has already 9513498266Sopenharmony_ci * elapsed. 9613498266Sopenharmony_ci * @param data the transfer to check on 9713498266Sopenharmony_ci * @param nowp timestamp to use for calculdation, NULL to use Curl_now() 9813498266Sopenharmony_ci * @param duringconnect TRUE iff connect timeout is also taken into account. 9913498266Sopenharmony_ci * @unittest: 1303 10013498266Sopenharmony_ci */ 10113498266Sopenharmony_citimediff_t Curl_timeleft(struct Curl_easy *data, 10213498266Sopenharmony_ci struct curltime *nowp, 10313498266Sopenharmony_ci bool duringconnect) 10413498266Sopenharmony_ci{ 10513498266Sopenharmony_ci timediff_t timeleft_ms = 0; 10613498266Sopenharmony_ci timediff_t ctimeleft_ms = 0; 10713498266Sopenharmony_ci struct curltime now; 10813498266Sopenharmony_ci 10913498266Sopenharmony_ci /* The duration of a connect and the total transfer are calculated from two 11013498266Sopenharmony_ci different time-stamps. It can end up with the total timeout being reached 11113498266Sopenharmony_ci before the connect timeout expires and we must acknowledge whichever 11213498266Sopenharmony_ci timeout that is reached first. The total timeout is set per entire 11313498266Sopenharmony_ci operation, while the connect timeout is set per connect. */ 11413498266Sopenharmony_ci if(data->set.timeout <= 0 && !duringconnect) 11513498266Sopenharmony_ci return 0; /* no timeout in place or checked, return "no limit" */ 11613498266Sopenharmony_ci 11713498266Sopenharmony_ci if(!nowp) { 11813498266Sopenharmony_ci now = Curl_now(); 11913498266Sopenharmony_ci nowp = &now; 12013498266Sopenharmony_ci } 12113498266Sopenharmony_ci 12213498266Sopenharmony_ci if(data->set.timeout > 0) { 12313498266Sopenharmony_ci timeleft_ms = data->set.timeout - 12413498266Sopenharmony_ci Curl_timediff(*nowp, data->progress.t_startop); 12513498266Sopenharmony_ci if(!timeleft_ms) 12613498266Sopenharmony_ci timeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */ 12713498266Sopenharmony_ci if(!duringconnect) 12813498266Sopenharmony_ci return timeleft_ms; /* no connect check, this is it */ 12913498266Sopenharmony_ci } 13013498266Sopenharmony_ci 13113498266Sopenharmony_ci if(duringconnect) { 13213498266Sopenharmony_ci timediff_t ctimeout_ms = (data->set.connecttimeout > 0) ? 13313498266Sopenharmony_ci data->set.connecttimeout : DEFAULT_CONNECT_TIMEOUT; 13413498266Sopenharmony_ci ctimeleft_ms = ctimeout_ms - 13513498266Sopenharmony_ci Curl_timediff(*nowp, data->progress.t_startsingle); 13613498266Sopenharmony_ci if(!ctimeleft_ms) 13713498266Sopenharmony_ci ctimeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */ 13813498266Sopenharmony_ci if(!timeleft_ms) 13913498266Sopenharmony_ci return ctimeleft_ms; /* no general timeout, this is it */ 14013498266Sopenharmony_ci } 14113498266Sopenharmony_ci /* return minimal time left or max amount already expired */ 14213498266Sopenharmony_ci return (ctimeleft_ms < timeleft_ms)? ctimeleft_ms : timeleft_ms; 14313498266Sopenharmony_ci} 14413498266Sopenharmony_ci 14513498266Sopenharmony_ci/* Copies connection info into the transfer handle to make it available when 14613498266Sopenharmony_ci the transfer handle is no longer associated with the connection. */ 14713498266Sopenharmony_civoid Curl_persistconninfo(struct Curl_easy *data, struct connectdata *conn, 14813498266Sopenharmony_ci char *local_ip, int local_port) 14913498266Sopenharmony_ci{ 15013498266Sopenharmony_ci memcpy(data->info.conn_primary_ip, conn->primary_ip, MAX_IPADR_LEN); 15113498266Sopenharmony_ci if(local_ip && local_ip[0]) 15213498266Sopenharmony_ci memcpy(data->info.conn_local_ip, local_ip, MAX_IPADR_LEN); 15313498266Sopenharmony_ci else 15413498266Sopenharmony_ci data->info.conn_local_ip[0] = 0; 15513498266Sopenharmony_ci data->info.conn_scheme = conn->handler->scheme; 15613498266Sopenharmony_ci /* conn_protocol can only provide "old" protocols */ 15713498266Sopenharmony_ci data->info.conn_protocol = (conn->handler->protocol) & CURLPROTO_MASK; 15813498266Sopenharmony_ci data->info.conn_primary_port = conn->port; 15913498266Sopenharmony_ci data->info.conn_remote_port = conn->remote_port; 16013498266Sopenharmony_ci data->info.conn_local_port = local_port; 16113498266Sopenharmony_ci} 16213498266Sopenharmony_ci 16313498266Sopenharmony_cistatic const struct Curl_addrinfo * 16413498266Sopenharmony_ciaddr_first_match(const struct Curl_addrinfo *addr, int family) 16513498266Sopenharmony_ci{ 16613498266Sopenharmony_ci while(addr) { 16713498266Sopenharmony_ci if(addr->ai_family == family) 16813498266Sopenharmony_ci return addr; 16913498266Sopenharmony_ci addr = addr->ai_next; 17013498266Sopenharmony_ci } 17113498266Sopenharmony_ci return NULL; 17213498266Sopenharmony_ci} 17313498266Sopenharmony_ci 17413498266Sopenharmony_cistatic const struct Curl_addrinfo * 17513498266Sopenharmony_ciaddr_next_match(const struct Curl_addrinfo *addr, int family) 17613498266Sopenharmony_ci{ 17713498266Sopenharmony_ci while(addr && addr->ai_next) { 17813498266Sopenharmony_ci addr = addr->ai_next; 17913498266Sopenharmony_ci if(addr->ai_family == family) 18013498266Sopenharmony_ci return addr; 18113498266Sopenharmony_ci } 18213498266Sopenharmony_ci return NULL; 18313498266Sopenharmony_ci} 18413498266Sopenharmony_ci 18513498266Sopenharmony_ci/* retrieves ip address and port from a sockaddr structure. 18613498266Sopenharmony_ci note it calls Curl_inet_ntop which sets errno on fail, not SOCKERRNO. */ 18713498266Sopenharmony_cibool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen, 18813498266Sopenharmony_ci char *addr, int *port) 18913498266Sopenharmony_ci{ 19013498266Sopenharmony_ci struct sockaddr_in *si = NULL; 19113498266Sopenharmony_ci#ifdef ENABLE_IPV6 19213498266Sopenharmony_ci struct sockaddr_in6 *si6 = NULL; 19313498266Sopenharmony_ci#endif 19413498266Sopenharmony_ci#if (defined(HAVE_SYS_UN_H) || defined(WIN32_SOCKADDR_UN)) && defined(AF_UNIX) 19513498266Sopenharmony_ci struct sockaddr_un *su = NULL; 19613498266Sopenharmony_ci#else 19713498266Sopenharmony_ci (void)salen; 19813498266Sopenharmony_ci#endif 19913498266Sopenharmony_ci 20013498266Sopenharmony_ci switch(sa->sa_family) { 20113498266Sopenharmony_ci case AF_INET: 20213498266Sopenharmony_ci si = (struct sockaddr_in *)(void *) sa; 20313498266Sopenharmony_ci if(Curl_inet_ntop(sa->sa_family, &si->sin_addr, 20413498266Sopenharmony_ci addr, MAX_IPADR_LEN)) { 20513498266Sopenharmony_ci unsigned short us_port = ntohs(si->sin_port); 20613498266Sopenharmony_ci *port = us_port; 20713498266Sopenharmony_ci return TRUE; 20813498266Sopenharmony_ci } 20913498266Sopenharmony_ci break; 21013498266Sopenharmony_ci#ifdef ENABLE_IPV6 21113498266Sopenharmony_ci case AF_INET6: 21213498266Sopenharmony_ci si6 = (struct sockaddr_in6 *)(void *) sa; 21313498266Sopenharmony_ci if(Curl_inet_ntop(sa->sa_family, &si6->sin6_addr, 21413498266Sopenharmony_ci addr, MAX_IPADR_LEN)) { 21513498266Sopenharmony_ci unsigned short us_port = ntohs(si6->sin6_port); 21613498266Sopenharmony_ci *port = us_port; 21713498266Sopenharmony_ci return TRUE; 21813498266Sopenharmony_ci } 21913498266Sopenharmony_ci break; 22013498266Sopenharmony_ci#endif 22113498266Sopenharmony_ci#if (defined(HAVE_SYS_UN_H) || defined(WIN32_SOCKADDR_UN)) && defined(AF_UNIX) 22213498266Sopenharmony_ci case AF_UNIX: 22313498266Sopenharmony_ci if(salen > (curl_socklen_t)sizeof(CURL_SA_FAMILY_T)) { 22413498266Sopenharmony_ci su = (struct sockaddr_un*)sa; 22513498266Sopenharmony_ci msnprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path); 22613498266Sopenharmony_ci } 22713498266Sopenharmony_ci else 22813498266Sopenharmony_ci addr[0] = 0; /* socket with no name */ 22913498266Sopenharmony_ci *port = 0; 23013498266Sopenharmony_ci return TRUE; 23113498266Sopenharmony_ci#endif 23213498266Sopenharmony_ci default: 23313498266Sopenharmony_ci break; 23413498266Sopenharmony_ci } 23513498266Sopenharmony_ci 23613498266Sopenharmony_ci addr[0] = '\0'; 23713498266Sopenharmony_ci *port = 0; 23813498266Sopenharmony_ci errno = EAFNOSUPPORT; 23913498266Sopenharmony_ci return FALSE; 24013498266Sopenharmony_ci} 24113498266Sopenharmony_ci 24213498266Sopenharmony_cistruct connfind { 24313498266Sopenharmony_ci curl_off_t id_tofind; 24413498266Sopenharmony_ci struct connectdata *found; 24513498266Sopenharmony_ci}; 24613498266Sopenharmony_ci 24713498266Sopenharmony_cistatic int conn_is_conn(struct Curl_easy *data, 24813498266Sopenharmony_ci struct connectdata *conn, void *param) 24913498266Sopenharmony_ci{ 25013498266Sopenharmony_ci struct connfind *f = (struct connfind *)param; 25113498266Sopenharmony_ci (void)data; 25213498266Sopenharmony_ci if(conn->connection_id == f->id_tofind) { 25313498266Sopenharmony_ci f->found = conn; 25413498266Sopenharmony_ci return 1; 25513498266Sopenharmony_ci } 25613498266Sopenharmony_ci return 0; 25713498266Sopenharmony_ci} 25813498266Sopenharmony_ci 25913498266Sopenharmony_ci/* 26013498266Sopenharmony_ci * Used to extract socket and connectdata struct for the most recent 26113498266Sopenharmony_ci * transfer on the given Curl_easy. 26213498266Sopenharmony_ci * 26313498266Sopenharmony_ci * The returned socket will be CURL_SOCKET_BAD in case of failure! 26413498266Sopenharmony_ci */ 26513498266Sopenharmony_cicurl_socket_t Curl_getconnectinfo(struct Curl_easy *data, 26613498266Sopenharmony_ci struct connectdata **connp) 26713498266Sopenharmony_ci{ 26813498266Sopenharmony_ci DEBUGASSERT(data); 26913498266Sopenharmony_ci 27013498266Sopenharmony_ci /* this works for an easy handle: 27113498266Sopenharmony_ci * - that has been used for curl_easy_perform() 27213498266Sopenharmony_ci * - that is associated with a multi handle, and whose connection 27313498266Sopenharmony_ci * was detached with CURLOPT_CONNECT_ONLY 27413498266Sopenharmony_ci */ 27513498266Sopenharmony_ci if((data->state.lastconnect_id != -1) && (data->multi_easy || data->multi)) { 27613498266Sopenharmony_ci struct connectdata *c; 27713498266Sopenharmony_ci struct connfind find; 27813498266Sopenharmony_ci find.id_tofind = data->state.lastconnect_id; 27913498266Sopenharmony_ci find.found = NULL; 28013498266Sopenharmony_ci 28113498266Sopenharmony_ci Curl_conncache_foreach(data, 28213498266Sopenharmony_ci data->share && (data->share->specifier 28313498266Sopenharmony_ci & (1<< CURL_LOCK_DATA_CONNECT))? 28413498266Sopenharmony_ci &data->share->conn_cache: 28513498266Sopenharmony_ci data->multi_easy? 28613498266Sopenharmony_ci &data->multi_easy->conn_cache: 28713498266Sopenharmony_ci &data->multi->conn_cache, &find, conn_is_conn); 28813498266Sopenharmony_ci 28913498266Sopenharmony_ci if(!find.found) { 29013498266Sopenharmony_ci data->state.lastconnect_id = -1; 29113498266Sopenharmony_ci return CURL_SOCKET_BAD; 29213498266Sopenharmony_ci } 29313498266Sopenharmony_ci 29413498266Sopenharmony_ci c = find.found; 29513498266Sopenharmony_ci if(connp) 29613498266Sopenharmony_ci /* only store this if the caller cares for it */ 29713498266Sopenharmony_ci *connp = c; 29813498266Sopenharmony_ci return c->sock[FIRSTSOCKET]; 29913498266Sopenharmony_ci } 30013498266Sopenharmony_ci return CURL_SOCKET_BAD; 30113498266Sopenharmony_ci} 30213498266Sopenharmony_ci 30313498266Sopenharmony_ci/* 30413498266Sopenharmony_ci * Curl_conncontrol() marks streams or connection for closure. 30513498266Sopenharmony_ci */ 30613498266Sopenharmony_civoid Curl_conncontrol(struct connectdata *conn, 30713498266Sopenharmony_ci int ctrl /* see defines in header */ 30813498266Sopenharmony_ci#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) 30913498266Sopenharmony_ci , const char *reason 31013498266Sopenharmony_ci#endif 31113498266Sopenharmony_ci ) 31213498266Sopenharmony_ci{ 31313498266Sopenharmony_ci /* close if a connection, or a stream that isn't multiplexed. */ 31413498266Sopenharmony_ci /* This function will be called both before and after this connection is 31513498266Sopenharmony_ci associated with a transfer. */ 31613498266Sopenharmony_ci bool closeit, is_multiplex; 31713498266Sopenharmony_ci DEBUGASSERT(conn); 31813498266Sopenharmony_ci#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) 31913498266Sopenharmony_ci (void)reason; /* useful for debugging */ 32013498266Sopenharmony_ci#endif 32113498266Sopenharmony_ci is_multiplex = Curl_conn_is_multiplex(conn, FIRSTSOCKET); 32213498266Sopenharmony_ci closeit = (ctrl == CONNCTRL_CONNECTION) || 32313498266Sopenharmony_ci ((ctrl == CONNCTRL_STREAM) && !is_multiplex); 32413498266Sopenharmony_ci if((ctrl == CONNCTRL_STREAM) && is_multiplex) 32513498266Sopenharmony_ci ; /* stream signal on multiplex conn never affects close state */ 32613498266Sopenharmony_ci else if((bit)closeit != conn->bits.close) { 32713498266Sopenharmony_ci conn->bits.close = closeit; /* the only place in the source code that 32813498266Sopenharmony_ci should assign this bit */ 32913498266Sopenharmony_ci } 33013498266Sopenharmony_ci} 33113498266Sopenharmony_ci 33213498266Sopenharmony_ci/** 33313498266Sopenharmony_ci * job walking the matching addr infos, creating a sub-cfilter with the 33413498266Sopenharmony_ci * provided method `cf_create` and running setup/connect on it. 33513498266Sopenharmony_ci */ 33613498266Sopenharmony_cistruct eyeballer { 33713498266Sopenharmony_ci const char *name; 33813498266Sopenharmony_ci const struct Curl_addrinfo *first; /* complete address list, not owned */ 33913498266Sopenharmony_ci const struct Curl_addrinfo *addr; /* List of addresses to try, not owned */ 34013498266Sopenharmony_ci int ai_family; /* matching address family only */ 34113498266Sopenharmony_ci cf_ip_connect_create *cf_create; /* for creating cf */ 34213498266Sopenharmony_ci struct Curl_cfilter *cf; /* current sub-cfilter connecting */ 34313498266Sopenharmony_ci struct eyeballer *primary; /* eyeballer this one is backup for */ 34413498266Sopenharmony_ci timediff_t delay_ms; /* delay until start */ 34513498266Sopenharmony_ci struct curltime started; /* start of current attempt */ 34613498266Sopenharmony_ci timediff_t timeoutms; /* timeout for current attempt */ 34713498266Sopenharmony_ci expire_id timeout_id; /* ID for Curl_expire() */ 34813498266Sopenharmony_ci CURLcode result; 34913498266Sopenharmony_ci int error; 35013498266Sopenharmony_ci BIT(rewinded); /* if we rewinded the addr list */ 35113498266Sopenharmony_ci BIT(has_started); /* attempts have started */ 35213498266Sopenharmony_ci BIT(is_done); /* out of addresses/time */ 35313498266Sopenharmony_ci BIT(connected); /* cf has connected */ 35413498266Sopenharmony_ci BIT(inconclusive); /* connect was not a hard failure, we 35513498266Sopenharmony_ci * might talk to a restarting server */ 35613498266Sopenharmony_ci}; 35713498266Sopenharmony_ci 35813498266Sopenharmony_ci 35913498266Sopenharmony_citypedef enum { 36013498266Sopenharmony_ci SCFST_INIT, 36113498266Sopenharmony_ci SCFST_WAITING, 36213498266Sopenharmony_ci SCFST_DONE 36313498266Sopenharmony_ci} cf_connect_state; 36413498266Sopenharmony_ci 36513498266Sopenharmony_cistruct cf_he_ctx { 36613498266Sopenharmony_ci int transport; 36713498266Sopenharmony_ci cf_ip_connect_create *cf_create; 36813498266Sopenharmony_ci const struct Curl_dns_entry *remotehost; 36913498266Sopenharmony_ci cf_connect_state state; 37013498266Sopenharmony_ci struct eyeballer *baller[2]; 37113498266Sopenharmony_ci struct eyeballer *winner; 37213498266Sopenharmony_ci struct curltime started; 37313498266Sopenharmony_ci}; 37413498266Sopenharmony_ci 37513498266Sopenharmony_ci/* when there are more than one IP address left to use, this macro returns how 37613498266Sopenharmony_ci much of the given timeout to spend on *this* attempt */ 37713498266Sopenharmony_ci#define TIMEOUT_LARGE 600 37813498266Sopenharmony_ci#define USETIME(ms) ((ms > TIMEOUT_LARGE) ? (ms / 2) : ms) 37913498266Sopenharmony_ci 38013498266Sopenharmony_cistatic CURLcode eyeballer_new(struct eyeballer **pballer, 38113498266Sopenharmony_ci cf_ip_connect_create *cf_create, 38213498266Sopenharmony_ci const struct Curl_addrinfo *addr, 38313498266Sopenharmony_ci int ai_family, 38413498266Sopenharmony_ci struct eyeballer *primary, 38513498266Sopenharmony_ci timediff_t delay_ms, 38613498266Sopenharmony_ci timediff_t timeout_ms, 38713498266Sopenharmony_ci expire_id timeout_id) 38813498266Sopenharmony_ci{ 38913498266Sopenharmony_ci struct eyeballer *baller; 39013498266Sopenharmony_ci 39113498266Sopenharmony_ci *pballer = NULL; 39213498266Sopenharmony_ci baller = calloc(1, sizeof(*baller)); 39313498266Sopenharmony_ci if(!baller) 39413498266Sopenharmony_ci return CURLE_OUT_OF_MEMORY; 39513498266Sopenharmony_ci 39613498266Sopenharmony_ci baller->name = ((ai_family == AF_INET)? "ipv4" : ( 39713498266Sopenharmony_ci#ifdef ENABLE_IPV6 39813498266Sopenharmony_ci (ai_family == AF_INET6)? "ipv6" : 39913498266Sopenharmony_ci#endif 40013498266Sopenharmony_ci "ip")); 40113498266Sopenharmony_ci baller->cf_create = cf_create; 40213498266Sopenharmony_ci baller->first = baller->addr = addr; 40313498266Sopenharmony_ci baller->ai_family = ai_family; 40413498266Sopenharmony_ci baller->primary = primary; 40513498266Sopenharmony_ci baller->delay_ms = delay_ms; 40613498266Sopenharmony_ci baller->timeoutms = addr_next_match(baller->addr, baller->ai_family)? 40713498266Sopenharmony_ci USETIME(timeout_ms) : timeout_ms; 40813498266Sopenharmony_ci baller->timeout_id = timeout_id; 40913498266Sopenharmony_ci baller->result = CURLE_COULDNT_CONNECT; 41013498266Sopenharmony_ci 41113498266Sopenharmony_ci *pballer = baller; 41213498266Sopenharmony_ci return CURLE_OK; 41313498266Sopenharmony_ci} 41413498266Sopenharmony_ci 41513498266Sopenharmony_cistatic void baller_close(struct eyeballer *baller, 41613498266Sopenharmony_ci struct Curl_easy *data) 41713498266Sopenharmony_ci{ 41813498266Sopenharmony_ci if(baller && baller->cf) { 41913498266Sopenharmony_ci Curl_conn_cf_discard_chain(&baller->cf, data); 42013498266Sopenharmony_ci } 42113498266Sopenharmony_ci} 42213498266Sopenharmony_ci 42313498266Sopenharmony_cistatic void baller_free(struct eyeballer *baller, 42413498266Sopenharmony_ci struct Curl_easy *data) 42513498266Sopenharmony_ci{ 42613498266Sopenharmony_ci if(baller) { 42713498266Sopenharmony_ci baller_close(baller, data); 42813498266Sopenharmony_ci free(baller); 42913498266Sopenharmony_ci } 43013498266Sopenharmony_ci} 43113498266Sopenharmony_ci 43213498266Sopenharmony_cistatic void baller_rewind(struct eyeballer *baller) 43313498266Sopenharmony_ci{ 43413498266Sopenharmony_ci baller->rewinded = TRUE; 43513498266Sopenharmony_ci baller->addr = baller->first; 43613498266Sopenharmony_ci baller->inconclusive = FALSE; 43713498266Sopenharmony_ci} 43813498266Sopenharmony_ci 43913498266Sopenharmony_cistatic void baller_next_addr(struct eyeballer *baller) 44013498266Sopenharmony_ci{ 44113498266Sopenharmony_ci baller->addr = addr_next_match(baller->addr, baller->ai_family); 44213498266Sopenharmony_ci} 44313498266Sopenharmony_ci 44413498266Sopenharmony_ci/* 44513498266Sopenharmony_ci * Initiate a connect attempt walk. 44613498266Sopenharmony_ci * 44713498266Sopenharmony_ci * Note that even on connect fail it returns CURLE_OK, but with 'sock' set to 44813498266Sopenharmony_ci * CURL_SOCKET_BAD. Other errors will however return proper errors. 44913498266Sopenharmony_ci */ 45013498266Sopenharmony_cistatic void baller_initiate(struct Curl_cfilter *cf, 45113498266Sopenharmony_ci struct Curl_easy *data, 45213498266Sopenharmony_ci struct eyeballer *baller) 45313498266Sopenharmony_ci{ 45413498266Sopenharmony_ci struct cf_he_ctx *ctx = cf->ctx; 45513498266Sopenharmony_ci struct Curl_cfilter *cf_prev = baller->cf; 45613498266Sopenharmony_ci struct Curl_cfilter *wcf; 45713498266Sopenharmony_ci CURLcode result; 45813498266Sopenharmony_ci 45913498266Sopenharmony_ci 46013498266Sopenharmony_ci /* Don't close a previous cfilter yet to ensure that the next IP's 46113498266Sopenharmony_ci socket gets a different file descriptor, which can prevent bugs when 46213498266Sopenharmony_ci the curl_multi_socket_action interface is used with certain select() 46313498266Sopenharmony_ci replacements such as kqueue. */ 46413498266Sopenharmony_ci result = baller->cf_create(&baller->cf, data, cf->conn, baller->addr, 46513498266Sopenharmony_ci ctx->transport); 46613498266Sopenharmony_ci if(result) 46713498266Sopenharmony_ci goto out; 46813498266Sopenharmony_ci 46913498266Sopenharmony_ci /* the new filter might have sub-filters */ 47013498266Sopenharmony_ci for(wcf = baller->cf; wcf; wcf = wcf->next) { 47113498266Sopenharmony_ci wcf->conn = cf->conn; 47213498266Sopenharmony_ci wcf->sockindex = cf->sockindex; 47313498266Sopenharmony_ci } 47413498266Sopenharmony_ci 47513498266Sopenharmony_ci if(addr_next_match(baller->addr, baller->ai_family)) { 47613498266Sopenharmony_ci Curl_expire(data, baller->timeoutms, baller->timeout_id); 47713498266Sopenharmony_ci } 47813498266Sopenharmony_ci 47913498266Sopenharmony_ciout: 48013498266Sopenharmony_ci if(result) { 48113498266Sopenharmony_ci CURL_TRC_CF(data, cf, "%s failed", baller->name); 48213498266Sopenharmony_ci baller_close(baller, data); 48313498266Sopenharmony_ci } 48413498266Sopenharmony_ci if(cf_prev) 48513498266Sopenharmony_ci Curl_conn_cf_discard_chain(&cf_prev, data); 48613498266Sopenharmony_ci baller->result = result; 48713498266Sopenharmony_ci} 48813498266Sopenharmony_ci 48913498266Sopenharmony_ci/** 49013498266Sopenharmony_ci * Start a connection attempt on the current baller address. 49113498266Sopenharmony_ci * Will return CURLE_OK on the first address where a socket 49213498266Sopenharmony_ci * could be created and the non-blocking connect started. 49313498266Sopenharmony_ci * Returns error when all remaining addresses have been tried. 49413498266Sopenharmony_ci */ 49513498266Sopenharmony_cistatic CURLcode baller_start(struct Curl_cfilter *cf, 49613498266Sopenharmony_ci struct Curl_easy *data, 49713498266Sopenharmony_ci struct eyeballer *baller, 49813498266Sopenharmony_ci timediff_t timeoutms) 49913498266Sopenharmony_ci{ 50013498266Sopenharmony_ci baller->error = 0; 50113498266Sopenharmony_ci baller->connected = FALSE; 50213498266Sopenharmony_ci baller->has_started = TRUE; 50313498266Sopenharmony_ci 50413498266Sopenharmony_ci while(baller->addr) { 50513498266Sopenharmony_ci baller->started = Curl_now(); 50613498266Sopenharmony_ci baller->timeoutms = addr_next_match(baller->addr, baller->ai_family) ? 50713498266Sopenharmony_ci USETIME(timeoutms) : timeoutms; 50813498266Sopenharmony_ci baller_initiate(cf, data, baller); 50913498266Sopenharmony_ci if(!baller->result) 51013498266Sopenharmony_ci break; 51113498266Sopenharmony_ci baller_next_addr(baller); 51213498266Sopenharmony_ci } 51313498266Sopenharmony_ci if(!baller->addr) { 51413498266Sopenharmony_ci baller->is_done = TRUE; 51513498266Sopenharmony_ci } 51613498266Sopenharmony_ci return baller->result; 51713498266Sopenharmony_ci} 51813498266Sopenharmony_ci 51913498266Sopenharmony_ci 52013498266Sopenharmony_ci/* Used within the multi interface. Try next IP address, returns error if no 52113498266Sopenharmony_ci more address exists or error */ 52213498266Sopenharmony_cistatic CURLcode baller_start_next(struct Curl_cfilter *cf, 52313498266Sopenharmony_ci struct Curl_easy *data, 52413498266Sopenharmony_ci struct eyeballer *baller, 52513498266Sopenharmony_ci timediff_t timeoutms) 52613498266Sopenharmony_ci{ 52713498266Sopenharmony_ci if(cf->sockindex == FIRSTSOCKET) { 52813498266Sopenharmony_ci baller_next_addr(baller); 52913498266Sopenharmony_ci /* If we get inconclusive answers from the server(s), we make 53013498266Sopenharmony_ci * a second iteration over the address list */ 53113498266Sopenharmony_ci if(!baller->addr && baller->inconclusive && !baller->rewinded) 53213498266Sopenharmony_ci baller_rewind(baller); 53313498266Sopenharmony_ci baller_start(cf, data, baller, timeoutms); 53413498266Sopenharmony_ci } 53513498266Sopenharmony_ci else { 53613498266Sopenharmony_ci baller->error = 0; 53713498266Sopenharmony_ci baller->connected = FALSE; 53813498266Sopenharmony_ci baller->has_started = TRUE; 53913498266Sopenharmony_ci baller->is_done = TRUE; 54013498266Sopenharmony_ci baller->result = CURLE_COULDNT_CONNECT; 54113498266Sopenharmony_ci } 54213498266Sopenharmony_ci return baller->result; 54313498266Sopenharmony_ci} 54413498266Sopenharmony_ci 54513498266Sopenharmony_cistatic CURLcode baller_connect(struct Curl_cfilter *cf, 54613498266Sopenharmony_ci struct Curl_easy *data, 54713498266Sopenharmony_ci struct eyeballer *baller, 54813498266Sopenharmony_ci struct curltime *now, 54913498266Sopenharmony_ci bool *connected) 55013498266Sopenharmony_ci{ 55113498266Sopenharmony_ci (void)cf; 55213498266Sopenharmony_ci *connected = baller->connected; 55313498266Sopenharmony_ci if(!baller->result && !*connected) { 55413498266Sopenharmony_ci /* evaluate again */ 55513498266Sopenharmony_ci baller->result = Curl_conn_cf_connect(baller->cf, data, 0, connected); 55613498266Sopenharmony_ci 55713498266Sopenharmony_ci if(!baller->result) { 55813498266Sopenharmony_ci if(*connected) { 55913498266Sopenharmony_ci baller->connected = TRUE; 56013498266Sopenharmony_ci baller->is_done = TRUE; 56113498266Sopenharmony_ci } 56213498266Sopenharmony_ci else if(Curl_timediff(*now, baller->started) >= baller->timeoutms) { 56313498266Sopenharmony_ci infof(data, "%s connect timeout after %" CURL_FORMAT_TIMEDIFF_T 56413498266Sopenharmony_ci "ms, move on!", baller->name, baller->timeoutms); 56513498266Sopenharmony_ci#if defined(ETIMEDOUT) 56613498266Sopenharmony_ci baller->error = ETIMEDOUT; 56713498266Sopenharmony_ci#endif 56813498266Sopenharmony_ci baller->result = CURLE_OPERATION_TIMEDOUT; 56913498266Sopenharmony_ci } 57013498266Sopenharmony_ci } 57113498266Sopenharmony_ci else if(baller->result == CURLE_WEIRD_SERVER_REPLY) 57213498266Sopenharmony_ci baller->inconclusive = TRUE; 57313498266Sopenharmony_ci } 57413498266Sopenharmony_ci return baller->result; 57513498266Sopenharmony_ci} 57613498266Sopenharmony_ci 57713498266Sopenharmony_ci/* 57813498266Sopenharmony_ci * is_connected() checks if the socket has connected. 57913498266Sopenharmony_ci */ 58013498266Sopenharmony_cistatic CURLcode is_connected(struct Curl_cfilter *cf, 58113498266Sopenharmony_ci struct Curl_easy *data, 58213498266Sopenharmony_ci bool *connected) 58313498266Sopenharmony_ci{ 58413498266Sopenharmony_ci struct cf_he_ctx *ctx = cf->ctx; 58513498266Sopenharmony_ci struct connectdata *conn = cf->conn; 58613498266Sopenharmony_ci CURLcode result; 58713498266Sopenharmony_ci struct curltime now; 58813498266Sopenharmony_ci size_t i; 58913498266Sopenharmony_ci int ongoing, not_started; 59013498266Sopenharmony_ci const char *hostname; 59113498266Sopenharmony_ci 59213498266Sopenharmony_ci /* Check if any of the conn->tempsock we use for establishing connections 59313498266Sopenharmony_ci * succeeded and, if so, close any ongoing other ones. 59413498266Sopenharmony_ci * Transfer the successful conn->tempsock to conn->sock[sockindex] 59513498266Sopenharmony_ci * and set conn->tempsock to CURL_SOCKET_BAD. 59613498266Sopenharmony_ci * If transport is QUIC, we need to shutdown the ongoing 'other' 59713498266Sopenharmony_ci * cot ballers in a QUIC appropriate way. */ 59813498266Sopenharmony_cievaluate: 59913498266Sopenharmony_ci *connected = FALSE; /* a very negative world view is best */ 60013498266Sopenharmony_ci now = Curl_now(); 60113498266Sopenharmony_ci ongoing = not_started = 0; 60213498266Sopenharmony_ci for(i = 0; i < ARRAYSIZE(ctx->baller); i++) { 60313498266Sopenharmony_ci struct eyeballer *baller = ctx->baller[i]; 60413498266Sopenharmony_ci 60513498266Sopenharmony_ci if(!baller || baller->is_done) 60613498266Sopenharmony_ci continue; 60713498266Sopenharmony_ci 60813498266Sopenharmony_ci if(!baller->has_started) { 60913498266Sopenharmony_ci ++not_started; 61013498266Sopenharmony_ci continue; 61113498266Sopenharmony_ci } 61213498266Sopenharmony_ci baller->result = baller_connect(cf, data, baller, &now, connected); 61313498266Sopenharmony_ci CURL_TRC_CF(data, cf, "%s connect -> %d, connected=%d", 61413498266Sopenharmony_ci baller->name, baller->result, *connected); 61513498266Sopenharmony_ci 61613498266Sopenharmony_ci if(!baller->result) { 61713498266Sopenharmony_ci if(*connected) { 61813498266Sopenharmony_ci /* connected, declare the winner */ 61913498266Sopenharmony_ci ctx->winner = baller; 62013498266Sopenharmony_ci ctx->baller[i] = NULL; 62113498266Sopenharmony_ci break; 62213498266Sopenharmony_ci } 62313498266Sopenharmony_ci else { /* still waiting */ 62413498266Sopenharmony_ci ++ongoing; 62513498266Sopenharmony_ci } 62613498266Sopenharmony_ci } 62713498266Sopenharmony_ci else if(!baller->is_done) { 62813498266Sopenharmony_ci /* The baller failed to connect, start its next attempt */ 62913498266Sopenharmony_ci if(baller->error) { 63013498266Sopenharmony_ci data->state.os_errno = baller->error; 63113498266Sopenharmony_ci SET_SOCKERRNO(baller->error); 63213498266Sopenharmony_ci } 63313498266Sopenharmony_ci baller_start_next(cf, data, baller, Curl_timeleft(data, &now, TRUE)); 63413498266Sopenharmony_ci if(baller->is_done) { 63513498266Sopenharmony_ci CURL_TRC_CF(data, cf, "%s done", baller->name); 63613498266Sopenharmony_ci } 63713498266Sopenharmony_ci else { 63813498266Sopenharmony_ci /* next attempt was started */ 63913498266Sopenharmony_ci CURL_TRC_CF(data, cf, "%s trying next", baller->name); 64013498266Sopenharmony_ci ++ongoing; 64113498266Sopenharmony_ci Curl_expire(data, 0, EXPIRE_RUN_NOW); 64213498266Sopenharmony_ci } 64313498266Sopenharmony_ci } 64413498266Sopenharmony_ci } 64513498266Sopenharmony_ci 64613498266Sopenharmony_ci if(ctx->winner) { 64713498266Sopenharmony_ci *connected = TRUE; 64813498266Sopenharmony_ci return CURLE_OK; 64913498266Sopenharmony_ci } 65013498266Sopenharmony_ci 65113498266Sopenharmony_ci /* Nothing connected, check the time before we might 65213498266Sopenharmony_ci * start new ballers or return ok. */ 65313498266Sopenharmony_ci if((ongoing || not_started) && Curl_timeleft(data, &now, TRUE) < 0) { 65413498266Sopenharmony_ci failf(data, "Connection timeout after %" CURL_FORMAT_CURL_OFF_T " ms", 65513498266Sopenharmony_ci Curl_timediff(now, data->progress.t_startsingle)); 65613498266Sopenharmony_ci return CURLE_OPERATION_TIMEDOUT; 65713498266Sopenharmony_ci } 65813498266Sopenharmony_ci 65913498266Sopenharmony_ci /* Check if we have any waiting ballers to start now. */ 66013498266Sopenharmony_ci if(not_started > 0) { 66113498266Sopenharmony_ci int added = 0; 66213498266Sopenharmony_ci 66313498266Sopenharmony_ci for(i = 0; i < ARRAYSIZE(ctx->baller); i++) { 66413498266Sopenharmony_ci struct eyeballer *baller = ctx->baller[i]; 66513498266Sopenharmony_ci 66613498266Sopenharmony_ci if(!baller || baller->has_started) 66713498266Sopenharmony_ci continue; 66813498266Sopenharmony_ci /* We start its primary baller has failed to connect or if 66913498266Sopenharmony_ci * its start delay_ms have expired */ 67013498266Sopenharmony_ci if((baller->primary && baller->primary->is_done) || 67113498266Sopenharmony_ci Curl_timediff(now, ctx->started) >= baller->delay_ms) { 67213498266Sopenharmony_ci baller_start(cf, data, baller, Curl_timeleft(data, &now, TRUE)); 67313498266Sopenharmony_ci if(baller->is_done) { 67413498266Sopenharmony_ci CURL_TRC_CF(data, cf, "%s done", baller->name); 67513498266Sopenharmony_ci } 67613498266Sopenharmony_ci else { 67713498266Sopenharmony_ci CURL_TRC_CF(data, cf, "%s starting (timeout=%" 67813498266Sopenharmony_ci CURL_FORMAT_TIMEDIFF_T "ms)", 67913498266Sopenharmony_ci baller->name, baller->timeoutms); 68013498266Sopenharmony_ci ++ongoing; 68113498266Sopenharmony_ci ++added; 68213498266Sopenharmony_ci } 68313498266Sopenharmony_ci } 68413498266Sopenharmony_ci } 68513498266Sopenharmony_ci if(added > 0) 68613498266Sopenharmony_ci goto evaluate; 68713498266Sopenharmony_ci } 68813498266Sopenharmony_ci 68913498266Sopenharmony_ci if(ongoing > 0) { 69013498266Sopenharmony_ci /* We are still trying, return for more waiting */ 69113498266Sopenharmony_ci *connected = FALSE; 69213498266Sopenharmony_ci return CURLE_OK; 69313498266Sopenharmony_ci } 69413498266Sopenharmony_ci 69513498266Sopenharmony_ci /* all ballers have failed to connect. */ 69613498266Sopenharmony_ci CURL_TRC_CF(data, cf, "all eyeballers failed"); 69713498266Sopenharmony_ci result = CURLE_COULDNT_CONNECT; 69813498266Sopenharmony_ci for(i = 0; i < ARRAYSIZE(ctx->baller); i++) { 69913498266Sopenharmony_ci struct eyeballer *baller = ctx->baller[i]; 70013498266Sopenharmony_ci if(!baller) 70113498266Sopenharmony_ci continue; 70213498266Sopenharmony_ci CURL_TRC_CF(data, cf, "%s assess started=%d, result=%d", 70313498266Sopenharmony_ci baller->name, baller->has_started, baller->result); 70413498266Sopenharmony_ci if(baller->has_started && baller->result) { 70513498266Sopenharmony_ci result = baller->result; 70613498266Sopenharmony_ci break; 70713498266Sopenharmony_ci } 70813498266Sopenharmony_ci } 70913498266Sopenharmony_ci 71013498266Sopenharmony_ci#ifndef CURL_DISABLE_PROXY 71113498266Sopenharmony_ci if(conn->bits.socksproxy) 71213498266Sopenharmony_ci hostname = conn->socks_proxy.host.name; 71313498266Sopenharmony_ci else if(conn->bits.httpproxy) 71413498266Sopenharmony_ci hostname = conn->http_proxy.host.name; 71513498266Sopenharmony_ci else 71613498266Sopenharmony_ci#endif 71713498266Sopenharmony_ci if(conn->bits.conn_to_host) 71813498266Sopenharmony_ci hostname = conn->conn_to_host.name; 71913498266Sopenharmony_ci else 72013498266Sopenharmony_ci hostname = conn->host.name; 72113498266Sopenharmony_ci 72213498266Sopenharmony_ci failf(data, "Failed to connect to %s port %u after " 72313498266Sopenharmony_ci "%" CURL_FORMAT_TIMEDIFF_T " ms: %s", 72413498266Sopenharmony_ci hostname, conn->port, 72513498266Sopenharmony_ci Curl_timediff(now, data->progress.t_startsingle), 72613498266Sopenharmony_ci curl_easy_strerror(result)); 72713498266Sopenharmony_ci 72813498266Sopenharmony_ci#ifdef WSAETIMEDOUT 72913498266Sopenharmony_ci if(WSAETIMEDOUT == data->state.os_errno) 73013498266Sopenharmony_ci result = CURLE_OPERATION_TIMEDOUT; 73113498266Sopenharmony_ci#elif defined(ETIMEDOUT) 73213498266Sopenharmony_ci if(ETIMEDOUT == data->state.os_errno) 73313498266Sopenharmony_ci result = CURLE_OPERATION_TIMEDOUT; 73413498266Sopenharmony_ci#endif 73513498266Sopenharmony_ci 73613498266Sopenharmony_ci return result; 73713498266Sopenharmony_ci} 73813498266Sopenharmony_ci 73913498266Sopenharmony_ci/* 74013498266Sopenharmony_ci * Connect to the given host with timeout, proxy or remote doesn't matter. 74113498266Sopenharmony_ci * There might be more than one IP address to try out. 74213498266Sopenharmony_ci */ 74313498266Sopenharmony_cistatic CURLcode start_connect(struct Curl_cfilter *cf, 74413498266Sopenharmony_ci struct Curl_easy *data, 74513498266Sopenharmony_ci const struct Curl_dns_entry *remotehost) 74613498266Sopenharmony_ci{ 74713498266Sopenharmony_ci struct cf_he_ctx *ctx = cf->ctx; 74813498266Sopenharmony_ci struct connectdata *conn = cf->conn; 74913498266Sopenharmony_ci CURLcode result = CURLE_COULDNT_CONNECT; 75013498266Sopenharmony_ci int ai_family0, ai_family1; 75113498266Sopenharmony_ci timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); 75213498266Sopenharmony_ci const struct Curl_addrinfo *addr0, *addr1; 75313498266Sopenharmony_ci 75413498266Sopenharmony_ci if(timeout_ms < 0) { 75513498266Sopenharmony_ci /* a precaution, no need to continue if time already is up */ 75613498266Sopenharmony_ci failf(data, "Connection time-out"); 75713498266Sopenharmony_ci return CURLE_OPERATION_TIMEDOUT; 75813498266Sopenharmony_ci } 75913498266Sopenharmony_ci 76013498266Sopenharmony_ci ctx->started = Curl_now(); 76113498266Sopenharmony_ci 76213498266Sopenharmony_ci /* remotehost->addr is the list of addresses from the resolver, each 76313498266Sopenharmony_ci * with an address family. The list has at least one entry, possibly 76413498266Sopenharmony_ci * many more. 76513498266Sopenharmony_ci * We try at most 2 at a time, until we either get a connection or 76613498266Sopenharmony_ci * run out of addresses to try. Since likelihood of success is tied 76713498266Sopenharmony_ci * to the address family (e.g. IPV6 might not work at all ), we want 76813498266Sopenharmony_ci * the 2 connect attempt ballers to try different families, if possible. 76913498266Sopenharmony_ci * 77013498266Sopenharmony_ci */ 77113498266Sopenharmony_ci if(conn->ip_version == CURL_IPRESOLVE_WHATEVER) { 77213498266Sopenharmony_ci /* any IP version is allowed */ 77313498266Sopenharmony_ci ai_family0 = remotehost->addr? 77413498266Sopenharmony_ci remotehost->addr->ai_family : 0; 77513498266Sopenharmony_ci#ifdef ENABLE_IPV6 77613498266Sopenharmony_ci ai_family1 = ai_family0 == AF_INET6 ? 77713498266Sopenharmony_ci AF_INET : AF_INET6; 77813498266Sopenharmony_ci#else 77913498266Sopenharmony_ci ai_family1 = AF_UNSPEC; 78013498266Sopenharmony_ci#endif 78113498266Sopenharmony_ci } 78213498266Sopenharmony_ci else { 78313498266Sopenharmony_ci /* only one IP version is allowed */ 78413498266Sopenharmony_ci ai_family0 = (conn->ip_version == CURL_IPRESOLVE_V4) ? 78513498266Sopenharmony_ci AF_INET : 78613498266Sopenharmony_ci#ifdef ENABLE_IPV6 78713498266Sopenharmony_ci AF_INET6; 78813498266Sopenharmony_ci#else 78913498266Sopenharmony_ci AF_UNSPEC; 79013498266Sopenharmony_ci#endif 79113498266Sopenharmony_ci ai_family1 = AF_UNSPEC; 79213498266Sopenharmony_ci } 79313498266Sopenharmony_ci 79413498266Sopenharmony_ci /* Get the first address in the list that matches the family, 79513498266Sopenharmony_ci * this might give NULL, if we do not have any matches. */ 79613498266Sopenharmony_ci addr0 = addr_first_match(remotehost->addr, ai_family0); 79713498266Sopenharmony_ci addr1 = addr_first_match(remotehost->addr, ai_family1); 79813498266Sopenharmony_ci if(!addr0 && addr1) { 79913498266Sopenharmony_ci /* switch around, so a single baller always uses addr0 */ 80013498266Sopenharmony_ci addr0 = addr1; 80113498266Sopenharmony_ci ai_family0 = ai_family1; 80213498266Sopenharmony_ci addr1 = NULL; 80313498266Sopenharmony_ci } 80413498266Sopenharmony_ci 80513498266Sopenharmony_ci /* We found no address that matches our criteria, we cannot connect */ 80613498266Sopenharmony_ci if(!addr0) { 80713498266Sopenharmony_ci return CURLE_COULDNT_CONNECT; 80813498266Sopenharmony_ci } 80913498266Sopenharmony_ci 81013498266Sopenharmony_ci memset(ctx->baller, 0, sizeof(ctx->baller)); 81113498266Sopenharmony_ci result = eyeballer_new(&ctx->baller[0], ctx->cf_create, addr0, ai_family0, 81213498266Sopenharmony_ci NULL, 0, /* no primary/delay, start now */ 81313498266Sopenharmony_ci timeout_ms, EXPIRE_DNS_PER_NAME); 81413498266Sopenharmony_ci if(result) 81513498266Sopenharmony_ci return result; 81613498266Sopenharmony_ci CURL_TRC_CF(data, cf, "created %s (timeout %" 81713498266Sopenharmony_ci CURL_FORMAT_TIMEDIFF_T "ms)", 81813498266Sopenharmony_ci ctx->baller[0]->name, ctx->baller[0]->timeoutms); 81913498266Sopenharmony_ci if(addr1) { 82013498266Sopenharmony_ci /* second one gets a delayed start */ 82113498266Sopenharmony_ci result = eyeballer_new(&ctx->baller[1], ctx->cf_create, addr1, ai_family1, 82213498266Sopenharmony_ci ctx->baller[0], /* wait on that to fail */ 82313498266Sopenharmony_ci /* or start this delayed */ 82413498266Sopenharmony_ci data->set.happy_eyeballs_timeout, 82513498266Sopenharmony_ci timeout_ms, EXPIRE_DNS_PER_NAME2); 82613498266Sopenharmony_ci if(result) 82713498266Sopenharmony_ci return result; 82813498266Sopenharmony_ci CURL_TRC_CF(data, cf, "created %s (timeout %" 82913498266Sopenharmony_ci CURL_FORMAT_TIMEDIFF_T "ms)", 83013498266Sopenharmony_ci ctx->baller[1]->name, ctx->baller[1]->timeoutms); 83113498266Sopenharmony_ci Curl_expire(data, data->set.happy_eyeballs_timeout, 83213498266Sopenharmony_ci EXPIRE_HAPPY_EYEBALLS); 83313498266Sopenharmony_ci } 83413498266Sopenharmony_ci 83513498266Sopenharmony_ci return CURLE_OK; 83613498266Sopenharmony_ci} 83713498266Sopenharmony_ci 83813498266Sopenharmony_cistatic void cf_he_ctx_clear(struct Curl_cfilter *cf, struct Curl_easy *data) 83913498266Sopenharmony_ci{ 84013498266Sopenharmony_ci struct cf_he_ctx *ctx = cf->ctx; 84113498266Sopenharmony_ci size_t i; 84213498266Sopenharmony_ci 84313498266Sopenharmony_ci DEBUGASSERT(ctx); 84413498266Sopenharmony_ci DEBUGASSERT(data); 84513498266Sopenharmony_ci for(i = 0; i < ARRAYSIZE(ctx->baller); i++) { 84613498266Sopenharmony_ci baller_free(ctx->baller[i], data); 84713498266Sopenharmony_ci ctx->baller[i] = NULL; 84813498266Sopenharmony_ci } 84913498266Sopenharmony_ci baller_free(ctx->winner, data); 85013498266Sopenharmony_ci ctx->winner = NULL; 85113498266Sopenharmony_ci} 85213498266Sopenharmony_ci 85313498266Sopenharmony_cistatic void cf_he_adjust_pollset(struct Curl_cfilter *cf, 85413498266Sopenharmony_ci struct Curl_easy *data, 85513498266Sopenharmony_ci struct easy_pollset *ps) 85613498266Sopenharmony_ci{ 85713498266Sopenharmony_ci struct cf_he_ctx *ctx = cf->ctx; 85813498266Sopenharmony_ci size_t i; 85913498266Sopenharmony_ci 86013498266Sopenharmony_ci if(!cf->connected) { 86113498266Sopenharmony_ci for(i = 0; i < ARRAYSIZE(ctx->baller); i++) { 86213498266Sopenharmony_ci struct eyeballer *baller = ctx->baller[i]; 86313498266Sopenharmony_ci if(!baller || !baller->cf) 86413498266Sopenharmony_ci continue; 86513498266Sopenharmony_ci Curl_conn_cf_adjust_pollset(baller->cf, data, ps); 86613498266Sopenharmony_ci } 86713498266Sopenharmony_ci CURL_TRC_CF(data, cf, "adjust_pollset -> %d socks", ps->num); 86813498266Sopenharmony_ci } 86913498266Sopenharmony_ci} 87013498266Sopenharmony_ci 87113498266Sopenharmony_cistatic CURLcode cf_he_connect(struct Curl_cfilter *cf, 87213498266Sopenharmony_ci struct Curl_easy *data, 87313498266Sopenharmony_ci bool blocking, bool *done) 87413498266Sopenharmony_ci{ 87513498266Sopenharmony_ci struct cf_he_ctx *ctx = cf->ctx; 87613498266Sopenharmony_ci CURLcode result = CURLE_OK; 87713498266Sopenharmony_ci 87813498266Sopenharmony_ci if(cf->connected) { 87913498266Sopenharmony_ci *done = TRUE; 88013498266Sopenharmony_ci return CURLE_OK; 88113498266Sopenharmony_ci } 88213498266Sopenharmony_ci 88313498266Sopenharmony_ci (void)blocking; /* TODO: do we want to support this? */ 88413498266Sopenharmony_ci DEBUGASSERT(ctx); 88513498266Sopenharmony_ci *done = FALSE; 88613498266Sopenharmony_ci 88713498266Sopenharmony_ci switch(ctx->state) { 88813498266Sopenharmony_ci case SCFST_INIT: 88913498266Sopenharmony_ci DEBUGASSERT(CURL_SOCKET_BAD == Curl_conn_cf_get_socket(cf, data)); 89013498266Sopenharmony_ci DEBUGASSERT(!cf->connected); 89113498266Sopenharmony_ci result = start_connect(cf, data, ctx->remotehost); 89213498266Sopenharmony_ci if(result) 89313498266Sopenharmony_ci return result; 89413498266Sopenharmony_ci ctx->state = SCFST_WAITING; 89513498266Sopenharmony_ci FALLTHROUGH(); 89613498266Sopenharmony_ci case SCFST_WAITING: 89713498266Sopenharmony_ci result = is_connected(cf, data, done); 89813498266Sopenharmony_ci if(!result && *done) { 89913498266Sopenharmony_ci DEBUGASSERT(ctx->winner); 90013498266Sopenharmony_ci DEBUGASSERT(ctx->winner->cf); 90113498266Sopenharmony_ci DEBUGASSERT(ctx->winner->cf->connected); 90213498266Sopenharmony_ci /* we have a winner. Install and activate it. 90313498266Sopenharmony_ci * close/free all others. */ 90413498266Sopenharmony_ci ctx->state = SCFST_DONE; 90513498266Sopenharmony_ci cf->connected = TRUE; 90613498266Sopenharmony_ci cf->next = ctx->winner->cf; 90713498266Sopenharmony_ci ctx->winner->cf = NULL; 90813498266Sopenharmony_ci cf_he_ctx_clear(cf, data); 90913498266Sopenharmony_ci Curl_conn_cf_cntrl(cf->next, data, TRUE, 91013498266Sopenharmony_ci CF_CTRL_CONN_INFO_UPDATE, 0, NULL); 91113498266Sopenharmony_ci 91213498266Sopenharmony_ci if(cf->conn->handler->protocol & PROTO_FAMILY_SSH) 91313498266Sopenharmony_ci Curl_pgrsTime(data, TIMER_APPCONNECT); /* we're connected already */ 91413498266Sopenharmony_ci Curl_verboseconnect(data, cf->conn); 91513498266Sopenharmony_ci data->info.numconnects++; /* to track the # of connections made */ 91613498266Sopenharmony_ci } 91713498266Sopenharmony_ci break; 91813498266Sopenharmony_ci case SCFST_DONE: 91913498266Sopenharmony_ci *done = TRUE; 92013498266Sopenharmony_ci break; 92113498266Sopenharmony_ci } 92213498266Sopenharmony_ci return result; 92313498266Sopenharmony_ci} 92413498266Sopenharmony_ci 92513498266Sopenharmony_cistatic void cf_he_close(struct Curl_cfilter *cf, 92613498266Sopenharmony_ci struct Curl_easy *data) 92713498266Sopenharmony_ci{ 92813498266Sopenharmony_ci struct cf_he_ctx *ctx = cf->ctx; 92913498266Sopenharmony_ci 93013498266Sopenharmony_ci CURL_TRC_CF(data, cf, "close"); 93113498266Sopenharmony_ci cf_he_ctx_clear(cf, data); 93213498266Sopenharmony_ci cf->connected = FALSE; 93313498266Sopenharmony_ci ctx->state = SCFST_INIT; 93413498266Sopenharmony_ci 93513498266Sopenharmony_ci if(cf->next) { 93613498266Sopenharmony_ci cf->next->cft->do_close(cf->next, data); 93713498266Sopenharmony_ci Curl_conn_cf_discard_chain(&cf->next, data); 93813498266Sopenharmony_ci } 93913498266Sopenharmony_ci} 94013498266Sopenharmony_ci 94113498266Sopenharmony_cistatic bool cf_he_data_pending(struct Curl_cfilter *cf, 94213498266Sopenharmony_ci const struct Curl_easy *data) 94313498266Sopenharmony_ci{ 94413498266Sopenharmony_ci struct cf_he_ctx *ctx = cf->ctx; 94513498266Sopenharmony_ci size_t i; 94613498266Sopenharmony_ci 94713498266Sopenharmony_ci if(cf->connected) 94813498266Sopenharmony_ci return cf->next->cft->has_data_pending(cf->next, data); 94913498266Sopenharmony_ci 95013498266Sopenharmony_ci for(i = 0; i < ARRAYSIZE(ctx->baller); i++) { 95113498266Sopenharmony_ci struct eyeballer *baller = ctx->baller[i]; 95213498266Sopenharmony_ci if(!baller || !baller->cf) 95313498266Sopenharmony_ci continue; 95413498266Sopenharmony_ci if(baller->cf->cft->has_data_pending(baller->cf, data)) 95513498266Sopenharmony_ci return TRUE; 95613498266Sopenharmony_ci } 95713498266Sopenharmony_ci return FALSE; 95813498266Sopenharmony_ci} 95913498266Sopenharmony_ci 96013498266Sopenharmony_cistatic struct curltime get_max_baller_time(struct Curl_cfilter *cf, 96113498266Sopenharmony_ci struct Curl_easy *data, 96213498266Sopenharmony_ci int query) 96313498266Sopenharmony_ci{ 96413498266Sopenharmony_ci struct cf_he_ctx *ctx = cf->ctx; 96513498266Sopenharmony_ci struct curltime t, tmax; 96613498266Sopenharmony_ci size_t i; 96713498266Sopenharmony_ci 96813498266Sopenharmony_ci memset(&tmax, 0, sizeof(tmax)); 96913498266Sopenharmony_ci for(i = 0; i < ARRAYSIZE(ctx->baller); i++) { 97013498266Sopenharmony_ci struct eyeballer *baller = ctx->baller[i]; 97113498266Sopenharmony_ci 97213498266Sopenharmony_ci memset(&t, 0, sizeof(t)); 97313498266Sopenharmony_ci if(baller && baller->cf && 97413498266Sopenharmony_ci !baller->cf->cft->query(baller->cf, data, query, NULL, &t)) { 97513498266Sopenharmony_ci if((t.tv_sec || t.tv_usec) && Curl_timediff_us(t, tmax) > 0) 97613498266Sopenharmony_ci tmax = t; 97713498266Sopenharmony_ci } 97813498266Sopenharmony_ci } 97913498266Sopenharmony_ci return tmax; 98013498266Sopenharmony_ci} 98113498266Sopenharmony_ci 98213498266Sopenharmony_cistatic CURLcode cf_he_query(struct Curl_cfilter *cf, 98313498266Sopenharmony_ci struct Curl_easy *data, 98413498266Sopenharmony_ci int query, int *pres1, void *pres2) 98513498266Sopenharmony_ci{ 98613498266Sopenharmony_ci struct cf_he_ctx *ctx = cf->ctx; 98713498266Sopenharmony_ci 98813498266Sopenharmony_ci if(!cf->connected) { 98913498266Sopenharmony_ci switch(query) { 99013498266Sopenharmony_ci case CF_QUERY_CONNECT_REPLY_MS: { 99113498266Sopenharmony_ci int reply_ms = -1; 99213498266Sopenharmony_ci size_t i; 99313498266Sopenharmony_ci 99413498266Sopenharmony_ci for(i = 0; i < ARRAYSIZE(ctx->baller); i++) { 99513498266Sopenharmony_ci struct eyeballer *baller = ctx->baller[i]; 99613498266Sopenharmony_ci int breply_ms; 99713498266Sopenharmony_ci 99813498266Sopenharmony_ci if(baller && baller->cf && 99913498266Sopenharmony_ci !baller->cf->cft->query(baller->cf, data, query, 100013498266Sopenharmony_ci &breply_ms, NULL)) { 100113498266Sopenharmony_ci if(breply_ms >= 0 && (reply_ms < 0 || breply_ms < reply_ms)) 100213498266Sopenharmony_ci reply_ms = breply_ms; 100313498266Sopenharmony_ci } 100413498266Sopenharmony_ci } 100513498266Sopenharmony_ci *pres1 = reply_ms; 100613498266Sopenharmony_ci CURL_TRC_CF(data, cf, "query connect reply: %dms", *pres1); 100713498266Sopenharmony_ci return CURLE_OK; 100813498266Sopenharmony_ci } 100913498266Sopenharmony_ci case CF_QUERY_TIMER_CONNECT: { 101013498266Sopenharmony_ci struct curltime *when = pres2; 101113498266Sopenharmony_ci *when = get_max_baller_time(cf, data, CF_QUERY_TIMER_CONNECT); 101213498266Sopenharmony_ci return CURLE_OK; 101313498266Sopenharmony_ci } 101413498266Sopenharmony_ci case CF_QUERY_TIMER_APPCONNECT: { 101513498266Sopenharmony_ci struct curltime *when = pres2; 101613498266Sopenharmony_ci *when = get_max_baller_time(cf, data, CF_QUERY_TIMER_APPCONNECT); 101713498266Sopenharmony_ci return CURLE_OK; 101813498266Sopenharmony_ci } 101913498266Sopenharmony_ci default: 102013498266Sopenharmony_ci break; 102113498266Sopenharmony_ci } 102213498266Sopenharmony_ci } 102313498266Sopenharmony_ci 102413498266Sopenharmony_ci return cf->next? 102513498266Sopenharmony_ci cf->next->cft->query(cf->next, data, query, pres1, pres2) : 102613498266Sopenharmony_ci CURLE_UNKNOWN_OPTION; 102713498266Sopenharmony_ci} 102813498266Sopenharmony_ci 102913498266Sopenharmony_cistatic void cf_he_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) 103013498266Sopenharmony_ci{ 103113498266Sopenharmony_ci struct cf_he_ctx *ctx = cf->ctx; 103213498266Sopenharmony_ci 103313498266Sopenharmony_ci CURL_TRC_CF(data, cf, "destroy"); 103413498266Sopenharmony_ci if(ctx) { 103513498266Sopenharmony_ci cf_he_ctx_clear(cf, data); 103613498266Sopenharmony_ci } 103713498266Sopenharmony_ci /* release any resources held in state */ 103813498266Sopenharmony_ci Curl_safefree(ctx); 103913498266Sopenharmony_ci} 104013498266Sopenharmony_ci 104113498266Sopenharmony_cistruct Curl_cftype Curl_cft_happy_eyeballs = { 104213498266Sopenharmony_ci "HAPPY-EYEBALLS", 104313498266Sopenharmony_ci 0, 104413498266Sopenharmony_ci CURL_LOG_LVL_NONE, 104513498266Sopenharmony_ci cf_he_destroy, 104613498266Sopenharmony_ci cf_he_connect, 104713498266Sopenharmony_ci cf_he_close, 104813498266Sopenharmony_ci Curl_cf_def_get_host, 104913498266Sopenharmony_ci cf_he_adjust_pollset, 105013498266Sopenharmony_ci cf_he_data_pending, 105113498266Sopenharmony_ci Curl_cf_def_send, 105213498266Sopenharmony_ci Curl_cf_def_recv, 105313498266Sopenharmony_ci Curl_cf_def_cntrl, 105413498266Sopenharmony_ci Curl_cf_def_conn_is_alive, 105513498266Sopenharmony_ci Curl_cf_def_conn_keep_alive, 105613498266Sopenharmony_ci cf_he_query, 105713498266Sopenharmony_ci}; 105813498266Sopenharmony_ci 105913498266Sopenharmony_ci/** 106013498266Sopenharmony_ci * Create a happy eyeball connection filter that uses the, once resolved, 106113498266Sopenharmony_ci * address information to connect on ip families based on connection 106213498266Sopenharmony_ci * configuration. 106313498266Sopenharmony_ci * @param pcf output, the created cfilter 106413498266Sopenharmony_ci * @param data easy handle used in creation 106513498266Sopenharmony_ci * @param conn connection the filter is created for 106613498266Sopenharmony_ci * @param cf_create method to create the sub-filters performing the 106713498266Sopenharmony_ci * actual connects. 106813498266Sopenharmony_ci */ 106913498266Sopenharmony_cistatic CURLcode 107013498266Sopenharmony_cicf_happy_eyeballs_create(struct Curl_cfilter **pcf, 107113498266Sopenharmony_ci struct Curl_easy *data, 107213498266Sopenharmony_ci struct connectdata *conn, 107313498266Sopenharmony_ci cf_ip_connect_create *cf_create, 107413498266Sopenharmony_ci const struct Curl_dns_entry *remotehost, 107513498266Sopenharmony_ci int transport) 107613498266Sopenharmony_ci{ 107713498266Sopenharmony_ci struct cf_he_ctx *ctx = NULL; 107813498266Sopenharmony_ci CURLcode result; 107913498266Sopenharmony_ci 108013498266Sopenharmony_ci (void)data; 108113498266Sopenharmony_ci (void)conn; 108213498266Sopenharmony_ci *pcf = NULL; 108313498266Sopenharmony_ci ctx = calloc(1, sizeof(*ctx)); 108413498266Sopenharmony_ci if(!ctx) { 108513498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 108613498266Sopenharmony_ci goto out; 108713498266Sopenharmony_ci } 108813498266Sopenharmony_ci ctx->transport = transport; 108913498266Sopenharmony_ci ctx->cf_create = cf_create; 109013498266Sopenharmony_ci ctx->remotehost = remotehost; 109113498266Sopenharmony_ci 109213498266Sopenharmony_ci result = Curl_cf_create(pcf, &Curl_cft_happy_eyeballs, ctx); 109313498266Sopenharmony_ci 109413498266Sopenharmony_ciout: 109513498266Sopenharmony_ci if(result) { 109613498266Sopenharmony_ci Curl_safefree(*pcf); 109713498266Sopenharmony_ci Curl_safefree(ctx); 109813498266Sopenharmony_ci } 109913498266Sopenharmony_ci return result; 110013498266Sopenharmony_ci} 110113498266Sopenharmony_ci 110213498266Sopenharmony_cistruct transport_provider { 110313498266Sopenharmony_ci int transport; 110413498266Sopenharmony_ci cf_ip_connect_create *cf_create; 110513498266Sopenharmony_ci}; 110613498266Sopenharmony_ci 110713498266Sopenharmony_cistatic 110813498266Sopenharmony_ci#ifndef DEBUGBUILD 110913498266Sopenharmony_ciconst 111013498266Sopenharmony_ci#endif 111113498266Sopenharmony_cistruct transport_provider transport_providers[] = { 111213498266Sopenharmony_ci { TRNSPRT_TCP, Curl_cf_tcp_create }, 111313498266Sopenharmony_ci#ifdef ENABLE_QUIC 111413498266Sopenharmony_ci { TRNSPRT_QUIC, Curl_cf_quic_create }, 111513498266Sopenharmony_ci#endif 111613498266Sopenharmony_ci#ifndef CURL_DISABLE_TFTP 111713498266Sopenharmony_ci { TRNSPRT_UDP, Curl_cf_udp_create }, 111813498266Sopenharmony_ci#endif 111913498266Sopenharmony_ci#ifdef USE_UNIX_SOCKETS 112013498266Sopenharmony_ci { TRNSPRT_UNIX, Curl_cf_unix_create }, 112113498266Sopenharmony_ci#endif 112213498266Sopenharmony_ci}; 112313498266Sopenharmony_ci 112413498266Sopenharmony_cistatic cf_ip_connect_create *get_cf_create(int transport) 112513498266Sopenharmony_ci{ 112613498266Sopenharmony_ci size_t i; 112713498266Sopenharmony_ci for(i = 0; i < ARRAYSIZE(transport_providers); ++i) { 112813498266Sopenharmony_ci if(transport == transport_providers[i].transport) 112913498266Sopenharmony_ci return transport_providers[i].cf_create; 113013498266Sopenharmony_ci } 113113498266Sopenharmony_ci return NULL; 113213498266Sopenharmony_ci} 113313498266Sopenharmony_ci 113413498266Sopenharmony_cistatic CURLcode cf_he_insert_after(struct Curl_cfilter *cf_at, 113513498266Sopenharmony_ci struct Curl_easy *data, 113613498266Sopenharmony_ci const struct Curl_dns_entry *remotehost, 113713498266Sopenharmony_ci int transport) 113813498266Sopenharmony_ci{ 113913498266Sopenharmony_ci cf_ip_connect_create *cf_create; 114013498266Sopenharmony_ci struct Curl_cfilter *cf; 114113498266Sopenharmony_ci CURLcode result; 114213498266Sopenharmony_ci 114313498266Sopenharmony_ci /* Need to be first */ 114413498266Sopenharmony_ci DEBUGASSERT(cf_at); 114513498266Sopenharmony_ci cf_create = get_cf_create(transport); 114613498266Sopenharmony_ci if(!cf_create) { 114713498266Sopenharmony_ci CURL_TRC_CF(data, cf_at, "unsupported transport type %d", transport); 114813498266Sopenharmony_ci return CURLE_UNSUPPORTED_PROTOCOL; 114913498266Sopenharmony_ci } 115013498266Sopenharmony_ci result = cf_happy_eyeballs_create(&cf, data, cf_at->conn, 115113498266Sopenharmony_ci cf_create, remotehost, 115213498266Sopenharmony_ci transport); 115313498266Sopenharmony_ci if(result) 115413498266Sopenharmony_ci return result; 115513498266Sopenharmony_ci 115613498266Sopenharmony_ci Curl_conn_cf_insert_after(cf_at, cf); 115713498266Sopenharmony_ci return CURLE_OK; 115813498266Sopenharmony_ci} 115913498266Sopenharmony_ci 116013498266Sopenharmony_citypedef enum { 116113498266Sopenharmony_ci CF_SETUP_INIT, 116213498266Sopenharmony_ci CF_SETUP_CNNCT_EYEBALLS, 116313498266Sopenharmony_ci CF_SETUP_CNNCT_SOCKS, 116413498266Sopenharmony_ci CF_SETUP_CNNCT_HTTP_PROXY, 116513498266Sopenharmony_ci CF_SETUP_CNNCT_HAPROXY, 116613498266Sopenharmony_ci CF_SETUP_CNNCT_SSL, 116713498266Sopenharmony_ci CF_SETUP_DONE 116813498266Sopenharmony_ci} cf_setup_state; 116913498266Sopenharmony_ci 117013498266Sopenharmony_cistruct cf_setup_ctx { 117113498266Sopenharmony_ci cf_setup_state state; 117213498266Sopenharmony_ci const struct Curl_dns_entry *remotehost; 117313498266Sopenharmony_ci int ssl_mode; 117413498266Sopenharmony_ci int transport; 117513498266Sopenharmony_ci}; 117613498266Sopenharmony_ci 117713498266Sopenharmony_cistatic CURLcode cf_setup_connect(struct Curl_cfilter *cf, 117813498266Sopenharmony_ci struct Curl_easy *data, 117913498266Sopenharmony_ci bool blocking, bool *done) 118013498266Sopenharmony_ci{ 118113498266Sopenharmony_ci struct cf_setup_ctx *ctx = cf->ctx; 118213498266Sopenharmony_ci CURLcode result = CURLE_OK; 118313498266Sopenharmony_ci 118413498266Sopenharmony_ci if(cf->connected) { 118513498266Sopenharmony_ci *done = TRUE; 118613498266Sopenharmony_ci return CURLE_OK; 118713498266Sopenharmony_ci } 118813498266Sopenharmony_ci 118913498266Sopenharmony_ci /* connect current sub-chain */ 119013498266Sopenharmony_ciconnect_sub_chain: 119113498266Sopenharmony_ci if(cf->next && !cf->next->connected) { 119213498266Sopenharmony_ci result = Curl_conn_cf_connect(cf->next, data, blocking, done); 119313498266Sopenharmony_ci if(result || !*done) 119413498266Sopenharmony_ci return result; 119513498266Sopenharmony_ci } 119613498266Sopenharmony_ci 119713498266Sopenharmony_ci if(ctx->state < CF_SETUP_CNNCT_EYEBALLS) { 119813498266Sopenharmony_ci result = cf_he_insert_after(cf, data, ctx->remotehost, ctx->transport); 119913498266Sopenharmony_ci if(result) 120013498266Sopenharmony_ci return result; 120113498266Sopenharmony_ci ctx->state = CF_SETUP_CNNCT_EYEBALLS; 120213498266Sopenharmony_ci if(!cf->next || !cf->next->connected) 120313498266Sopenharmony_ci goto connect_sub_chain; 120413498266Sopenharmony_ci } 120513498266Sopenharmony_ci 120613498266Sopenharmony_ci /* sub-chain connected, do we need to add more? */ 120713498266Sopenharmony_ci#ifndef CURL_DISABLE_PROXY 120813498266Sopenharmony_ci if(ctx->state < CF_SETUP_CNNCT_SOCKS && cf->conn->bits.socksproxy) { 120913498266Sopenharmony_ci result = Curl_cf_socks_proxy_insert_after(cf, data); 121013498266Sopenharmony_ci if(result) 121113498266Sopenharmony_ci return result; 121213498266Sopenharmony_ci ctx->state = CF_SETUP_CNNCT_SOCKS; 121313498266Sopenharmony_ci if(!cf->next || !cf->next->connected) 121413498266Sopenharmony_ci goto connect_sub_chain; 121513498266Sopenharmony_ci } 121613498266Sopenharmony_ci 121713498266Sopenharmony_ci if(ctx->state < CF_SETUP_CNNCT_HTTP_PROXY && cf->conn->bits.httpproxy) { 121813498266Sopenharmony_ci#ifdef USE_SSL 121913498266Sopenharmony_ci if(IS_HTTPS_PROXY(cf->conn->http_proxy.proxytype) 122013498266Sopenharmony_ci && !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { 122113498266Sopenharmony_ci result = Curl_cf_ssl_proxy_insert_after(cf, data); 122213498266Sopenharmony_ci if(result) 122313498266Sopenharmony_ci return result; 122413498266Sopenharmony_ci } 122513498266Sopenharmony_ci#endif /* USE_SSL */ 122613498266Sopenharmony_ci 122713498266Sopenharmony_ci#if !defined(CURL_DISABLE_HTTP) 122813498266Sopenharmony_ci if(cf->conn->bits.tunnel_proxy) { 122913498266Sopenharmony_ci result = Curl_cf_http_proxy_insert_after(cf, data); 123013498266Sopenharmony_ci if(result) 123113498266Sopenharmony_ci return result; 123213498266Sopenharmony_ci } 123313498266Sopenharmony_ci#endif /* !CURL_DISABLE_HTTP */ 123413498266Sopenharmony_ci ctx->state = CF_SETUP_CNNCT_HTTP_PROXY; 123513498266Sopenharmony_ci if(!cf->next || !cf->next->connected) 123613498266Sopenharmony_ci goto connect_sub_chain; 123713498266Sopenharmony_ci } 123813498266Sopenharmony_ci#endif /* !CURL_DISABLE_PROXY */ 123913498266Sopenharmony_ci 124013498266Sopenharmony_ci if(ctx->state < CF_SETUP_CNNCT_HAPROXY) { 124113498266Sopenharmony_ci#if !defined(CURL_DISABLE_PROXY) 124213498266Sopenharmony_ci if(data->set.haproxyprotocol) { 124313498266Sopenharmony_ci if(Curl_conn_is_ssl(cf->conn, cf->sockindex)) { 124413498266Sopenharmony_ci failf(data, "haproxy protocol not support with SSL " 124513498266Sopenharmony_ci "encryption in place (QUIC?)"); 124613498266Sopenharmony_ci return CURLE_UNSUPPORTED_PROTOCOL; 124713498266Sopenharmony_ci } 124813498266Sopenharmony_ci result = Curl_cf_haproxy_insert_after(cf, data); 124913498266Sopenharmony_ci if(result) 125013498266Sopenharmony_ci return result; 125113498266Sopenharmony_ci } 125213498266Sopenharmony_ci#endif /* !CURL_DISABLE_PROXY */ 125313498266Sopenharmony_ci ctx->state = CF_SETUP_CNNCT_HAPROXY; 125413498266Sopenharmony_ci if(!cf->next || !cf->next->connected) 125513498266Sopenharmony_ci goto connect_sub_chain; 125613498266Sopenharmony_ci } 125713498266Sopenharmony_ci 125813498266Sopenharmony_ci if(ctx->state < CF_SETUP_CNNCT_SSL) { 125913498266Sopenharmony_ci#ifdef USE_SSL 126013498266Sopenharmony_ci if((ctx->ssl_mode == CURL_CF_SSL_ENABLE 126113498266Sopenharmony_ci || (ctx->ssl_mode != CURL_CF_SSL_DISABLE 126213498266Sopenharmony_ci && cf->conn->handler->flags & PROTOPT_SSL)) /* we want SSL */ 126313498266Sopenharmony_ci && !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { /* it is missing */ 126413498266Sopenharmony_ci result = Curl_cf_ssl_insert_after(cf, data); 126513498266Sopenharmony_ci if(result) 126613498266Sopenharmony_ci return result; 126713498266Sopenharmony_ci } 126813498266Sopenharmony_ci#endif /* USE_SSL */ 126913498266Sopenharmony_ci ctx->state = CF_SETUP_CNNCT_SSL; 127013498266Sopenharmony_ci if(!cf->next || !cf->next->connected) 127113498266Sopenharmony_ci goto connect_sub_chain; 127213498266Sopenharmony_ci } 127313498266Sopenharmony_ci 127413498266Sopenharmony_ci ctx->state = CF_SETUP_DONE; 127513498266Sopenharmony_ci cf->connected = TRUE; 127613498266Sopenharmony_ci *done = TRUE; 127713498266Sopenharmony_ci return CURLE_OK; 127813498266Sopenharmony_ci} 127913498266Sopenharmony_ci 128013498266Sopenharmony_cistatic void cf_setup_close(struct Curl_cfilter *cf, 128113498266Sopenharmony_ci struct Curl_easy *data) 128213498266Sopenharmony_ci{ 128313498266Sopenharmony_ci struct cf_setup_ctx *ctx = cf->ctx; 128413498266Sopenharmony_ci 128513498266Sopenharmony_ci CURL_TRC_CF(data, cf, "close"); 128613498266Sopenharmony_ci cf->connected = FALSE; 128713498266Sopenharmony_ci ctx->state = CF_SETUP_INIT; 128813498266Sopenharmony_ci 128913498266Sopenharmony_ci if(cf->next) { 129013498266Sopenharmony_ci cf->next->cft->do_close(cf->next, data); 129113498266Sopenharmony_ci Curl_conn_cf_discard_chain(&cf->next, data); 129213498266Sopenharmony_ci } 129313498266Sopenharmony_ci} 129413498266Sopenharmony_ci 129513498266Sopenharmony_cistatic void cf_setup_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) 129613498266Sopenharmony_ci{ 129713498266Sopenharmony_ci struct cf_setup_ctx *ctx = cf->ctx; 129813498266Sopenharmony_ci 129913498266Sopenharmony_ci (void)data; 130013498266Sopenharmony_ci CURL_TRC_CF(data, cf, "destroy"); 130113498266Sopenharmony_ci Curl_safefree(ctx); 130213498266Sopenharmony_ci} 130313498266Sopenharmony_ci 130413498266Sopenharmony_ci 130513498266Sopenharmony_cistruct Curl_cftype Curl_cft_setup = { 130613498266Sopenharmony_ci "SETUP", 130713498266Sopenharmony_ci 0, 130813498266Sopenharmony_ci CURL_LOG_LVL_NONE, 130913498266Sopenharmony_ci cf_setup_destroy, 131013498266Sopenharmony_ci cf_setup_connect, 131113498266Sopenharmony_ci cf_setup_close, 131213498266Sopenharmony_ci Curl_cf_def_get_host, 131313498266Sopenharmony_ci Curl_cf_def_adjust_pollset, 131413498266Sopenharmony_ci Curl_cf_def_data_pending, 131513498266Sopenharmony_ci Curl_cf_def_send, 131613498266Sopenharmony_ci Curl_cf_def_recv, 131713498266Sopenharmony_ci Curl_cf_def_cntrl, 131813498266Sopenharmony_ci Curl_cf_def_conn_is_alive, 131913498266Sopenharmony_ci Curl_cf_def_conn_keep_alive, 132013498266Sopenharmony_ci Curl_cf_def_query, 132113498266Sopenharmony_ci}; 132213498266Sopenharmony_ci 132313498266Sopenharmony_cistatic CURLcode cf_setup_create(struct Curl_cfilter **pcf, 132413498266Sopenharmony_ci struct Curl_easy *data, 132513498266Sopenharmony_ci const struct Curl_dns_entry *remotehost, 132613498266Sopenharmony_ci int transport, 132713498266Sopenharmony_ci int ssl_mode) 132813498266Sopenharmony_ci{ 132913498266Sopenharmony_ci struct Curl_cfilter *cf = NULL; 133013498266Sopenharmony_ci struct cf_setup_ctx *ctx; 133113498266Sopenharmony_ci CURLcode result = CURLE_OK; 133213498266Sopenharmony_ci 133313498266Sopenharmony_ci (void)data; 133413498266Sopenharmony_ci ctx = calloc(1, sizeof(*ctx)); 133513498266Sopenharmony_ci if(!ctx) { 133613498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 133713498266Sopenharmony_ci goto out; 133813498266Sopenharmony_ci } 133913498266Sopenharmony_ci ctx->state = CF_SETUP_INIT; 134013498266Sopenharmony_ci ctx->remotehost = remotehost; 134113498266Sopenharmony_ci ctx->ssl_mode = ssl_mode; 134213498266Sopenharmony_ci ctx->transport = transport; 134313498266Sopenharmony_ci 134413498266Sopenharmony_ci result = Curl_cf_create(&cf, &Curl_cft_setup, ctx); 134513498266Sopenharmony_ci if(result) 134613498266Sopenharmony_ci goto out; 134713498266Sopenharmony_ci ctx = NULL; 134813498266Sopenharmony_ci 134913498266Sopenharmony_ciout: 135013498266Sopenharmony_ci *pcf = result? NULL : cf; 135113498266Sopenharmony_ci free(ctx); 135213498266Sopenharmony_ci return result; 135313498266Sopenharmony_ci} 135413498266Sopenharmony_ci 135513498266Sopenharmony_cistatic CURLcode cf_setup_add(struct Curl_easy *data, 135613498266Sopenharmony_ci struct connectdata *conn, 135713498266Sopenharmony_ci int sockindex, 135813498266Sopenharmony_ci const struct Curl_dns_entry *remotehost, 135913498266Sopenharmony_ci int transport, 136013498266Sopenharmony_ci int ssl_mode) 136113498266Sopenharmony_ci{ 136213498266Sopenharmony_ci struct Curl_cfilter *cf; 136313498266Sopenharmony_ci CURLcode result = CURLE_OK; 136413498266Sopenharmony_ci 136513498266Sopenharmony_ci DEBUGASSERT(data); 136613498266Sopenharmony_ci result = cf_setup_create(&cf, data, remotehost, transport, ssl_mode); 136713498266Sopenharmony_ci if(result) 136813498266Sopenharmony_ci goto out; 136913498266Sopenharmony_ci Curl_conn_cf_add(data, conn, sockindex, cf); 137013498266Sopenharmony_ciout: 137113498266Sopenharmony_ci return result; 137213498266Sopenharmony_ci} 137313498266Sopenharmony_ci 137413498266Sopenharmony_ci#ifdef DEBUGBUILD 137513498266Sopenharmony_ci/* used by unit2600.c */ 137613498266Sopenharmony_civoid Curl_debug_set_transport_provider(int transport, 137713498266Sopenharmony_ci cf_ip_connect_create *cf_create) 137813498266Sopenharmony_ci{ 137913498266Sopenharmony_ci size_t i; 138013498266Sopenharmony_ci for(i = 0; i < ARRAYSIZE(transport_providers); ++i) { 138113498266Sopenharmony_ci if(transport == transport_providers[i].transport) { 138213498266Sopenharmony_ci transport_providers[i].cf_create = cf_create; 138313498266Sopenharmony_ci return; 138413498266Sopenharmony_ci } 138513498266Sopenharmony_ci } 138613498266Sopenharmony_ci} 138713498266Sopenharmony_ci#endif /* DEBUGBUILD */ 138813498266Sopenharmony_ci 138913498266Sopenharmony_ciCURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at, 139013498266Sopenharmony_ci struct Curl_easy *data, 139113498266Sopenharmony_ci const struct Curl_dns_entry *remotehost, 139213498266Sopenharmony_ci int transport, 139313498266Sopenharmony_ci int ssl_mode) 139413498266Sopenharmony_ci{ 139513498266Sopenharmony_ci struct Curl_cfilter *cf; 139613498266Sopenharmony_ci CURLcode result; 139713498266Sopenharmony_ci 139813498266Sopenharmony_ci DEBUGASSERT(data); 139913498266Sopenharmony_ci result = cf_setup_create(&cf, data, remotehost, transport, ssl_mode); 140013498266Sopenharmony_ci if(result) 140113498266Sopenharmony_ci goto out; 140213498266Sopenharmony_ci Curl_conn_cf_insert_after(cf_at, cf); 140313498266Sopenharmony_ciout: 140413498266Sopenharmony_ci return result; 140513498266Sopenharmony_ci} 140613498266Sopenharmony_ci 140713498266Sopenharmony_ciCURLcode Curl_conn_setup(struct Curl_easy *data, 140813498266Sopenharmony_ci struct connectdata *conn, 140913498266Sopenharmony_ci int sockindex, 141013498266Sopenharmony_ci const struct Curl_dns_entry *remotehost, 141113498266Sopenharmony_ci int ssl_mode) 141213498266Sopenharmony_ci{ 141313498266Sopenharmony_ci CURLcode result = CURLE_OK; 141413498266Sopenharmony_ci 141513498266Sopenharmony_ci DEBUGASSERT(data); 141613498266Sopenharmony_ci DEBUGASSERT(conn->handler); 141713498266Sopenharmony_ci 141813498266Sopenharmony_ci#if !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) 141913498266Sopenharmony_ci if(!conn->cfilter[sockindex] && 142013498266Sopenharmony_ci conn->handler->protocol == CURLPROTO_HTTPS) { 142113498266Sopenharmony_ci DEBUGASSERT(ssl_mode != CURL_CF_SSL_DISABLE); 142213498266Sopenharmony_ci result = Curl_cf_https_setup(data, conn, sockindex, remotehost); 142313498266Sopenharmony_ci if(result) 142413498266Sopenharmony_ci goto out; 142513498266Sopenharmony_ci } 142613498266Sopenharmony_ci#endif /* !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) */ 142713498266Sopenharmony_ci 142813498266Sopenharmony_ci /* Still no cfilter set, apply default. */ 142913498266Sopenharmony_ci if(!conn->cfilter[sockindex]) { 143013498266Sopenharmony_ci result = cf_setup_add(data, conn, sockindex, remotehost, 143113498266Sopenharmony_ci conn->transport, ssl_mode); 143213498266Sopenharmony_ci if(result) 143313498266Sopenharmony_ci goto out; 143413498266Sopenharmony_ci } 143513498266Sopenharmony_ci 143613498266Sopenharmony_ci DEBUGASSERT(conn->cfilter[sockindex]); 143713498266Sopenharmony_ciout: 143813498266Sopenharmony_ci return result; 143913498266Sopenharmony_ci} 1440