Coverage Report

Created: 2025-07-18 07:19

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