Coverage Report

Created: 2025-06-09 07:43

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