Coverage Report

Created: 2026-08-31 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/curl/lib/cf-https-connect.c
Line
Count
Source
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
#include "curl_setup.h"
25
26
#ifndef CURL_DISABLE_HTTP
27
28
#include "urldata.h"
29
#include "curl_trc.h"
30
#include "cfilters.h"
31
#include "cf-setup.h"
32
#include "connect.h"
33
#include "multiif.h"
34
#include "cf-https-connect.h"
35
#include "http2.h"
36
#include "progress.h"
37
#include "select.h"
38
#include "vdns/cf-dns.h"
39
#include "vdns/hostip.h"
40
#include "vdns/httpsrr.h"
41
#include "vquic/vquic.h"
42
43
typedef enum {
44
  CF_HC_RESOLV,
45
  CF_HC_INIT,
46
  CF_HC_CONNECT,
47
  CF_HC_SUCCESS,
48
  CF_HC_FAILURE
49
} cf_hc_state;
50
51
struct cf_hc_baller {
52
  const char *name;
53
  struct Curl_cfilter *cf;
54
  CURLcode result;
55
  struct curltime started;
56
  int reply_ms;
57
  uint8_t transport;
58
  enum alpnid alpn_id;
59
  BIT(shutdown);
60
};
61
62
static void cf_hc_baller_discard(struct cf_hc_baller *b,
63
                                 struct Curl_easy *data)
64
6.15k
{
65
6.15k
  if(b->cf) {
66
6.15k
    Curl_conn_cf_discard_chain(&b->cf, data);
67
6.15k
    b->cf = NULL;
68
6.15k
  }
69
6.15k
}
70
71
static bool cf_hc_baller_is_connecting(struct cf_hc_baller *b)
72
14.4k
{
73
14.4k
  return b->cf && !b->result;
74
14.4k
}
75
76
static bool cf_hc_baller_has_started(struct cf_hc_baller *b)
77
0
{
78
0
  return !!b->cf;
79
0
}
80
81
static int cf_hc_baller_reply_ms(struct cf_hc_baller *b,
82
                                 struct Curl_easy *data)
83
0
{
84
0
  if(b->cf && (b->reply_ms < 0))
85
0
    b->cf->cft->query(b->cf, data, CF_QUERY_CONNECT_REPLY_MS,
86
0
                      &b->reply_ms, NULL);
87
0
  return b->reply_ms;
88
0
}
89
90
static bool cf_hc_baller_data_pending(struct cf_hc_baller *b,
91
                                      const struct Curl_easy *data)
92
0
{
93
0
  return b->cf && !b->result && b->cf->cft->has_data_pending(b->cf, data);
94
0
}
95
96
static bool cf_hc_baller_needs_flush(struct cf_hc_baller *b,
97
                                     struct Curl_easy *data)
98
726
{
99
726
  return b->cf && !b->result && Curl_conn_cf_needs_flush(b->cf, data);
100
726
}
101
102
static CURLcode cf_hc_baller_cntrl(struct cf_hc_baller *b,
103
                                   struct Curl_easy *data,
104
                                   int event, int arg1, void *arg2)
105
6.15k
{
106
6.15k
  if(b->cf && !b->result)
107
24
    return Curl_conn_cf_cntrl(b->cf, data, FALSE, event, arg1, arg2);
108
6.13k
  return CURLE_OK;
109
6.15k
}
110
111
struct cf_hc_ctx {
112
  cf_hc_state state;
113
  struct Curl_peer *destination; /* who we ultimately want to talk to */
114
  struct curltime started;  /* when connect started */
115
  CURLcode result;          /* overall result */
116
  CURLcode check_h3_result;
117
  struct cf_hc_baller ballers[2];
118
  size_t baller_count;
119
  timediff_t soft_eyeballs_timeout_ms;
120
  timediff_t hard_eyeballs_timeout_ms;
121
  uint8_t def_transport;
122
  BIT(httpsrr_resolved);
123
  BIT(checked_h3);
124
  BIT(ballers_complete);
125
};
126
127
static void cf_hc_ctx_destroy(struct Curl_easy *data,
128
                              struct cf_hc_ctx *ctx)
129
12.3k
{
130
12.3k
  if(ctx) {
131
6.16k
    size_t i;
132
12.3k
    for(i = 0; i < ctx->baller_count; ++i)
133
6.15k
      cf_hc_baller_discard(&ctx->ballers[i], data);
134
6.16k
    Curl_peer_unlink(&ctx->destination);
135
6.16k
    curlx_free(ctx);
136
6.16k
  }
137
12.3k
}
138
139
static void cf_hc_baller_assign(struct cf_hc_baller *b,
140
                                enum alpnid alpn_id,
141
                                uint8_t def_transport)
142
6.15k
{
143
6.15k
  b->alpn_id = alpn_id;
144
6.15k
  b->transport = def_transport;
145
6.15k
  b->cf = NULL;
146
6.15k
  b->result = CURLE_OK;
147
6.15k
  b->reply_ms = -1;
148
6.15k
  b->shutdown = FALSE;
149
6.15k
  switch(b->alpn_id) {
150
0
  case ALPN_h3:
151
0
    b->name = "h3";
152
0
    b->transport = TRNSPRT_QUIC;
153
0
    break;
154
6.15k
  case ALPN_h2:
155
6.15k
    b->name = "h2";
156
6.15k
    break;
157
4
  case ALPN_h1:
158
4
    b->name = "h1";
159
4
    break;
160
0
  case ALPN_none:
161
0
    b->name = "no-alpn";
162
0
    break;
163
0
  default:
164
0
    b->result = CURLE_FAILED_INIT;
165
0
    break;
166
6.15k
  }
167
6.15k
}
168
169
static void cf_hc_baller_init(struct cf_hc_baller *b,
170
                              struct Curl_cfilter *cf,
171
                              struct Curl_easy *data)
172
6.15k
{
173
6.15k
  struct Curl_cfilter *save = cf->next;
174
175
6.15k
  cf->next = NULL;
176
6.15k
  b->started = *Curl_pgrs_now(data);
177
6.15k
  b->result = Curl_cf_setup_insert_after(cf, data, b->transport,
178
6.15k
                                         CURL_CF_SSL_ENABLE);
179
6.15k
  b->cf = cf->next;
180
6.15k
  cf->next = save;
181
6.15k
}
182
183
static CURLcode cf_hc_baller_connect(struct cf_hc_baller *b,
184
                                     struct Curl_cfilter *cf,
185
                                     struct Curl_easy *data,
186
                                     bool *done)
187
6.88k
{
188
6.88k
  struct Curl_cfilter *save = cf->next;
189
190
6.88k
  cf->next = b->cf;
191
6.88k
  b->result = Curl_conn_cf_connect(cf->next, data, done);
192
6.88k
  b->cf = cf->next; /* it might mutate */
193
6.88k
  cf->next = save;
194
6.88k
  return b->result;
195
6.88k
}
196
197
static CURLcode baller_connected(struct Curl_cfilter *cf,
198
                                 struct Curl_easy *data,
199
                                 struct cf_hc_baller *winner)
200
0
{
201
0
  struct cf_hc_ctx *ctx = cf->ctx;
202
203
  /* Make the winner's connection filter out own sub-filter, check, move,
204
   * close all remaining. */
205
0
  if(cf->next) {
206
0
    DEBUGASSERT(0);
207
0
    return CURLE_FAILED_INIT;
208
0
  }
209
0
  if(!winner->cf) {
210
0
    DEBUGASSERT(0);
211
0
    return CURLE_FAILED_INIT;
212
0
  }
213
214
0
  cf->next = winner->cf;
215
0
  winner->cf = NULL;
216
0
  ctx->state = CF_HC_SUCCESS;
217
0
  cf->connected = TRUE;
218
219
  /* ballers may have failf()'d, the winner resets it, so our
220
   * errorbuf is clean again. */
221
0
  Curl_reset_fail(data);
222
223
0
#ifdef USE_NGHTTP2
224
0
  {
225
    /* For a negotiated HTTP/2 connection insert the h2 filter. */
226
0
    const char *alpn = Curl_conn_cf_get_alpn_negotiated(cf->next, data);
227
0
    if(alpn && !strcmp("h2", alpn)) {
228
0
      CURLcode result = Curl_http2_switch_at(cf, data);
229
0
      if(result) {
230
0
        ctx->state = CF_HC_FAILURE;
231
0
        ctx->result = result;
232
0
        return result;
233
0
      }
234
0
    }
235
0
  }
236
0
#endif
237
0
  return CURLE_OK;
238
0
}
239
240
static bool time_to_start_baller2(struct Curl_cfilter *cf,
241
                                  struct Curl_easy *data)
242
6.88k
{
243
6.88k
  struct cf_hc_ctx *ctx = cf->ctx;
244
6.88k
  timediff_t elapsed_ms;
245
246
6.88k
  if(ctx->baller_count < 2)
247
6.88k
    return FALSE;
248
0
  else if(cf_hc_baller_has_started(&ctx->ballers[1]))
249
0
    return FALSE;
250
0
  else if(ctx->ballers[0].result) {
251
0
    CURL_TRC_CF(data, cf, "%s baller failed, starting %s",
252
0
                ctx->ballers[0].name, ctx->ballers[1].name);
253
0
    return TRUE;
254
0
  }
255
256
0
  elapsed_ms = curlx_ptimediff_ms(Curl_pgrs_now(data), &ctx->started);
257
0
  if(elapsed_ms >= ctx->hard_eyeballs_timeout_ms) {
258
0
    CURL_TRC_CF(data, cf, "%s inconclusive after %" FMT_TIMEDIFF_T ", "
259
0
                "starting %s", ctx->ballers[0].name,
260
0
                ctx->hard_eyeballs_timeout_ms, ctx->ballers[1].name);
261
0
    return TRUE;
262
0
  }
263
0
  else if(elapsed_ms >= ctx->soft_eyeballs_timeout_ms) {
264
0
    if(cf_hc_baller_reply_ms(&ctx->ballers[0], data) < 0) {
265
0
      CURL_TRC_CF(data, cf, "%s has not seen any data after %"
266
0
                  FMT_TIMEDIFF_T "ms, starting %s",
267
0
                  ctx->ballers[0].name, ctx->soft_eyeballs_timeout_ms,
268
0
                  ctx->ballers[1].name);
269
0
      return TRUE;
270
0
    }
271
0
  }
272
0
  return FALSE;
273
0
}
274
275
static bool cf_hc_may_h3(struct Curl_cfilter *cf,
276
                         struct Curl_easy *data)
277
0
{
278
0
  struct cf_hc_ctx *ctx = cf->ctx;
279
0
  if(!ctx->checked_h3) {
280
0
    ctx->check_h3_result =
281
0
      Curl_conn_may_http3(data, cf->conn, ctx->def_transport);
282
0
    ctx->checked_h3 = TRUE;
283
0
  }
284
0
  return !ctx->check_h3_result;
285
0
}
286
287
static enum alpnid cf_hc_get_httpsrr_alpn(struct Curl_cfilter *cf,
288
                                          struct Curl_easy *data,
289
                                          enum alpnid not_this_one)
290
12.3k
{
291
#ifdef USE_HTTPSRR
292
  struct cf_hc_ctx *ctx = cf->ctx;
293
  /* Is there an HTTPSRR use its ALPNs here.
294
   * We are here after having selected a connection to a host+port and
295
   * can no longer change that. Any HTTPSRR advice for other hosts and ports
296
   * we need to ignore. */
297
  const struct Curl_https_rrinfo *rr;
298
  size_t i;
299
300
  /* Do we have HTTPS-RR information? */
301
  rr = Curl_conn_dns_get_https(data, cf->sockindex, ctx->destination);
302
303
  CURL_TRC_CF(data, cf, "HTTPS-RR %savailable", rr ? "" : "not ");
304
  /* We do not support `rr->no_def_alpn`. */
305
  if(Curl_httpsrr_applicable(data, rr) && !rr->no_def_alpn) {
306
    for(i = 0; i < CURL_ARRAYSIZE(rr->alpns); ++i) {
307
      enum alpnid alpn_rr = (enum alpnid)rr->alpns[i];
308
      if(alpn_rr == not_this_one) /* do not want this one */
309
        continue;
310
      switch(alpn_rr) {
311
      case ALPN_h3:
312
        if((data->state.http_neg.allowed & CURL_HTTP_V3x) &&
313
           cf_hc_may_h3(cf, data)) {
314
          return alpn_rr;
315
        }
316
        break;
317
      case ALPN_h2:
318
        if(data->state.http_neg.allowed & CURL_HTTP_V2x) {
319
          return alpn_rr;
320
        }
321
        break;
322
      case ALPN_h1:
323
        if(data->state.http_neg.allowed & CURL_HTTP_V1x) {
324
          return alpn_rr;
325
        }
326
        break;
327
      default: /* ignore */
328
        break;
329
      }
330
    }
331
  }
332
#else
333
12.3k
  (void)cf;
334
12.3k
  (void)data;
335
12.3k
  (void)not_this_one;
336
12.3k
#endif
337
12.3k
  return ALPN_none;
338
12.3k
}
339
340
static enum alpnid cf_hc_get_pref_alpn(struct Curl_cfilter *cf,
341
                                       struct Curl_easy *data,
342
                                       enum alpnid not_this_one)
343
12.3k
{
344
12.3k
  if((data->state.http_neg.preferred & data->state.http_neg.allowed)) {
345
0
    switch(data->state.http_neg.preferred) {
346
0
    case CURL_HTTP_V3x:
347
0
      if(cf_hc_may_h3(cf, data) && (ALPN_h3 != not_this_one))
348
0
        return ALPN_h3;
349
0
      break;
350
0
    case CURL_HTTP_V2x:
351
0
      if(ALPN_h2 != not_this_one)
352
0
        return ALPN_h2;
353
0
      break;
354
0
    case CURL_HTTP_V1x:
355
      /* If we are trying h2 already, h1 is already used as fallback */
356
0
      if((ALPN_h1 != not_this_one) && (ALPN_h2 != not_this_one))
357
0
        return ALPN_h1;
358
0
      break;
359
0
    default:
360
0
      break;
361
0
    }
362
0
  }
363
12.3k
  return ALPN_none;
364
12.3k
}
365
366
static enum alpnid cf_hc_get_first_alpn(struct Curl_cfilter *cf,
367
                                        struct Curl_easy *data,
368
                                        http_majors choices,
369
                                        enum alpnid not_this_one)
370
12.3k
{
371
  /* When told to not try h2, we also do not try h1 and vice versa */
372
12.3k
  bool allow_h1_or_h2 = (not_this_one != ALPN_h1) &&
373
12.3k
                        (not_this_one != ALPN_h2);
374
12.3k
  if((ALPN_h3 != not_this_one) && (choices & CURL_HTTP_V3x) &&
375
0
     cf_hc_may_h3(cf, data)) {
376
0
    return ALPN_h3;
377
0
  }
378
12.3k
  if(allow_h1_or_h2 && (choices & CURL_HTTP_V2x)) {
379
6.15k
    return ALPN_h2;
380
6.15k
  }
381
6.15k
  if(allow_h1_or_h2 && (choices & CURL_HTTP_V1x)) {
382
4
    return ALPN_h1;
383
4
  }
384
6.15k
  return ALPN_none;
385
6.15k
}
386
387
static CURLcode cf_hc_set_baller1(struct Curl_cfilter *cf,
388
                                  struct Curl_easy *data)
389
6.15k
{
390
6.15k
  struct cf_hc_ctx *ctx = cf->ctx;
391
6.15k
  enum alpnid alpn1 = ALPN_none;
392
6.15k
  VERBOSE(const char *source = "HTTPS-RR");
393
394
6.15k
  DEBUGASSERT(cf->conn->bits.tls_enable_alpn);
395
396
6.15k
  alpn1 = cf_hc_get_httpsrr_alpn(cf, data, ALPN_none);
397
6.15k
  if(alpn1 == ALPN_none) {
398
    /* preference is configured and allowed, can we use it? */
399
6.15k
    VERBOSE(source = "preferred version");
400
6.15k
    alpn1 = cf_hc_get_pref_alpn(cf, data, ALPN_none);
401
6.15k
  }
402
6.15k
  if(alpn1 == ALPN_none) {
403
6.15k
    VERBOSE(source = "wanted versions");
404
6.15k
    alpn1 = cf_hc_get_first_alpn(cf, data,
405
6.15k
                                 data->state.http_neg.wanted,
406
6.15k
                                 ALPN_none);
407
6.15k
  }
408
6.15k
  if(alpn1 == ALPN_none) {
409
0
    VERBOSE(source = "allowed versions");
410
0
    alpn1 = cf_hc_get_first_alpn(cf, data,
411
0
                                 data->state.http_neg.allowed,
412
0
                                 ALPN_none);
413
0
  }
414
415
6.15k
  if(alpn1 == ALPN_none) {
416
    /* None of the wanted/allowed HTTP versions could be chosen */
417
0
    if(ctx->check_h3_result) {
418
0
      CURL_TRC_CF(data, cf, "unable to use HTTP/3");
419
0
      return ctx->check_h3_result;
420
0
    }
421
0
    CURL_TRC_CF(data, cf, "unable to select HTTP version");
422
0
    return CURLE_FAILED_INIT;
423
0
  }
424
425
6.15k
  cf_hc_baller_assign(&ctx->ballers[0], alpn1, ctx->def_transport);
426
6.15k
  ctx->baller_count = 1;
427
6.15k
  CURL_TRC_CF(data, cf, "1st attempt uses %s from %s",
428
6.15k
              ctx->ballers[0].name, source);
429
430
6.15k
  switch(alpn1) {
431
4
  case ALPN_h1:
432
    /* We really want h1, switch off h2 to make it disappear in ALPN */
433
4
    data->state.http_neg.wanted &= (uint8_t)~CURL_HTTP_V2x;
434
4
    break;
435
6.15k
  default:
436
6.15k
    break;
437
6.15k
  }
438
439
6.15k
  return CURLE_OK;
440
6.15k
}
441
442
static void cf_hc_set_baller2(struct Curl_cfilter *cf,
443
                              struct Curl_easy *data)
444
6.15k
{
445
6.15k
  struct cf_hc_ctx *ctx = cf->ctx;
446
6.15k
  enum alpnid alpn2 = ALPN_none, alpn1 = ctx->ballers[0].alpn_id;
447
6.15k
  VERBOSE(const char *source = "HTTPS-RR");
448
449
6.15k
  if(ctx->ballers_complete)
450
0
    return; /* already done */
451
6.15k
  if(!ctx->httpsrr_resolved)
452
0
    return; /* HTTPS-RR pending */
453
454
6.15k
  alpn2 = cf_hc_get_httpsrr_alpn(cf, data, alpn1);
455
6.15k
  if(alpn2 == ALPN_none) {
456
    /* preference is configured and allowed, can we use it? */
457
6.15k
    VERBOSE(source = "preferred version");
458
6.15k
    alpn2 = cf_hc_get_pref_alpn(cf, data, alpn1);
459
6.15k
  }
460
6.15k
  if(alpn2 == ALPN_none) {
461
6.15k
    VERBOSE(source = "wanted versions");
462
6.15k
    alpn2 = cf_hc_get_first_alpn(cf, data,
463
6.15k
                                 data->state.http_neg.wanted,
464
6.15k
                                 alpn1);
465
6.15k
  }
466
467
6.15k
  if(alpn2 != ALPN_none) {
468
0
    cf_hc_baller_assign(&ctx->ballers[1], alpn2, ctx->def_transport);
469
0
    ctx->baller_count = 2;
470
0
    CURL_TRC_CF(data, cf, "2nd attempt uses %s from %s",
471
0
                ctx->ballers[1].name, source);
472
0
  }
473
6.15k
  ctx->ballers_complete = TRUE;
474
6.15k
}
475
476
static CURLcode cf_hc_connect(struct Curl_cfilter *cf,
477
                              struct Curl_easy *data,
478
                              bool *done)
479
6.88k
{
480
6.88k
  struct cf_hc_ctx *ctx = cf->ctx;
481
6.88k
  CURLcode result = CURLE_OK;
482
483
6.88k
  if(cf->connected) {
484
0
    *done = TRUE;
485
0
    return CURLE_OK;
486
0
  }
487
488
6.88k
  *done = FALSE;
489
490
6.88k
  if(!ctx->httpsrr_resolved) {
491
6.15k
    ctx->httpsrr_resolved = Curl_conn_dns_resolved_https(
492
6.15k
      data, cf->sockindex, ctx->destination);
493
6.15k
#ifdef DEBUGBUILD
494
6.15k
    if(!ctx->httpsrr_resolved && getenv("CURL_DBG_AWAIT_HTTPSRR")) {
495
0
      CURL_TRC_CF(data, cf, "awaiting HTTPS-RR");
496
0
      return CURLE_OK;
497
0
    }
498
6.15k
#endif
499
6.15k
  }
500
501
6.88k
  switch(ctx->state) {
502
6.15k
  case CF_HC_RESOLV:
503
6.15k
    ctx->state = CF_HC_INIT;
504
6.15k
    FALLTHROUGH();
505
506
6.15k
  case CF_HC_INIT:
507
6.15k
    DEBUGASSERT(!cf->next);
508
6.15k
    CURL_TRC_CF(data, cf, "connect, init");
509
6.15k
    result = cf_hc_set_baller1(cf, data);
510
6.15k
    if(result) {
511
0
      ctx->result = result;
512
0
      ctx->state = CF_HC_FAILURE;
513
0
      goto out;
514
0
    }
515
6.15k
    cf_hc_set_baller2(cf, data);
516
6.15k
    ctx->started = *Curl_pgrs_now(data);
517
6.15k
    cf_hc_baller_init(&ctx->ballers[0], cf, data);
518
6.15k
    if((ctx->baller_count > 1) || !ctx->ballers_complete) {
519
0
      Curl_expire(data, ctx->soft_eyeballs_timeout_ms, EXPIRE_ALPN_EYEBALLS);
520
0
    }
521
6.15k
    ctx->state = CF_HC_CONNECT;
522
6.15k
    FALLTHROUGH();
523
524
6.88k
  case CF_HC_CONNECT:
525
6.88k
    if(!ctx->ballers_complete)
526
0
      cf_hc_set_baller2(cf, data);
527
528
6.88k
    if(cf_hc_baller_is_connecting(&ctx->ballers[0])) {
529
6.88k
      result = cf_hc_baller_connect(&ctx->ballers[0], cf, data, done);
530
6.88k
      if(!result && *done) {
531
0
        result = baller_connected(cf, data, &ctx->ballers[0]);
532
0
        goto out;
533
0
      }
534
6.88k
    }
535
536
6.88k
    if(time_to_start_baller2(cf, data)) {
537
0
      cf_hc_baller_init(&ctx->ballers[1], cf, data);
538
0
    }
539
540
6.88k
    if(cf_hc_baller_is_connecting(&ctx->ballers[1])) {
541
0
      result = cf_hc_baller_connect(&ctx->ballers[1], cf, data, done);
542
0
      if(!result && *done) {
543
0
        result = baller_connected(cf, data, &ctx->ballers[1]);
544
0
        goto out;
545
0
      }
546
0
    }
547
548
6.88k
    if(ctx->ballers[0].result &&
549
6.13k
       (ctx->ballers[1].result ||
550
6.13k
        (ctx->ballers_complete && (ctx->baller_count < 2)))) {
551
      /* all have failed. we give up */
552
6.13k
      CURL_TRC_CF(data, cf, "connect, all attempts failed");
553
6.13k
      ctx->result = result = ctx->ballers[0].result;
554
6.13k
      ctx->state = CF_HC_FAILURE;
555
6.13k
      goto out;
556
6.13k
    }
557
750
    result = CURLE_OK;
558
750
    *done = FALSE;
559
750
    break;
560
561
0
  case CF_HC_FAILURE:
562
0
    result = ctx->result;
563
0
    cf->connected = FALSE;
564
0
    *done = FALSE;
565
0
    break;
566
567
0
  case CF_HC_SUCCESS:
568
0
    result = CURLE_OK;
569
0
    cf->connected = TRUE;
570
0
    *done = TRUE;
571
0
    break;
572
6.88k
  }
573
574
6.88k
out:
575
6.88k
  CURL_TRC_CF(data, cf, "connect -> %d, done=%d", (int)result, *done);
576
6.88k
  return result;
577
6.88k
}
578
579
static CURLcode cf_hc_shutdown(struct Curl_cfilter *cf,
580
                               struct Curl_easy *data, bool *done)
581
0
{
582
0
  struct cf_hc_ctx *ctx = cf->ctx;
583
0
  size_t i;
584
0
  CURLcode result = CURLE_OK;
585
586
0
  DEBUGASSERT(data);
587
0
  if(cf->connected) {
588
0
    *done = TRUE;
589
0
    return CURLE_OK;
590
0
  }
591
592
  /* shutdown all ballers that have not done so already. If one fails,
593
   * continue shutting down others until all are shutdown. */
594
0
  for(i = 0; i < ctx->baller_count; i++) {
595
0
    struct cf_hc_baller *b = &ctx->ballers[i];
596
0
    bool bdone = FALSE;
597
0
    if(!cf_hc_baller_is_connecting(b) || b->shutdown)
598
0
      continue;
599
0
    b->result = b->cf->cft->do_shutdown(b->cf, data, &bdone);
600
0
    if(b->result || bdone)
601
0
      b->shutdown = TRUE; /* treat a failed shutdown as done */
602
0
  }
603
604
0
  *done = TRUE;
605
0
  for(i = 0; i < ctx->baller_count; i++) {
606
0
    if(!ctx->ballers[i].shutdown)
607
0
      *done = FALSE;
608
0
  }
609
0
  if(*done) {
610
0
    for(i = 0; i < ctx->baller_count; i++) {
611
0
      if(ctx->ballers[i].result)
612
0
        result = ctx->ballers[i].result;
613
0
    }
614
0
  }
615
0
  CURL_TRC_CF(data, cf, "shutdown -> %d, done=%d", (int)result, *done);
616
0
  return result;
617
0
}
618
619
static CURLcode cf_hc_adjust_pollset(struct Curl_cfilter *cf,
620
                                     struct Curl_easy *data,
621
                                     struct easy_pollset *ps)
622
744
{
623
744
  CURLcode result = CURLE_OK;
624
744
  if(!cf->connected) {
625
744
    struct cf_hc_ctx *ctx = cf->ctx;
626
744
    size_t i;
627
628
1.48k
    for(i = 0; (i < ctx->baller_count) && !result; i++) {
629
738
      struct cf_hc_baller *b = &ctx->ballers[i];
630
738
      if(!cf_hc_baller_is_connecting(b))
631
0
        continue;
632
738
      result = Curl_conn_cf_adjust_pollset(b->cf, data, ps);
633
738
    }
634
744
    CURL_TRC_CF(data, cf, "adjust_pollset -> %d, %u socks", (int)result,
635
744
                ps->n);
636
744
  }
637
744
  return result;
638
744
}
639
640
static bool cf_hc_data_pending(struct Curl_cfilter *cf,
641
                               const struct Curl_easy *data)
642
0
{
643
0
  struct cf_hc_ctx *ctx = cf->ctx;
644
0
  size_t i;
645
646
0
  if(cf->connected)
647
0
    return cf->next->cft->has_data_pending(cf->next, data);
648
649
0
  for(i = 0; i < ctx->baller_count; i++)
650
0
    if(cf_hc_baller_data_pending(&ctx->ballers[i], data))
651
0
      return TRUE;
652
0
  return FALSE;
653
0
}
654
655
static CURLcode cf_hc_query(struct Curl_cfilter *cf,
656
                            struct Curl_easy *data,
657
                            int query, int *pres1, void *pres2)
658
7.64k
{
659
7.64k
  struct cf_hc_ctx *ctx = cf->ctx;
660
7.64k
  size_t i;
661
662
7.64k
  if(!cf->connected) {
663
7.64k
    switch(query) {
664
6.89k
    case CF_QUERY_NEED_FLUSH: {
665
7.62k
      for(i = 0; i < ctx->baller_count; i++)
666
726
        if(cf_hc_baller_needs_flush(&ctx->ballers[i], data)) {
667
0
          *pres1 = TRUE;
668
0
          return CURLE_OK;
669
0
        }
670
6.89k
      break;
671
6.89k
    }
672
6.89k
    default:
673
744
      break;
674
7.64k
    }
675
7.64k
  }
676
7.64k
  return cf->next ?
677
0
    cf->next->cft->query(cf->next, data, query, pres1, pres2) :
678
7.64k
    CURLE_UNKNOWN_OPTION;
679
7.64k
}
680
681
static CURLcode cf_hc_cntrl(struct Curl_cfilter *cf,
682
                            struct Curl_easy *data,
683
                            int event, int arg1, void *arg2)
684
12.3k
{
685
12.3k
  struct cf_hc_ctx *ctx = cf->ctx;
686
12.3k
  CURLcode result = CURLE_OK;
687
12.3k
  size_t i;
688
689
12.3k
  if(!cf->connected) {
690
12.3k
    switch(event) {
691
6.14k
    case CF_CTRL_REPORT_STATS:
692
6.42k
      for(i = 0; i < ctx->baller_count; i++) {
693
        /* Make the first baller that connected at network level report */
694
6.13k
        if(Curl_conn_cf_is_ip_connected(ctx->ballers[i].cf, data)) {
695
5.84k
          Curl_conn_cf_cntrl(ctx->ballers[i].cf, data, TRUE,
696
5.84k
                             event, arg1, arg2);
697
5.84k
          break;
698
5.84k
        }
699
6.13k
      }
700
6.14k
      break;
701
6.16k
    default:
702
12.3k
      for(i = 0; i < ctx->baller_count; i++) {
703
6.15k
        result = cf_hc_baller_cntrl(&ctx->ballers[i], data, event, arg1, arg2);
704
6.15k
        if(result && (result != CURLE_AGAIN))
705
0
          goto out;
706
6.15k
      }
707
6.16k
      result = CURLE_OK;
708
6.16k
      break;
709
12.3k
    }
710
12.3k
  }
711
12.3k
out:
712
12.3k
  return result;
713
12.3k
}
714
715
static void cf_hc_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
716
6.16k
{
717
6.16k
  struct cf_hc_ctx *ctx = cf->ctx;
718
719
6.16k
  CURL_TRC_CF(data, cf, "destroy");
720
6.16k
  cf_hc_ctx_destroy(data, ctx);
721
6.16k
}
722
723
struct Curl_cftype Curl_cft_http_connect = {
724
  "HTTPS-CONNECT",
725
  CF_TYPE_SETUP,
726
  CURL_LOG_LVL_NONE,
727
  cf_hc_destroy,
728
  cf_hc_connect,
729
  cf_hc_shutdown,
730
  cf_hc_adjust_pollset,
731
  cf_hc_data_pending,
732
  Curl_cf_def_send,
733
  Curl_cf_def_recv,
734
  cf_hc_cntrl,
735
  Curl_cf_def_conn_is_alive,
736
  Curl_cf_def_conn_keep_alive,
737
  cf_hc_query,
738
};
739
740
static CURLcode cf_hc_create(struct Curl_cfilter **pcf,
741
                             struct Curl_easy *data,
742
                             struct Curl_peer *destination,
743
                             uint8_t def_transport)
744
6.16k
{
745
6.16k
  struct Curl_cfilter *cf = NULL;
746
6.16k
  struct cf_hc_ctx *ctx;
747
6.16k
  CURLcode result = CURLE_OK;
748
749
6.16k
  ctx = curlx_calloc(1, sizeof(*ctx));
750
6.16k
  if(!ctx) {
751
0
    result = CURLE_OUT_OF_MEMORY;
752
0
    goto out;
753
0
  }
754
6.16k
  Curl_peer_link(&ctx->destination, destination);
755
6.16k
  ctx->def_transport = def_transport;
756
6.16k
  ctx->hard_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout;
757
6.16k
  ctx->soft_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout / 2;
758
759
6.16k
  result = Curl_cf_create(&cf, &Curl_cft_http_connect, ctx);
760
6.16k
  if(result)
761
0
    goto out;
762
6.16k
  ctx = NULL;
763
764
6.16k
out:
765
6.16k
  *pcf = result ? NULL : cf;
766
6.16k
  cf_hc_ctx_destroy(data, ctx);
767
6.16k
  return result;
768
6.16k
}
769
770
static CURLcode cf_hc_add(struct Curl_easy *data,
771
                          struct Curl_peer *destination,
772
                          struct connectdata *conn,
773
                          int8_t sockindex,
774
                          uint8_t def_transport)
775
6.16k
{
776
6.16k
  struct Curl_cfilter *cf;
777
6.16k
  CURLcode result = CURLE_OK;
778
779
6.16k
  DEBUGASSERT(data);
780
6.16k
  result = cf_hc_create(&cf, data, destination, def_transport);
781
6.16k
  if(result)
782
0
    goto out;
783
6.16k
  Curl_conn_cf_add(data, conn, sockindex, cf);
784
785
#ifdef USE_HTTPSRR
786
  result = Curl_conn_dns_add_https_resolve(data, cf->conn, cf->sockindex,
787
                                           destination);
788
#endif
789
6.16k
out:
790
6.16k
  return result;
791
6.16k
}
792
793
CURLcode Curl_cf_https_setup(struct Curl_easy *data,
794
                             struct Curl_peer *destination,
795
                             struct connectdata *conn,
796
                             int8_t sockindex)
797
6.23k
{
798
6.23k
  CURLcode result = CURLE_OK;
799
800
6.23k
  DEBUGASSERT(conn->scheme->protocol == CURLPROTO_HTTPS);
801
802
  /* This filter is intended for HTTPS using ALPN and does
803
   * not support HTTPS Eyeballing to a proxy. */
804
6.23k
  if((conn->scheme->protocol != CURLPROTO_HTTPS) ||
805
6.23k
#ifndef CURL_DISABLE_PROXY
806
6.23k
     conn->bits.origin_is_proxy ||
807
6.23k
#endif
808
6.23k
     !conn->bits.tls_enable_alpn)
809
66
    goto out;
810
811
6.16k
  result = cf_hc_add(data, destination, conn, sockindex,
812
6.16k
                     conn->transport_wanted);
813
814
6.23k
out:
815
6.23k
  return result;
816
6.16k
}
817
818
#endif /* !CURL_DISABLE_HTTP */