Coverage Report

Created: 2024-02-25 06:14

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