• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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