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 #if !defined(CURL_DISABLE_HTTP)
28
29 #include "urldata.h"
30 #include <curl/curl.h>
31 #include "curl_trc.h"
32 #include "cfilters.h"
33 #include "connect.h"
34 #include "hostip.h"
35 #include "multiif.h"
36 #include "cf-https-connect.h"
37 #include "http2.h"
38 #include "vquic/vquic.h"
39
40 /* The last 3 #include files should be in this order */
41 #include "curl_printf.h"
42 #include "curl_memory.h"
43 #include "memdebug.h"
44
45
46 #ifndef ARRAYSIZE
47 #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
48 #endif
49
50 typedef enum {
51 CF_HC_INIT,
52 CF_HC_CONNECT,
53 CF_HC_SUCCESS,
54 CF_HC_FAILURE
55 } cf_hc_state;
56
57 struct cf_hc_baller {
58 const char *name;
59 struct Curl_cfilter *cf;
60 CURLcode result;
61 struct curltime started;
62 int reply_ms;
63 enum alpnid alpn_id;
64 BIT(shutdown);
65 };
66
cf_hc_baller_reset(struct cf_hc_baller * b,struct Curl_easy * data)67 static void cf_hc_baller_reset(struct cf_hc_baller *b,
68 struct Curl_easy *data)
69 {
70 if(b->cf) {
71 Curl_conn_cf_close(b->cf, data);
72 Curl_conn_cf_discard_chain(&b->cf, data);
73 b->cf = NULL;
74 }
75 b->result = CURLE_OK;
76 b->reply_ms = -1;
77 }
78
cf_hc_baller_is_active(struct cf_hc_baller * b)79 static bool cf_hc_baller_is_active(struct cf_hc_baller *b)
80 {
81 return b->cf && !b->result;
82 }
83
cf_hc_baller_has_started(struct cf_hc_baller * b)84 static bool cf_hc_baller_has_started(struct cf_hc_baller *b)
85 {
86 return !!b->cf;
87 }
88
cf_hc_baller_reply_ms(struct cf_hc_baller * b,struct Curl_easy * data)89 static int cf_hc_baller_reply_ms(struct cf_hc_baller *b,
90 struct Curl_easy *data)
91 {
92 if(b->cf && (b->reply_ms < 0))
93 b->cf->cft->query(b->cf, data, CF_QUERY_CONNECT_REPLY_MS,
94 &b->reply_ms, NULL);
95 return b->reply_ms;
96 }
97
cf_hc_baller_data_pending(struct cf_hc_baller * b,const struct Curl_easy * data)98 static bool cf_hc_baller_data_pending(struct cf_hc_baller *b,
99 const struct Curl_easy *data)
100 {
101 return b->cf && !b->result && b->cf->cft->has_data_pending(b->cf, data);
102 }
103
cf_hc_baller_needs_flush(struct cf_hc_baller * b,struct Curl_easy * data)104 static bool cf_hc_baller_needs_flush(struct cf_hc_baller *b,
105 struct Curl_easy *data)
106 {
107 return b->cf && !b->result && Curl_conn_cf_needs_flush(b->cf, data);
108 }
109
cf_hc_baller_cntrl(struct cf_hc_baller * b,struct Curl_easy * data,int event,int arg1,void * arg2)110 static CURLcode cf_hc_baller_cntrl(struct cf_hc_baller *b,
111 struct Curl_easy *data,
112 int event, int arg1, void *arg2)
113 {
114 if(b->cf && !b->result)
115 return Curl_conn_cf_cntrl(b->cf, data, FALSE, event, arg1, arg2);
116 return CURLE_OK;
117 }
118
119 struct cf_hc_ctx {
120 cf_hc_state state;
121 const struct Curl_dns_entry *remotehost;
122 struct curltime started; /* when connect started */
123 CURLcode result; /* overall result */
124 struct cf_hc_baller ballers[2];
125 size_t baller_count;
126 unsigned int soft_eyeballs_timeout_ms;
127 unsigned int hard_eyeballs_timeout_ms;
128 };
129
cf_hc_baller_assign(struct cf_hc_baller * b,enum alpnid alpn_id)130 static void cf_hc_baller_assign(struct cf_hc_baller *b,
131 enum alpnid alpn_id)
132 {
133 b->alpn_id = alpn_id;
134 switch(b->alpn_id) {
135 case ALPN_h3:
136 b->name = "h3";
137 break;
138 case ALPN_h2:
139 b->name = "h2";
140 break;
141 case ALPN_h1:
142 b->name = "h1";
143 break;
144 default:
145 b->result = CURLE_FAILED_INIT;
146 break;
147 }
148 }
149
cf_hc_baller_init(struct cf_hc_baller * b,struct Curl_cfilter * cf,struct Curl_easy * data,int transport)150 static void cf_hc_baller_init(struct cf_hc_baller *b,
151 struct Curl_cfilter *cf,
152 struct Curl_easy *data,
153 int transport)
154 {
155 struct cf_hc_ctx *ctx = cf->ctx;
156 struct Curl_cfilter *save = cf->next;
157
158 cf->next = NULL;
159 b->started = Curl_now();
160 switch(b->alpn_id) {
161 case ALPN_h3:
162 transport = TRNSPRT_QUIC;
163 break;
164 default:
165 break;
166 }
167
168 if(!b->result)
169 b->result = Curl_cf_setup_insert_after(cf, data, ctx->remotehost,
170 transport, CURL_CF_SSL_ENABLE);
171 b->cf = cf->next;
172 cf->next = save;
173 }
174
cf_hc_baller_connect(struct cf_hc_baller * b,struct Curl_cfilter * cf,struct Curl_easy * data,bool * done)175 static CURLcode cf_hc_baller_connect(struct cf_hc_baller *b,
176 struct Curl_cfilter *cf,
177 struct Curl_easy *data,
178 bool *done)
179 {
180 struct Curl_cfilter *save = cf->next;
181
182 cf->next = b->cf;
183 b->result = Curl_conn_cf_connect(cf->next, data, FALSE, done);
184 b->cf = cf->next; /* it might mutate */
185 cf->next = save;
186 return b->result;
187 }
188
cf_hc_reset(struct Curl_cfilter * cf,struct Curl_easy * data)189 static void cf_hc_reset(struct Curl_cfilter *cf, struct Curl_easy *data)
190 {
191 struct cf_hc_ctx *ctx = cf->ctx;
192 size_t i;
193
194 if(ctx) {
195 for(i = 0; i < ctx->baller_count; ++i)
196 cf_hc_baller_reset(&ctx->ballers[i], data);
197 ctx->state = CF_HC_INIT;
198 ctx->result = CURLE_OK;
199 ctx->hard_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout;
200 ctx->soft_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout / 2;
201 }
202 }
203
baller_connected(struct Curl_cfilter * cf,struct Curl_easy * data,struct cf_hc_baller * winner)204 static CURLcode baller_connected(struct Curl_cfilter *cf,
205 struct Curl_easy *data,
206 struct cf_hc_baller *winner)
207 {
208 struct cf_hc_ctx *ctx = cf->ctx;
209 CURLcode result = CURLE_OK;
210 int reply_ms;
211 size_t i;
212
213 DEBUGASSERT(winner->cf);
214 for(i = 0; i < ctx->baller_count; ++i)
215 if(winner != &ctx->ballers[i])
216 cf_hc_baller_reset(&ctx->ballers[i], data);
217
218 reply_ms = cf_hc_baller_reply_ms(winner, data);
219 if(reply_ms >= 0)
220 CURL_TRC_CF(data, cf, "connect+handshake %s: %dms, 1st data: %dms",
221 winner->name, (int)Curl_timediff(Curl_now(), winner->started),
222 reply_ms);
223 else
224 CURL_TRC_CF(data, cf, "deferred handshake %s: %dms",
225 winner->name, (int)Curl_timediff(Curl_now(), winner->started));
226
227 cf->next = winner->cf;
228 winner->cf = NULL;
229
230 switch(cf->conn->alpn) {
231 case CURL_HTTP_VERSION_3:
232 break;
233 case CURL_HTTP_VERSION_2:
234 #ifdef USE_NGHTTP2
235 /* Using nghttp2, we add the filter "below" us, so when the conn
236 * closes, we tear it down for a fresh reconnect */
237 result = Curl_http2_switch_at(cf, data);
238 if(result) {
239 ctx->state = CF_HC_FAILURE;
240 ctx->result = result;
241 return result;
242 }
243 #endif
244 break;
245 default:
246 break;
247 }
248 ctx->state = CF_HC_SUCCESS;
249 cf->connected = TRUE;
250 return result;
251 }
252
253
time_to_start_next(struct Curl_cfilter * cf,struct Curl_easy * data,size_t idx,struct curltime now)254 static bool time_to_start_next(struct Curl_cfilter *cf,
255 struct Curl_easy *data,
256 size_t idx, struct curltime now)
257 {
258 struct cf_hc_ctx *ctx = cf->ctx;
259 timediff_t elapsed_ms;
260 size_t i;
261
262 if(idx >= ctx->baller_count)
263 return FALSE;
264 if(cf_hc_baller_has_started(&ctx->ballers[idx]))
265 return FALSE;
266 for(i = 0; i < idx; i++) {
267 if(!ctx->ballers[i].result)
268 break;
269 }
270 if(i == idx) {
271 CURL_TRC_CF(data, cf, "all previous ballers have failed, time to start "
272 "baller %zu [%s]", idx, ctx->ballers[idx].name);
273 return TRUE;
274 }
275 elapsed_ms = Curl_timediff(now, ctx->started);
276 if(elapsed_ms >= ctx->hard_eyeballs_timeout_ms) {
277 CURL_TRC_CF(data, cf, "hard timeout of %dms reached, starting %s",
278 ctx->hard_eyeballs_timeout_ms, ctx->ballers[idx].name);
279 return TRUE;
280 }
281
282 if((idx > 0) && (elapsed_ms >= ctx->soft_eyeballs_timeout_ms)) {
283 if(cf_hc_baller_reply_ms(&ctx->ballers[idx - 1], data) < 0) {
284 CURL_TRC_CF(data, cf, "soft timeout of %dms reached, %s has not "
285 "seen any data, starting %s",
286 ctx->soft_eyeballs_timeout_ms,
287 ctx->ballers[idx - 1].name, ctx->ballers[idx].name);
288 return TRUE;
289 }
290 /* set the effective hard timeout again */
291 Curl_expire(data, ctx->hard_eyeballs_timeout_ms - elapsed_ms,
292 EXPIRE_ALPN_EYEBALLS);
293 }
294 return FALSE;
295 }
296
cf_hc_connect(struct Curl_cfilter * cf,struct Curl_easy * data,bool blocking,bool * done)297 static CURLcode cf_hc_connect(struct Curl_cfilter *cf,
298 struct Curl_easy *data,
299 bool blocking, bool *done)
300 {
301 struct cf_hc_ctx *ctx = cf->ctx;
302 struct curltime now;
303 CURLcode result = CURLE_OK;
304 size_t i, failed_ballers;
305
306 (void)blocking;
307 if(cf->connected) {
308 *done = TRUE;
309 return CURLE_OK;
310 }
311
312 *done = FALSE;
313 now = Curl_now();
314 switch(ctx->state) {
315 case CF_HC_INIT:
316 DEBUGASSERT(!cf->next);
317 for(i = 0; i < ctx->baller_count; i++)
318 DEBUGASSERT(!ctx->ballers[i].cf);
319 CURL_TRC_CF(data, cf, "connect, init");
320 ctx->started = now;
321 cf_hc_baller_init(&ctx->ballers[0], cf, data, cf->conn->transport);
322 if(ctx->baller_count > 1) {
323 Curl_expire(data, ctx->soft_eyeballs_timeout_ms, EXPIRE_ALPN_EYEBALLS);
324 CURL_TRC_CF(data, cf, "set expire for starting next baller in %ums",
325 ctx->soft_eyeballs_timeout_ms);
326 }
327 ctx->state = CF_HC_CONNECT;
328 FALLTHROUGH();
329
330 case CF_HC_CONNECT:
331 if(cf_hc_baller_is_active(&ctx->ballers[0])) {
332 result = cf_hc_baller_connect(&ctx->ballers[0], cf, data, done);
333 if(!result && *done) {
334 result = baller_connected(cf, data, &ctx->ballers[0]);
335 goto out;
336 }
337 }
338
339 if(time_to_start_next(cf, data, 1, now)) {
340 cf_hc_baller_init(&ctx->ballers[1], cf, data, cf->conn->transport);
341 }
342
343 if((ctx->baller_count > 1) && cf_hc_baller_is_active(&ctx->ballers[1])) {
344 CURL_TRC_CF(data, cf, "connect, check %s", ctx->ballers[1].name);
345 result = cf_hc_baller_connect(&ctx->ballers[1], cf, data, done);
346 if(!result && *done) {
347 result = baller_connected(cf, data, &ctx->ballers[1]);
348 goto out;
349 }
350 }
351
352 failed_ballers = 0;
353 for(i = 0; i < ctx->baller_count; i++) {
354 if(ctx->ballers[i].result)
355 ++failed_ballers;
356 }
357
358 if(failed_ballers == ctx->baller_count) {
359 /* all have failed. we give up */
360 CURL_TRC_CF(data, cf, "connect, all failed");
361 for(i = 0; i < ctx->baller_count; i++) {
362 if(ctx->ballers[i].result) {
363 result = ctx->ballers[i].result;
364 break;
365 }
366 }
367 ctx->state = CF_HC_FAILURE;
368 goto out;
369 }
370 result = CURLE_OK;
371 *done = FALSE;
372 break;
373
374 case CF_HC_FAILURE:
375 result = ctx->result;
376 cf->connected = FALSE;
377 *done = FALSE;
378 break;
379
380 case CF_HC_SUCCESS:
381 result = CURLE_OK;
382 cf->connected = TRUE;
383 *done = TRUE;
384 break;
385 }
386
387 out:
388 CURL_TRC_CF(data, cf, "connect -> %d, done=%d", result, *done);
389 return result;
390 }
391
cf_hc_shutdown(struct Curl_cfilter * cf,struct Curl_easy * data,bool * done)392 static CURLcode cf_hc_shutdown(struct Curl_cfilter *cf,
393 struct Curl_easy *data, bool *done)
394 {
395 struct cf_hc_ctx *ctx = cf->ctx;
396 size_t i;
397 CURLcode result = CURLE_OK;
398
399 DEBUGASSERT(data);
400 if(cf->connected) {
401 *done = TRUE;
402 return CURLE_OK;
403 }
404
405 /* shutdown all ballers that have not done so already. If one fails,
406 * continue shutting down others until all are shutdown. */
407 for(i = 0; i < ctx->baller_count; i++) {
408 struct cf_hc_baller *b = &ctx->ballers[i];
409 bool bdone = FALSE;
410 if(!cf_hc_baller_is_active(b) || b->shutdown)
411 continue;
412 b->result = b->cf->cft->do_shutdown(b->cf, data, &bdone);
413 if(b->result || bdone)
414 b->shutdown = TRUE; /* treat a failed shutdown as done */
415 }
416
417 *done = TRUE;
418 for(i = 0; i < ctx->baller_count; i++) {
419 if(!ctx->ballers[i].shutdown)
420 *done = FALSE;
421 }
422 if(*done) {
423 for(i = 0; i < ctx->baller_count; i++) {
424 if(ctx->ballers[i].result)
425 result = ctx->ballers[i].result;
426 }
427 }
428 CURL_TRC_CF(data, cf, "shutdown -> %d, done=%d", result, *done);
429 return result;
430 }
431
cf_hc_adjust_pollset(struct Curl_cfilter * cf,struct Curl_easy * data,struct easy_pollset * ps)432 static void cf_hc_adjust_pollset(struct Curl_cfilter *cf,
433 struct Curl_easy *data,
434 struct easy_pollset *ps)
435 {
436 if(!cf->connected) {
437 struct cf_hc_ctx *ctx = cf->ctx;
438 size_t i;
439
440 for(i = 0; i < ctx->baller_count; i++) {
441 struct cf_hc_baller *b = &ctx->ballers[i];
442 if(!cf_hc_baller_is_active(b))
443 continue;
444 Curl_conn_cf_adjust_pollset(b->cf, data, ps);
445 }
446 CURL_TRC_CF(data, cf, "adjust_pollset -> %d socks", ps->num);
447 }
448 }
449
cf_hc_data_pending(struct Curl_cfilter * cf,const struct Curl_easy * data)450 static bool cf_hc_data_pending(struct Curl_cfilter *cf,
451 const struct Curl_easy *data)
452 {
453 struct cf_hc_ctx *ctx = cf->ctx;
454 size_t i;
455
456 if(cf->connected)
457 return cf->next->cft->has_data_pending(cf->next, data);
458
459 CURL_TRC_CF((struct Curl_easy *)data, cf, "data_pending");
460 for(i = 0; i < ctx->baller_count; i++)
461 if(cf_hc_baller_data_pending(&ctx->ballers[i], data))
462 return TRUE;
463 return FALSE;
464 }
465
cf_get_max_baller_time(struct Curl_cfilter * cf,struct Curl_easy * data,int query)466 static struct curltime cf_get_max_baller_time(struct Curl_cfilter *cf,
467 struct Curl_easy *data,
468 int query)
469 {
470 struct cf_hc_ctx *ctx = cf->ctx;
471 struct curltime t, tmax;
472 size_t i;
473
474 memset(&tmax, 0, sizeof(tmax));
475 for(i = 0; i < ctx->baller_count; i++) {
476 struct Curl_cfilter *cfb = ctx->ballers[i].cf;
477 memset(&t, 0, sizeof(t));
478 if(cfb && !cfb->cft->query(cfb, data, query, NULL, &t)) {
479 if((t.tv_sec || t.tv_usec) && Curl_timediff_us(t, tmax) > 0)
480 tmax = t;
481 }
482 }
483 return tmax;
484 }
485
cf_hc_query(struct Curl_cfilter * cf,struct Curl_easy * data,int query,int * pres1,void * pres2)486 static CURLcode cf_hc_query(struct Curl_cfilter *cf,
487 struct Curl_easy *data,
488 int query, int *pres1, void *pres2)
489 {
490 struct cf_hc_ctx *ctx = cf->ctx;
491 size_t i;
492
493 if(!cf->connected) {
494 switch(query) {
495 case CF_QUERY_TIMER_CONNECT: {
496 struct curltime *when = pres2;
497 *when = cf_get_max_baller_time(cf, data, CF_QUERY_TIMER_CONNECT);
498 return CURLE_OK;
499 }
500 case CF_QUERY_TIMER_APPCONNECT: {
501 struct curltime *when = pres2;
502 *when = cf_get_max_baller_time(cf, data, CF_QUERY_TIMER_APPCONNECT);
503 return CURLE_OK;
504 }
505 case CF_QUERY_NEED_FLUSH: {
506 for(i = 0; i < ctx->baller_count; i++)
507 if(cf_hc_baller_needs_flush(&ctx->ballers[i], data)) {
508 *pres1 = TRUE;
509 return CURLE_OK;
510 }
511 break;
512 }
513 default:
514 break;
515 }
516 }
517 return cf->next ?
518 cf->next->cft->query(cf->next, data, query, pres1, pres2) :
519 CURLE_UNKNOWN_OPTION;
520 }
521
cf_hc_cntrl(struct Curl_cfilter * cf,struct Curl_easy * data,int event,int arg1,void * arg2)522 static CURLcode cf_hc_cntrl(struct Curl_cfilter *cf,
523 struct Curl_easy *data,
524 int event, int arg1, void *arg2)
525 {
526 struct cf_hc_ctx *ctx = cf->ctx;
527 CURLcode result = CURLE_OK;
528 size_t i;
529
530 if(!cf->connected) {
531 for(i = 0; i < ctx->baller_count; i++) {
532 result = cf_hc_baller_cntrl(&ctx->ballers[i], data, event, arg1, arg2);
533 if(result && (result != CURLE_AGAIN))
534 goto out;
535 }
536 result = CURLE_OK;
537 }
538 out:
539 return result;
540 }
541
cf_hc_close(struct Curl_cfilter * cf,struct Curl_easy * data)542 static void cf_hc_close(struct Curl_cfilter *cf, struct Curl_easy *data)
543 {
544 CURL_TRC_CF(data, cf, "close");
545 cf_hc_reset(cf, data);
546 cf->connected = FALSE;
547
548 if(cf->next) {
549 cf->next->cft->do_close(cf->next, data);
550 Curl_conn_cf_discard_chain(&cf->next, data);
551 }
552 }
553
cf_hc_destroy(struct Curl_cfilter * cf,struct Curl_easy * data)554 static void cf_hc_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
555 {
556 struct cf_hc_ctx *ctx = cf->ctx;
557
558 (void)data;
559 CURL_TRC_CF(data, cf, "destroy");
560 cf_hc_reset(cf, data);
561 Curl_safefree(ctx);
562 }
563
564 struct Curl_cftype Curl_cft_http_connect = {
565 "HTTPS-CONNECT",
566 0,
567 CURL_LOG_LVL_NONE,
568 cf_hc_destroy,
569 cf_hc_connect,
570 cf_hc_close,
571 cf_hc_shutdown,
572 Curl_cf_def_get_host,
573 cf_hc_adjust_pollset,
574 cf_hc_data_pending,
575 Curl_cf_def_send,
576 Curl_cf_def_recv,
577 cf_hc_cntrl,
578 Curl_cf_def_conn_is_alive,
579 Curl_cf_def_conn_keep_alive,
580 cf_hc_query,
581 };
582
cf_hc_create(struct Curl_cfilter ** pcf,struct Curl_easy * data,const struct Curl_dns_entry * remotehost,enum alpnid * alpnids,size_t alpn_count)583 static CURLcode cf_hc_create(struct Curl_cfilter **pcf,
584 struct Curl_easy *data,
585 const struct Curl_dns_entry *remotehost,
586 enum alpnid *alpnids, size_t alpn_count)
587 {
588 struct Curl_cfilter *cf = NULL;
589 struct cf_hc_ctx *ctx;
590 CURLcode result = CURLE_OK;
591 size_t i;
592
593 DEBUGASSERT(alpnids);
594 DEBUGASSERT(alpn_count);
595 DEBUGASSERT(alpn_count <= ARRAYSIZE(ctx->ballers));
596 if(!alpn_count || (alpn_count > ARRAYSIZE(ctx->ballers))) {
597 failf(data, "https-connect filter create with unsupported %zu ALPN ids",
598 alpn_count);
599 return CURLE_FAILED_INIT;
600 }
601
602 ctx = calloc(1, sizeof(*ctx));
603 if(!ctx) {
604 result = CURLE_OUT_OF_MEMORY;
605 goto out;
606 }
607 ctx->remotehost = remotehost;
608 for(i = 0; i < alpn_count; ++i)
609 cf_hc_baller_assign(&ctx->ballers[i], alpnids[i]);
610 for(; i < ARRAYSIZE(ctx->ballers); ++i)
611 ctx->ballers[i].alpn_id = ALPN_none;
612 ctx->baller_count = alpn_count;
613
614 result = Curl_cf_create(&cf, &Curl_cft_http_connect, ctx);
615 CURL_TRC_CF(data, cf, "created with %zu ALPNs -> %d",
616 ctx->baller_count, result);
617 if(result)
618 goto out;
619 ctx = NULL;
620 cf_hc_reset(cf, data);
621
622 out:
623 *pcf = result ? NULL : cf;
624 free(ctx);
625 return result;
626 }
627
cf_http_connect_add(struct Curl_easy * data,struct connectdata * conn,int sockindex,const struct Curl_dns_entry * remotehost,enum alpnid * alpn_ids,size_t alpn_count)628 static CURLcode cf_http_connect_add(struct Curl_easy *data,
629 struct connectdata *conn,
630 int sockindex,
631 const struct Curl_dns_entry *remotehost,
632 enum alpnid *alpn_ids, size_t alpn_count)
633 {
634 struct Curl_cfilter *cf;
635 CURLcode result = CURLE_OK;
636
637 DEBUGASSERT(data);
638 result = cf_hc_create(&cf, data, remotehost, alpn_ids, alpn_count);
639 if(result)
640 goto out;
641 Curl_conn_cf_add(data, conn, sockindex, cf);
642 out:
643 return result;
644 }
645
Curl_cf_https_setup(struct Curl_easy * data,struct connectdata * conn,int sockindex,const struct Curl_dns_entry * remotehost)646 CURLcode Curl_cf_https_setup(struct Curl_easy *data,
647 struct connectdata *conn,
648 int sockindex,
649 const struct Curl_dns_entry *remotehost)
650 {
651 enum alpnid alpn_ids[2];
652 size_t alpn_count = 0;
653 CURLcode result = CURLE_OK;
654
655 (void)sockindex;
656 (void)remotehost;
657
658 if(conn->bits.tls_enable_alpn) {
659 switch(data->state.httpwant) {
660 case CURL_HTTP_VERSION_NONE:
661 /* No preferences by transfer setup. Choose best defaults */
662 #ifdef USE_HTTPSRR
663 if(conn->dns_entry && conn->dns_entry->hinfo &&
664 !conn->dns_entry->hinfo->no_def_alpn) {
665 size_t i, j;
666 for(i = 0; i < ARRAYSIZE(conn->dns_entry->hinfo->alpns) &&
667 alpn_count < ARRAYSIZE(alpn_ids); ++i) {
668 bool present = FALSE;
669 enum alpnid alpn = conn->dns_entry->hinfo->alpns[i];
670 for(j = 0; j < alpn_count; ++j) {
671 if(alpn == alpn_ids[j]) {
672 present = TRUE;
673 break;
674 }
675 }
676 if(!present) {
677 switch(alpn) {
678 case ALPN_h3:
679 if(Curl_conn_may_http3(data, conn))
680 break; /* not possible */
681 FALLTHROUGH();
682 case ALPN_h2:
683 case ALPN_h1:
684 alpn_ids[alpn_count++] = alpn;
685 break;
686 default: /* ignore */
687 break;
688 }
689 }
690 }
691 }
692 #endif
693 if(!alpn_count)
694 alpn_ids[alpn_count++] = ALPN_h2;
695 break;
696 case CURL_HTTP_VERSION_3ONLY:
697 result = Curl_conn_may_http3(data, conn);
698 if(result) /* cannot do it */
699 goto out;
700 alpn_ids[alpn_count++] = ALPN_h3;
701 break;
702 case CURL_HTTP_VERSION_3:
703 /* We assume that silently not even trying H3 is ok here */
704 /* TODO: should we fail instead? */
705 if(Curl_conn_may_http3(data, conn) == CURLE_OK)
706 alpn_ids[alpn_count++] = ALPN_h3;
707 alpn_ids[alpn_count++] = ALPN_h2;
708 break;
709 case CURL_HTTP_VERSION_2_0:
710 case CURL_HTTP_VERSION_2TLS:
711 case CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE:
712 alpn_ids[alpn_count++] = ALPN_h2;
713 break;
714 case CURL_HTTP_VERSION_1_0:
715 case CURL_HTTP_VERSION_1_1:
716 alpn_ids[alpn_count++] = ALPN_h1;
717 break;
718 default:
719 alpn_ids[alpn_count++] = ALPN_h2;
720 break;
721 }
722 }
723
724 /* If we identified ALPNs to use, install our filter. Otherwise,
725 * install nothing, so our call will use a default connect setup. */
726 if(alpn_count) {
727 result = cf_http_connect_add(data, conn, sockindex, remotehost,
728 alpn_ids, alpn_count);
729 }
730
731 out:
732 return result;
733 }
734
735 #endif /* !defined(CURL_DISABLE_HTTP) */
736