quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

connect.c (44542B)


      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
      9  *
     10  * This software is licensed as described in the file COPYING, which
     11  * you should have received as part of this distribution. The terms
     12  * are also available at https://curl.se/docs/copyright.html.
     13  *
     14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
     15  * copies of the Software, and permit persons to whom the Software is
     16  * furnished to do so, under the terms of the COPYING file.
     17  *
     18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
     19  * KIND, either express or implied.
     20  *
     21  * SPDX-License-Identifier: curl
     22  *
     23  ***************************************************************************/
     24 
     25 #include "curl_setup.h"
     26 
     27 #ifdef HAVE_NETINET_IN_H
     28 #include <netinet/in.h> /* <netinet/tcp.h> may need it */
     29 #endif
     30 #ifdef HAVE_SYS_UN_H
     31 #include <sys/un.h> /* for sockaddr_un */
     32 #endif
     33 #ifdef HAVE_LINUX_TCP_H
     34 #include <linux/tcp.h>
     35 #elif defined(HAVE_NETINET_TCP_H)
     36 #include <netinet/tcp.h>
     37 #endif
     38 #ifdef HAVE_SYS_IOCTL_H
     39 #include <sys/ioctl.h>
     40 #endif
     41 #ifdef HAVE_NETDB_H
     42 #include <netdb.h>
     43 #endif
     44 #ifdef HAVE_FCNTL_H
     45 #include <fcntl.h>
     46 #endif
     47 #ifdef HAVE_ARPA_INET_H
     48 #include <arpa/inet.h>
     49 #endif
     50 
     51 #ifdef __VMS
     52 #include <in.h>
     53 #include <inet.h>
     54 #endif
     55 
     56 #include "urldata.h"
     57 #include "sendf.h"
     58 #include "if2ip.h"
     59 #include "strerror.h"
     60 #include "cfilters.h"
     61 #include "connect.h"
     62 #include "cf-haproxy.h"
     63 #include "cf-https-connect.h"
     64 #include "cf-socket.h"
     65 #include "select.h"
     66 #include "url.h" /* for Curl_safefree() */
     67 #include "multiif.h"
     68 #include "sockaddr.h" /* required for Curl_sockaddr_storage */
     69 #include "curlx/inet_ntop.h"
     70 #include "curlx/inet_pton.h"
     71 #include "vtls/vtls.h" /* for vtsl cfilters */
     72 #include "progress.h"
     73 #include "curlx/warnless.h"
     74 #include "conncache.h"
     75 #include "multihandle.h"
     76 #include "share.h"
     77 #include "curlx/version_win32.h"
     78 #include "vquic/vquic.h" /* for quic cfilters */
     79 #include "http_proxy.h"
     80 #include "socks.h"
     81 
     82 /* The last 3 #include files should be in this order */
     83 #include "curl_printf.h"
     84 #include "curl_memory.h"
     85 #include "memdebug.h"
     86 
     87 #if !defined(CURL_DISABLE_ALTSVC) || defined(USE_HTTPSRR)
     88 
     89 enum alpnid Curl_alpn2alpnid(const char *name, size_t len)
     90 {
     91   if(len == 2) {
     92     if(curl_strnequal(name, "h1", 2))
     93       return ALPN_h1;
     94     if(curl_strnequal(name, "h2", 2))
     95       return ALPN_h2;
     96     if(curl_strnequal(name, "h3", 2))
     97       return ALPN_h3;
     98   }
     99   else if(len == 8) {
    100     if(curl_strnequal(name, "http/1.1", 8))
    101       return ALPN_h1;
    102   }
    103   return ALPN_none; /* unknown, probably rubbish input */
    104 }
    105 
    106 #endif
    107 
    108 /*
    109  * Curl_timeleft() returns the amount of milliseconds left allowed for the
    110  * transfer/connection. If the value is 0, there is no timeout (ie there is
    111  * infinite time left). If the value is negative, the timeout time has already
    112  * elapsed.
    113  * @param data the transfer to check on
    114  * @param nowp timestamp to use for calculation, NULL to use curlx_now()
    115  * @param duringconnect TRUE iff connect timeout is also taken into account.
    116  * @unittest: 1303
    117  */
    118 timediff_t Curl_timeleft(struct Curl_easy *data,
    119                          struct curltime *nowp,
    120                          bool duringconnect)
    121 {
    122   timediff_t timeleft_ms = 0;
    123   timediff_t ctimeleft_ms = 0;
    124   struct curltime now;
    125 
    126   /* The duration of a connect and the total transfer are calculated from two
    127      different time-stamps. It can end up with the total timeout being reached
    128      before the connect timeout expires and we must acknowledge whichever
    129      timeout that is reached first. The total timeout is set per entire
    130      operation, while the connect timeout is set per connect. */
    131   if(data->set.timeout <= 0 && !duringconnect)
    132     return 0; /* no timeout in place or checked, return "no limit" */
    133 
    134   if(!nowp) {
    135     now = curlx_now();
    136     nowp = &now;
    137   }
    138 
    139   if(data->set.timeout > 0) {
    140     timeleft_ms = data->set.timeout -
    141                   curlx_timediff(*nowp, data->progress.t_startop);
    142     if(!timeleft_ms)
    143       timeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */
    144     if(!duringconnect)
    145       return timeleft_ms; /* no connect check, this is it */
    146   }
    147 
    148   if(duringconnect) {
    149     timediff_t ctimeout_ms = (data->set.connecttimeout > 0) ?
    150       data->set.connecttimeout : DEFAULT_CONNECT_TIMEOUT;
    151     ctimeleft_ms = ctimeout_ms -
    152                    curlx_timediff(*nowp, data->progress.t_startsingle);
    153     if(!ctimeleft_ms)
    154       ctimeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */
    155     if(!timeleft_ms)
    156       return ctimeleft_ms; /* no general timeout, this is it */
    157   }
    158   /* return minimal time left or max amount already expired */
    159   return (ctimeleft_ms < timeleft_ms) ? ctimeleft_ms : timeleft_ms;
    160 }
    161 
    162 void Curl_shutdown_start(struct Curl_easy *data, int sockindex,
    163                          int timeout_ms, struct curltime *nowp)
    164 {
    165   struct curltime now;
    166 
    167   DEBUGASSERT(data->conn);
    168   if(!nowp) {
    169     now = curlx_now();
    170     nowp = &now;
    171   }
    172   data->conn->shutdown.start[sockindex] = *nowp;
    173   data->conn->shutdown.timeout_ms = (timeout_ms > 0) ?
    174     (timediff_t)timeout_ms :
    175     ((data->set.shutdowntimeout > 0) ?
    176      data->set.shutdowntimeout : DEFAULT_SHUTDOWN_TIMEOUT_MS);
    177   /* Set a timer, unless we operate on the admin handle */
    178   if(data->mid && (data->conn->shutdown.timeout_ms > 0))
    179     Curl_expire_ex(data, nowp, data->conn->shutdown.timeout_ms,
    180                    EXPIRE_SHUTDOWN);
    181 }
    182 
    183 timediff_t Curl_shutdown_timeleft(struct connectdata *conn, int sockindex,
    184                                   struct curltime *nowp)
    185 {
    186   struct curltime now;
    187   timediff_t left_ms;
    188 
    189   if(!conn->shutdown.start[sockindex].tv_sec ||
    190      (conn->shutdown.timeout_ms <= 0))
    191     return 0; /* not started or no limits */
    192 
    193   if(!nowp) {
    194     now = curlx_now();
    195     nowp = &now;
    196   }
    197   left_ms = conn->shutdown.timeout_ms -
    198             curlx_timediff(*nowp, conn->shutdown.start[sockindex]);
    199   return left_ms ? left_ms : -1;
    200 }
    201 
    202 timediff_t Curl_conn_shutdown_timeleft(struct connectdata *conn,
    203                                        struct curltime *nowp)
    204 {
    205   timediff_t left_ms = 0, ms;
    206   struct curltime now;
    207   int i;
    208 
    209   for(i = 0; conn->shutdown.timeout_ms && (i < 2); ++i) {
    210     if(!conn->shutdown.start[i].tv_sec)
    211       continue;
    212     if(!nowp) {
    213       now = curlx_now();
    214       nowp = &now;
    215     }
    216     ms = Curl_shutdown_timeleft(conn, i, nowp);
    217     if(ms && (!left_ms || ms < left_ms))
    218       left_ms = ms;
    219   }
    220   return left_ms;
    221 }
    222 
    223 void Curl_shutdown_clear(struct Curl_easy *data, int sockindex)
    224 {
    225   struct curltime *pt = &data->conn->shutdown.start[sockindex];
    226   memset(pt, 0, sizeof(*pt));
    227 }
    228 
    229 bool Curl_shutdown_started(struct Curl_easy *data, int sockindex)
    230 {
    231   struct curltime *pt = &data->conn->shutdown.start[sockindex];
    232   return (pt->tv_sec > 0) || (pt->tv_usec > 0);
    233 }
    234 
    235 static const struct Curl_addrinfo *
    236 addr_first_match(const struct Curl_addrinfo *addr, int family)
    237 {
    238   while(addr) {
    239     if(addr->ai_family == family)
    240       return addr;
    241     addr = addr->ai_next;
    242   }
    243   return NULL;
    244 }
    245 
    246 static const struct Curl_addrinfo *
    247 addr_next_match(const struct Curl_addrinfo *addr, int family)
    248 {
    249   while(addr && addr->ai_next) {
    250     addr = addr->ai_next;
    251     if(addr->ai_family == family)
    252       return addr;
    253   }
    254   return NULL;
    255 }
    256 
    257 /* retrieves ip address and port from a sockaddr structure. note it calls
    258    curlx_inet_ntop which sets errno on fail, not SOCKERRNO. */
    259 bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
    260                       char *addr, int *port)
    261 {
    262   struct sockaddr_in *si = NULL;
    263 #ifdef USE_IPV6
    264   struct sockaddr_in6 *si6 = NULL;
    265 #endif
    266 #if (defined(HAVE_SYS_UN_H) || defined(WIN32_SOCKADDR_UN)) && defined(AF_UNIX)
    267   struct sockaddr_un *su = NULL;
    268 #else
    269   (void)salen;
    270 #endif
    271 
    272   switch(sa->sa_family) {
    273     case AF_INET:
    274       si = (struct sockaddr_in *)(void *) sa;
    275       if(curlx_inet_ntop(sa->sa_family, &si->sin_addr, addr, MAX_IPADR_LEN)) {
    276         unsigned short us_port = ntohs(si->sin_port);
    277         *port = us_port;
    278         return TRUE;
    279       }
    280       break;
    281 #ifdef USE_IPV6
    282     case AF_INET6:
    283       si6 = (struct sockaddr_in6 *)(void *) sa;
    284       if(curlx_inet_ntop(sa->sa_family, &si6->sin6_addr, addr,
    285                          MAX_IPADR_LEN)) {
    286         unsigned short us_port = ntohs(si6->sin6_port);
    287         *port = us_port;
    288         return TRUE;
    289       }
    290       break;
    291 #endif
    292 #if (defined(HAVE_SYS_UN_H) || defined(WIN32_SOCKADDR_UN)) && defined(AF_UNIX)
    293     case AF_UNIX:
    294       if(salen > (curl_socklen_t)sizeof(CURL_SA_FAMILY_T)) {
    295         su = (struct sockaddr_un*)sa;
    296         msnprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path);
    297       }
    298       else
    299         addr[0] = 0; /* socket with no name */
    300       *port = 0;
    301       return TRUE;
    302 #endif
    303     default:
    304       break;
    305   }
    306 
    307   addr[0] = '\0';
    308   *port = 0;
    309   CURL_SETERRNO(SOCKEAFNOSUPPORT);
    310   return FALSE;
    311 }
    312 
    313 /*
    314  * Used to extract socket and connectdata struct for the most recent
    315  * transfer on the given Curl_easy.
    316  *
    317  * The returned socket will be CURL_SOCKET_BAD in case of failure!
    318  */
    319 curl_socket_t Curl_getconnectinfo(struct Curl_easy *data,
    320                                   struct connectdata **connp)
    321 {
    322   DEBUGASSERT(data);
    323 
    324   /* this works for an easy handle:
    325    * - that has been used for curl_easy_perform()
    326    * - that is associated with a multi handle, and whose connection
    327    *   was detached with CURLOPT_CONNECT_ONLY
    328    */
    329   if(data->state.lastconnect_id != -1) {
    330     struct connectdata *conn;
    331 
    332     conn = Curl_cpool_get_conn(data, data->state.lastconnect_id);
    333     if(!conn) {
    334       data->state.lastconnect_id = -1;
    335       return CURL_SOCKET_BAD;
    336     }
    337 
    338     if(connp)
    339       /* only store this if the caller cares for it */
    340       *connp = conn;
    341     return conn->sock[FIRSTSOCKET];
    342   }
    343   return CURL_SOCKET_BAD;
    344 }
    345 
    346 /*
    347  * Curl_conncontrol() marks streams or connection for closure.
    348  */
    349 void Curl_conncontrol(struct connectdata *conn,
    350                       int ctrl /* see defines in header */
    351 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
    352                       , const char *reason
    353 #endif
    354   )
    355 {
    356   /* close if a connection, or a stream that is not multiplexed. */
    357   /* This function will be called both before and after this connection is
    358      associated with a transfer. */
    359   bool closeit, is_multiplex;
    360   DEBUGASSERT(conn);
    361 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
    362   (void)reason; /* useful for debugging */
    363 #endif
    364   is_multiplex = Curl_conn_is_multiplex(conn, FIRSTSOCKET);
    365   closeit = (ctrl == CONNCTRL_CONNECTION) ||
    366     ((ctrl == CONNCTRL_STREAM) && !is_multiplex);
    367   if((ctrl == CONNCTRL_STREAM) && is_multiplex)
    368     ;  /* stream signal on multiplex conn never affects close state */
    369   else if((bit)closeit != conn->bits.close) {
    370     conn->bits.close = closeit; /* the only place in the source code that
    371                                    should assign this bit */
    372   }
    373 }
    374 
    375 /**
    376  * job walking the matching addr infos, creating a sub-cfilter with the
    377  * provided method `cf_create` and running setup/connect on it.
    378  */
    379 struct eyeballer {
    380   const char *name;
    381   const struct Curl_addrinfo *first; /* complete address list, not owned */
    382   const struct Curl_addrinfo *addr;  /* List of addresses to try, not owned */
    383   int ai_family;                     /* matching address family only */
    384   cf_ip_connect_create *cf_create;   /* for creating cf */
    385   struct Curl_cfilter *cf;           /* current sub-cfilter connecting */
    386   struct eyeballer *primary;         /* eyeballer this one is backup for */
    387   timediff_t delay_ms;               /* delay until start */
    388   struct curltime started;           /* start of current attempt */
    389   timediff_t timeoutms;              /* timeout for current attempt */
    390   expire_id timeout_id;              /* ID for Curl_expire() */
    391   CURLcode result;
    392   int error;
    393   BIT(has_started);                  /* attempts have started */
    394   BIT(is_done);                      /* out of addresses/time */
    395   BIT(connected);                    /* cf has connected */
    396   BIT(shutdown);                     /* cf has shutdown */
    397   BIT(inconclusive);                 /* connect was not a hard failure, we
    398                                       * might talk to a restarting server */
    399 };
    400 
    401 
    402 typedef enum {
    403   SCFST_INIT,
    404   SCFST_WAITING,
    405   SCFST_DONE
    406 } cf_connect_state;
    407 
    408 struct cf_he_ctx {
    409   int transport;
    410   cf_ip_connect_create *cf_create;
    411   cf_connect_state state;
    412   struct eyeballer *baller[2];
    413   struct eyeballer *winner;
    414   struct curltime started;
    415 };
    416 
    417 /* when there are more than one IP address left to use, this macro returns how
    418    much of the given timeout to spend on *this* attempt */
    419 #define TIMEOUT_LARGE 600
    420 #define USETIME(ms) ((ms > TIMEOUT_LARGE) ? (ms / 2) : ms)
    421 
    422 static CURLcode eyeballer_new(struct eyeballer **pballer,
    423                               cf_ip_connect_create *cf_create,
    424                               const struct Curl_addrinfo *addr,
    425                               int ai_family,
    426                               struct eyeballer *primary,
    427                               timediff_t delay_ms,
    428                               timediff_t timeout_ms,
    429                               expire_id timeout_id)
    430 {
    431   struct eyeballer *baller;
    432 
    433   *pballer = NULL;
    434   baller = calloc(1, sizeof(*baller));
    435   if(!baller)
    436     return CURLE_OUT_OF_MEMORY;
    437 
    438   baller->name = ((ai_family == AF_INET) ? "ipv4" : (
    439 #ifdef USE_IPV6
    440                   (ai_family == AF_INET6) ? "ipv6" :
    441 #endif
    442                   "ip"));
    443   baller->cf_create = cf_create;
    444   baller->first = baller->addr = addr;
    445   baller->ai_family = ai_family;
    446   baller->primary = primary;
    447   baller->delay_ms = delay_ms;
    448   baller->timeoutms = addr_next_match(baller->addr, baller->ai_family) ?
    449     USETIME(timeout_ms) : timeout_ms;
    450   baller->timeout_id = timeout_id;
    451   baller->result = CURLE_COULDNT_CONNECT;
    452 
    453   *pballer = baller;
    454   return CURLE_OK;
    455 }
    456 
    457 static void baller_close(struct eyeballer *baller,
    458                          struct Curl_easy *data)
    459 {
    460   if(baller && baller->cf) {
    461     Curl_conn_cf_discard_chain(&baller->cf, data);
    462   }
    463 }
    464 
    465 static void baller_free(struct eyeballer *baller,
    466                         struct Curl_easy *data)
    467 {
    468   if(baller) {
    469     baller_close(baller, data);
    470     free(baller);
    471   }
    472 }
    473 
    474 static void baller_rewind(struct eyeballer *baller)
    475 {
    476   baller->addr = baller->first;
    477   baller->inconclusive = FALSE;
    478 }
    479 
    480 static void baller_next_addr(struct eyeballer *baller)
    481 {
    482   baller->addr = addr_next_match(baller->addr, baller->ai_family);
    483 }
    484 
    485 /*
    486  * Initiate a connect attempt walk.
    487  *
    488  * Note that even on connect fail it returns CURLE_OK, but with 'sock' set to
    489  * CURL_SOCKET_BAD. Other errors will however return proper errors.
    490  */
    491 static void baller_initiate(struct Curl_cfilter *cf,
    492                             struct Curl_easy *data,
    493                             struct eyeballer *baller)
    494 {
    495   struct cf_he_ctx *ctx = cf->ctx;
    496   struct Curl_cfilter *cf_prev = baller->cf;
    497   struct Curl_cfilter *wcf;
    498   CURLcode result;
    499 
    500 
    501   /* Do not close a previous cfilter yet to ensure that the next IP's
    502      socket gets a different file descriptor, which can prevent bugs when
    503      the curl_multi_socket_action interface is used with certain select()
    504      replacements such as kqueue. */
    505   result = baller->cf_create(&baller->cf, data, cf->conn, baller->addr,
    506                              ctx->transport);
    507   if(result)
    508     goto out;
    509 
    510   /* the new filter might have sub-filters */
    511   for(wcf = baller->cf; wcf; wcf = wcf->next) {
    512     wcf->conn = cf->conn;
    513     wcf->sockindex = cf->sockindex;
    514   }
    515 
    516   if(addr_next_match(baller->addr, baller->ai_family)) {
    517     Curl_expire(data, baller->timeoutms, baller->timeout_id);
    518   }
    519 
    520 out:
    521   if(result) {
    522     CURL_TRC_CF(data, cf, "%s failed", baller->name);
    523     baller_close(baller, data);
    524   }
    525   if(cf_prev)
    526     Curl_conn_cf_discard_chain(&cf_prev, data);
    527   baller->result = result;
    528 }
    529 
    530 /**
    531  * Start a connection attempt on the current baller address.
    532  * Will return CURLE_OK on the first address where a socket
    533  * could be created and the non-blocking connect started.
    534  * Returns error when all remaining addresses have been tried.
    535  */
    536 static CURLcode baller_start(struct Curl_cfilter *cf,
    537                              struct Curl_easy *data,
    538                              struct eyeballer *baller,
    539                              timediff_t timeoutms)
    540 {
    541   baller->error = 0;
    542   baller->connected = FALSE;
    543   baller->has_started = TRUE;
    544 
    545   while(baller->addr) {
    546     baller->started = curlx_now();
    547     baller->timeoutms = addr_next_match(baller->addr, baller->ai_family) ?
    548       USETIME(timeoutms) : timeoutms;
    549     baller_initiate(cf, data, baller);
    550     if(!baller->result)
    551       break;
    552     baller_next_addr(baller);
    553   }
    554   if(!baller->addr) {
    555     baller->is_done = TRUE;
    556   }
    557   return baller->result;
    558 }
    559 
    560 
    561 /* Used within the multi interface. Try next IP address, returns error if no
    562    more address exists or error */
    563 static CURLcode baller_start_next(struct Curl_cfilter *cf,
    564                                   struct Curl_easy *data,
    565                                   struct eyeballer *baller,
    566                                   timediff_t timeoutms)
    567 {
    568   if(cf->sockindex == FIRSTSOCKET) {
    569     baller_next_addr(baller);
    570     /* If we get inconclusive answers from the server(s), we start
    571      * again until this whole thing times out. This allows us to
    572      * connect to servers that are gracefully restarting and the
    573      * packet routing to the new instance has not happened yet (e.g. QUIC). */
    574     if(!baller->addr && baller->inconclusive)
    575       baller_rewind(baller);
    576     baller_start(cf, data, baller, timeoutms);
    577   }
    578   else {
    579     baller->error = 0;
    580     baller->connected = FALSE;
    581     baller->has_started = TRUE;
    582     baller->is_done = TRUE;
    583     baller->result = CURLE_COULDNT_CONNECT;
    584   }
    585   return baller->result;
    586 }
    587 
    588 static CURLcode baller_connect(struct Curl_cfilter *cf,
    589                                struct Curl_easy *data,
    590                                struct eyeballer *baller,
    591                                struct curltime *now,
    592                                bool *connected)
    593 {
    594   (void)cf;
    595   *connected = baller->connected;
    596   if(!baller->result &&  !*connected) {
    597     /* evaluate again */
    598     baller->result = Curl_conn_cf_connect(baller->cf, data, connected);
    599 
    600     if(!baller->result) {
    601       if(*connected) {
    602         baller->connected = TRUE;
    603         baller->is_done = TRUE;
    604       }
    605       else if(curlx_timediff(*now, baller->started) >= baller->timeoutms) {
    606         infof(data, "%s connect timeout after %" FMT_TIMEDIFF_T
    607               "ms, move on!", baller->name, baller->timeoutms);
    608 #ifdef SOCKETIMEDOUT
    609         baller->error = SOCKETIMEDOUT;
    610 #endif
    611         baller->result = CURLE_OPERATION_TIMEDOUT;
    612       }
    613     }
    614     else if(baller->result == CURLE_WEIRD_SERVER_REPLY)
    615       baller->inconclusive = TRUE;
    616   }
    617   return baller->result;
    618 }
    619 
    620 /*
    621  * is_connected() checks if the socket has connected.
    622  */
    623 static CURLcode is_connected(struct Curl_cfilter *cf,
    624                              struct Curl_easy *data,
    625                              bool *connected)
    626 {
    627   struct cf_he_ctx *ctx = cf->ctx;
    628   struct connectdata *conn = cf->conn;
    629   CURLcode result;
    630   struct curltime now;
    631   size_t i;
    632   int ongoing, not_started;
    633   const char *hostname;
    634 
    635   /* Check if any of the conn->tempsock we use for establishing connections
    636    * succeeded and, if so, close any ongoing other ones.
    637    * Transfer the successful conn->tempsock to conn->sock[sockindex]
    638    * and set conn->tempsock to CURL_SOCKET_BAD.
    639    * If transport is QUIC, we need to shutdown the ongoing 'other'
    640    * cot ballers in a QUIC appropriate way. */
    641 evaluate:
    642   *connected = FALSE; /* a negative world view is best */
    643   now = curlx_now();
    644   ongoing = not_started = 0;
    645   for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
    646     struct eyeballer *baller = ctx->baller[i];
    647 
    648     if(!baller || baller->is_done)
    649       continue;
    650 
    651     if(!baller->has_started) {
    652       ++not_started;
    653       continue;
    654     }
    655     baller->result = baller_connect(cf, data, baller, &now, connected);
    656     CURL_TRC_CF(data, cf, "%s connect -> %d, connected=%d",
    657                 baller->name, baller->result, *connected);
    658 
    659     if(!baller->result) {
    660       if(*connected) {
    661         /* connected, declare the winner */
    662         ctx->winner = baller;
    663         ctx->baller[i] = NULL;
    664         break;
    665       }
    666       else { /* still waiting */
    667         ++ongoing;
    668       }
    669     }
    670     else if(!baller->is_done) {
    671       /* The baller failed to connect, start its next attempt */
    672       if(baller->error) {
    673         data->state.os_errno = baller->error;
    674         SET_SOCKERRNO(baller->error);
    675       }
    676       baller_start_next(cf, data, baller, Curl_timeleft(data, &now, TRUE));
    677       if(baller->is_done) {
    678         CURL_TRC_CF(data, cf, "%s done", baller->name);
    679       }
    680       else {
    681         /* next attempt was started */
    682         CURL_TRC_CF(data, cf, "%s trying next", baller->name);
    683         ++ongoing;
    684         Curl_multi_mark_dirty(data);
    685       }
    686     }
    687   }
    688 
    689   if(ctx->winner) {
    690     *connected = TRUE;
    691     return CURLE_OK;
    692   }
    693 
    694   /* Nothing connected, check the time before we might
    695    * start new ballers or return ok. */
    696   if((ongoing || not_started) && Curl_timeleft(data, &now, TRUE) < 0) {
    697     failf(data, "Connection timeout after %" FMT_OFF_T " ms",
    698           curlx_timediff(now, data->progress.t_startsingle));
    699     return CURLE_OPERATION_TIMEDOUT;
    700   }
    701 
    702   /* Check if we have any waiting ballers to start now. */
    703   if(not_started > 0) {
    704     int added = 0;
    705 
    706     for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
    707       struct eyeballer *baller = ctx->baller[i];
    708 
    709       if(!baller || baller->has_started)
    710         continue;
    711       /* We start its primary baller has failed to connect or if
    712        * its start delay_ms have expired */
    713       if((baller->primary && baller->primary->is_done) ||
    714           curlx_timediff(now, ctx->started) >= baller->delay_ms) {
    715         baller_start(cf, data, baller, Curl_timeleft(data, &now, TRUE));
    716         if(baller->is_done) {
    717           CURL_TRC_CF(data, cf, "%s done", baller->name);
    718         }
    719         else {
    720           CURL_TRC_CF(data, cf, "%s starting (timeout=%" FMT_TIMEDIFF_T "ms)",
    721                       baller->name, baller->timeoutms);
    722           ++ongoing;
    723           ++added;
    724         }
    725       }
    726     }
    727     if(added > 0)
    728       goto evaluate;
    729   }
    730 
    731   if(ongoing > 0) {
    732     /* We are still trying, return for more waiting */
    733     *connected = FALSE;
    734     return CURLE_OK;
    735   }
    736 
    737   /* all ballers have failed to connect. */
    738   CURL_TRC_CF(data, cf, "all eyeballers failed");
    739   result = CURLE_COULDNT_CONNECT;
    740   for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
    741     struct eyeballer *baller = ctx->baller[i];
    742     if(!baller)
    743       continue;
    744     CURL_TRC_CF(data, cf, "%s assess started=%d, result=%d",
    745                 baller->name, baller->has_started, baller->result);
    746     if(baller->has_started && baller->result) {
    747       result = baller->result;
    748       break;
    749     }
    750   }
    751 
    752 #ifndef CURL_DISABLE_PROXY
    753   if(conn->bits.socksproxy)
    754     hostname = conn->socks_proxy.host.name;
    755   else if(conn->bits.httpproxy)
    756     hostname = conn->http_proxy.host.name;
    757   else
    758 #endif
    759     if(conn->bits.conn_to_host)
    760       hostname = conn->conn_to_host.name;
    761   else
    762     hostname = conn->host.name;
    763 
    764   failf(data, "Failed to connect to %s port %u after "
    765         "%" FMT_TIMEDIFF_T " ms: %s",
    766         hostname, conn->primary.remote_port,
    767         curlx_timediff(now, data->progress.t_startsingle),
    768         curl_easy_strerror(result));
    769 
    770 #ifdef SOCKETIMEDOUT
    771   if(SOCKETIMEDOUT == data->state.os_errno)
    772     result = CURLE_OPERATION_TIMEDOUT;
    773 #endif
    774 
    775   return result;
    776 }
    777 
    778 /*
    779  * Connect to the given host with timeout, proxy or remote does not matter.
    780  * There might be more than one IP address to try out.
    781  */
    782 static CURLcode start_connect(struct Curl_cfilter *cf,
    783                               struct Curl_easy *data)
    784 {
    785   struct cf_he_ctx *ctx = cf->ctx;
    786   struct connectdata *conn = cf->conn;
    787   CURLcode result = CURLE_COULDNT_CONNECT;
    788   int ai_family0 = 0, ai_family1 = 0;
    789   timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
    790   const struct Curl_addrinfo *addr0 = NULL, *addr1 = NULL;
    791   struct Curl_dns_entry *dns = data->state.dns[cf->sockindex];
    792 
    793   if(!dns)
    794     return CURLE_FAILED_INIT;
    795 
    796   if(timeout_ms < 0) {
    797     /* a precaution, no need to continue if time already is up */
    798     failf(data, "Connection time-out");
    799     return CURLE_OPERATION_TIMEDOUT;
    800   }
    801 
    802   ctx->started = curlx_now();
    803 
    804   /* dns->addr is the list of addresses from the resolver, each
    805    * with an address family. The list has at least one entry, possibly
    806    * many more.
    807    * We try at most 2 at a time, until we either get a connection or
    808    * run out of addresses to try. Since likelihood of success is tied
    809    * to the address family (e.g. IPV6 might not work at all ), we want
    810    * the 2 connect attempt ballers to try different families, if possible.
    811    *
    812    */
    813   if(conn->ip_version == CURL_IPRESOLVE_V6) {
    814 #ifdef USE_IPV6
    815     ai_family0 = AF_INET6;
    816     addr0 = addr_first_match(dns->addr, ai_family0);
    817 #endif
    818   }
    819   else if(conn->ip_version == CURL_IPRESOLVE_V4) {
    820     ai_family0 = AF_INET;
    821     addr0 = addr_first_match(dns->addr, ai_family0);
    822   }
    823   else {
    824     /* no user preference, we try ipv6 always first when available */
    825 #ifdef USE_IPV6
    826     ai_family0 = AF_INET6;
    827     addr0 = addr_first_match(dns->addr, ai_family0);
    828 #endif
    829     /* next candidate is ipv4 */
    830     ai_family1 = AF_INET;
    831     addr1 = addr_first_match(dns->addr, ai_family1);
    832     /* no ip address families, probably AF_UNIX or something, use the
    833      * address family given to us */
    834     if(!addr1 && !addr0 && dns->addr) {
    835       ai_family0 = dns->addr->ai_family;
    836       addr0 = addr_first_match(dns->addr, ai_family0);
    837     }
    838   }
    839 
    840   if(!addr0 && addr1) {
    841     /* switch around, so a single baller always uses addr0 */
    842     addr0 = addr1;
    843     ai_family0 = ai_family1;
    844     addr1 = NULL;
    845   }
    846 
    847   /* We found no address that matches our criteria, we cannot connect */
    848   if(!addr0) {
    849     return CURLE_COULDNT_CONNECT;
    850   }
    851 
    852   memset(ctx->baller, 0, sizeof(ctx->baller));
    853   result = eyeballer_new(&ctx->baller[0], ctx->cf_create, addr0, ai_family0,
    854                           NULL, 0, /* no primary/delay, start now */
    855                           timeout_ms,  EXPIRE_DNS_PER_NAME);
    856   if(result)
    857     return result;
    858   CURL_TRC_CF(data, cf, "created %s (timeout %" FMT_TIMEDIFF_T "ms)",
    859               ctx->baller[0]->name, ctx->baller[0]->timeoutms);
    860   if(addr1) {
    861     /* second one gets a delayed start */
    862     result = eyeballer_new(&ctx->baller[1], ctx->cf_create, addr1, ai_family1,
    863                             ctx->baller[0], /* wait on that to fail */
    864                             /* or start this delayed */
    865                             data->set.happy_eyeballs_timeout,
    866                             timeout_ms,  EXPIRE_DNS_PER_NAME2);
    867     if(result)
    868       return result;
    869     CURL_TRC_CF(data, cf, "created %s (timeout %" FMT_TIMEDIFF_T "ms)",
    870                 ctx->baller[1]->name, ctx->baller[1]->timeoutms);
    871     Curl_expire(data, data->set.happy_eyeballs_timeout,
    872                 EXPIRE_HAPPY_EYEBALLS);
    873   }
    874 
    875   return CURLE_OK;
    876 }
    877 
    878 static void cf_he_ctx_clear(struct Curl_cfilter *cf, struct Curl_easy *data)
    879 {
    880   struct cf_he_ctx *ctx = cf->ctx;
    881   size_t i;
    882 
    883   DEBUGASSERT(ctx);
    884   DEBUGASSERT(data);
    885   for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
    886     baller_free(ctx->baller[i], data);
    887     ctx->baller[i] = NULL;
    888   }
    889   baller_free(ctx->winner, data);
    890   ctx->winner = NULL;
    891 }
    892 
    893 static CURLcode cf_he_shutdown(struct Curl_cfilter *cf,
    894                                struct Curl_easy *data, bool *done)
    895 {
    896   struct cf_he_ctx *ctx = cf->ctx;
    897   size_t i;
    898   CURLcode result = CURLE_OK;
    899 
    900   DEBUGASSERT(data);
    901   if(cf->connected) {
    902     *done = TRUE;
    903     return CURLE_OK;
    904   }
    905 
    906   /* shutdown all ballers that have not done so already. If one fails,
    907    * continue shutting down others until all are shutdown. */
    908   for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
    909     struct eyeballer *baller = ctx->baller[i];
    910     bool bdone = FALSE;
    911     if(!baller || !baller->cf || baller->shutdown)
    912       continue;
    913     baller->result = baller->cf->cft->do_shutdown(baller->cf, data, &bdone);
    914     if(baller->result || bdone)
    915       baller->shutdown = TRUE; /* treat a failed shutdown as done */
    916   }
    917 
    918   *done = TRUE;
    919   for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
    920     if(ctx->baller[i] && !ctx->baller[i]->shutdown)
    921       *done = FALSE;
    922   }
    923   if(*done) {
    924     for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
    925       if(ctx->baller[i] && ctx->baller[i]->result)
    926         result = ctx->baller[i]->result;
    927     }
    928   }
    929   CURL_TRC_CF(data, cf, "shutdown -> %d, done=%d", result, *done);
    930   return result;
    931 }
    932 
    933 static void cf_he_adjust_pollset(struct Curl_cfilter *cf,
    934                                  struct Curl_easy *data,
    935                                  struct easy_pollset *ps)
    936 {
    937   struct cf_he_ctx *ctx = cf->ctx;
    938   size_t i;
    939 
    940   if(!cf->connected) {
    941     for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
    942       struct eyeballer *baller = ctx->baller[i];
    943       if(!baller || !baller->cf)
    944         continue;
    945       Curl_conn_cf_adjust_pollset(baller->cf, data, ps);
    946     }
    947     CURL_TRC_CF(data, cf, "adjust_pollset -> %d socks", ps->num);
    948   }
    949 }
    950 
    951 static CURLcode cf_he_connect(struct Curl_cfilter *cf,
    952                               struct Curl_easy *data,
    953                               bool *done)
    954 {
    955   struct cf_he_ctx *ctx = cf->ctx;
    956   CURLcode result = CURLE_OK;
    957 
    958   if(cf->connected) {
    959     *done = TRUE;
    960     return CURLE_OK;
    961   }
    962 
    963   DEBUGASSERT(ctx);
    964   *done = FALSE;
    965 
    966   switch(ctx->state) {
    967     case SCFST_INIT:
    968       DEBUGASSERT(CURL_SOCKET_BAD == Curl_conn_cf_get_socket(cf, data));
    969       DEBUGASSERT(!cf->connected);
    970       result = start_connect(cf, data);
    971       if(result)
    972         return result;
    973       ctx->state = SCFST_WAITING;
    974       FALLTHROUGH();
    975     case SCFST_WAITING:
    976       result = is_connected(cf, data, done);
    977       if(!result && *done) {
    978         DEBUGASSERT(ctx->winner);
    979         DEBUGASSERT(ctx->winner->cf);
    980         DEBUGASSERT(ctx->winner->cf->connected);
    981         /* we have a winner. Install and activate it.
    982          * close/free all others. */
    983         ctx->state = SCFST_DONE;
    984         cf->connected = TRUE;
    985         cf->next = ctx->winner->cf;
    986         ctx->winner->cf = NULL;
    987         cf_he_ctx_clear(cf, data);
    988 
    989         if(cf->conn->handler->protocol & PROTO_FAMILY_SSH)
    990           Curl_pgrsTime(data, TIMER_APPCONNECT); /* we are connected already */
    991         if(Curl_trc_cf_is_verbose(cf, data)) {
    992           struct ip_quadruple ipquad;
    993           int is_ipv6;
    994           if(!Curl_conn_cf_get_ip_info(cf->next, data, &is_ipv6, &ipquad)) {
    995             const char *host;
    996             int port;
    997             Curl_conn_get_current_host(data, cf->sockindex, &host, &port);
    998             CURL_TRC_CF(data, cf, "Connected to %s (%s) port %u",
    999                         host, ipquad.remote_ip, ipquad.remote_port);
   1000           }
   1001         }
   1002         data->info.numconnects++; /* to track the # of connections made */
   1003       }
   1004       break;
   1005     case SCFST_DONE:
   1006       *done = TRUE;
   1007       break;
   1008   }
   1009   return result;
   1010 }
   1011 
   1012 static void cf_he_close(struct Curl_cfilter *cf,
   1013                         struct Curl_easy *data)
   1014 {
   1015   struct cf_he_ctx *ctx = cf->ctx;
   1016 
   1017   CURL_TRC_CF(data, cf, "close");
   1018   cf_he_ctx_clear(cf, data);
   1019   cf->connected = FALSE;
   1020   ctx->state = SCFST_INIT;
   1021 
   1022   if(cf->next) {
   1023     cf->next->cft->do_close(cf->next, data);
   1024     Curl_conn_cf_discard_chain(&cf->next, data);
   1025   }
   1026 }
   1027 
   1028 static bool cf_he_data_pending(struct Curl_cfilter *cf,
   1029                                const struct Curl_easy *data)
   1030 {
   1031   struct cf_he_ctx *ctx = cf->ctx;
   1032   size_t i;
   1033 
   1034   if(cf->connected)
   1035     return cf->next->cft->has_data_pending(cf->next, data);
   1036 
   1037   for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
   1038     struct eyeballer *baller = ctx->baller[i];
   1039     if(!baller || !baller->cf)
   1040       continue;
   1041     if(baller->cf->cft->has_data_pending(baller->cf, data))
   1042       return TRUE;
   1043   }
   1044   return FALSE;
   1045 }
   1046 
   1047 static struct curltime get_max_baller_time(struct Curl_cfilter *cf,
   1048                                            struct Curl_easy *data,
   1049                                            int query)
   1050 {
   1051   struct cf_he_ctx *ctx = cf->ctx;
   1052   struct curltime t, tmax;
   1053   size_t i;
   1054 
   1055   memset(&tmax, 0, sizeof(tmax));
   1056   for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
   1057     struct eyeballer *baller = ctx->baller[i];
   1058 
   1059     memset(&t, 0, sizeof(t));
   1060     if(baller && baller->cf &&
   1061        !baller->cf->cft->query(baller->cf, data, query, NULL, &t)) {
   1062       if((t.tv_sec || t.tv_usec) && curlx_timediff_us(t, tmax) > 0)
   1063         tmax = t;
   1064     }
   1065   }
   1066   return tmax;
   1067 }
   1068 
   1069 static CURLcode cf_he_query(struct Curl_cfilter *cf,
   1070                             struct Curl_easy *data,
   1071                             int query, int *pres1, void *pres2)
   1072 {
   1073   struct cf_he_ctx *ctx = cf->ctx;
   1074 
   1075   if(!cf->connected) {
   1076     switch(query) {
   1077     case CF_QUERY_CONNECT_REPLY_MS: {
   1078       int reply_ms = -1;
   1079       size_t i;
   1080 
   1081       for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
   1082         struct eyeballer *baller = ctx->baller[i];
   1083         int breply_ms;
   1084 
   1085         if(baller && baller->cf &&
   1086            !baller->cf->cft->query(baller->cf, data, query,
   1087                                    &breply_ms, NULL)) {
   1088           if(breply_ms >= 0 && (reply_ms < 0 || breply_ms < reply_ms))
   1089             reply_ms = breply_ms;
   1090         }
   1091       }
   1092       *pres1 = reply_ms;
   1093       CURL_TRC_CF(data, cf, "query connect reply: %dms", *pres1);
   1094       return CURLE_OK;
   1095     }
   1096     case CF_QUERY_TIMER_CONNECT: {
   1097       struct curltime *when = pres2;
   1098       *when = get_max_baller_time(cf, data, CF_QUERY_TIMER_CONNECT);
   1099       return CURLE_OK;
   1100     }
   1101     case CF_QUERY_TIMER_APPCONNECT: {
   1102       struct curltime *when = pres2;
   1103       *when = get_max_baller_time(cf, data, CF_QUERY_TIMER_APPCONNECT);
   1104       return CURLE_OK;
   1105     }
   1106     default:
   1107       break;
   1108     }
   1109   }
   1110 
   1111   return cf->next ?
   1112     cf->next->cft->query(cf->next, data, query, pres1, pres2) :
   1113     CURLE_UNKNOWN_OPTION;
   1114 }
   1115 
   1116 static void cf_he_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
   1117 {
   1118   struct cf_he_ctx *ctx = cf->ctx;
   1119 
   1120   CURL_TRC_CF(data, cf, "destroy");
   1121   if(ctx) {
   1122     cf_he_ctx_clear(cf, data);
   1123   }
   1124   /* release any resources held in state */
   1125   Curl_safefree(ctx);
   1126 }
   1127 
   1128 struct Curl_cftype Curl_cft_happy_eyeballs = {
   1129   "HAPPY-EYEBALLS",
   1130   0,
   1131   CURL_LOG_LVL_NONE,
   1132   cf_he_destroy,
   1133   cf_he_connect,
   1134   cf_he_close,
   1135   cf_he_shutdown,
   1136   cf_he_adjust_pollset,
   1137   cf_he_data_pending,
   1138   Curl_cf_def_send,
   1139   Curl_cf_def_recv,
   1140   Curl_cf_def_cntrl,
   1141   Curl_cf_def_conn_is_alive,
   1142   Curl_cf_def_conn_keep_alive,
   1143   cf_he_query,
   1144 };
   1145 
   1146 /**
   1147  * Create a happy eyeball connection filter that uses the, once resolved,
   1148  * address information to connect on ip families based on connection
   1149  * configuration.
   1150  * @param pcf        output, the created cfilter
   1151  * @param data       easy handle used in creation
   1152  * @param conn       connection the filter is created for
   1153  * @param cf_create  method to create the sub-filters performing the
   1154  *                   actual connects.
   1155  */
   1156 static CURLcode
   1157 cf_happy_eyeballs_create(struct Curl_cfilter **pcf,
   1158                          struct Curl_easy *data,
   1159                          struct connectdata *conn,
   1160                          cf_ip_connect_create *cf_create,
   1161                          int transport)
   1162 {
   1163   struct cf_he_ctx *ctx = NULL;
   1164   CURLcode result;
   1165 
   1166   (void)data;
   1167   (void)conn;
   1168   *pcf = NULL;
   1169   ctx = calloc(1, sizeof(*ctx));
   1170   if(!ctx) {
   1171     result = CURLE_OUT_OF_MEMORY;
   1172     goto out;
   1173   }
   1174   ctx->transport = transport;
   1175   ctx->cf_create = cf_create;
   1176 
   1177   result = Curl_cf_create(pcf, &Curl_cft_happy_eyeballs, ctx);
   1178 
   1179 out:
   1180   if(result) {
   1181     Curl_safefree(*pcf);
   1182     free(ctx);
   1183   }
   1184   return result;
   1185 }
   1186 
   1187 struct transport_provider {
   1188   int transport;
   1189   cf_ip_connect_create *cf_create;
   1190 };
   1191 
   1192 static
   1193 #ifndef UNITTESTS
   1194 const
   1195 #endif
   1196 struct transport_provider transport_providers[] = {
   1197   { TRNSPRT_TCP, Curl_cf_tcp_create },
   1198 #ifdef USE_HTTP3
   1199   { TRNSPRT_QUIC, Curl_cf_quic_create },
   1200 #endif
   1201 #ifndef CURL_DISABLE_TFTP
   1202   { TRNSPRT_UDP, Curl_cf_udp_create },
   1203 #endif
   1204 #ifdef USE_UNIX_SOCKETS
   1205   { TRNSPRT_UNIX, Curl_cf_unix_create },
   1206 #endif
   1207 };
   1208 
   1209 static cf_ip_connect_create *get_cf_create(int transport)
   1210 {
   1211   size_t i;
   1212   for(i = 0; i < CURL_ARRAYSIZE(transport_providers); ++i) {
   1213     if(transport == transport_providers[i].transport)
   1214       return transport_providers[i].cf_create;
   1215   }
   1216   return NULL;
   1217 }
   1218 
   1219 static CURLcode cf_he_insert_after(struct Curl_cfilter *cf_at,
   1220                                    struct Curl_easy *data,
   1221                                    int transport)
   1222 {
   1223   cf_ip_connect_create *cf_create;
   1224   struct Curl_cfilter *cf;
   1225   CURLcode result;
   1226 
   1227   /* Need to be first */
   1228   DEBUGASSERT(cf_at);
   1229   cf_create = get_cf_create(transport);
   1230   if(!cf_create) {
   1231     CURL_TRC_CF(data, cf_at, "unsupported transport type %d", transport);
   1232     return CURLE_UNSUPPORTED_PROTOCOL;
   1233   }
   1234   result = cf_happy_eyeballs_create(&cf, data, cf_at->conn,
   1235                                     cf_create, transport);
   1236   if(result)
   1237     return result;
   1238 
   1239   Curl_conn_cf_insert_after(cf_at, cf);
   1240   return CURLE_OK;
   1241 }
   1242 
   1243 typedef enum {
   1244   CF_SETUP_INIT,
   1245   CF_SETUP_CNNCT_EYEBALLS,
   1246   CF_SETUP_CNNCT_SOCKS,
   1247   CF_SETUP_CNNCT_HTTP_PROXY,
   1248   CF_SETUP_CNNCT_HAPROXY,
   1249   CF_SETUP_CNNCT_SSL,
   1250   CF_SETUP_DONE
   1251 } cf_setup_state;
   1252 
   1253 struct cf_setup_ctx {
   1254   cf_setup_state state;
   1255   int ssl_mode;
   1256   int transport;
   1257 };
   1258 
   1259 static CURLcode cf_setup_connect(struct Curl_cfilter *cf,
   1260                                  struct Curl_easy *data,
   1261                                  bool *done)
   1262 {
   1263   struct cf_setup_ctx *ctx = cf->ctx;
   1264   CURLcode result = CURLE_OK;
   1265   struct Curl_dns_entry *dns = data->state.dns[cf->sockindex];
   1266 
   1267   if(cf->connected) {
   1268     *done = TRUE;
   1269     return CURLE_OK;
   1270   }
   1271 
   1272   /* connect current sub-chain */
   1273 connect_sub_chain:
   1274   if(!dns)
   1275     return CURLE_FAILED_INIT;
   1276 
   1277   if(cf->next && !cf->next->connected) {
   1278     result = Curl_conn_cf_connect(cf->next, data, done);
   1279     if(result || !*done)
   1280       return result;
   1281   }
   1282 
   1283   if(ctx->state < CF_SETUP_CNNCT_EYEBALLS) {
   1284     result = cf_he_insert_after(cf, data, ctx->transport);
   1285     if(result)
   1286       return result;
   1287     ctx->state = CF_SETUP_CNNCT_EYEBALLS;
   1288     if(!cf->next || !cf->next->connected)
   1289       goto connect_sub_chain;
   1290   }
   1291 
   1292   /* sub-chain connected, do we need to add more? */
   1293 #ifndef CURL_DISABLE_PROXY
   1294   if(ctx->state < CF_SETUP_CNNCT_SOCKS && cf->conn->bits.socksproxy) {
   1295     result = Curl_cf_socks_proxy_insert_after(cf, data);
   1296     if(result)
   1297       return result;
   1298     ctx->state = CF_SETUP_CNNCT_SOCKS;
   1299     if(!cf->next || !cf->next->connected)
   1300       goto connect_sub_chain;
   1301   }
   1302 
   1303   if(ctx->state < CF_SETUP_CNNCT_HTTP_PROXY && cf->conn->bits.httpproxy) {
   1304 #ifdef USE_SSL
   1305     if(IS_HTTPS_PROXY(cf->conn->http_proxy.proxytype)
   1306        && !Curl_conn_is_ssl(cf->conn, cf->sockindex)) {
   1307       result = Curl_cf_ssl_proxy_insert_after(cf, data);
   1308       if(result)
   1309         return result;
   1310     }
   1311 #endif /* USE_SSL */
   1312 
   1313 #if !defined(CURL_DISABLE_HTTP)
   1314     if(cf->conn->bits.tunnel_proxy) {
   1315       result = Curl_cf_http_proxy_insert_after(cf, data);
   1316       if(result)
   1317         return result;
   1318     }
   1319 #endif /* !CURL_DISABLE_HTTP */
   1320     ctx->state = CF_SETUP_CNNCT_HTTP_PROXY;
   1321     if(!cf->next || !cf->next->connected)
   1322       goto connect_sub_chain;
   1323   }
   1324 #endif /* !CURL_DISABLE_PROXY */
   1325 
   1326   if(ctx->state < CF_SETUP_CNNCT_HAPROXY) {
   1327 #if !defined(CURL_DISABLE_PROXY)
   1328     if(data->set.haproxyprotocol) {
   1329       if(Curl_conn_is_ssl(cf->conn, cf->sockindex)) {
   1330         failf(data, "haproxy protocol not support with SSL "
   1331               "encryption in place (QUIC?)");
   1332         return CURLE_UNSUPPORTED_PROTOCOL;
   1333       }
   1334       result = Curl_cf_haproxy_insert_after(cf, data);
   1335       if(result)
   1336         return result;
   1337     }
   1338 #endif /* !CURL_DISABLE_PROXY */
   1339     ctx->state = CF_SETUP_CNNCT_HAPROXY;
   1340     if(!cf->next || !cf->next->connected)
   1341       goto connect_sub_chain;
   1342   }
   1343 
   1344   if(ctx->state < CF_SETUP_CNNCT_SSL) {
   1345 #ifdef USE_SSL
   1346     if((ctx->ssl_mode == CURL_CF_SSL_ENABLE
   1347         || (ctx->ssl_mode != CURL_CF_SSL_DISABLE
   1348            && cf->conn->handler->flags & PROTOPT_SSL)) /* we want SSL */
   1349        && !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { /* it is missing */
   1350       result = Curl_cf_ssl_insert_after(cf, data);
   1351       if(result)
   1352         return result;
   1353     }
   1354 #endif /* USE_SSL */
   1355     ctx->state = CF_SETUP_CNNCT_SSL;
   1356     if(!cf->next || !cf->next->connected)
   1357       goto connect_sub_chain;
   1358   }
   1359 
   1360   ctx->state = CF_SETUP_DONE;
   1361   cf->connected = TRUE;
   1362   *done = TRUE;
   1363   return CURLE_OK;
   1364 }
   1365 
   1366 static void cf_setup_close(struct Curl_cfilter *cf,
   1367                            struct Curl_easy *data)
   1368 {
   1369   struct cf_setup_ctx *ctx = cf->ctx;
   1370 
   1371   CURL_TRC_CF(data, cf, "close");
   1372   cf->connected = FALSE;
   1373   ctx->state = CF_SETUP_INIT;
   1374 
   1375   if(cf->next) {
   1376     cf->next->cft->do_close(cf->next, data);
   1377     Curl_conn_cf_discard_chain(&cf->next, data);
   1378   }
   1379 }
   1380 
   1381 static void cf_setup_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
   1382 {
   1383   struct cf_setup_ctx *ctx = cf->ctx;
   1384 
   1385   (void)data;
   1386   CURL_TRC_CF(data, cf, "destroy");
   1387   Curl_safefree(ctx);
   1388 }
   1389 
   1390 
   1391 struct Curl_cftype Curl_cft_setup = {
   1392   "SETUP",
   1393   0,
   1394   CURL_LOG_LVL_NONE,
   1395   cf_setup_destroy,
   1396   cf_setup_connect,
   1397   cf_setup_close,
   1398   Curl_cf_def_shutdown,
   1399   Curl_cf_def_adjust_pollset,
   1400   Curl_cf_def_data_pending,
   1401   Curl_cf_def_send,
   1402   Curl_cf_def_recv,
   1403   Curl_cf_def_cntrl,
   1404   Curl_cf_def_conn_is_alive,
   1405   Curl_cf_def_conn_keep_alive,
   1406   Curl_cf_def_query,
   1407 };
   1408 
   1409 static CURLcode cf_setup_create(struct Curl_cfilter **pcf,
   1410                                 struct Curl_easy *data,
   1411                                 int transport,
   1412                                 int ssl_mode)
   1413 {
   1414   struct Curl_cfilter *cf = NULL;
   1415   struct cf_setup_ctx *ctx;
   1416   CURLcode result = CURLE_OK;
   1417 
   1418   (void)data;
   1419   ctx = calloc(1, sizeof(*ctx));
   1420   if(!ctx) {
   1421     result = CURLE_OUT_OF_MEMORY;
   1422     goto out;
   1423   }
   1424   ctx->state = CF_SETUP_INIT;
   1425   ctx->ssl_mode = ssl_mode;
   1426   ctx->transport = transport;
   1427 
   1428   result = Curl_cf_create(&cf, &Curl_cft_setup, ctx);
   1429   if(result)
   1430     goto out;
   1431   ctx = NULL;
   1432 
   1433 out:
   1434   *pcf = result ? NULL : cf;
   1435   if(ctx) {
   1436     free(ctx);
   1437   }
   1438   return result;
   1439 }
   1440 
   1441 static CURLcode cf_setup_add(struct Curl_easy *data,
   1442                              struct connectdata *conn,
   1443                              int sockindex,
   1444                              int transport,
   1445                              int ssl_mode)
   1446 {
   1447   struct Curl_cfilter *cf;
   1448   CURLcode result = CURLE_OK;
   1449 
   1450   DEBUGASSERT(data);
   1451   result = cf_setup_create(&cf, data, transport, ssl_mode);
   1452   if(result)
   1453     goto out;
   1454   Curl_conn_cf_add(data, conn, sockindex, cf);
   1455 out:
   1456   return result;
   1457 }
   1458 
   1459 #ifdef UNITTESTS
   1460 /* used by unit2600.c */
   1461 void Curl_debug_set_transport_provider(int transport,
   1462                                        cf_ip_connect_create *cf_create)
   1463 {
   1464   size_t i;
   1465   for(i = 0; i < CURL_ARRAYSIZE(transport_providers); ++i) {
   1466     if(transport == transport_providers[i].transport) {
   1467       transport_providers[i].cf_create = cf_create;
   1468       return;
   1469     }
   1470   }
   1471 }
   1472 #endif /* UNITTESTS */
   1473 
   1474 CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at,
   1475                                     struct Curl_easy *data,
   1476                                     int transport,
   1477                                     int ssl_mode)
   1478 {
   1479   struct Curl_cfilter *cf;
   1480   CURLcode result;
   1481 
   1482   DEBUGASSERT(data);
   1483   result = cf_setup_create(&cf, data, transport, ssl_mode);
   1484   if(result)
   1485     goto out;
   1486   Curl_conn_cf_insert_after(cf_at, cf);
   1487 out:
   1488   return result;
   1489 }
   1490 
   1491 CURLcode Curl_conn_setup(struct Curl_easy *data,
   1492                          struct connectdata *conn,
   1493                          int sockindex,
   1494                          struct Curl_dns_entry *dns,
   1495                          int ssl_mode)
   1496 {
   1497   CURLcode result = CURLE_OK;
   1498 
   1499   DEBUGASSERT(data);
   1500   DEBUGASSERT(conn->handler);
   1501   DEBUGASSERT(dns);
   1502 
   1503   Curl_resolv_unlink(data, &data->state.dns[sockindex]);
   1504   data->state.dns[sockindex] = dns;
   1505 
   1506 #if !defined(CURL_DISABLE_HTTP)
   1507   if(!conn->cfilter[sockindex] &&
   1508      conn->handler->protocol == CURLPROTO_HTTPS) {
   1509     DEBUGASSERT(ssl_mode != CURL_CF_SSL_DISABLE);
   1510     result = Curl_cf_https_setup(data, conn, sockindex);
   1511     if(result)
   1512       goto out;
   1513   }
   1514 #endif /* !defined(CURL_DISABLE_HTTP) */
   1515 
   1516   /* Still no cfilter set, apply default. */
   1517   if(!conn->cfilter[sockindex]) {
   1518     result = cf_setup_add(data, conn, sockindex,
   1519                           conn->transport_wanted, ssl_mode);
   1520     if(result)
   1521       goto out;
   1522   }
   1523 
   1524   DEBUGASSERT(conn->cfilter[sockindex]);
   1525 out:
   1526   if(result)
   1527     Curl_resolv_unlink(data, &data->state.dns[sockindex]);
   1528   return result;
   1529 }