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