Coverage Report

Created: 2025-07-23 06:58

/src/PROJ/curl/lib/connect.c
Line
Count
Source (jump to first uncovered line)
1
/***************************************************************************
2
 *                                  _   _ ____  _
3
 *  Project                     ___| | | |  _ \| |
4
 *                             / __| | | | |_) | |
5
 *                            | (__| |_| |  _ <| |___
6
 *                             \___|\___/|_| \_\_____|
7
 *
8
 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9
 *
10
 * This software is licensed as described in the file COPYING, which
11
 * you should have received as part of this distribution. The terms
12
 * are also available at https://curl.se/docs/copyright.html.
13
 *
14
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15
 * copies of the Software, and permit persons to whom the Software is
16
 * furnished to do so, under the terms of the COPYING file.
17
 *
18
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19
 * KIND, either express or implied.
20
 *
21
 * SPDX-License-Identifier: curl
22
 *
23
 ***************************************************************************/
24
25
#include "curl_setup.h"
26
27
#ifdef HAVE_NETINET_IN_H
28
#include <netinet/in.h> /* <netinet/tcp.h> may need it */
29
#endif
30
#ifdef HAVE_SYS_UN_H
31
#include <sys/un.h> /* for sockaddr_un */
32
#endif
33
#ifdef HAVE_LINUX_TCP_H
34
#include <linux/tcp.h>
35
#elif defined(HAVE_NETINET_TCP_H)
36
#include <netinet/tcp.h>
37
#endif
38
#ifdef HAVE_SYS_IOCTL_H
39
#include <sys/ioctl.h>
40
#endif
41
#ifdef HAVE_NETDB_H
42
#include <netdb.h>
43
#endif
44
#ifdef HAVE_FCNTL_H
45
#include <fcntl.h>
46
#endif
47
#ifdef HAVE_ARPA_INET_H
48
#include <arpa/inet.h>
49
#endif
50
51
#ifdef __VMS
52
#include <in.h>
53
#include <inet.h>
54
#endif
55
56
#include "urldata.h"
57
#include "sendf.h"
58
#include "if2ip.h"
59
#include "strerror.h"
60
#include "cfilters.h"
61
#include "connect.h"
62
#include "cf-haproxy.h"
63
#include "cf-https-connect.h"
64
#include "cf-socket.h"
65
#include "select.h"
66
#include "url.h" /* for Curl_safefree() */
67
#include "multiif.h"
68
#include "sockaddr.h" /* required for Curl_sockaddr_storage */
69
#include "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
0
{
91
0
  if(len == 2) {
92
0
    if(curl_strnequal(name, "h1", 2))
93
0
      return ALPN_h1;
94
0
    if(curl_strnequal(name, "h2", 2))
95
0
      return ALPN_h2;
96
0
    if(curl_strnequal(name, "h3", 2))
97
0
      return ALPN_h3;
98
0
  }
99
0
  else if(len == 8) {
100
0
    if(curl_strnequal(name, "http/1.1", 8))
101
0
      return ALPN_h1;
102
0
  }
103
0
  return ALPN_none; /* unknown, probably rubbish input */
104
0
}
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
0
{
122
0
  timediff_t timeleft_ms = 0;
123
0
  timediff_t ctimeleft_ms = 0;
124
0
  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
0
  if(data->set.timeout <= 0 && !duringconnect)
132
0
    return 0; /* no timeout in place or checked, return "no limit" */
133
134
0
  if(!nowp) {
135
0
    now = curlx_now();
136
0
    nowp = &now;
137
0
  }
138
139
0
  if(data->set.timeout > 0) {
140
0
    timeleft_ms = data->set.timeout -
141
0
                  curlx_timediff(*nowp, data->progress.t_startop);
142
0
    if(!timeleft_ms)
143
0
      timeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */
144
0
    if(!duringconnect)
145
0
      return timeleft_ms; /* no connect check, this is it */
146
0
  }
147
148
0
  if(duringconnect) {
149
0
    timediff_t ctimeout_ms = (data->set.connecttimeout > 0) ?
150
0
      data->set.connecttimeout : DEFAULT_CONNECT_TIMEOUT;
151
0
    ctimeleft_ms = ctimeout_ms -
152
0
                   curlx_timediff(*nowp, data->progress.t_startsingle);
153
0
    if(!ctimeleft_ms)
154
0
      ctimeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */
155
0
    if(!timeleft_ms)
156
0
      return ctimeleft_ms; /* no general timeout, this is it */
157
0
  }
158
  /* return minimal time left or max amount already expired */
159
0
  return (ctimeleft_ms < timeleft_ms) ? ctimeleft_ms : timeleft_ms;
160
0
}
161
162
void Curl_shutdown_start(struct Curl_easy *data, int sockindex,
163
                         int timeout_ms, struct curltime *nowp)
164
0
{
165
0
  struct curltime now;
166
167
0
  DEBUGASSERT(data->conn);
168
0
  if(!nowp) {
169
0
    now = curlx_now();
170
0
    nowp = &now;
171
0
  }
172
0
  data->conn->shutdown.start[sockindex] = *nowp;
173
0
  data->conn->shutdown.timeout_ms = (timeout_ms > 0) ?
174
0
    (timediff_t)timeout_ms :
175
0
    ((data->set.shutdowntimeout > 0) ?
176
0
     data->set.shutdowntimeout : DEFAULT_SHUTDOWN_TIMEOUT_MS);
177
  /* Set a timer, unless we operate on the admin handle */
178
0
  if(data->mid && (data->conn->shutdown.timeout_ms > 0))
179
0
    Curl_expire_ex(data, nowp, data->conn->shutdown.timeout_ms,
180
0
                   EXPIRE_SHUTDOWN);
181
0
}
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
0
{
225
0
  struct curltime *pt = &data->conn->shutdown.start[sockindex];
226
0
  memset(pt, 0, sizeof(*pt));
227
0
}
228
229
bool Curl_shutdown_started(struct Curl_easy *data, int sockindex)
230
0
{
231
0
  struct curltime *pt = &data->conn->shutdown.start[sockindex];
232
0
  return (pt->tv_sec > 0) || (pt->tv_usec > 0);
233
0
}
234
235
static const struct Curl_addrinfo *
236
addr_first_match(const struct Curl_addrinfo *addr, int family)
237
0
{
238
0
  while(addr) {
239
0
    if(addr->ai_family == family)
240
0
      return addr;
241
0
    addr = addr->ai_next;
242
0
  }
243
0
  return NULL;
244
0
}
245
246
static const struct Curl_addrinfo *
247
addr_next_match(const struct Curl_addrinfo *addr, int family)
248
0
{
249
0
  while(addr && addr->ai_next) {
250
0
    addr = addr->ai_next;
251
0
    if(addr->ai_family == family)
252
0
      return addr;
253
0
  }
254
0
  return NULL;
255
0
}
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
0
{
262
0
  struct sockaddr_in *si = NULL;
263
0
#ifdef USE_IPV6
264
0
  struct sockaddr_in6 *si6 = NULL;
265
0
#endif
266
0
#if (defined(HAVE_SYS_UN_H) || defined(WIN32_SOCKADDR_UN)) && defined(AF_UNIX)
267
0
  struct sockaddr_un *su = NULL;
268
#else
269
  (void)salen;
270
#endif
271
272
0
  switch(sa->sa_family) {
273
0
    case AF_INET:
274
0
      si = (struct sockaddr_in *)(void *) sa;
275
0
      if(curlx_inet_ntop(sa->sa_family, &si->sin_addr, addr, MAX_IPADR_LEN)) {
276
0
        unsigned short us_port = ntohs(si->sin_port);
277
0
        *port = us_port;
278
0
        return TRUE;
279
0
      }
280
0
      break;
281
0
#ifdef USE_IPV6
282
0
    case AF_INET6:
283
0
      si6 = (struct sockaddr_in6 *)(void *) sa;
284
0
      if(curlx_inet_ntop(sa->sa_family, &si6->sin6_addr, addr,
285
0
                         MAX_IPADR_LEN)) {
286
0
        unsigned short us_port = ntohs(si6->sin6_port);
287
0
        *port = us_port;
288
0
        return TRUE;
289
0
      }
290
0
      break;
291
0
#endif
292
0
#if (defined(HAVE_SYS_UN_H) || defined(WIN32_SOCKADDR_UN)) && defined(AF_UNIX)
293
0
    case AF_UNIX:
294
0
      if(salen > (curl_socklen_t)sizeof(CURL_SA_FAMILY_T)) {
295
0
        su = (struct sockaddr_un*)sa;
296
0
        msnprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path);
297
0
      }
298
0
      else
299
0
        addr[0] = 0; /* socket with no name */
300
0
      *port = 0;
301
0
      return TRUE;
302
0
#endif
303
0
    default:
304
0
      break;
305
0
  }
306
307
0
  addr[0] = '\0';
308
0
  *port = 0;
309
0
  CURL_SETERRNO(SOCKEAFNOSUPPORT);
310
0
  return FALSE;
311
0
}
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
0
{
322
0
  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
0
  if(data->state.lastconnect_id != -1) {
330
0
    struct connectdata *conn;
331
332
0
    conn = Curl_cpool_get_conn(data, data->state.lastconnect_id);
333
0
    if(!conn) {
334
0
      data->state.lastconnect_id = -1;
335
0
      return CURL_SOCKET_BAD;
336
0
    }
337
338
0
    if(connp)
339
      /* only store this if the caller cares for it */
340
0
      *connp = conn;
341
0
    return conn->sock[FIRSTSOCKET];
342
0
  }
343
0
  return CURL_SOCKET_BAD;
344
0
}
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
0
{
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
0
  bool closeit, is_multiplex;
360
0
  DEBUGASSERT(conn);
361
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
362
  (void)reason; /* useful for debugging */
363
#endif
364
0
  is_multiplex = Curl_conn_is_multiplex(conn, FIRSTSOCKET);
365
0
  closeit = (ctrl == CONNCTRL_CONNECTION) ||
366
0
    ((ctrl == CONNCTRL_STREAM) && !is_multiplex);
367
0
  if((ctrl == CONNCTRL_STREAM) && is_multiplex)
368
0
    ;  /* stream signal on multiplex conn never affects close state */
369
0
  else if((bit)closeit != conn->bits.close) {
370
0
    conn->bits.close = closeit; /* the only place in the source code that
371
                                   should assign this bit */
372
0
  }
373
0
}
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
0
{
431
0
  struct eyeballer *baller;
432
433
0
  *pballer = NULL;
434
0
  baller = calloc(1, sizeof(*baller));
435
0
  if(!baller)
436
0
    return CURLE_OUT_OF_MEMORY;
437
438
0
  baller->name = ((ai_family == AF_INET) ? "ipv4" : (
439
0
#ifdef USE_IPV6
440
0
                  (ai_family == AF_INET6) ? "ipv6" :
441
0
#endif
442
0
                  "ip"));
443
0
  baller->cf_create = cf_create;
444
0
  baller->first = baller->addr = addr;
445
0
  baller->ai_family = ai_family;
446
0
  baller->primary = primary;
447
0
  baller->delay_ms = delay_ms;
448
0
  baller->timeoutms = addr_next_match(baller->addr, baller->ai_family) ?
449
0
    USETIME(timeout_ms) : timeout_ms;
450
0
  baller->timeout_id = timeout_id;
451
0
  baller->result = CURLE_COULDNT_CONNECT;
452
453
0
  *pballer = baller;
454
0
  return CURLE_OK;
455
0
}
456
457
static void baller_close(struct eyeballer *baller,
458
                         struct Curl_easy *data)
459
0
{
460
0
  if(baller && baller->cf) {
461
0
    Curl_conn_cf_discard_chain(&baller->cf, data);
462
0
  }
463
0
}
464
465
static void baller_free(struct eyeballer *baller,
466
                        struct Curl_easy *data)
467
0
{
468
0
  if(baller) {
469
0
    baller_close(baller, data);
470
0
    free(baller);
471
0
  }
472
0
}
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
0
{
482
0
  baller->addr = addr_next_match(baller->addr, baller->ai_family);
483
0
}
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
0
{
495
0
  struct cf_he_ctx *ctx = cf->ctx;
496
0
  struct Curl_cfilter *cf_prev = baller->cf;
497
0
  struct Curl_cfilter *wcf;
498
0
  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
0
  result = baller->cf_create(&baller->cf, data, cf->conn, baller->addr,
506
0
                             ctx->transport);
507
0
  if(result)
508
0
    goto out;
509
510
  /* the new filter might have sub-filters */
511
0
  for(wcf = baller->cf; wcf; wcf = wcf->next) {
512
0
    wcf->conn = cf->conn;
513
0
    wcf->sockindex = cf->sockindex;
514
0
  }
515
516
0
  if(addr_next_match(baller->addr, baller->ai_family)) {
517
0
    Curl_expire(data, baller->timeoutms, baller->timeout_id);
518
0
  }
519
520
0
out:
521
0
  if(result) {
522
0
    CURL_TRC_CF(data, cf, "%s failed", baller->name);
523
0
    baller_close(baller, data);
524
0
  }
525
0
  if(cf_prev)
526
0
    Curl_conn_cf_discard_chain(&cf_prev, data);
527
0
  baller->result = result;
528
0
}
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
0
{
541
0
  baller->error = 0;
542
0
  baller->connected = FALSE;
543
0
  baller->has_started = TRUE;
544
545
0
  while(baller->addr) {
546
0
    baller->started = curlx_now();
547
0
    baller->timeoutms = addr_next_match(baller->addr, baller->ai_family) ?
548
0
      USETIME(timeoutms) : timeoutms;
549
0
    baller_initiate(cf, data, baller);
550
0
    if(!baller->result)
551
0
      break;
552
0
    baller_next_addr(baller);
553
0
  }
554
0
  if(!baller->addr) {
555
0
    baller->is_done = TRUE;
556
0
  }
557
0
  return baller->result;
558
0
}
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
0
{
568
0
  if(cf->sockindex == FIRSTSOCKET) {
569
0
    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
0
    if(!baller->addr && baller->inconclusive)
575
0
      baller_rewind(baller);
576
0
    baller_start(cf, data, baller, timeoutms);
577
0
  }
578
0
  else {
579
0
    baller->error = 0;
580
0
    baller->connected = FALSE;
581
0
    baller->has_started = TRUE;
582
0
    baller->is_done = TRUE;
583
0
    baller->result = CURLE_COULDNT_CONNECT;
584
0
  }
585
0
  return baller->result;
586
0
}
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
0
{
594
0
  (void)cf;
595
0
  *connected = baller->connected;
596
0
  if(!baller->result &&  !*connected) {
597
    /* evaluate again */
598
0
    baller->result = Curl_conn_cf_connect(baller->cf, data, connected);
599
600
0
    if(!baller->result) {
601
0
      if(*connected) {
602
0
        baller->connected = TRUE;
603
0
        baller->is_done = TRUE;
604
0
      }
605
0
      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
0
    }
614
0
    else if(baller->result == CURLE_WEIRD_SERVER_REPLY)
615
0
      baller->inconclusive = TRUE;
616
0
  }
617
0
  return baller->result;
618
0
}
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
0
{
627
0
  struct cf_he_ctx *ctx = cf->ctx;
628
0
  struct connectdata *conn = cf->conn;
629
0
  CURLcode result;
630
0
  struct curltime now;
631
0
  size_t i;
632
0
  int ongoing, not_started;
633
0
  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
0
evaluate:
642
0
  *connected = FALSE; /* a negative world view is best */
643
0
  now = curlx_now();
644
0
  ongoing = not_started = 0;
645
0
  for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
646
0
    struct eyeballer *baller = ctx->baller[i];
647
648
0
    if(!baller || baller->is_done)
649
0
      continue;
650
651
0
    if(!baller->has_started) {
652
0
      ++not_started;
653
0
      continue;
654
0
    }
655
0
    baller->result = baller_connect(cf, data, baller, &now, connected);
656
0
    CURL_TRC_CF(data, cf, "%s connect -> %d, connected=%d",
657
0
                baller->name, baller->result, *connected);
658
659
0
    if(!baller->result) {
660
0
      if(*connected) {
661
        /* connected, declare the winner */
662
0
        ctx->winner = baller;
663
0
        ctx->baller[i] = NULL;
664
0
        break;
665
0
      }
666
0
      else { /* still waiting */
667
0
        ++ongoing;
668
0
      }
669
0
    }
670
0
    else if(!baller->is_done) {
671
      /* The baller failed to connect, start its next attempt */
672
0
      if(baller->error) {
673
0
        data->state.os_errno = baller->error;
674
0
        SET_SOCKERRNO(baller->error);
675
0
      }
676
0
      baller_start_next(cf, data, baller, Curl_timeleft(data, &now, TRUE));
677
0
      if(baller->is_done) {
678
0
        CURL_TRC_CF(data, cf, "%s done", baller->name);
679
0
      }
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
0
    }
687
0
  }
688
689
0
  if(ctx->winner) {
690
0
    *connected = TRUE;
691
0
    return CURLE_OK;
692
0
  }
693
694
  /* Nothing connected, check the time before we might
695
   * start new ballers or return ok. */
696
0
  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
0
  if(not_started > 0) {
704
0
    int added = 0;
705
706
0
    for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
707
0
      struct eyeballer *baller = ctx->baller[i];
708
709
0
      if(!baller || baller->has_started)
710
0
        continue;
711
      /* We start its primary baller has failed to connect or if
712
       * its start delay_ms have expired */
713
0
      if((baller->primary && baller->primary->is_done) ||
714
0
          curlx_timediff(now, ctx->started) >= baller->delay_ms) {
715
0
        baller_start(cf, data, baller, Curl_timeleft(data, &now, TRUE));
716
0
        if(baller->is_done) {
717
0
          CURL_TRC_CF(data, cf, "%s done", baller->name);
718
0
        }
719
0
        else {
720
0
          CURL_TRC_CF(data, cf, "%s starting (timeout=%" FMT_TIMEDIFF_T "ms)",
721
0
                      baller->name, baller->timeoutms);
722
0
          ++ongoing;
723
0
          ++added;
724
0
        }
725
0
      }
726
0
    }
727
0
    if(added > 0)
728
0
      goto evaluate;
729
0
  }
730
731
0
  if(ongoing > 0) {
732
    /* We are still trying, return for more waiting */
733
0
    *connected = FALSE;
734
0
    return CURLE_OK;
735
0
  }
736
737
  /* all ballers have failed to connect. */
738
0
  CURL_TRC_CF(data, cf, "all eyeballers failed");
739
0
  result = CURLE_COULDNT_CONNECT;
740
0
  for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
741
0
    struct eyeballer *baller = ctx->baller[i];
742
0
    if(!baller)
743
0
      continue;
744
0
    CURL_TRC_CF(data, cf, "%s assess started=%d, result=%d",
745
0
                baller->name, baller->has_started, baller->result);
746
0
    if(baller->has_started && baller->result) {
747
0
      result = baller->result;
748
0
      break;
749
0
    }
750
0
  }
751
752
0
#ifndef CURL_DISABLE_PROXY
753
0
  if(conn->bits.socksproxy)
754
0
    hostname = conn->socks_proxy.host.name;
755
0
  else if(conn->bits.httpproxy)
756
0
    hostname = conn->http_proxy.host.name;
757
0
  else
758
0
#endif
759
0
    if(conn->bits.conn_to_host)
760
0
      hostname = conn->conn_to_host.name;
761
0
  else
762
0
    hostname = conn->host.name;
763
764
0
  failf(data, "Failed to connect to %s port %u after "
765
0
        "%" FMT_TIMEDIFF_T " ms: %s",
766
0
        hostname, conn->primary.remote_port,
767
0
        curlx_timediff(now, data->progress.t_startsingle),
768
0
        curl_easy_strerror(result));
769
770
0
#ifdef SOCKETIMEDOUT
771
0
  if(SOCKETIMEDOUT == data->state.os_errno)
772
0
    result = CURLE_OPERATION_TIMEDOUT;
773
0
#endif
774
775
0
  return result;
776
0
}
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
0
{
785
0
  struct cf_he_ctx *ctx = cf->ctx;
786
0
  struct connectdata *conn = cf->conn;
787
0
  CURLcode result = CURLE_COULDNT_CONNECT;
788
0
  int ai_family0 = 0, ai_family1 = 0;
789
0
  timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
790
0
  const struct Curl_addrinfo *addr0 = NULL, *addr1 = NULL;
791
0
  struct Curl_dns_entry *dns = data->state.dns[cf->sockindex];
792
793
0
  if(!dns)
794
0
    return CURLE_FAILED_INIT;
795
796
0
  if(timeout_ms < 0) {
797
    /* a precaution, no need to continue if time already is up */
798
0
    failf(data, "Connection time-out");
799
0
    return CURLE_OPERATION_TIMEDOUT;
800
0
  }
801
802
0
  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
0
  if(conn->ip_version == CURL_IPRESOLVE_V6) {
814
0
#ifdef USE_IPV6
815
0
    ai_family0 = AF_INET6;
816
0
    addr0 = addr_first_match(dns->addr, ai_family0);
817
0
#endif
818
0
  }
819
0
  else if(conn->ip_version == CURL_IPRESOLVE_V4) {
820
0
    ai_family0 = AF_INET;
821
0
    addr0 = addr_first_match(dns->addr, ai_family0);
822
0
  }
823
0
  else {
824
    /* no user preference, we try ipv6 always first when available */
825
0
#ifdef USE_IPV6
826
0
    ai_family0 = AF_INET6;
827
0
    addr0 = addr_first_match(dns->addr, ai_family0);
828
0
#endif
829
    /* next candidate is ipv4 */
830
0
    ai_family1 = AF_INET;
831
0
    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
0
    if(!addr1 && !addr0 && dns->addr) {
835
0
      ai_family0 = dns->addr->ai_family;
836
0
      addr0 = addr_first_match(dns->addr, ai_family0);
837
0
    }
838
0
  }
839
840
0
  if(!addr0 && addr1) {
841
    /* switch around, so a single baller always uses addr0 */
842
0
    addr0 = addr1;
843
0
    ai_family0 = ai_family1;
844
0
    addr1 = NULL;
845
0
  }
846
847
  /* We found no address that matches our criteria, we cannot connect */
848
0
  if(!addr0) {
849
0
    return CURLE_COULDNT_CONNECT;
850
0
  }
851
852
0
  memset(ctx->baller, 0, sizeof(ctx->baller));
853
0
  result = eyeballer_new(&ctx->baller[0], ctx->cf_create, addr0, ai_family0,
854
0
                          NULL, 0, /* no primary/delay, start now */
855
0
                          timeout_ms,  EXPIRE_DNS_PER_NAME);
856
0
  if(result)
857
0
    return result;
858
0
  CURL_TRC_CF(data, cf, "created %s (timeout %" FMT_TIMEDIFF_T "ms)",
859
0
              ctx->baller[0]->name, ctx->baller[0]->timeoutms);
860
0
  if(addr1) {
861
    /* second one gets a delayed start */
862
0
    result = eyeballer_new(&ctx->baller[1], ctx->cf_create, addr1, ai_family1,
863
0
                            ctx->baller[0], /* wait on that to fail */
864
                            /* or start this delayed */
865
0
                            data->set.happy_eyeballs_timeout,
866
0
                            timeout_ms,  EXPIRE_DNS_PER_NAME2);
867
0
    if(result)
868
0
      return result;
869
0
    CURL_TRC_CF(data, cf, "created %s (timeout %" FMT_TIMEDIFF_T "ms)",
870
0
                ctx->baller[1]->name, ctx->baller[1]->timeoutms);
871
0
    Curl_expire(data, data->set.happy_eyeballs_timeout,
872
0
                EXPIRE_HAPPY_EYEBALLS);
873
0
  }
874
875
0
  return CURLE_OK;
876
0
}
877
878
static void cf_he_ctx_clear(struct Curl_cfilter *cf, struct Curl_easy *data)
879
0
{
880
0
  struct cf_he_ctx *ctx = cf->ctx;
881
0
  size_t i;
882
883
0
  DEBUGASSERT(ctx);
884
0
  DEBUGASSERT(data);
885
0
  for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
886
0
    baller_free(ctx->baller[i], data);
887
0
    ctx->baller[i] = NULL;
888
0
  }
889
0
  baller_free(ctx->winner, data);
890
0
  ctx->winner = NULL;
891
0
}
892
893
static CURLcode cf_he_shutdown(struct Curl_cfilter *cf,
894
                               struct Curl_easy *data, bool *done)
895
0
{
896
0
  struct cf_he_ctx *ctx = cf->ctx;
897
0
  size_t i;
898
0
  CURLcode result = CURLE_OK;
899
900
0
  DEBUGASSERT(data);
901
0
  if(cf->connected) {
902
0
    *done = TRUE;
903
0
    return CURLE_OK;
904
0
  }
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
0
}
932
933
static void cf_he_adjust_pollset(struct Curl_cfilter *cf,
934
                                 struct Curl_easy *data,
935
                                 struct easy_pollset *ps)
936
0
{
937
0
  struct cf_he_ctx *ctx = cf->ctx;
938
0
  size_t i;
939
940
0
  if(!cf->connected) {
941
0
    for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
942
0
      struct eyeballer *baller = ctx->baller[i];
943
0
      if(!baller || !baller->cf)
944
0
        continue;
945
0
      Curl_conn_cf_adjust_pollset(baller->cf, data, ps);
946
0
    }
947
0
    CURL_TRC_CF(data, cf, "adjust_pollset -> %d socks", ps->num);
948
0
  }
949
0
}
950
951
static CURLcode cf_he_connect(struct Curl_cfilter *cf,
952
                              struct Curl_easy *data,
953
                              bool *done)
954
0
{
955
0
  struct cf_he_ctx *ctx = cf->ctx;
956
0
  CURLcode result = CURLE_OK;
957
958
0
  if(cf->connected) {
959
0
    *done = TRUE;
960
0
    return CURLE_OK;
961
0
  }
962
963
0
  DEBUGASSERT(ctx);
964
0
  *done = FALSE;
965
966
0
  switch(ctx->state) {
967
0
    case SCFST_INIT:
968
0
      DEBUGASSERT(CURL_SOCKET_BAD == Curl_conn_cf_get_socket(cf, data));
969
0
      DEBUGASSERT(!cf->connected);
970
0
      result = start_connect(cf, data);
971
0
      if(result)
972
0
        return result;
973
0
      ctx->state = SCFST_WAITING;
974
0
      FALLTHROUGH();
975
0
    case SCFST_WAITING:
976
0
      result = is_connected(cf, data, done);
977
0
      if(!result && *done) {
978
0
        DEBUGASSERT(ctx->winner);
979
0
        DEBUGASSERT(ctx->winner->cf);
980
0
        DEBUGASSERT(ctx->winner->cf->connected);
981
        /* we have a winner. Install and activate it.
982
         * close/free all others. */
983
0
        ctx->state = SCFST_DONE;
984
0
        cf->connected = TRUE;
985
0
        cf->next = ctx->winner->cf;
986
0
        ctx->winner->cf = NULL;
987
0
        cf_he_ctx_clear(cf, data);
988
989
0
        if(cf->conn->handler->protocol & PROTO_FAMILY_SSH)
990
0
          Curl_pgrsTime(data, TIMER_APPCONNECT); /* we are connected already */
991
0
        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
0
        data->info.numconnects++; /* to track the # of connections made */
1003
0
      }
1004
0
      break;
1005
0
    case SCFST_DONE:
1006
0
      *done = TRUE;
1007
0
      break;
1008
0
  }
1009
0
  return result;
1010
0
}
1011
1012
static void cf_he_close(struct Curl_cfilter *cf,
1013
                        struct Curl_easy *data)
1014
0
{
1015
0
  struct cf_he_ctx *ctx = cf->ctx;
1016
1017
0
  CURL_TRC_CF(data, cf, "close");
1018
0
  cf_he_ctx_clear(cf, data);
1019
0
  cf->connected = FALSE;
1020
0
  ctx->state = SCFST_INIT;
1021
1022
0
  if(cf->next) {
1023
0
    cf->next->cft->do_close(cf->next, data);
1024
0
    Curl_conn_cf_discard_chain(&cf->next, data);
1025
0
  }
1026
0
}
1027
1028
static bool cf_he_data_pending(struct Curl_cfilter *cf,
1029
                               const struct Curl_easy *data)
1030
0
{
1031
0
  struct cf_he_ctx *ctx = cf->ctx;
1032
0
  size_t i;
1033
1034
0
  if(cf->connected)
1035
0
    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
0
{
1051
0
  struct cf_he_ctx *ctx = cf->ctx;
1052
0
  struct curltime t, tmax;
1053
0
  size_t i;
1054
1055
0
  memset(&tmax, 0, sizeof(tmax));
1056
0
  for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
1057
0
    struct eyeballer *baller = ctx->baller[i];
1058
1059
0
    memset(&t, 0, sizeof(t));
1060
0
    if(baller && baller->cf &&
1061
0
       !baller->cf->cft->query(baller->cf, data, query, NULL, &t)) {
1062
0
      if((t.tv_sec || t.tv_usec) && curlx_timediff_us(t, tmax) > 0)
1063
0
        tmax = t;
1064
0
    }
1065
0
  }
1066
0
  return tmax;
1067
0
}
1068
1069
static CURLcode cf_he_query(struct Curl_cfilter *cf,
1070
                            struct Curl_easy *data,
1071
                            int query, int *pres1, void *pres2)
1072
0
{
1073
0
  struct cf_he_ctx *ctx = cf->ctx;
1074
1075
0
  if(!cf->connected) {
1076
0
    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
0
    case CF_QUERY_TIMER_CONNECT: {
1097
0
      struct curltime *when = pres2;
1098
0
      *when = get_max_baller_time(cf, data, CF_QUERY_TIMER_CONNECT);
1099
0
      return CURLE_OK;
1100
0
    }
1101
0
    case CF_QUERY_TIMER_APPCONNECT: {
1102
0
      struct curltime *when = pres2;
1103
0
      *when = get_max_baller_time(cf, data, CF_QUERY_TIMER_APPCONNECT);
1104
0
      return CURLE_OK;
1105
0
    }
1106
0
    default:
1107
0
      break;
1108
0
    }
1109
0
  }
1110
1111
0
  return cf->next ?
1112
0
    cf->next->cft->query(cf->next, data, query, pres1, pres2) :
1113
0
    CURLE_UNKNOWN_OPTION;
1114
0
}
1115
1116
static void cf_he_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
1117
0
{
1118
0
  struct cf_he_ctx *ctx = cf->ctx;
1119
1120
0
  CURL_TRC_CF(data, cf, "destroy");
1121
0
  if(ctx) {
1122
0
    cf_he_ctx_clear(cf, data);
1123
0
  }
1124
  /* release any resources held in state */
1125
0
  Curl_safefree(ctx);
1126
0
}
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
0
{
1163
0
  struct cf_he_ctx *ctx = NULL;
1164
0
  CURLcode result;
1165
1166
0
  (void)data;
1167
0
  (void)conn;
1168
0
  *pcf = NULL;
1169
0
  ctx = calloc(1, sizeof(*ctx));
1170
0
  if(!ctx) {
1171
0
    result = CURLE_OUT_OF_MEMORY;
1172
0
    goto out;
1173
0
  }
1174
0
  ctx->transport = transport;
1175
0
  ctx->cf_create = cf_create;
1176
1177
0
  result = Curl_cf_create(pcf, &Curl_cft_happy_eyeballs, ctx);
1178
1179
0
out:
1180
0
  if(result) {
1181
0
    Curl_safefree(*pcf);
1182
0
    free(ctx);
1183
0
  }
1184
0
  return result;
1185
0
}
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
0
{
1211
0
  size_t i;
1212
0
  for(i = 0; i < CURL_ARRAYSIZE(transport_providers); ++i) {
1213
0
    if(transport == transport_providers[i].transport)
1214
0
      return transport_providers[i].cf_create;
1215
0
  }
1216
0
  return NULL;
1217
0
}
1218
1219
static CURLcode cf_he_insert_after(struct Curl_cfilter *cf_at,
1220
                                   struct Curl_easy *data,
1221
                                   int transport)
1222
0
{
1223
0
  cf_ip_connect_create *cf_create;
1224
0
  struct Curl_cfilter *cf;
1225
0
  CURLcode result;
1226
1227
  /* Need to be first */
1228
0
  DEBUGASSERT(cf_at);
1229
0
  cf_create = get_cf_create(transport);
1230
0
  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
0
  result = cf_happy_eyeballs_create(&cf, data, cf_at->conn,
1235
0
                                    cf_create, transport);
1236
0
  if(result)
1237
0
    return result;
1238
1239
0
  Curl_conn_cf_insert_after(cf_at, cf);
1240
0
  return CURLE_OK;
1241
0
}
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
0
{
1263
0
  struct cf_setup_ctx *ctx = cf->ctx;
1264
0
  CURLcode result = CURLE_OK;
1265
0
  struct Curl_dns_entry *dns = data->state.dns[cf->sockindex];
1266
1267
0
  if(cf->connected) {
1268
0
    *done = TRUE;
1269
0
    return CURLE_OK;
1270
0
  }
1271
1272
  /* connect current sub-chain */
1273
0
connect_sub_chain:
1274
0
  if(!dns)
1275
0
    return CURLE_FAILED_INIT;
1276
1277
0
  if(cf->next && !cf->next->connected) {
1278
0
    result = Curl_conn_cf_connect(cf->next, data, done);
1279
0
    if(result || !*done)
1280
0
      return result;
1281
0
  }
1282
1283
0
  if(ctx->state < CF_SETUP_CNNCT_EYEBALLS) {
1284
0
    result = cf_he_insert_after(cf, data, ctx->transport);
1285
0
    if(result)
1286
0
      return result;
1287
0
    ctx->state = CF_SETUP_CNNCT_EYEBALLS;
1288
0
    if(!cf->next || !cf->next->connected)
1289
0
      goto connect_sub_chain;
1290
0
  }
1291
1292
  /* sub-chain connected, do we need to add more? */
1293
0
#ifndef CURL_DISABLE_PROXY
1294
0
  if(ctx->state < CF_SETUP_CNNCT_SOCKS && cf->conn->bits.socksproxy) {
1295
0
    result = Curl_cf_socks_proxy_insert_after(cf, data);
1296
0
    if(result)
1297
0
      return result;
1298
0
    ctx->state = CF_SETUP_CNNCT_SOCKS;
1299
0
    if(!cf->next || !cf->next->connected)
1300
0
      goto connect_sub_chain;
1301
0
  }
1302
1303
0
  if(ctx->state < CF_SETUP_CNNCT_HTTP_PROXY && cf->conn->bits.httpproxy) {
1304
0
#ifdef USE_SSL
1305
0
    if(IS_HTTPS_PROXY(cf->conn->http_proxy.proxytype)
1306
0
       && !Curl_conn_is_ssl(cf->conn, cf->sockindex)) {
1307
0
      result = Curl_cf_ssl_proxy_insert_after(cf, data);
1308
0
      if(result)
1309
0
        return result;
1310
0
    }
1311
0
#endif /* USE_SSL */
1312
1313
0
#if !defined(CURL_DISABLE_HTTP)
1314
0
    if(cf->conn->bits.tunnel_proxy) {
1315
0
      result = Curl_cf_http_proxy_insert_after(cf, data);
1316
0
      if(result)
1317
0
        return result;
1318
0
    }
1319
0
#endif /* !CURL_DISABLE_HTTP */
1320
0
    ctx->state = CF_SETUP_CNNCT_HTTP_PROXY;
1321
0
    if(!cf->next || !cf->next->connected)
1322
0
      goto connect_sub_chain;
1323
0
  }
1324
0
#endif /* !CURL_DISABLE_PROXY */
1325
1326
0
  if(ctx->state < CF_SETUP_CNNCT_HAPROXY) {
1327
0
#if !defined(CURL_DISABLE_PROXY)
1328
0
    if(data->set.haproxyprotocol) {
1329
0
      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
0
      result = Curl_cf_haproxy_insert_after(cf, data);
1335
0
      if(result)
1336
0
        return result;
1337
0
    }
1338
0
#endif /* !CURL_DISABLE_PROXY */
1339
0
    ctx->state = CF_SETUP_CNNCT_HAPROXY;
1340
0
    if(!cf->next || !cf->next->connected)
1341
0
      goto connect_sub_chain;
1342
0
  }
1343
1344
0
  if(ctx->state < CF_SETUP_CNNCT_SSL) {
1345
0
#ifdef USE_SSL
1346
0
    if((ctx->ssl_mode == CURL_CF_SSL_ENABLE
1347
0
        || (ctx->ssl_mode != CURL_CF_SSL_DISABLE
1348
0
           && cf->conn->handler->flags & PROTOPT_SSL)) /* we want SSL */
1349
0
       && !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { /* it is missing */
1350
0
      result = Curl_cf_ssl_insert_after(cf, data);
1351
0
      if(result)
1352
0
        return result;
1353
0
    }
1354
0
#endif /* USE_SSL */
1355
0
    ctx->state = CF_SETUP_CNNCT_SSL;
1356
0
    if(!cf->next || !cf->next->connected)
1357
0
      goto connect_sub_chain;
1358
0
  }
1359
1360
0
  ctx->state = CF_SETUP_DONE;
1361
0
  cf->connected = TRUE;
1362
0
  *done = TRUE;
1363
0
  return CURLE_OK;
1364
0
}
1365
1366
static void cf_setup_close(struct Curl_cfilter *cf,
1367
                           struct Curl_easy *data)
1368
0
{
1369
0
  struct cf_setup_ctx *ctx = cf->ctx;
1370
1371
0
  CURL_TRC_CF(data, cf, "close");
1372
0
  cf->connected = FALSE;
1373
0
  ctx->state = CF_SETUP_INIT;
1374
1375
0
  if(cf->next) {
1376
0
    cf->next->cft->do_close(cf->next, data);
1377
0
    Curl_conn_cf_discard_chain(&cf->next, data);
1378
0
  }
1379
0
}
1380
1381
static void cf_setup_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
1382
0
{
1383
0
  struct cf_setup_ctx *ctx = cf->ctx;
1384
1385
0
  (void)data;
1386
0
  CURL_TRC_CF(data, cf, "destroy");
1387
0
  Curl_safefree(ctx);
1388
0
}
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
0
{
1414
0
  struct Curl_cfilter *cf = NULL;
1415
0
  struct cf_setup_ctx *ctx;
1416
0
  CURLcode result = CURLE_OK;
1417
1418
0
  (void)data;
1419
0
  ctx = calloc(1, sizeof(*ctx));
1420
0
  if(!ctx) {
1421
0
    result = CURLE_OUT_OF_MEMORY;
1422
0
    goto out;
1423
0
  }
1424
0
  ctx->state = CF_SETUP_INIT;
1425
0
  ctx->ssl_mode = ssl_mode;
1426
0
  ctx->transport = transport;
1427
1428
0
  result = Curl_cf_create(&cf, &Curl_cft_setup, ctx);
1429
0
  if(result)
1430
0
    goto out;
1431
0
  ctx = NULL;
1432
1433
0
out:
1434
0
  *pcf = result ? NULL : cf;
1435
0
  if(ctx) {
1436
0
    free(ctx);
1437
0
  }
1438
0
  return result;
1439
0
}
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
0
{
1447
0
  struct Curl_cfilter *cf;
1448
0
  CURLcode result = CURLE_OK;
1449
1450
0
  DEBUGASSERT(data);
1451
0
  result = cf_setup_create(&cf, data, transport, ssl_mode);
1452
0
  if(result)
1453
0
    goto out;
1454
0
  Curl_conn_cf_add(data, conn, sockindex, cf);
1455
0
out:
1456
0
  return result;
1457
0
}
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
0
{
1479
0
  struct Curl_cfilter *cf;
1480
0
  CURLcode result;
1481
1482
0
  DEBUGASSERT(data);
1483
0
  result = cf_setup_create(&cf, data, transport, ssl_mode);
1484
0
  if(result)
1485
0
    goto out;
1486
0
  Curl_conn_cf_insert_after(cf_at, cf);
1487
0
out:
1488
0
  return result;
1489
0
}
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
0
{
1497
0
  CURLcode result = CURLE_OK;
1498
1499
0
  DEBUGASSERT(data);
1500
0
  DEBUGASSERT(conn->handler);
1501
0
  DEBUGASSERT(dns);
1502
1503
0
  Curl_resolv_unlink(data, &data->state.dns[sockindex]);
1504
0
  data->state.dns[sockindex] = dns;
1505
1506
0
#if !defined(CURL_DISABLE_HTTP)
1507
0
  if(!conn->cfilter[sockindex] &&
1508
0
     conn->handler->protocol == CURLPROTO_HTTPS) {
1509
0
    DEBUGASSERT(ssl_mode != CURL_CF_SSL_DISABLE);
1510
0
    result = Curl_cf_https_setup(data, conn, sockindex);
1511
0
    if(result)
1512
0
      goto out;
1513
0
  }
1514
0
#endif /* !defined(CURL_DISABLE_HTTP) */
1515
1516
  /* Still no cfilter set, apply default. */
1517
0
  if(!conn->cfilter[sockindex]) {
1518
0
    result = cf_setup_add(data, conn, sockindex,
1519
0
                          conn->transport_wanted, ssl_mode);
1520
0
    if(result)
1521
0
      goto out;
1522
0
  }
1523
1524
0
  DEBUGASSERT(conn->cfilter[sockindex]);
1525
0
out:
1526
0
  if(result)
1527
0
    Curl_resolv_unlink(data, &data->state.dns[sockindex]);
1528
0
  return result;
1529
0
}