Coverage Report

Created: 2023-03-26 06:11

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