Coverage Report

Created: 2023-12-08 06:48

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