Coverage Report

Created: 2025-07-11 06:33

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