Coverage Report

Created: 2023-06-07 07:02

/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-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
  long 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
0
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
332
0
  (void)reason; /* useful for debugging */
333
0
#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
static CURLcode eyeballer_new(struct eyeballer **pballer,
385
                              cf_ip_connect_create *cf_create,
386
                              const struct Curl_addrinfo *addr,
387
                              int ai_family,
388
                              struct eyeballer *primary,
389
                              timediff_t delay_ms,
390
                              timediff_t timeout_ms,
391
                              expire_id timeout_id)
392
0
{
393
0
  struct eyeballer *baller;
394
395
0
  *pballer = NULL;
396
0
  baller = calloc(1, sizeof(*baller) + 1000);
397
0
  if(!baller)
398
0
    return CURLE_OUT_OF_MEMORY;
399
400
0
  baller->name = ((ai_family == AF_INET)? "ipv4" : (
401
0
#ifdef ENABLE_IPV6
402
0
                  (ai_family == AF_INET6)? "ipv6" :
403
0
#endif
404
0
                  "ip"));
405
0
  baller->cf_create = cf_create;
406
0
  baller->addr = addr;
407
0
  baller->ai_family = ai_family;
408
0
  baller->primary = primary;
409
0
  baller->delay_ms = delay_ms;
410
0
  baller->timeoutms = addr_next_match(baller->addr, baller->ai_family)?
411
0
                        timeout_ms / 2 : timeout_ms;
412
0
  baller->timeout_id = timeout_id;
413
0
  baller->result = CURLE_COULDNT_CONNECT;
414
415
0
  *pballer = baller;
416
0
  return CURLE_OK;
417
0
}
418
419
static void baller_close(struct eyeballer *baller,
420
                          struct Curl_easy *data)
421
0
{
422
0
  if(baller && baller->cf) {
423
0
    Curl_conn_cf_discard_chain(&baller->cf, data);
424
0
  }
425
0
}
426
427
static void baller_free(struct eyeballer *baller,
428
                         struct Curl_easy *data)
429
0
{
430
0
  if(baller) {
431
0
    baller_close(baller, data);
432
0
    free(baller);
433
0
  }
434
0
}
435
436
static void baller_next_addr(struct eyeballer *baller)
437
0
{
438
0
  baller->addr = addr_next_match(baller->addr, baller->ai_family);
439
0
}
440
441
/*
442
 * Initiate a connect attempt walk.
443
 *
444
 * Note that even on connect fail it returns CURLE_OK, but with 'sock' set to
445
 * CURL_SOCKET_BAD. Other errors will however return proper errors.
446
 */
447
static void baller_initiate(struct Curl_cfilter *cf,
448
                            struct Curl_easy *data,
449
                            struct eyeballer *baller)
450
0
{
451
0
  struct cf_he_ctx *ctx = cf->ctx;
452
0
  struct Curl_cfilter *cf_prev = baller->cf;
453
0
  struct Curl_cfilter *wcf;
454
0
  CURLcode result;
455
456
457
  /* Don't close a previous cfilter yet to ensure that the next IP's
458
     socket gets a different file descriptor, which can prevent bugs when
459
     the curl_multi_socket_action interface is used with certain select()
460
     replacements such as kqueue. */
461
0
  result = baller->cf_create(&baller->cf, data, cf->conn, baller->addr,
462
0
                             ctx->transport);
463
0
  if(result)
464
0
    goto out;
465
466
  /* the new filter might have sub-filters */
467
0
  for(wcf = baller->cf; wcf; wcf = wcf->next) {
468
0
    wcf->conn = cf->conn;
469
0
    wcf->sockindex = cf->sockindex;
470
0
  }
471
472
0
  if(addr_next_match(baller->addr, baller->ai_family)) {
473
0
    Curl_expire(data, baller->timeoutms, baller->timeout_id);
474
0
  }
475
476
0
out:
477
0
  if(result) {
478
0
    DEBUGF(LOG_CF(data, cf, "%s failed", baller->name));
479
0
    baller_close(baller, data);
480
0
  }
481
0
  if(cf_prev)
482
0
    Curl_conn_cf_discard_chain(&cf_prev, data);
483
0
  baller->result = result;
484
0
}
485
486
/**
487
 * Start a connection attempt on the current baller address.
488
 * Will return CURLE_OK on the first address where a socket
489
 * could be created and the non-blocking connect started.
490
 * Returns error when all remaining addresses have been tried.
491
 */
492
static CURLcode baller_start(struct Curl_cfilter *cf,
493
                             struct Curl_easy *data,
494
                             struct eyeballer *baller,
495
                             timediff_t timeoutms)
496
0
{
497
0
  baller->error = 0;
498
0
  baller->connected = FALSE;
499
0
  baller->has_started = TRUE;
500
501
0
  while(baller->addr) {
502
0
    baller->started = Curl_now();
503
0
    baller->timeoutms = addr_next_match(baller->addr, baller->ai_family) ?
504
0
                         timeoutms / 2 : timeoutms;
505
0
    baller_initiate(cf, data, baller);
506
0
    if(!baller->result)
507
0
      break;
508
0
    baller_next_addr(baller);
509
0
  }
510
0
  if(!baller->addr) {
511
0
    baller->is_done = TRUE;
512
0
  }
513
0
  return baller->result;
514
0
}
515
516
517
/* Used within the multi interface. Try next IP address, returns error if no
518
   more address exists or error */
519
static CURLcode baller_start_next(struct Curl_cfilter *cf,
520
                                  struct Curl_easy *data,
521
                                  struct eyeballer *baller,
522
                                  timediff_t timeoutms)
523
0
{
524
0
  if(cf->sockindex == FIRSTSOCKET) {
525
0
    baller_next_addr(baller);
526
0
    baller_start(cf, data, baller, timeoutms);
527
0
  }
528
0
  else {
529
0
    baller->error = 0;
530
0
    baller->connected = FALSE;
531
0
    baller->has_started = TRUE;
532
0
    baller->is_done = TRUE;
533
0
    baller->result = CURLE_COULDNT_CONNECT;
534
0
  }
535
0
  return baller->result;
536
0
}
537
538
static CURLcode baller_connect(struct Curl_cfilter *cf,
539
                               struct Curl_easy *data,
540
                               struct eyeballer *baller,
541
                               struct curltime *now,
542
                               bool *connected)
543
0
{
544
0
  (void)cf;
545
0
  *connected = baller->connected;
546
0
  if(!baller->result &&  !*connected) {
547
    /* evaluate again */
548
0
    baller->result = Curl_conn_cf_connect(baller->cf, data, 0, connected);
549
550
0
    if(!baller->result) {
551
0
      if(*connected) {
552
0
        baller->connected = TRUE;
553
0
        baller->is_done = TRUE;
554
0
      }
555
0
      else if(Curl_timediff(*now, baller->started) >= baller->timeoutms) {
556
0
        infof(data, "%s connect timeout after %" CURL_FORMAT_TIMEDIFF_T
557
0
              "ms, move on!", baller->name, baller->timeoutms);
558
0
#if defined(ETIMEDOUT)
559
0
        baller->error = ETIMEDOUT;
560
0
#endif
561
0
        baller->result = CURLE_OPERATION_TIMEDOUT;
562
0
      }
563
0
    }
564
0
  }
565
0
  return baller->result;
566
0
}
567
568
/*
569
 * is_connected() checks if the socket has connected.
570
 */
571
static CURLcode is_connected(struct Curl_cfilter *cf,
572
                             struct Curl_easy *data,
573
                             bool *connected)
574
0
{
575
0
  struct cf_he_ctx *ctx = cf->ctx;
576
0
  struct connectdata *conn = cf->conn;
577
0
  CURLcode result;
578
0
  struct curltime now;
579
0
  size_t i;
580
0
  int ongoing, not_started;
581
0
  const char *hostname;
582
583
  /* Check if any of the conn->tempsock we use for establishing connections
584
   * succeeded and, if so, close any ongoing other ones.
585
   * Transfer the successful conn->tempsock to conn->sock[sockindex]
586
   * and set conn->tempsock to CURL_SOCKET_BAD.
587
   * If transport is QUIC, we need to shutdown the ongoing 'other'
588
   * cot ballers in a QUIC appropriate way. */
589
0
evaluate:
590
0
  *connected = FALSE; /* a very negative world view is best */
591
0
  now = Curl_now();
592
0
  ongoing = not_started = 0;
593
0
  for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) {
594
0
    struct eyeballer *baller = ctx->baller[i];
595
596
0
    if(!baller || baller->is_done)
597
0
      continue;
598
599
0
    if(!baller->has_started) {
600
0
      ++not_started;
601
0
      continue;
602
0
    }
603
0
    baller->result = baller_connect(cf, data, baller, &now, connected);
604
0
    DEBUGF(LOG_CF(data, cf, "%s connect -> %d, connected=%d",
605
0
                  baller->name, baller->result, *connected));
606
607
0
    if(!baller->result) {
608
0
      if(*connected) {
609
        /* connected, declare the winner */
610
0
        ctx->winner = baller;
611
0
        ctx->baller[i] = NULL;
612
0
        break;
613
0
      }
614
0
      else { /* still waiting */
615
0
        ++ongoing;
616
0
      }
617
0
    }
618
0
    else if(!baller->is_done) {
619
      /* The baller failed to connect, start its next attempt */
620
0
      if(baller->error) {
621
0
        data->state.os_errno = baller->error;
622
0
        SET_SOCKERRNO(baller->error);
623
0
      }
624
0
      baller_start_next(cf, data, baller, Curl_timeleft(data, &now, TRUE));
625
0
      if(baller->is_done) {
626
0
        DEBUGF(LOG_CF(data, cf, "%s done", baller->name));
627
0
      }
628
0
      else {
629
        /* next attempt was started */
630
0
        DEBUGF(LOG_CF(data, cf, "%s trying next", baller->name));
631
0
        ++ongoing;
632
0
      }
633
0
    }
634
0
  }
635
636
0
  if(ctx->winner) {
637
0
    *connected = TRUE;
638
0
    return CURLE_OK;
639
0
  }
640
641
  /* Nothing connected, check the time before we might
642
   * start new ballers or return ok. */
643
0
  if((ongoing || not_started) && Curl_timeleft(data, &now, TRUE) < 0) {
644
0
    failf(data, "Connection timeout after %ld ms",
645
0
          Curl_timediff(now, data->progress.t_startsingle));
646
0
    return CURLE_OPERATION_TIMEDOUT;
647
0
  }
648
649
  /* Check if we have any waiting ballers to start now. */
650
0
  if(not_started > 0) {
651
0
    int added = 0;
652
653
0
    for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) {
654
0
      struct eyeballer *baller = ctx->baller[i];
655
656
0
      if(!baller || baller->has_started)
657
0
        continue;
658
      /* We start its primary baller has failed to connect or if
659
       * its start delay_ms have expired */
660
0
      if((baller->primary && baller->primary->is_done) ||
661
0
          Curl_timediff(now, ctx->started) >= baller->delay_ms) {
662
0
        baller_start(cf, data, baller, Curl_timeleft(data, &now, TRUE));
663
0
        if(baller->is_done) {
664
0
          DEBUGF(LOG_CF(data, cf, "%s done", baller->name));
665
0
        }
666
0
        else {
667
0
          DEBUGF(LOG_CF(data, cf, "%s starting (timeout=%"
668
0
                        CURL_FORMAT_TIMEDIFF_T "ms)",
669
0
                        baller->name, baller->timeoutms));
670
0
          ++ongoing;
671
0
          ++added;
672
0
        }
673
0
      }
674
0
    }
675
0
    if(added > 0)
676
0
      goto evaluate;
677
0
  }
678
679
0
  if(ongoing > 0) {
680
    /* We are still trying, return for more waiting */
681
0
    *connected = FALSE;
682
0
    return CURLE_OK;
683
0
  }
684
685
  /* all ballers have failed to connect. */
686
0
  DEBUGF(LOG_CF(data, cf, "all eyeballers failed"));
687
0
  result = CURLE_COULDNT_CONNECT;
688
0
  for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) {
689
0
    struct eyeballer *baller = ctx->baller[i];
690
0
    DEBUGF(LOG_CF(data, cf, "%s assess started=%d, result=%d",
691
0
                  baller?baller->name:NULL,
692
0
                  baller?baller->has_started:0,
693
0
                  baller?baller->result:0));
694
0
    if(baller && baller->has_started && baller->result) {
695
0
      result = baller->result;
696
0
      break;
697
0
    }
698
0
  }
699
700
0
#ifndef CURL_DISABLE_PROXY
701
0
  if(conn->bits.socksproxy)
702
0
    hostname = conn->socks_proxy.host.name;
703
0
  else if(conn->bits.httpproxy)
704
0
    hostname = conn->http_proxy.host.name;
705
0
  else
706
0
#endif
707
0
    if(conn->bits.conn_to_host)
708
0
      hostname = conn->conn_to_host.name;
709
0
  else
710
0
    hostname = conn->host.name;
711
712
0
  failf(data, "Failed to connect to %s port %u after "
713
0
        "%" CURL_FORMAT_TIMEDIFF_T " ms: %s",
714
0
        hostname, conn->port,
715
0
        Curl_timediff(now, data->progress.t_startsingle),
716
0
        curl_easy_strerror(result));
717
718
#ifdef WSAETIMEDOUT
719
  if(WSAETIMEDOUT == data->state.os_errno)
720
    result = CURLE_OPERATION_TIMEDOUT;
721
#elif defined(ETIMEDOUT)
722
0
  if(ETIMEDOUT == data->state.os_errno)
723
0
    result = CURLE_OPERATION_TIMEDOUT;
724
0
#endif
725
726
0
  return result;
727
0
}
728
729
/*
730
 * Connect to the given host with timeout, proxy or remote doesn't matter.
731
 * There might be more than one IP address to try out.
732
 */
733
static CURLcode start_connect(struct Curl_cfilter *cf,
734
                              struct Curl_easy *data,
735
                              const struct Curl_dns_entry *remotehost)
736
0
{
737
0
  struct cf_he_ctx *ctx = cf->ctx;
738
0
  struct connectdata *conn = cf->conn;
739
0
  CURLcode result = CURLE_COULDNT_CONNECT;
740
0
  int ai_family0, ai_family1;
741
0
  timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
742
0
  const struct Curl_addrinfo *addr0, *addr1;
743
744
0
  if(timeout_ms < 0) {
745
    /* a precaution, no need to continue if time already is up */
746
0
    failf(data, "Connection time-out");
747
0
    return CURLE_OPERATION_TIMEDOUT;
748
0
  }
749
750
0
  ctx->started = Curl_now();
751
752
  /* remotehost->addr is the list of addresses from the resolver, each
753
   * with an address family. The list has at least one entry, possibly
754
   * many more.
755
   * We try at most 2 at a time, until we either get a connection or
756
   * run out of addresses to try. Since likelihood of success is tied
757
   * to the address family (e.g. IPV6 might not work at all ), we want
758
   * the 2 connect attempt ballers to try different families, if possible.
759
   *
760
   */
761
0
  if(conn->ip_version == CURL_IPRESOLVE_WHATEVER) {
762
    /* any IP version is allowed */
763
0
    ai_family0 = remotehost->addr?
764
0
      remotehost->addr->ai_family : 0;
765
0
#ifdef ENABLE_IPV6
766
0
    ai_family1 = ai_family0 == AF_INET6 ?
767
0
      AF_INET : AF_INET6;
768
#else
769
    ai_family1 = AF_UNSPEC;
770
#endif
771
0
  }
772
0
  else {
773
    /* only one IP version is allowed */
774
0
    ai_family0 = (conn->ip_version == CURL_IPRESOLVE_V4) ?
775
0
      AF_INET :
776
0
#ifdef ENABLE_IPV6
777
0
      AF_INET6;
778
#else
779
      AF_UNSPEC;
780
#endif
781
0
    ai_family1 = AF_UNSPEC;
782
0
  }
783
784
  /* Get the first address in the list that matches the family,
785
   * this might give NULL, if we do not have any matches. */
786
0
  addr0 = addr_first_match(remotehost->addr, ai_family0);
787
0
  addr1 = addr_first_match(remotehost->addr, ai_family1);
788
0
  if(!addr0 && addr1) {
789
    /* switch around, so a single baller always uses addr0 */
790
0
    addr0 = addr1;
791
0
    ai_family0 = ai_family1;
792
0
    addr1 = NULL;
793
0
  }
794
795
  /* We found no address that matches our criteria, we cannot connect */
796
0
  if(!addr0) {
797
0
    return CURLE_COULDNT_CONNECT;
798
0
  }
799
800
0
  memset(ctx->baller, 0, sizeof(ctx->baller));
801
0
  result = eyeballer_new(&ctx->baller[0], ctx->cf_create, addr0, ai_family0,
802
0
                          NULL, 0, /* no primary/delay, start now */
803
0
                          timeout_ms,  EXPIRE_DNS_PER_NAME);
804
0
  if(result)
805
0
    return result;
806
0
  DEBUGF(LOG_CF(data, cf, "created %s (timeout %"
807
0
                CURL_FORMAT_TIMEDIFF_T "ms)",
808
0
                ctx->baller[0]->name, ctx->baller[0]->timeoutms));
809
0
  if(addr1) {
810
    /* second one gets a delayed start */
811
0
    result = eyeballer_new(&ctx->baller[1], ctx->cf_create, addr1, ai_family1,
812
0
                            ctx->baller[0], /* wait on that to fail */
813
                            /* or start this delayed */
814
0
                            data->set.happy_eyeballs_timeout,
815
0
                            timeout_ms,  EXPIRE_DNS_PER_NAME2);
816
0
    if(result)
817
0
      return result;
818
0
    DEBUGF(LOG_CF(data, cf, "created %s (timeout %"
819
0
                  CURL_FORMAT_TIMEDIFF_T "ms)",
820
0
                  ctx->baller[1]->name, ctx->baller[1]->timeoutms));
821
0
  }
822
823
0
  Curl_expire(data, data->set.happy_eyeballs_timeout,
824
0
              EXPIRE_HAPPY_EYEBALLS);
825
826
0
  return CURLE_OK;
827
0
}
828
829
static void cf_he_ctx_clear(struct Curl_cfilter *cf, struct Curl_easy *data)
830
0
{
831
0
  struct cf_he_ctx *ctx = cf->ctx;
832
0
  size_t i;
833
834
0
  DEBUGASSERT(ctx);
835
0
  DEBUGASSERT(data);
836
0
  for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) {
837
0
    baller_free(ctx->baller[i], data);
838
0
    ctx->baller[i] = NULL;
839
0
  }
840
0
  baller_free(ctx->winner, data);
841
0
  ctx->winner = NULL;
842
0
}
843
844
static int cf_he_get_select_socks(struct Curl_cfilter *cf,
845
                                  struct Curl_easy *data,
846
                                  curl_socket_t *socks)
847
0
{
848
0
  struct cf_he_ctx *ctx = cf->ctx;
849
0
  size_t i, s;
850
0
  int wrc, rc = GETSOCK_BLANK;
851
0
  curl_socket_t wsocks[MAX_SOCKSPEREASYHANDLE];
852
853
0
  if(cf->connected)
854
0
    return cf->next->cft->get_select_socks(cf->next, data, socks);
855
856
0
  for(i = s = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) {
857
0
    struct eyeballer *baller = ctx->baller[i];
858
0
    if(!baller || !baller->cf)
859
0
      continue;
860
861
0
    wrc = Curl_conn_cf_get_select_socks(baller->cf, data, wsocks);
862
0
    if(wrc) {
863
      /* TODO: we assume we get at most one socket back */
864
0
      socks[s] = wsocks[0];
865
0
      if(wrc & GETSOCK_WRITESOCK(0))
866
0
        rc |= GETSOCK_WRITESOCK(s);
867
0
      if(wrc & GETSOCK_READSOCK(0))
868
0
        rc |= GETSOCK_READSOCK(s);
869
0
      s++;
870
0
    }
871
0
  }
872
0
  return rc;
873
0
}
874
875
static CURLcode cf_he_connect(struct Curl_cfilter *cf,
876
                              struct Curl_easy *data,
877
                              bool blocking, bool *done)
878
0
{
879
0
  struct cf_he_ctx *ctx = cf->ctx;
880
0
  CURLcode result = CURLE_OK;
881
882
0
  if(cf->connected) {
883
0
    *done = TRUE;
884
0
    return CURLE_OK;
885
0
  }
886
887
0
  (void)blocking; /* TODO: do we want to support this? */
888
0
  DEBUGASSERT(ctx);
889
0
  *done = FALSE;
890
891
0
  switch(ctx->state) {
892
0
    case SCFST_INIT:
893
0
      DEBUGASSERT(CURL_SOCKET_BAD == Curl_conn_cf_get_socket(cf, data));
894
0
      DEBUGASSERT(!cf->connected);
895
0
      result = start_connect(cf, data, ctx->remotehost);
896
0
      if(result)
897
0
        return result;
898
0
      ctx->state = SCFST_WAITING;
899
      /* FALLTHROUGH */
900
0
    case SCFST_WAITING:
901
0
      result = is_connected(cf, data, done);
902
0
      if(!result && *done) {
903
0
        DEBUGASSERT(ctx->winner);
904
0
        DEBUGASSERT(ctx->winner->cf);
905
0
        DEBUGASSERT(ctx->winner->cf->connected);
906
        /* we have a winner. Install and activate it.
907
         * close/free all others. */
908
0
        ctx->state = SCFST_DONE;
909
0
        cf->connected = TRUE;
910
0
        cf->next = ctx->winner->cf;
911
0
        ctx->winner->cf = NULL;
912
0
        cf_he_ctx_clear(cf, data);
913
0
        Curl_conn_cf_cntrl(cf->next, data, TRUE,
914
0
                           CF_CTRL_CONN_INFO_UPDATE, 0, NULL);
915
916
0
        if(cf->conn->handler->protocol & PROTO_FAMILY_SSH)
917
0
          Curl_pgrsTime(data, TIMER_APPCONNECT); /* we're connected already */
918
0
        Curl_verboseconnect(data, cf->conn);
919
0
        data->info.numconnects++; /* to track the # of connections made */
920
0
      }
921
0
      break;
922
0
    case SCFST_DONE:
923
0
      *done = TRUE;
924
0
      break;
925
0
  }
926
0
  return result;
927
0
}
928
929
static void cf_he_close(struct Curl_cfilter *cf,
930
                        struct Curl_easy *data)
931
0
{
932
0
  struct cf_he_ctx *ctx = cf->ctx;
933
934
0
  DEBUGF(LOG_CF(data, cf, "close"));
935
0
  cf_he_ctx_clear(cf, data);
936
0
  cf->connected = FALSE;
937
0
  ctx->state = SCFST_INIT;
938
939
0
  if(cf->next) {
940
0
    cf->next->cft->close(cf->next, data);
941
0
    Curl_conn_cf_discard_chain(&cf->next, data);
942
0
  }
943
0
}
944
945
static bool cf_he_data_pending(struct Curl_cfilter *cf,
946
                               const struct Curl_easy *data)
947
0
{
948
0
  struct cf_he_ctx *ctx = cf->ctx;
949
0
  size_t i;
950
951
0
  if(cf->connected)
952
0
    return cf->next->cft->has_data_pending(cf->next, data);
953
954
0
  for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) {
955
0
    struct eyeballer *baller = ctx->baller[i];
956
0
    if(!baller || !baller->cf)
957
0
      continue;
958
0
    if(baller->cf->cft->has_data_pending(baller->cf, data))
959
0
      return TRUE;
960
0
  }
961
0
  return FALSE;
962
0
}
963
964
static struct curltime get_max_baller_time(struct Curl_cfilter *cf,
965
                                          struct Curl_easy *data,
966
                                          int query)
967
0
{
968
0
  struct cf_he_ctx *ctx = cf->ctx;
969
0
  struct curltime t, tmax;
970
0
  size_t i;
971
972
0
  memset(&tmax, 0, sizeof(tmax));
973
0
  for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) {
974
0
    struct eyeballer *baller = ctx->baller[i];
975
976
0
    memset(&t, 0, sizeof(t));
977
0
    if(baller && baller->cf &&
978
0
       !baller->cf->cft->query(baller->cf, data, query, NULL, &t)) {
979
0
      if((t.tv_sec || t.tv_usec) && Curl_timediff_us(t, tmax) > 0)
980
0
        tmax = t;
981
0
    }
982
0
  }
983
0
  return tmax;
984
0
}
985
986
static CURLcode cf_he_query(struct Curl_cfilter *cf,
987
                            struct Curl_easy *data,
988
                            int query, int *pres1, void *pres2)
989
0
{
990
0
  struct cf_he_ctx *ctx = cf->ctx;
991
992
0
  if(!cf->connected) {
993
0
    switch(query) {
994
0
    case CF_QUERY_CONNECT_REPLY_MS: {
995
0
      int reply_ms = -1;
996
0
      size_t i;
997
998
0
      for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) {
999
0
        struct eyeballer *baller = ctx->baller[i];
1000
0
        int breply_ms;
1001
1002
0
        if(baller && baller->cf &&
1003
0
           !baller->cf->cft->query(baller->cf, data, query,
1004
0
                                   &breply_ms, NULL)) {
1005
0
          if(breply_ms >= 0 && (reply_ms < 0 || breply_ms < reply_ms))
1006
0
            reply_ms = breply_ms;
1007
0
        }
1008
0
      }
1009
0
      *pres1 = reply_ms;
1010
0
      DEBUGF(LOG_CF(data, cf, "query connect reply: %dms", *pres1));
1011
0
      return CURLE_OK;
1012
0
    }
1013
0
    case CF_QUERY_TIMER_CONNECT: {
1014
0
      struct curltime *when = pres2;
1015
0
      *when = get_max_baller_time(cf, data, CF_QUERY_TIMER_CONNECT);
1016
0
      return CURLE_OK;
1017
0
    }
1018
0
    case CF_QUERY_TIMER_APPCONNECT: {
1019
0
      struct curltime *when = pres2;
1020
0
      *when = get_max_baller_time(cf, data, CF_QUERY_TIMER_APPCONNECT);
1021
0
      return CURLE_OK;
1022
0
    }
1023
0
    default:
1024
0
      break;
1025
0
    }
1026
0
  }
1027
1028
0
  return cf->next?
1029
0
    cf->next->cft->query(cf->next, data, query, pres1, pres2) :
1030
0
    CURLE_UNKNOWN_OPTION;
1031
0
}
1032
1033
static void cf_he_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
1034
0
{
1035
0
  struct cf_he_ctx *ctx = cf->ctx;
1036
1037
0
  DEBUGF(LOG_CF(data, cf, "destroy"));
1038
0
  if(ctx) {
1039
0
    cf_he_ctx_clear(cf, data);
1040
0
  }
1041
  /* release any resources held in state */
1042
0
  Curl_safefree(ctx);
1043
0
}
1044
1045
struct Curl_cftype Curl_cft_happy_eyeballs = {
1046
  "HAPPY-EYEBALLS",
1047
  0,
1048
  CURL_LOG_DEFAULT,
1049
  cf_he_destroy,
1050
  cf_he_connect,
1051
  cf_he_close,
1052
  Curl_cf_def_get_host,
1053
  cf_he_get_select_socks,
1054
  cf_he_data_pending,
1055
  Curl_cf_def_send,
1056
  Curl_cf_def_recv,
1057
  Curl_cf_def_cntrl,
1058
  Curl_cf_def_conn_is_alive,
1059
  Curl_cf_def_conn_keep_alive,
1060
  cf_he_query,
1061
};
1062
1063
/**
1064
 * Create a happy eyeball connection filter that uses the, once resolved,
1065
 * address information to connect on ip families based on connection
1066
 * configuration.
1067
 * @param pcf        output, the created cfilter
1068
 * @param data       easy handle used in creation
1069
 * @param conn       connection the filter is created for
1070
 * @param cf_create  method to create the sub-filters performing the
1071
 *                   actual connects.
1072
 */
1073
static CURLcode
1074
cf_happy_eyeballs_create(struct Curl_cfilter **pcf,
1075
                         struct Curl_easy *data,
1076
                         struct connectdata *conn,
1077
                         cf_ip_connect_create *cf_create,
1078
                         const struct Curl_dns_entry *remotehost,
1079
                         int transport)
1080
0
{
1081
0
  struct cf_he_ctx *ctx = NULL;
1082
0
  CURLcode result;
1083
1084
0
  (void)data;
1085
0
  (void)conn;
1086
0
  *pcf = NULL;
1087
0
  ctx = calloc(sizeof(*ctx), 1);
1088
0
  if(!ctx) {
1089
0
    result = CURLE_OUT_OF_MEMORY;
1090
0
    goto out;
1091
0
  }
1092
0
  ctx->transport = transport;
1093
0
  ctx->cf_create = cf_create;
1094
0
  ctx->remotehost = remotehost;
1095
1096
0
  result = Curl_cf_create(pcf, &Curl_cft_happy_eyeballs, ctx);
1097
1098
0
out:
1099
0
  if(result) {
1100
0
    Curl_safefree(*pcf);
1101
0
    Curl_safefree(ctx);
1102
0
  }
1103
0
  return result;
1104
0
}
1105
1106
struct transport_provider {
1107
  int transport;
1108
  cf_ip_connect_create *cf_create;
1109
};
1110
1111
static
1112
#ifndef DEBUGBUILD
1113
const
1114
#endif
1115
struct transport_provider transport_providers[] = {
1116
  { TRNSPRT_TCP, Curl_cf_tcp_create },
1117
#ifdef ENABLE_QUIC
1118
  { TRNSPRT_QUIC, Curl_cf_quic_create },
1119
#endif
1120
  { TRNSPRT_UDP, Curl_cf_udp_create },
1121
  { TRNSPRT_UNIX, Curl_cf_unix_create },
1122
};
1123
1124
#ifndef ARRAYSIZE
1125
0
#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
1126
#endif
1127
1128
static cf_ip_connect_create *get_cf_create(int transport)
1129
0
{
1130
0
  size_t i;
1131
0
  for(i = 0; i < ARRAYSIZE(transport_providers); ++i) {
1132
0
    if(transport == transport_providers[i].transport)
1133
0
      return transport_providers[i].cf_create;
1134
0
  }
1135
0
  return NULL;
1136
0
}
1137
1138
static CURLcode cf_he_insert_after(struct Curl_cfilter *cf_at,
1139
                                   struct Curl_easy *data,
1140
                                   const struct Curl_dns_entry *remotehost,
1141
                                   int transport)
1142
0
{
1143
0
  cf_ip_connect_create *cf_create;
1144
0
  struct Curl_cfilter *cf;
1145
0
  CURLcode result;
1146
1147
  /* Need to be first */
1148
0
  DEBUGASSERT(cf_at);
1149
0
  cf_create = get_cf_create(transport);
1150
0
  if(!cf_create) {
1151
0
    DEBUGF(LOG_CF(data, cf_at, "unsupported transport type %d", transport));
1152
0
    return CURLE_UNSUPPORTED_PROTOCOL;
1153
0
  }
1154
0
  result = cf_happy_eyeballs_create(&cf, data, cf_at->conn,
1155
0
                                    cf_create, remotehost,
1156
0
                                    transport);
1157
0
  if(result)
1158
0
    return result;
1159
1160
0
  Curl_conn_cf_insert_after(cf_at, cf);
1161
0
  return CURLE_OK;
1162
0
}
1163
1164
typedef enum {
1165
  CF_SETUP_INIT,
1166
  CF_SETUP_CNNCT_EYEBALLS,
1167
  CF_SETUP_CNNCT_SOCKS,
1168
  CF_SETUP_CNNCT_HTTP_PROXY,
1169
  CF_SETUP_CNNCT_HAPROXY,
1170
  CF_SETUP_CNNCT_SSL,
1171
  CF_SETUP_DONE
1172
} cf_setup_state;
1173
1174
struct cf_setup_ctx {
1175
  cf_setup_state state;
1176
  const struct Curl_dns_entry *remotehost;
1177
  int ssl_mode;
1178
  int transport;
1179
};
1180
1181
static CURLcode cf_setup_connect(struct Curl_cfilter *cf,
1182
                                 struct Curl_easy *data,
1183
                                 bool blocking, bool *done)
1184
0
{
1185
0
  struct cf_setup_ctx *ctx = cf->ctx;
1186
0
  CURLcode result = CURLE_OK;
1187
1188
0
  if(cf->connected) {
1189
0
    *done = TRUE;
1190
0
    return CURLE_OK;
1191
0
  }
1192
1193
  /* connect current sub-chain */
1194
0
connect_sub_chain:
1195
0
  if(cf->next && !cf->next->connected) {
1196
0
    result = Curl_conn_cf_connect(cf->next, data, blocking, done);
1197
0
    if(result || !*done)
1198
0
      return result;
1199
0
  }
1200
1201
0
  if(ctx->state < CF_SETUP_CNNCT_EYEBALLS) {
1202
0
    result = cf_he_insert_after(cf, data, ctx->remotehost, ctx->transport);
1203
0
    if(result)
1204
0
      return result;
1205
0
    ctx->state = CF_SETUP_CNNCT_EYEBALLS;
1206
0
    if(!cf->next || !cf->next->connected)
1207
0
      goto connect_sub_chain;
1208
0
  }
1209
1210
  /* sub-chain connected, do we need to add more? */
1211
0
#ifndef CURL_DISABLE_PROXY
1212
0
  if(ctx->state < CF_SETUP_CNNCT_SOCKS && cf->conn->bits.socksproxy) {
1213
0
    result = Curl_cf_socks_proxy_insert_after(cf, data);
1214
0
    if(result)
1215
0
      return result;
1216
0
    ctx->state = CF_SETUP_CNNCT_SOCKS;
1217
0
    if(!cf->next || !cf->next->connected)
1218
0
      goto connect_sub_chain;
1219
0
  }
1220
1221
0
  if(ctx->state < CF_SETUP_CNNCT_HTTP_PROXY && cf->conn->bits.httpproxy) {
1222
#ifdef USE_SSL
1223
    if(IS_HTTPS_PROXY(cf->conn->http_proxy.proxytype)
1224
       && !Curl_conn_is_ssl(cf->conn, cf->sockindex)) {
1225
      result = Curl_cf_ssl_proxy_insert_after(cf, data);
1226
      if(result)
1227
        return result;
1228
    }
1229
#endif /* USE_SSL */
1230
1231
0
#if !defined(CURL_DISABLE_HTTP)
1232
0
    if(cf->conn->bits.tunnel_proxy) {
1233
0
      result = Curl_cf_http_proxy_insert_after(cf, data);
1234
0
      if(result)
1235
0
        return result;
1236
0
    }
1237
0
#endif /* !CURL_DISABLE_HTTP */
1238
0
    ctx->state = CF_SETUP_CNNCT_HTTP_PROXY;
1239
0
    if(!cf->next || !cf->next->connected)
1240
0
      goto connect_sub_chain;
1241
0
  }
1242
0
#endif /* !CURL_DISABLE_PROXY */
1243
1244
0
  if(ctx->state < CF_SETUP_CNNCT_HAPROXY) {
1245
0
#if !defined(CURL_DISABLE_PROXY)
1246
0
    if(data->set.haproxyprotocol) {
1247
0
      if(Curl_conn_is_ssl(cf->conn, cf->sockindex)) {
1248
0
        failf(data, "haproxy protocol not support with SSL "
1249
0
              "encryption in place (QUIC?)");
1250
0
        return CURLE_UNSUPPORTED_PROTOCOL;
1251
0
      }
1252
0
      result = Curl_cf_haproxy_insert_after(cf, data);
1253
0
      if(result)
1254
0
        return result;
1255
0
    }
1256
0
#endif /* !CURL_DISABLE_PROXY */
1257
0
    ctx->state = CF_SETUP_CNNCT_HAPROXY;
1258
0
    if(!cf->next || !cf->next->connected)
1259
0
      goto connect_sub_chain;
1260
0
  }
1261
1262
0
  if(ctx->state < CF_SETUP_CNNCT_SSL) {
1263
#ifdef USE_SSL
1264
    if((ctx->ssl_mode == CURL_CF_SSL_ENABLE
1265
        || (ctx->ssl_mode != CURL_CF_SSL_DISABLE
1266
           && cf->conn->handler->flags & PROTOPT_SSL)) /* we want SSL */
1267
       && !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { /* it is missing */
1268
      result = Curl_cf_ssl_insert_after(cf, data);
1269
      if(result)
1270
        return result;
1271
    }
1272
#endif /* USE_SSL */
1273
0
    ctx->state = CF_SETUP_CNNCT_SSL;
1274
0
    if(!cf->next || !cf->next->connected)
1275
0
      goto connect_sub_chain;
1276
0
  }
1277
1278
0
  ctx->state = CF_SETUP_DONE;
1279
0
  cf->connected = TRUE;
1280
0
  *done = TRUE;
1281
0
  return CURLE_OK;
1282
0
}
1283
1284
static void cf_setup_close(struct Curl_cfilter *cf,
1285
                           struct Curl_easy *data)
1286
0
{
1287
0
  struct cf_setup_ctx *ctx = cf->ctx;
1288
1289
0
  DEBUGF(LOG_CF(data, cf, "close"));
1290
0
  cf->connected = FALSE;
1291
0
  ctx->state = CF_SETUP_INIT;
1292
1293
0
  if(cf->next) {
1294
0
    cf->next->cft->close(cf->next, data);
1295
0
    Curl_conn_cf_discard_chain(&cf->next, data);
1296
0
  }
1297
0
}
1298
1299
static void cf_setup_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
1300
0
{
1301
0
  struct cf_setup_ctx *ctx = cf->ctx;
1302
1303
0
  (void)data;
1304
0
  DEBUGF(LOG_CF(data, cf, "destroy"));
1305
0
  Curl_safefree(ctx);
1306
0
}
1307
1308
1309
struct Curl_cftype Curl_cft_setup = {
1310
  "SETUP",
1311
  0,
1312
  CURL_LOG_DEFAULT,
1313
  cf_setup_destroy,
1314
  cf_setup_connect,
1315
  cf_setup_close,
1316
  Curl_cf_def_get_host,
1317
  Curl_cf_def_get_select_socks,
1318
  Curl_cf_def_data_pending,
1319
  Curl_cf_def_send,
1320
  Curl_cf_def_recv,
1321
  Curl_cf_def_cntrl,
1322
  Curl_cf_def_conn_is_alive,
1323
  Curl_cf_def_conn_keep_alive,
1324
  Curl_cf_def_query,
1325
};
1326
1327
static CURLcode cf_setup_create(struct Curl_cfilter **pcf,
1328
                                struct Curl_easy *data,
1329
                                const struct Curl_dns_entry *remotehost,
1330
                                int transport,
1331
                                int ssl_mode)
1332
0
{
1333
0
  struct Curl_cfilter *cf = NULL;
1334
0
  struct cf_setup_ctx *ctx;
1335
0
  CURLcode result = CURLE_OK;
1336
1337
0
  (void)data;
1338
0
  ctx = calloc(sizeof(*ctx), 1);
1339
0
  if(!ctx) {
1340
0
    result = CURLE_OUT_OF_MEMORY;
1341
0
    goto out;
1342
0
  }
1343
0
  ctx->state = CF_SETUP_INIT;
1344
0
  ctx->remotehost = remotehost;
1345
0
  ctx->ssl_mode = ssl_mode;
1346
0
  ctx->transport = transport;
1347
1348
0
  result = Curl_cf_create(&cf, &Curl_cft_setup, ctx);
1349
0
  if(result)
1350
0
    goto out;
1351
0
  ctx = NULL;
1352
1353
0
out:
1354
0
  *pcf = result? NULL : cf;
1355
0
  free(ctx);
1356
0
  return result;
1357
0
}
1358
1359
static CURLcode cf_setup_add(struct Curl_easy *data,
1360
                             struct connectdata *conn,
1361
                             int sockindex,
1362
                             const struct Curl_dns_entry *remotehost,
1363
                             int transport,
1364
                             int ssl_mode)
1365
0
{
1366
0
  struct Curl_cfilter *cf;
1367
0
  CURLcode result = CURLE_OK;
1368
1369
0
  DEBUGASSERT(data);
1370
0
  result = cf_setup_create(&cf, data, remotehost, transport, ssl_mode);
1371
0
  if(result)
1372
0
    goto out;
1373
0
  Curl_conn_cf_add(data, conn, sockindex, cf);
1374
0
out:
1375
0
  return result;
1376
0
}
1377
1378
#ifdef DEBUGBUILD
1379
/* used by unit2600.c */
1380
void Curl_debug_set_transport_provider(int transport,
1381
                                       cf_ip_connect_create *cf_create)
1382
0
{
1383
0
  size_t i;
1384
0
  for(i = 0; i < ARRAYSIZE(transport_providers); ++i) {
1385
0
    if(transport == transport_providers[i].transport) {
1386
0
      transport_providers[i].cf_create = cf_create;
1387
0
      return;
1388
0
    }
1389
0
  }
1390
0
}
1391
#endif /* DEBUGBUILD */
1392
1393
CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at,
1394
                                    struct Curl_easy *data,
1395
                                    const struct Curl_dns_entry *remotehost,
1396
                                    int transport,
1397
                                    int ssl_mode)
1398
0
{
1399
0
  struct Curl_cfilter *cf;
1400
0
  CURLcode result;
1401
1402
0
  DEBUGASSERT(data);
1403
0
  result = cf_setup_create(&cf, data, remotehost, transport, ssl_mode);
1404
0
  if(result)
1405
0
    goto out;
1406
0
  Curl_conn_cf_insert_after(cf_at, cf);
1407
0
out:
1408
0
  return result;
1409
0
}
1410
1411
CURLcode Curl_conn_setup(struct Curl_easy *data,
1412
                         struct connectdata *conn,
1413
                         int sockindex,
1414
                         const struct Curl_dns_entry *remotehost,
1415
                         int ssl_mode)
1416
0
{
1417
0
  CURLcode result = CURLE_OK;
1418
1419
0
  DEBUGASSERT(data);
1420
0
  DEBUGASSERT(conn->handler);
1421
1422
0
#if !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER)
1423
0
  if(!conn->cfilter[sockindex] &&
1424
0
     conn->handler->protocol == CURLPROTO_HTTPS) {
1425
0
    DEBUGASSERT(ssl_mode != CURL_CF_SSL_DISABLE);
1426
0
    result = Curl_cf_https_setup(data, conn, sockindex, remotehost);
1427
0
    if(result)
1428
0
      goto out;
1429
0
  }
1430
0
#endif /* !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) */
1431
1432
  /* Still no cfilter set, apply default. */
1433
0
  if(!conn->cfilter[sockindex]) {
1434
0
    result = cf_setup_add(data, conn, sockindex, remotehost,
1435
0
                          conn->transport, ssl_mode);
1436
0
    if(result)
1437
0
      goto out;
1438
0
  }
1439
1440
0
  DEBUGASSERT(conn->cfilter[sockindex]);
1441
0
out:
1442
0
  return result;
1443
0
}
1444