Coverage Report

Created: 2024-05-04 12:45

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