cf-https-connect.c 21 KB

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