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