VirtualBox

source: vbox/trunk/src/libs/curl-8.0.1/lib/vquic/curl_ngtcp2.c@ 99797

Last change on this file since 99797 was 99459, checked in by vboxsync, 2 years ago

setting missed svn:sync-process property on curl-8.0.1 files

File size: 74.1 KB
Line 
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
25#include "curl_setup.h"
26
27#ifdef USE_NGTCP2
28#include <ngtcp2/ngtcp2.h>
29#include <nghttp3/nghttp3.h>
30
31#ifdef USE_OPENSSL
32#include <openssl/err.h>
33#ifdef OPENSSL_IS_BORINGSSL
34#include <ngtcp2/ngtcp2_crypto_boringssl.h>
35#else
36#include <ngtcp2/ngtcp2_crypto_openssl.h>
37#endif
38#include "vtls/openssl.h"
39#elif defined(USE_GNUTLS)
40#include <ngtcp2/ngtcp2_crypto_gnutls.h>
41#include "vtls/gtls.h"
42#elif defined(USE_WOLFSSL)
43#include <ngtcp2/ngtcp2_crypto_wolfssl.h>
44#include "vtls/wolfssl.h"
45#endif
46
47#include "urldata.h"
48#include "sendf.h"
49#include "strdup.h"
50#include "rand.h"
51#include "multiif.h"
52#include "strcase.h"
53#include "cfilters.h"
54#include "cf-socket.h"
55#include "connect.h"
56#include "progress.h"
57#include "strerror.h"
58#include "dynbuf.h"
59#include "select.h"
60#include "vquic.h"
61#include "vquic_int.h"
62#include "h2h3.h"
63#include "vtls/keylog.h"
64#include "vtls/vtls.h"
65#include "curl_ngtcp2.h"
66
67#include "warnless.h"
68
69/* The last 3 #include files should be in this order */
70#include "curl_printf.h"
71#include "curl_memory.h"
72#include "memdebug.h"
73
74
75#define H3_ALPN_H3_29 "\x5h3-29"
76#define H3_ALPN_H3 "\x2h3"
77
78/*
79 * This holds outgoing HTTP/3 stream data that is used by nghttp3 until acked.
80 * It is used as a circular buffer. Add new bytes at the end until it reaches
81 * the far end, then start over at index 0 again.
82 */
83
84#define H3_SEND_SIZE (256*1024)
85struct h3out {
86 uint8_t buf[H3_SEND_SIZE];
87 size_t used; /* number of bytes used in the buffer */
88 size_t windex; /* index in the buffer where to start writing the next
89 data block */
90};
91
92#define QUIC_MAX_STREAMS (256*1024)
93#define QUIC_MAX_DATA (1*1024*1024)
94#define QUIC_IDLE_TIMEOUT (60*NGTCP2_SECONDS)
95#define QUIC_HANDSHAKE_TIMEOUT (10*NGTCP2_SECONDS)
96
97#ifdef USE_OPENSSL
98#define QUIC_CIPHERS \
99 "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \
100 "POLY1305_SHA256:TLS_AES_128_CCM_SHA256"
101#define QUIC_GROUPS "P-256:X25519:P-384:P-521"
102#elif defined(USE_GNUTLS)
103#define QUIC_PRIORITY \
104 "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:" \
105 "+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:+GROUP-SECP256R1:" \
106 "+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1:" \
107 "%DISABLE_TLS13_COMPAT_MODE"
108#elif defined(USE_WOLFSSL)
109#define QUIC_CIPHERS \
110 "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \
111 "POLY1305_SHA256:TLS_AES_128_CCM_SHA256"
112#define QUIC_GROUPS "P-256:P-384:P-521"
113#endif
114
115
116/*
117 * Store ngtcp2 version info in this buffer.
118 */
119void Curl_ngtcp2_ver(char *p, size_t len)
120{
121 const ngtcp2_info *ng2 = ngtcp2_version(0);
122 const nghttp3_info *ht3 = nghttp3_version(0);
123 (void)msnprintf(p, len, "ngtcp2/%s nghttp3/%s",
124 ng2->version_str, ht3->version_str);
125}
126
127struct cf_ngtcp2_ctx {
128 struct cf_quic_ctx q;
129 ngtcp2_path connected_path;
130 ngtcp2_conn *qconn;
131 ngtcp2_cid dcid;
132 ngtcp2_cid scid;
133 uint32_t version;
134 ngtcp2_settings settings;
135 ngtcp2_transport_params transport_params;
136 ngtcp2_connection_close_error last_error;
137 ngtcp2_crypto_conn_ref conn_ref;
138#ifdef USE_OPENSSL
139 SSL_CTX *sslctx;
140 SSL *ssl;
141#elif defined(USE_GNUTLS)
142 struct gtls_instance *gtls;
143#elif defined(USE_WOLFSSL)
144 WOLFSSL_CTX *sslctx;
145 WOLFSSL *ssl;
146#endif
147 struct cf_call_data call_data;
148 nghttp3_conn *h3conn;
149 nghttp3_settings h3settings;
150 int qlogfd;
151 struct curltime started_at; /* time the current attempt started */
152 struct curltime handshake_at; /* time connect handshake finished */
153 struct curltime first_byte_at; /* when first byte was recvd */
154 struct curltime reconnect_at; /* time the next attempt should start */
155 BIT(got_first_byte); /* if first byte was received */
156};
157
158/* How to access `call_data` from a cf_ngtcp2 filter */
159#define CF_CTX_CALL_DATA(cf) \
160 ((struct cf_ngtcp2_ctx *)(cf)->ctx)->call_data
161
162
163/* ngtcp2 default congestion controller does not perform pacing. Limit
164 the maximum packet burst to MAX_PKT_BURST packets. */
165#define MAX_PKT_BURST 10
166
167static CURLcode cf_process_ingress(struct Curl_cfilter *cf,
168 struct Curl_easy *data);
169static CURLcode cf_flush_egress(struct Curl_cfilter *cf,
170 struct Curl_easy *data);
171static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id,
172 uint64_t datalen, void *user_data,
173 void *stream_user_data);
174
175static ngtcp2_conn *get_conn(ngtcp2_crypto_conn_ref *conn_ref)
176{
177 struct Curl_cfilter *cf = conn_ref->user_data;
178 struct cf_ngtcp2_ctx *ctx = cf->ctx;
179 return ctx->qconn;
180}
181
182static ngtcp2_tstamp timestamp(void)
183{
184 struct curltime ct = Curl_now();
185 return ct.tv_sec * NGTCP2_SECONDS + ct.tv_usec * NGTCP2_MICROSECONDS;
186}
187
188#ifdef DEBUG_NGTCP2
189static void quic_printf(void *user_data, const char *fmt, ...)
190{
191 struct Curl_cfilter *cf = user_data;
192 struct cf_ngtcp2_ctx *ctx = cf->ctx;
193
194 (void)ctx; /* TODO: need an easy handle to infof() message */
195 va_list ap;
196 va_start(ap, fmt);
197 vfprintf(stderr, fmt, ap);
198 va_end(ap);
199 fprintf(stderr, "\n");
200}
201#endif
202
203static void qlog_callback(void *user_data, uint32_t flags,
204 const void *data, size_t datalen)
205{
206 struct Curl_cfilter *cf = user_data;
207 struct cf_ngtcp2_ctx *ctx = cf->ctx;
208 (void)flags;
209 if(ctx->qlogfd != -1) {
210 ssize_t rc = write(ctx->qlogfd, data, datalen);
211 if(rc == -1) {
212 /* on write error, stop further write attempts */
213 close(ctx->qlogfd);
214 ctx->qlogfd = -1;
215 }
216 }
217
218}
219
220static void quic_settings(struct cf_ngtcp2_ctx *ctx,
221 struct Curl_easy *data)
222{
223 ngtcp2_settings *s = &ctx->settings;
224 ngtcp2_transport_params *t = &ctx->transport_params;
225 size_t stream_win_size = CURL_MAX_READ_SIZE;
226
227 ngtcp2_settings_default(s);
228 ngtcp2_transport_params_default(t);
229#ifdef DEBUG_NGTCP2
230 s->log_printf = quic_printf;
231#else
232 s->log_printf = NULL;
233#endif
234
235 (void)data;
236 s->initial_ts = timestamp();
237 s->handshake_timeout = QUIC_HANDSHAKE_TIMEOUT;
238 s->max_window = 100 * stream_win_size;
239 s->max_stream_window = stream_win_size;
240
241 t->initial_max_data = 10 * stream_win_size;
242 t->initial_max_stream_data_bidi_local = stream_win_size;
243 t->initial_max_stream_data_bidi_remote = stream_win_size;
244 t->initial_max_stream_data_uni = stream_win_size;
245 t->initial_max_streams_bidi = QUIC_MAX_STREAMS;
246 t->initial_max_streams_uni = QUIC_MAX_STREAMS;
247 t->max_idle_timeout = QUIC_IDLE_TIMEOUT;
248 if(ctx->qlogfd != -1) {
249 s->qlog.write = qlog_callback;
250 }
251}
252
253#ifdef USE_OPENSSL
254static void keylog_callback(const SSL *ssl, const char *line)
255{
256 (void)ssl;
257 Curl_tls_keylog_write_line(line);
258}
259#elif defined(USE_GNUTLS)
260static int keylog_callback(gnutls_session_t session, const char *label,
261 const gnutls_datum_t *secret)
262{
263 gnutls_datum_t crandom;
264 gnutls_datum_t srandom;
265
266 gnutls_session_get_random(session, &crandom, &srandom);
267 if(crandom.size != 32) {
268 return -1;
269 }
270
271 Curl_tls_keylog_write(label, crandom.data, secret->data, secret->size);
272 return 0;
273}
274#elif defined(USE_WOLFSSL)
275#if defined(HAVE_SECRET_CALLBACK)
276static void keylog_callback(const WOLFSSL *ssl, const char *line)
277{
278 (void)ssl;
279 Curl_tls_keylog_write_line(line);
280}
281#endif
282#endif
283
284static int init_ngh3_conn(struct Curl_cfilter *cf);
285
286#ifdef USE_OPENSSL
287static CURLcode quic_ssl_ctx(SSL_CTX **pssl_ctx,
288 struct Curl_cfilter *cf, struct Curl_easy *data)
289{
290 struct connectdata *conn = cf->conn;
291 CURLcode result = CURLE_FAILED_INIT;
292 SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method());
293
294 if(!ssl_ctx) {
295 result = CURLE_OUT_OF_MEMORY;
296 goto out;
297 }
298
299#ifdef OPENSSL_IS_BORINGSSL
300 if(ngtcp2_crypto_boringssl_configure_client_context(ssl_ctx) != 0) {
301 failf(data, "ngtcp2_crypto_boringssl_configure_client_context failed");
302 goto out;
303 }
304#else
305 if(ngtcp2_crypto_openssl_configure_client_context(ssl_ctx) != 0) {
306 failf(data, "ngtcp2_crypto_openssl_configure_client_context failed");
307 goto out;
308 }
309#endif
310
311 SSL_CTX_set_default_verify_paths(ssl_ctx);
312
313#ifdef OPENSSL_IS_BORINGSSL
314 if(SSL_CTX_set1_curves_list(ssl_ctx, QUIC_GROUPS) != 1) {
315 failf(data, "SSL_CTX_set1_curves_list failed");
316 goto out;
317 }
318#else
319 if(SSL_CTX_set_ciphersuites(ssl_ctx, QUIC_CIPHERS) != 1) {
320 char error_buffer[256];
321 ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer));
322 failf(data, "SSL_CTX_set_ciphersuites: %s", error_buffer);
323 goto out;
324 }
325
326 if(SSL_CTX_set1_groups_list(ssl_ctx, QUIC_GROUPS) != 1) {
327 failf(data, "SSL_CTX_set1_groups_list failed");
328 goto out;
329 }
330#endif
331
332 /* Open the file if a TLS or QUIC backend has not done this before. */
333 Curl_tls_keylog_open();
334 if(Curl_tls_keylog_enabled()) {
335 SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
336 }
337
338 result = Curl_ssl_setup_x509_store(cf, data, ssl_ctx);
339 if(result)
340 goto out;
341
342 /* OpenSSL always tries to verify the peer, this only says whether it should
343 * fail to connect if the verification fails, or if it should continue
344 * anyway. In the latter case the result of the verification is checked with
345 * SSL_get_verify_result() below. */
346 SSL_CTX_set_verify(ssl_ctx, conn->ssl_config.verifypeer ?
347 SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL);
348
349 /* give application a chance to interfere with SSL set up. */
350 if(data->set.ssl.fsslctx) {
351 Curl_set_in_callback(data, true);
352 result = (*data->set.ssl.fsslctx)(data, ssl_ctx,
353 data->set.ssl.fsslctxp);
354 Curl_set_in_callback(data, false);
355 if(result) {
356 failf(data, "error signaled by ssl ctx callback");
357 goto out;
358 }
359 }
360 result = CURLE_OK;
361
362out:
363 *pssl_ctx = result? NULL : ssl_ctx;
364 if(result && ssl_ctx)
365 SSL_CTX_free(ssl_ctx);
366 return result;
367}
368
369static CURLcode quic_set_client_cert(struct Curl_cfilter *cf,
370 struct Curl_easy *data)
371{
372 struct cf_ngtcp2_ctx *ctx = cf->ctx;
373 SSL_CTX *ssl_ctx = ctx->sslctx;
374 const struct ssl_config_data *ssl_config;
375
376 ssl_config = Curl_ssl_get_config(data, FIRSTSOCKET);
377 DEBUGASSERT(ssl_config);
378
379 if(ssl_config->primary.clientcert || ssl_config->primary.cert_blob
380 || ssl_config->cert_type) {
381 return Curl_ossl_set_client_cert(
382 data, ssl_ctx, ssl_config->primary.clientcert,
383 ssl_config->primary.cert_blob, ssl_config->cert_type,
384 ssl_config->key, ssl_config->key_blob,
385 ssl_config->key_type, ssl_config->key_passwd);
386 }
387
388 return CURLE_OK;
389}
390
391/** SSL callbacks ***/
392
393static CURLcode quic_init_ssl(struct Curl_cfilter *cf,
394 struct Curl_easy *data)
395{
396 struct cf_ngtcp2_ctx *ctx = cf->ctx;
397 const uint8_t *alpn = NULL;
398 size_t alpnlen = 0;
399
400 (void)data;
401 DEBUGASSERT(!ctx->ssl);
402 ctx->ssl = SSL_new(ctx->sslctx);
403
404 SSL_set_app_data(ctx->ssl, &ctx->conn_ref);
405 SSL_set_connect_state(ctx->ssl);
406 SSL_set_quic_use_legacy_codepoint(ctx->ssl, 0);
407
408 alpn = (const uint8_t *)H3_ALPN_H3_29 H3_ALPN_H3;
409 alpnlen = sizeof(H3_ALPN_H3_29) - 1 + sizeof(H3_ALPN_H3) - 1;
410 if(alpn)
411 SSL_set_alpn_protos(ctx->ssl, alpn, (int)alpnlen);
412
413 /* set SNI */
414 SSL_set_tlsext_host_name(ctx->ssl, cf->conn->host.name);
415 return CURLE_OK;
416}
417#elif defined(USE_GNUTLS)
418static CURLcode quic_init_ssl(struct Curl_cfilter *cf,
419 struct Curl_easy *data)
420{
421 struct cf_ngtcp2_ctx *ctx = cf->ctx;
422 CURLcode result;
423 gnutls_datum_t alpn[2];
424 /* this will need some attention when HTTPS proxy over QUIC get fixed */
425 const char * const hostname = cf->conn->host.name;
426 long * const pverifyresult = &data->set.ssl.certverifyresult;
427 int rc;
428
429 DEBUGASSERT(ctx->gtls == NULL);
430 ctx->gtls = calloc(1, sizeof(*(ctx->gtls)));
431 if(!ctx->gtls)
432 return CURLE_OUT_OF_MEMORY;
433
434 result = gtls_client_init(data, &cf->conn->ssl_config, &data->set.ssl,
435 hostname, ctx->gtls, pverifyresult);
436 if(result)
437 return result;
438
439 gnutls_session_set_ptr(ctx->gtls->session, &ctx->conn_ref);
440
441 if(ngtcp2_crypto_gnutls_configure_client_session(ctx->gtls->session) != 0) {
442 DEBUGF(LOG_CF(data, cf,
443 "ngtcp2_crypto_gnutls_configure_client_session failed\n"));
444 return CURLE_QUIC_CONNECT_ERROR;
445 }
446
447 rc = gnutls_priority_set_direct(ctx->gtls->session, QUIC_PRIORITY, NULL);
448 if(rc < 0) {
449 DEBUGF(LOG_CF(data, cf, "gnutls_priority_set_direct failed: %s\n",
450 gnutls_strerror(rc)));
451 return CURLE_QUIC_CONNECT_ERROR;
452 }
453
454 /* Open the file if a TLS or QUIC backend has not done this before. */
455 Curl_tls_keylog_open();
456 if(Curl_tls_keylog_enabled()) {
457 gnutls_session_set_keylog_function(ctx->gtls->session, keylog_callback);
458 }
459
460 /* strip the first byte (the length) from NGHTTP3_ALPN_H3 */
461 alpn[0].data = (unsigned char *)H3_ALPN_H3_29 + 1;
462 alpn[0].size = sizeof(H3_ALPN_H3_29) - 2;
463 alpn[1].data = (unsigned char *)H3_ALPN_H3 + 1;
464 alpn[1].size = sizeof(H3_ALPN_H3) - 2;
465
466 gnutls_alpn_set_protocols(ctx->gtls->session,
467 alpn, 2, GNUTLS_ALPN_MANDATORY);
468 return CURLE_OK;
469}
470#elif defined(USE_WOLFSSL)
471
472static CURLcode quic_ssl_ctx(WOLFSSL_CTX **pssl_ctx,
473 struct Curl_cfilter *cf, struct Curl_easy *data)
474{
475 struct connectdata *conn = cf->conn;
476 CURLcode result = CURLE_FAILED_INIT;
477 WOLFSSL_CTX *ssl_ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method());
478
479 if(!ssl_ctx) {
480 result = CURLE_OUT_OF_MEMORY;
481 goto out;
482 }
483
484 if(ngtcp2_crypto_wolfssl_configure_client_context(ssl_ctx) != 0) {
485 failf(data, "ngtcp2_crypto_wolfssl_configure_client_context failed");
486 goto out;
487 }
488
489 wolfSSL_CTX_set_default_verify_paths(ssl_ctx);
490
491 if(wolfSSL_CTX_set_cipher_list(ssl_ctx, QUIC_CIPHERS) != 1) {
492 char error_buffer[256];
493 ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer));
494 failf(data, "SSL_CTX_set_ciphersuites: %s", error_buffer);
495 goto out;
496 }
497
498 if(wolfSSL_CTX_set1_groups_list(ssl_ctx, (char *)QUIC_GROUPS) != 1) {
499 failf(data, "SSL_CTX_set1_groups_list failed");
500 goto out;
501 }
502
503 /* Open the file if a TLS or QUIC backend has not done this before. */
504 Curl_tls_keylog_open();
505 if(Curl_tls_keylog_enabled()) {
506#if defined(HAVE_SECRET_CALLBACK)
507 wolfSSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
508#else
509 failf(data, "wolfSSL was built without keylog callback");
510 goto out;
511#endif
512 }
513
514 if(conn->ssl_config.verifypeer) {
515 const char * const ssl_cafile = conn->ssl_config.CAfile;
516 const char * const ssl_capath = conn->ssl_config.CApath;
517
518 wolfSSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
519 if(conn->ssl_config.CAfile || conn->ssl_config.CApath) {
520 /* tell wolfSSL where to find CA certificates that are used to verify
521 the server's certificate. */
522 if(!wolfSSL_CTX_load_verify_locations(ssl_ctx, ssl_cafile, ssl_capath)) {
523 /* Fail if we insist on successfully verifying the server. */
524 failf(data, "error setting certificate verify locations:"
525 " CAfile: %s CApath: %s",
526 ssl_cafile ? ssl_cafile : "none",
527 ssl_capath ? ssl_capath : "none");
528 goto out;
529 }
530 infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
531 infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
532 }
533#ifdef CURL_CA_FALLBACK
534 else {
535 /* verifying the peer without any CA certificates won't work so
536 use wolfssl's built-in default as fallback */
537 wolfSSL_CTX_set_default_verify_paths(ssl_ctx);
538 }
539#endif
540 }
541 else {
542 wolfSSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL);
543 }
544
545 /* give application a chance to interfere with SSL set up. */
546 if(data->set.ssl.fsslctx) {
547 Curl_set_in_callback(data, true);
548 result = (*data->set.ssl.fsslctx)(data, ssl_ctx,
549 data->set.ssl.fsslctxp);
550 Curl_set_in_callback(data, false);
551 if(result) {
552 failf(data, "error signaled by ssl ctx callback");
553 goto out;
554 }
555 }
556 result = CURLE_OK;
557
558out:
559 *pssl_ctx = result? NULL : ssl_ctx;
560 if(result && ssl_ctx)
561 SSL_CTX_free(ssl_ctx);
562 return result;
563}
564
565/** SSL callbacks ***/
566
567static CURLcode quic_init_ssl(struct Curl_cfilter *cf,
568 struct Curl_easy *data)
569{
570 struct cf_ngtcp2_ctx *ctx = cf->ctx;
571 const uint8_t *alpn = NULL;
572 size_t alpnlen = 0;
573 /* this will need some attention when HTTPS proxy over QUIC get fixed */
574 const char * const hostname = cf->conn->host.name;
575
576 (void)data;
577 DEBUGASSERT(!ctx->ssl);
578 ctx->ssl = wolfSSL_new(ctx->sslctx);
579
580 wolfSSL_set_app_data(ctx->ssl, &ctx->conn_ref);
581 wolfSSL_set_connect_state(ctx->ssl);
582 wolfSSL_set_quic_use_legacy_codepoint(ctx->ssl, 0);
583
584 alpn = (const uint8_t *)H3_ALPN_H3_29 H3_ALPN_H3;
585 alpnlen = sizeof(H3_ALPN_H3_29) - 1 + sizeof(H3_ALPN_H3) - 1;
586 if(alpn)
587 wolfSSL_set_alpn_protos(ctx->ssl, alpn, (int)alpnlen);
588
589 /* set SNI */
590 wolfSSL_UseSNI(ctx->ssl, WOLFSSL_SNI_HOST_NAME,
591 hostname, (unsigned short)strlen(hostname));
592
593 return CURLE_OK;
594}
595#endif /* defined(USE_WOLFSSL) */
596
597static int cb_handshake_completed(ngtcp2_conn *tconn, void *user_data)
598{
599 (void)user_data;
600 (void)tconn;
601 return 0;
602}
603
604static void report_consumed_data(struct Curl_cfilter *cf,
605 struct Curl_easy *data,
606 size_t consumed)
607{
608 struct HTTP *stream = data->req.p.http;
609 struct cf_ngtcp2_ctx *ctx = cf->ctx;
610
611 /* the HTTP/1.1 response headers are written to the buffer, but
612 * consuming those does not count against flow control. */
613 if(stream->recv_buf_nonflow) {
614 if(consumed >= stream->recv_buf_nonflow) {
615 consumed -= stream->recv_buf_nonflow;
616 stream->recv_buf_nonflow = 0;
617 }
618 else {
619 stream->recv_buf_nonflow -= consumed;
620 consumed = 0;
621 }
622 }
623 if(consumed > 0) {
624 DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] consumed %zu DATA bytes",
625 stream->stream3_id, consumed));
626 ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream->stream3_id,
627 consumed);
628 ngtcp2_conn_extend_max_offset(ctx->qconn, consumed);
629 }
630 if(!stream->closed && data->state.drain
631 && !stream->memlen
632 && !Curl_dyn_len(&stream->overflow)) {
633 /* nothing buffered any more */
634 data->state.drain = 0;
635 }
636}
637
638static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags,
639 int64_t stream_id, uint64_t offset,
640 const uint8_t *buf, size_t buflen,
641 void *user_data, void *stream_user_data)
642{
643 struct Curl_cfilter *cf = user_data;
644 struct cf_ngtcp2_ctx *ctx = cf->ctx;
645 nghttp3_ssize nconsumed;
646 int fin = (flags & NGTCP2_STREAM_DATA_FLAG_FIN) ? 1 : 0;
647 struct Curl_easy *data = stream_user_data;
648 (void)offset;
649 (void)data;
650
651 nconsumed =
652 nghttp3_conn_read_stream(ctx->h3conn, stream_id, buf, buflen, fin);
653 DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] read_stream(len=%zu) -> %zd",
654 stream_id, buflen, nconsumed));
655 if(nconsumed < 0) {
656 ngtcp2_connection_close_error_set_application_error(
657 &ctx->last_error,
658 nghttp3_err_infer_quic_app_error_code((int)nconsumed), NULL, 0);
659 return NGTCP2_ERR_CALLBACK_FAILURE;
660 }
661
662 /* number of bytes inside buflen which consists of framing overhead
663 * including QPACK HEADERS. In other words, it does not consume payload of
664 * DATA frame. */
665 ngtcp2_conn_extend_max_stream_offset(tconn, stream_id, nconsumed);
666 ngtcp2_conn_extend_max_offset(tconn, nconsumed);
667
668 return 0;
669}
670
671static int
672cb_acked_stream_data_offset(ngtcp2_conn *tconn, int64_t stream_id,
673 uint64_t offset, uint64_t datalen, void *user_data,
674 void *stream_user_data)
675{
676 struct Curl_cfilter *cf = user_data;
677 struct cf_ngtcp2_ctx *ctx = cf->ctx;
678 int rv;
679 (void)stream_id;
680 (void)tconn;
681 (void)offset;
682 (void)datalen;
683 (void)stream_user_data;
684
685 rv = nghttp3_conn_add_ack_offset(ctx->h3conn, stream_id, datalen);
686 if(rv) {
687 return NGTCP2_ERR_CALLBACK_FAILURE;
688 }
689
690 return 0;
691}
692
693static int cb_stream_close(ngtcp2_conn *tconn, uint32_t flags,
694 int64_t stream3_id, uint64_t app_error_code,
695 void *user_data, void *stream_user_data)
696{
697 struct Curl_cfilter *cf = user_data;
698 struct Curl_easy *data = stream_user_data;
699 struct cf_ngtcp2_ctx *ctx = cf->ctx;
700 int rv;
701
702 (void)tconn;
703 (void)data;
704 /* stream is closed... */
705
706 if(!(flags & NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET)) {
707 app_error_code = NGHTTP3_H3_NO_ERROR;
708 }
709
710 rv = nghttp3_conn_close_stream(ctx->h3conn, stream3_id,
711 app_error_code);
712 DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] quic close(err=%"
713 PRIu64 ") -> %d", stream3_id, app_error_code, rv));
714 if(rv) {
715 ngtcp2_connection_close_error_set_application_error(
716 &ctx->last_error, nghttp3_err_infer_quic_app_error_code(rv), NULL, 0);
717 return NGTCP2_ERR_CALLBACK_FAILURE;
718 }
719
720 return 0;
721}
722
723static int cb_stream_reset(ngtcp2_conn *tconn, int64_t stream_id,
724 uint64_t final_size, uint64_t app_error_code,
725 void *user_data, void *stream_user_data)
726{
727 struct Curl_cfilter *cf = user_data;
728 struct cf_ngtcp2_ctx *ctx = cf->ctx;
729 struct Curl_easy *data = stream_user_data;
730 int rv;
731 (void)tconn;
732 (void)final_size;
733 (void)app_error_code;
734 (void)data;
735
736 rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id);
737 DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] reset -> %d", stream_id, rv));
738 if(rv) {
739 return NGTCP2_ERR_CALLBACK_FAILURE;
740 }
741
742 return 0;
743}
744
745static int cb_stream_stop_sending(ngtcp2_conn *tconn, int64_t stream_id,
746 uint64_t app_error_code, void *user_data,
747 void *stream_user_data)
748{
749 struct Curl_cfilter *cf = user_data;
750 struct cf_ngtcp2_ctx *ctx = cf->ctx;
751 int rv;
752 (void)tconn;
753 (void)app_error_code;
754 (void)stream_user_data;
755
756 rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id);
757 if(rv) {
758 return NGTCP2_ERR_CALLBACK_FAILURE;
759 }
760
761 return 0;
762}
763
764static int cb_extend_max_local_streams_bidi(ngtcp2_conn *tconn,
765 uint64_t max_streams,
766 void *user_data)
767{
768 (void)tconn;
769 (void)max_streams;
770 (void)user_data;
771
772 return 0;
773}
774
775static int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t stream_id,
776 uint64_t max_data, void *user_data,
777 void *stream_user_data)
778{
779 struct Curl_cfilter *cf = user_data;
780 struct cf_ngtcp2_ctx *ctx = cf->ctx;
781 int rv;
782 (void)tconn;
783 (void)max_data;
784 (void)stream_user_data;
785
786 rv = nghttp3_conn_unblock_stream(ctx->h3conn, stream_id);
787 if(rv) {
788 return NGTCP2_ERR_CALLBACK_FAILURE;
789 }
790
791 return 0;
792}
793
794static void cb_rand(uint8_t *dest, size_t destlen,
795 const ngtcp2_rand_ctx *rand_ctx)
796{
797 CURLcode result;
798 (void)rand_ctx;
799
800 result = Curl_rand(NULL, dest, destlen);
801 if(result) {
802 /* cb_rand is only used for non-cryptographic context. If Curl_rand
803 failed, just fill 0 and call it *random*. */
804 memset(dest, 0, destlen);
805 }
806}
807
808static int cb_get_new_connection_id(ngtcp2_conn *tconn, ngtcp2_cid *cid,
809 uint8_t *token, size_t cidlen,
810 void *user_data)
811{
812 CURLcode result;
813 (void)tconn;
814 (void)user_data;
815
816 result = Curl_rand(NULL, cid->data, cidlen);
817 if(result)
818 return NGTCP2_ERR_CALLBACK_FAILURE;
819 cid->datalen = cidlen;
820
821 result = Curl_rand(NULL, token, NGTCP2_STATELESS_RESET_TOKENLEN);
822 if(result)
823 return NGTCP2_ERR_CALLBACK_FAILURE;
824
825 return 0;
826}
827
828static int cb_recv_rx_key(ngtcp2_conn *tconn, ngtcp2_crypto_level level,
829 void *user_data)
830{
831 struct Curl_cfilter *cf = user_data;
832 (void)tconn;
833
834 if(level != NGTCP2_CRYPTO_LEVEL_APPLICATION) {
835 return 0;
836 }
837
838 if(init_ngh3_conn(cf) != CURLE_OK) {
839 return NGTCP2_ERR_CALLBACK_FAILURE;
840 }
841
842 return 0;
843}
844
845static ngtcp2_callbacks ng_callbacks = {
846 ngtcp2_crypto_client_initial_cb,
847 NULL, /* recv_client_initial */
848 ngtcp2_crypto_recv_crypto_data_cb,
849 cb_handshake_completed,
850 NULL, /* recv_version_negotiation */
851 ngtcp2_crypto_encrypt_cb,
852 ngtcp2_crypto_decrypt_cb,
853 ngtcp2_crypto_hp_mask_cb,
854 cb_recv_stream_data,
855 cb_acked_stream_data_offset,
856 NULL, /* stream_open */
857 cb_stream_close,
858 NULL, /* recv_stateless_reset */
859 ngtcp2_crypto_recv_retry_cb,
860 cb_extend_max_local_streams_bidi,
861 NULL, /* extend_max_local_streams_uni */
862 cb_rand,
863 cb_get_new_connection_id,
864 NULL, /* remove_connection_id */
865 ngtcp2_crypto_update_key_cb, /* update_key */
866 NULL, /* path_validation */
867 NULL, /* select_preferred_addr */
868 cb_stream_reset,
869 NULL, /* extend_max_remote_streams_bidi */
870 NULL, /* extend_max_remote_streams_uni */
871 cb_extend_max_stream_data,
872 NULL, /* dcid_status */
873 NULL, /* handshake_confirmed */
874 NULL, /* recv_new_token */
875 ngtcp2_crypto_delete_crypto_aead_ctx_cb,
876 ngtcp2_crypto_delete_crypto_cipher_ctx_cb,
877 NULL, /* recv_datagram */
878 NULL, /* ack_datagram */
879 NULL, /* lost_datagram */
880 ngtcp2_crypto_get_path_challenge_data_cb,
881 cb_stream_stop_sending,
882 NULL, /* version_negotiation */
883 cb_recv_rx_key,
884 NULL, /* recv_tx_key */
885 NULL, /* early_data_rejected */
886};
887
888static int cf_ngtcp2_get_select_socks(struct Curl_cfilter *cf,
889 struct Curl_easy *data,
890 curl_socket_t *socks)
891{
892 struct cf_ngtcp2_ctx *ctx = cf->ctx;
893 struct SingleRequest *k = &data->req;
894 int rv = GETSOCK_BLANK;
895 struct HTTP *stream = data->req.p.http;
896 struct cf_call_data save;
897
898 CF_DATA_SAVE(save, cf, data);
899 socks[0] = ctx->q.sockfd;
900
901 /* in an HTTP/3 connection we can basically always get a frame so we should
902 always be ready for one */
903 rv |= GETSOCK_READSOCK(0);
904
905 /* we're still uploading or the HTTP/2 layer wants to send data */
906 if((k->keepon & KEEP_SENDBITS) == KEEP_SEND &&
907 (!stream->h3out || stream->h3out->used < H3_SEND_SIZE) &&
908 ngtcp2_conn_get_cwnd_left(ctx->qconn) &&
909 ngtcp2_conn_get_max_data_left(ctx->qconn) &&
910 nghttp3_conn_is_stream_writable(ctx->h3conn, stream->stream3_id))
911 rv |= GETSOCK_WRITESOCK(0);
912
913 DEBUGF(LOG_CF(data, cf, "get_select_socks -> %x (sock=%d)",
914 rv, (int)socks[0]));
915 CF_DATA_RESTORE(cf, save);
916 return rv;
917}
918
919static void notify_drain(struct Curl_cfilter *cf,
920 struct Curl_easy *data)
921{
922 (void)cf;
923 if(!data->state.drain) {
924 data->state.drain = 1;
925 Curl_expire(data, 0, EXPIRE_RUN_NOW);
926 }
927}
928
929
930static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id,
931 uint64_t app_error_code, void *user_data,
932 void *stream_user_data)
933{
934 struct Curl_cfilter *cf = user_data;
935 struct Curl_easy *data = stream_user_data;
936 struct HTTP *stream = data->req.p.http;
937 (void)conn;
938 (void)stream_id;
939 (void)app_error_code;
940 (void)cf;
941
942 DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] h3 close(err=%" PRIx64 ")",
943 stream_id, app_error_code));
944 stream->closed = TRUE;
945 stream->error3 = app_error_code;
946 if(app_error_code == NGHTTP3_H3_INTERNAL_ERROR) {
947 /* TODO: we do not get a specific error when the remote end closed
948 * the response before it was complete. */
949 stream->reset = TRUE;
950 }
951 notify_drain(cf, data);
952 return 0;
953}
954
955/*
956 * write_resp_raw() copies response data in raw format to the `data`'s
957 * receive buffer. If not enough space is available, it appends to the
958 * `data`'s overflow buffer.
959 */
960static CURLcode write_resp_raw(struct Curl_cfilter *cf,
961 struct Curl_easy *data,
962 const void *mem, size_t memlen,
963 bool flow)
964{
965 struct HTTP *stream = data->req.p.http;
966 CURLcode result = CURLE_OK;
967 const char *buf = mem;
968 size_t ncopy = memlen;
969 /* copy as much as possible to the receive buffer */
970 if(stream->len) {
971 size_t len = CURLMIN(ncopy, stream->len);
972 memcpy(stream->mem + stream->memlen, buf, len);
973 stream->len -= len;
974 stream->memlen += len;
975 buf += len;
976 ncopy -= len;
977 DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] resp_raw: added %zu bytes"
978 " to data buffer", stream->stream3_id, len));
979 }
980 /* copy the rest to the overflow buffer */
981 if(ncopy) {
982 result = Curl_dyn_addn(&stream->overflow, buf, ncopy);
983 DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] resp_raw: added %zu bytes"
984 " to overflow buffer -> %d",
985 stream->stream3_id, ncopy, result));
986 notify_drain(cf, data);
987 }
988
989 if(!flow)
990 stream->recv_buf_nonflow += memlen;
991 if(CF_DATA_CURRENT(cf) != data) {
992 notify_drain(cf, data);
993 }
994 return result;
995}
996
997static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id,
998 const uint8_t *buf, size_t buflen,
999 void *user_data, void *stream_user_data)
1000{
1001 struct Curl_cfilter *cf = user_data;
1002 struct Curl_easy *data = stream_user_data;
1003 CURLcode result;
1004
1005 (void)conn;
1006 (void)stream3_id;
1007
1008 result = write_resp_raw(cf, data, buf, buflen, TRUE);
1009 return result? -1 : 0;
1010}
1011
1012static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream3_id,
1013 size_t consumed, void *user_data,
1014 void *stream_user_data)
1015{
1016 struct Curl_cfilter *cf = user_data;
1017 struct cf_ngtcp2_ctx *ctx = cf->ctx;
1018 (void)conn;
1019 (void)stream_user_data;
1020
1021 /* nghttp3 has consumed bytes on the QUIC stream and we need to
1022 * tell the QUIC connection to increase its flow control */
1023 ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream3_id, consumed);
1024 ngtcp2_conn_extend_max_offset(ctx->qconn, consumed);
1025 return 0;
1026}
1027
1028/* Decode HTTP status code. Returns -1 if no valid status code was
1029 decoded. (duplicate from http2.c) */
1030static int decode_status_code(const uint8_t *value, size_t len)
1031{
1032 int i;
1033 int res;
1034
1035 if(len != 3) {
1036 return -1;
1037 }
1038
1039 res = 0;
1040
1041 for(i = 0; i < 3; ++i) {
1042 char c = value[i];
1043
1044 if(c < '0' || c > '9') {
1045 return -1;
1046 }
1047
1048 res *= 10;
1049 res += c - '0';
1050 }
1051
1052 return res;
1053}
1054
1055static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id,
1056 int fin, void *user_data, void *stream_user_data)
1057{
1058 struct Curl_cfilter *cf = user_data;
1059 struct Curl_easy *data = stream_user_data;
1060 struct HTTP *stream = data->req.p.http;
1061 CURLcode result = CURLE_OK;
1062 (void)conn;
1063 (void)stream_id;
1064 (void)fin;
1065 (void)cf;
1066
1067 /* add a CRLF only if we've received some headers */
1068 if(stream->firstheader) {
1069 result = write_resp_raw(cf, data, "\r\n", 2, FALSE);
1070 if(result) {
1071 return -1;
1072 }
1073 }
1074
1075 DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] end_headers(status_code=%d",
1076 stream_id, stream->status_code));
1077 if(stream->status_code / 100 != 1) {
1078 stream->bodystarted = TRUE;
1079 }
1080 return 0;
1081}
1082
1083static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id,
1084 int32_t token, nghttp3_rcbuf *name,
1085 nghttp3_rcbuf *value, uint8_t flags,
1086 void *user_data, void *stream_user_data)
1087{
1088 struct Curl_cfilter *cf = user_data;
1089 nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name);
1090 nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value);
1091 struct Curl_easy *data = stream_user_data;
1092 struct HTTP *stream = data->req.p.http;
1093 CURLcode result = CURLE_OK;
1094 (void)conn;
1095 (void)stream_id;
1096 (void)token;
1097 (void)flags;
1098 (void)cf;
1099
1100 if(token == NGHTTP3_QPACK_TOKEN__STATUS) {
1101 char line[14]; /* status line is always 13 characters long */
1102 size_t ncopy;
1103
1104 DEBUGASSERT(!stream->firstheader);
1105 stream->status_code = decode_status_code(h3val.base, h3val.len);
1106 DEBUGASSERT(stream->status_code != -1);
1107 ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n",
1108 stream->status_code);
1109 DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] status: %s",
1110 stream_id, line));
1111 result = write_resp_raw(cf, data, line, ncopy, FALSE);
1112 if(result) {
1113 return -1;
1114 }
1115 stream->firstheader = TRUE;
1116 }
1117 else {
1118 /* store as an HTTP1-style header */
1119 DEBUGASSERT(stream->firstheader);
1120 DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] header: %.*s: %.*s",
1121 stream_id, (int)h3name.len, h3name.base,
1122 (int)h3val.len, h3val.base));
1123 result = write_resp_raw(cf, data, h3name.base, h3name.len, FALSE);
1124 if(result) {
1125 return -1;
1126 }
1127 result = write_resp_raw(cf, data, ": ", 2, FALSE);
1128 if(result) {
1129 return -1;
1130 }
1131 result = write_resp_raw(cf, data, h3val.base, h3val.len, FALSE);
1132 if(result) {
1133 return -1;
1134 }
1135 result = write_resp_raw(cf, data, "\r\n", 2, FALSE);
1136 if(result) {
1137 return -1;
1138 }
1139 }
1140 return 0;
1141}
1142
1143static int cb_h3_stop_sending(nghttp3_conn *conn, int64_t stream_id,
1144 uint64_t app_error_code, void *user_data,
1145 void *stream_user_data)
1146{
1147 struct Curl_cfilter *cf = user_data;
1148 struct cf_ngtcp2_ctx *ctx = cf->ctx;
1149 int rv;
1150 (void)conn;
1151 (void)stream_user_data;
1152
1153 rv = ngtcp2_conn_shutdown_stream_read(ctx->qconn, stream_id, app_error_code);
1154 if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) {
1155 return NGTCP2_ERR_CALLBACK_FAILURE;
1156 }
1157
1158 return 0;
1159}
1160
1161static int cb_h3_reset_stream(nghttp3_conn *conn, int64_t stream_id,
1162 uint64_t app_error_code, void *user_data,
1163 void *stream_user_data) {
1164 struct Curl_cfilter *cf = user_data;
1165 struct cf_ngtcp2_ctx *ctx = cf->ctx;
1166 struct Curl_easy *data = stream_user_data;
1167 int rv;
1168 (void)conn;
1169 (void)data;
1170
1171 rv = ngtcp2_conn_shutdown_stream_write(ctx->qconn, stream_id,
1172 app_error_code);
1173 DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] reset -> %d", stream_id, rv));
1174 if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) {
1175 return NGTCP2_ERR_CALLBACK_FAILURE;
1176 }
1177
1178 return 0;
1179}
1180
1181static nghttp3_callbacks ngh3_callbacks = {
1182 cb_h3_acked_stream_data, /* acked_stream_data */
1183 cb_h3_stream_close,
1184 cb_h3_recv_data,
1185 cb_h3_deferred_consume,
1186 NULL, /* begin_headers */
1187 cb_h3_recv_header,
1188 cb_h3_end_headers,
1189 NULL, /* begin_trailers */
1190 cb_h3_recv_header,
1191 NULL, /* end_trailers */
1192 cb_h3_stop_sending,
1193 NULL, /* end_stream */
1194 cb_h3_reset_stream,
1195 NULL /* shutdown */
1196};
1197
1198static int init_ngh3_conn(struct Curl_cfilter *cf)
1199{
1200 struct cf_ngtcp2_ctx *ctx = cf->ctx;
1201 CURLcode result;
1202 int rc;
1203 int64_t ctrl_stream_id, qpack_enc_stream_id, qpack_dec_stream_id;
1204
1205 if(ngtcp2_conn_get_max_local_streams_uni(ctx->qconn) < 3) {
1206 return CURLE_QUIC_CONNECT_ERROR;
1207 }
1208
1209 nghttp3_settings_default(&ctx->h3settings);
1210
1211 rc = nghttp3_conn_client_new(&ctx->h3conn,
1212 &ngh3_callbacks,
1213 &ctx->h3settings,
1214 nghttp3_mem_default(),
1215 cf);
1216 if(rc) {
1217 result = CURLE_OUT_OF_MEMORY;
1218 goto fail;
1219 }
1220
1221 rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &ctrl_stream_id, NULL);
1222 if(rc) {
1223 result = CURLE_QUIC_CONNECT_ERROR;
1224 goto fail;
1225 }
1226
1227 rc = nghttp3_conn_bind_control_stream(ctx->h3conn, ctrl_stream_id);
1228 if(rc) {
1229 result = CURLE_QUIC_CONNECT_ERROR;
1230 goto fail;
1231 }
1232
1233 rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &qpack_enc_stream_id, NULL);
1234 if(rc) {
1235 result = CURLE_QUIC_CONNECT_ERROR;
1236 goto fail;
1237 }
1238
1239 rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &qpack_dec_stream_id, NULL);
1240 if(rc) {
1241 result = CURLE_QUIC_CONNECT_ERROR;
1242 goto fail;
1243 }
1244
1245 rc = nghttp3_conn_bind_qpack_streams(ctx->h3conn, qpack_enc_stream_id,
1246 qpack_dec_stream_id);
1247 if(rc) {
1248 result = CURLE_QUIC_CONNECT_ERROR;
1249 goto fail;
1250 }
1251
1252 return CURLE_OK;
1253 fail:
1254
1255 return result;
1256}
1257
1258static void drain_overflow_buffer(struct Curl_cfilter *cf,
1259 struct Curl_easy *data)
1260{
1261 struct HTTP *stream = data->req.p.http;
1262 size_t overlen = Curl_dyn_len(&stream->overflow);
1263 size_t ncopy = CURLMIN(overlen, stream->len);
1264
1265 (void)cf;
1266 if(ncopy > 0) {
1267 memcpy(stream->mem + stream->memlen,
1268 Curl_dyn_ptr(&stream->overflow), ncopy);
1269 stream->len -= ncopy;
1270 stream->memlen += ncopy;
1271 if(ncopy != overlen)
1272 /* make the buffer only keep the tail */
1273 (void)Curl_dyn_tail(&stream->overflow, overlen - ncopy);
1274 else {
1275 Curl_dyn_reset(&stream->overflow);
1276 }
1277 }
1278}
1279
1280static ssize_t recv_closed_stream(struct Curl_cfilter *cf,
1281 struct Curl_easy *data,
1282 CURLcode *err)
1283{
1284 struct HTTP *stream = data->req.p.http;
1285 ssize_t nread = -1;
1286
1287 (void)cf;
1288
1289 if(stream->reset) {
1290 failf(data,
1291 "HTTP/3 stream %" PRId64 " reset by server", stream->stream3_id);
1292 *err = CURLE_PARTIAL_FILE;
1293 DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, was reset -> %d",
1294 stream->stream3_id, *err));
1295 goto out;
1296 }
1297 else if(stream->error3 != NGHTTP3_H3_NO_ERROR) {
1298 failf(data,
1299 "HTTP/3 stream %" PRId64 " was not closed cleanly: (err 0x%" PRIx64
1300 ")",
1301 stream->stream3_id, stream->error3);
1302 *err = CURLE_HTTP3;
1303 DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed uncleanly"
1304 " -> %d", stream->stream3_id, *err));
1305 goto out;
1306 }
1307
1308 if(!stream->bodystarted) {
1309 failf(data,
1310 "HTTP/3 stream %" PRId64 " was closed cleanly, but before getting"
1311 " all response header fields, treated as error",
1312 stream->stream3_id);
1313 *err = CURLE_HTTP3;
1314 DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed incomplete"
1315 " -> %d", stream->stream3_id, *err));
1316 goto out;
1317 }
1318 else {
1319 DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv, closed ok"
1320 " -> %d", stream->stream3_id, *err));
1321 }
1322 *err = CURLE_OK;
1323 nread = 0;
1324
1325out:
1326 data->state.drain = 0;
1327 return nread;
1328}
1329
1330/* incoming data frames on the h3 stream */
1331static ssize_t cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
1332 char *buf, size_t len, CURLcode *err)
1333{
1334 struct cf_ngtcp2_ctx *ctx = cf->ctx;
1335 struct HTTP *stream = data->req.p.http;
1336 ssize_t nread = -1;
1337 struct cf_call_data save;
1338
1339 (void)ctx;
1340
1341 CF_DATA_SAVE(save, cf, data);
1342 DEBUGASSERT(cf->connected);
1343 DEBUGASSERT(ctx);
1344 DEBUGASSERT(ctx->qconn);
1345 DEBUGASSERT(ctx->h3conn);
1346 *err = CURLE_OK;
1347
1348 DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv(len=%zu) start",
1349 stream->stream3_id, len));
1350 /* TODO: this implementation of response DATA buffering is fragile.
1351 * It makes the following assumptions:
1352 * - the `buf` passed here has the same lifetime as the easy handle
1353 * - data returned in `buf` from this call is immediately used and `buf`
1354 * can be overwritten during any handling of other transfers at
1355 * this connection.
1356 */
1357 if(!stream->memlen) {
1358 /* `buf` was not known before or is currently not used by stream,
1359 * assign it (again). */
1360 stream->mem = buf;
1361 stream->len = len;
1362 }
1363
1364 /* if there's data in the overflow buffer, move as much
1365 as possible to the receive buffer now */
1366 drain_overflow_buffer(cf, data);
1367
1368 if(cf_process_ingress(cf, data)) {
1369 *err = CURLE_RECV_ERROR;
1370 nread = -1;
1371 goto out;
1372 }
1373
1374 if(stream->memlen) {
1375 nread = stream->memlen;
1376 /* reset to allow more data to come */
1377 /* TODO: very brittle buffer use design:
1378 * - stream->mem has now `nread` bytes of response data
1379 * - we assume that the caller will use those immediately and
1380 * we can overwrite that with new data on our next invocation from
1381 * anywhere.
1382 */
1383 stream->mem = buf;
1384 stream->memlen = 0;
1385 stream->len = len;
1386 /* extend the stream window with the data we're consuming and send out
1387 any additional packets to tell the server that we can receive more */
1388 DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv -> %zd bytes",
1389 stream->stream3_id, nread));
1390 report_consumed_data(cf, data, nread);
1391 if(cf_flush_egress(cf, data)) {
1392 *err = CURLE_SEND_ERROR;
1393 nread = -1;
1394 }
1395 goto out;
1396 }
1397
1398 if(stream->closed) {
1399 nread = recv_closed_stream(cf, data, err);
1400 goto out;
1401 }
1402
1403 DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv -> EAGAIN",
1404 stream->stream3_id));
1405 *err = CURLE_AGAIN;
1406 nread = -1;
1407out:
1408 if(cf_flush_egress(cf, data)) {
1409 *err = CURLE_SEND_ERROR;
1410 nread = -1;
1411 goto out;
1412 }
1413
1414 CF_DATA_RESTORE(cf, save);
1415 return nread;
1416}
1417
1418/* this amount of data has now been acked on this stream */
1419static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id,
1420 uint64_t datalen, void *user_data,
1421 void *stream_user_data)
1422{
1423 struct Curl_cfilter *cf = user_data;
1424 struct Curl_easy *data = stream_user_data;
1425 struct HTTP *stream = data->req.p.http;
1426 (void)user_data;
1427
1428 (void)cf;
1429 if(!data->set.postfields) {
1430 stream->h3out->used -= datalen;
1431 DEBUGF(LOG_CF(data, cf, "cb_h3_acked_stream_data, %"PRIu64" bytes, "
1432 "%zd left unacked", datalen, stream->h3out->used));
1433 DEBUGASSERT(stream->h3out->used < H3_SEND_SIZE);
1434
1435 if(stream->h3out->used == 0) {
1436 int rv = nghttp3_conn_resume_stream(conn, stream_id);
1437 if(rv) {
1438 return NGTCP2_ERR_CALLBACK_FAILURE;
1439 }
1440 }
1441 }
1442 return 0;
1443}
1444
1445static nghttp3_ssize cb_h3_readfunction(nghttp3_conn *conn, int64_t stream_id,
1446 nghttp3_vec *vec, size_t veccnt,
1447 uint32_t *pflags, void *user_data,
1448 void *stream_user_data)
1449{
1450 struct Curl_cfilter *cf = user_data;
1451 struct Curl_easy *data = stream_user_data;
1452 size_t nread;
1453 struct HTTP *stream = data->req.p.http;
1454 (void)cf;
1455 (void)conn;
1456 (void)stream_id;
1457 (void)user_data;
1458 (void)veccnt;
1459
1460 if(data->set.postfields) {
1461 vec[0].base = data->set.postfields;
1462 vec[0].len = data->state.infilesize;
1463 *pflags = NGHTTP3_DATA_FLAG_EOF;
1464 return 1;
1465 }
1466
1467 if(stream->upload_len && H3_SEND_SIZE <= stream->h3out->used) {
1468 return NGHTTP3_ERR_WOULDBLOCK;
1469 }
1470
1471 nread = CURLMIN(stream->upload_len, H3_SEND_SIZE - stream->h3out->used);
1472 if(nread > 0) {
1473 /* nghttp3 wants us to hold on to the data until it tells us it is okay to
1474 delete it. Append the data at the end of the h3out buffer. Since we can
1475 only return consecutive data, copy the amount that fits and the next
1476 part comes in next invoke. */
1477 struct h3out *out = stream->h3out;
1478 if(nread + out->windex > H3_SEND_SIZE)
1479 nread = H3_SEND_SIZE - out->windex;
1480
1481 memcpy(&out->buf[out->windex], stream->upload_mem, nread);
1482
1483 /* that's the chunk we return to nghttp3 */
1484 vec[0].base = &out->buf[out->windex];
1485 vec[0].len = nread;
1486
1487 out->windex += nread;
1488 out->used += nread;
1489
1490 if(out->windex == H3_SEND_SIZE)
1491 out->windex = 0; /* wrap */
1492 stream->upload_mem += nread;
1493 stream->upload_len -= nread;
1494 if(data->state.infilesize != -1) {
1495 stream->upload_left -= nread;
1496 if(!stream->upload_left)
1497 *pflags = NGHTTP3_DATA_FLAG_EOF;
1498 }
1499 DEBUGF(LOG_CF(data, cf, "cb_h3_readfunction %zd bytes%s (at %zd unacked)",
1500 nread, *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"",
1501 out->used));
1502 }
1503 if(stream->upload_done && !stream->upload_len &&
1504 (stream->upload_left <= 0)) {
1505 DEBUGF(LOG_CF(data, cf, "cb_h3_readfunction sets EOF"));
1506 *pflags = NGHTTP3_DATA_FLAG_EOF;
1507 return nread ? 1 : 0;
1508 }
1509 else if(!nread) {
1510 return NGHTTP3_ERR_WOULDBLOCK;
1511 }
1512 return 1;
1513}
1514
1515/* Index where :authority header field will appear in request header
1516 field list. */
1517#define AUTHORITY_DST_IDX 3
1518
1519static CURLcode h3_stream_open(struct Curl_cfilter *cf,
1520 struct Curl_easy *data,
1521 const void *mem,
1522 size_t len)
1523{
1524 struct cf_ngtcp2_ctx *ctx = cf->ctx;
1525 struct HTTP *stream = data->req.p.http;
1526 size_t nheader;
1527 CURLcode result = CURLE_OK;
1528 nghttp3_nv *nva = NULL;
1529 int64_t stream3_id;
1530 int rc = 0;
1531 struct h3out *h3out = NULL;
1532 struct h2h3req *hreq = NULL;
1533
1534 rc = ngtcp2_conn_open_bidi_stream(ctx->qconn, &stream3_id, NULL);
1535 if(rc) {
1536 failf(data, "can get bidi streams");
1537 goto fail;
1538 }
1539
1540 stream->stream3_id = stream3_id;
1541 stream->h3req = TRUE;
1542 Curl_dyn_init(&stream->overflow, CURL_MAX_READ_SIZE);
1543 stream->recv_buf_nonflow = 0;
1544
1545 result = Curl_pseudo_headers(data, mem, len, NULL, &hreq);
1546 if(result)
1547 goto fail;
1548 nheader = hreq->entries;
1549
1550 nva = malloc(sizeof(nghttp3_nv) * nheader);
1551 if(!nva) {
1552 result = CURLE_OUT_OF_MEMORY;
1553 goto fail;
1554 }
1555 else {
1556 unsigned int i;
1557 for(i = 0; i < nheader; i++) {
1558 nva[i].name = (unsigned char *)hreq->header[i].name;
1559 nva[i].namelen = hreq->header[i].namelen;
1560 nva[i].value = (unsigned char *)hreq->header[i].value;
1561 nva[i].valuelen = hreq->header[i].valuelen;
1562 nva[i].flags = NGHTTP3_NV_FLAG_NONE;
1563 }
1564 }
1565
1566 switch(data->state.httpreq) {
1567 case HTTPREQ_POST:
1568 case HTTPREQ_POST_FORM:
1569 case HTTPREQ_POST_MIME:
1570 case HTTPREQ_PUT: {
1571 nghttp3_data_reader data_reader;
1572 if(data->state.infilesize != -1)
1573 stream->upload_left = data->state.infilesize;
1574 else
1575 /* data sending without specifying the data amount up front */
1576 stream->upload_left = -1; /* unknown, but not zero */
1577
1578 data_reader.read_data = cb_h3_readfunction;
1579
1580 h3out = calloc(sizeof(struct h3out), 1);
1581 if(!h3out) {
1582 result = CURLE_OUT_OF_MEMORY;
1583 goto fail;
1584 }
1585 stream->h3out = h3out;
1586
1587 rc = nghttp3_conn_submit_request(ctx->h3conn, stream->stream3_id,
1588 nva, nheader, &data_reader, data);
1589 if(rc)
1590 goto fail;
1591 break;
1592 }
1593 default:
1594 stream->upload_left = 0; /* nothing left to send */
1595 rc = nghttp3_conn_submit_request(ctx->h3conn, stream->stream3_id,
1596 nva, nheader, NULL, data);
1597 if(rc)
1598 goto fail;
1599 break;
1600 }
1601
1602 Curl_safefree(nva);
1603
1604 infof(data, "Using HTTP/3 Stream ID: %" PRId64 " (easy handle %p)",
1605 stream3_id, (void *)data);
1606 DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] opened for %s",
1607 stream3_id, data->state.url));
1608
1609 Curl_pseudo_free(hreq);
1610 return CURLE_OK;
1611
1612fail:
1613 if(rc) {
1614 switch(rc) {
1615 case NGHTTP3_ERR_CONN_CLOSING:
1616 DEBUGF(LOG_CF(data, cf, "h3sid[%"PRId64"] failed to send, "
1617 "connection is closing", stream->stream3_id));
1618 result = CURLE_RECV_ERROR;
1619 break;
1620 default:
1621 DEBUGF(LOG_CF(data, cf, "h3sid[%"PRId64"] failed to send -> %d (%s)",
1622 stream->stream3_id, rc, ngtcp2_strerror(rc)));
1623 result = CURLE_SEND_ERROR;
1624 break;
1625 }
1626 }
1627 free(nva);
1628 Curl_pseudo_free(hreq);
1629 return result;
1630}
1631
1632static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
1633 const void *buf, size_t len, CURLcode *err)
1634{
1635 struct cf_ngtcp2_ctx *ctx = cf->ctx;
1636 ssize_t sent = 0;
1637 struct HTTP *stream = data->req.p.http;
1638 struct cf_call_data save;
1639
1640 CF_DATA_SAVE(save, cf, data);
1641 DEBUGASSERT(cf->connected);
1642 DEBUGASSERT(ctx->qconn);
1643 DEBUGASSERT(ctx->h3conn);
1644 *err = CURLE_OK;
1645
1646 if(stream->closed) {
1647 *err = CURLE_HTTP3;
1648 sent = -1;
1649 goto out;
1650 }
1651
1652 if(!stream->h3req) {
1653 CURLcode result = h3_stream_open(cf, data, buf, len);
1654 if(result) {
1655 DEBUGF(LOG_CF(data, cf, "failed to open stream -> %d", result));
1656 sent = -1;
1657 goto out;
1658 }
1659 /* Assume that mem of length len only includes HTTP/1.1 style
1660 header fields. In other words, it does not contain request
1661 body. */
1662 sent = len;
1663 }
1664 else {
1665 DEBUGF(LOG_CF(data, cf, "ngh3_stream_send() wants to send %zd bytes",
1666 len));
1667 if(!stream->upload_len) {
1668 stream->upload_mem = buf;
1669 stream->upload_len = len;
1670 (void)nghttp3_conn_resume_stream(ctx->h3conn, stream->stream3_id);
1671 }
1672 else {
1673 *err = CURLE_AGAIN;
1674 sent = -1;
1675 goto out;
1676 }
1677 }
1678
1679 if(cf_flush_egress(cf, data)) {
1680 *err = CURLE_SEND_ERROR;
1681 sent = -1;
1682 goto out;
1683 }
1684
1685 /* Reset post upload buffer after resumed. */
1686 if(stream->upload_mem) {
1687 if(data->set.postfields) {
1688 sent = len;
1689 }
1690 else {
1691 sent = len - stream->upload_len;
1692 }
1693
1694 stream->upload_mem = NULL;
1695 stream->upload_len = 0;
1696
1697 if(sent == 0) {
1698 *err = CURLE_AGAIN;
1699 sent = -1;
1700 goto out;
1701 }
1702 }
1703out:
1704 CF_DATA_RESTORE(cf, save);
1705 return sent;
1706}
1707
1708static CURLcode qng_verify_peer(struct Curl_cfilter *cf,
1709 struct Curl_easy *data)
1710{
1711 struct cf_ngtcp2_ctx *ctx = cf->ctx;
1712 CURLcode result = CURLE_OK;
1713 const char *hostname, *disp_hostname;
1714 int port;
1715 char *snihost;
1716
1717 Curl_conn_get_host(data, cf->sockindex, &hostname, &disp_hostname, &port);
1718 snihost = Curl_ssl_snihost(data, hostname, NULL);
1719 if(!snihost)
1720 return CURLE_PEER_FAILED_VERIFICATION;
1721
1722 cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
1723 cf->conn->httpversion = 30;
1724 cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX;
1725
1726 if(cf->conn->ssl_config.verifyhost) {
1727#ifdef USE_OPENSSL
1728 X509 *server_cert;
1729 server_cert = SSL_get_peer_certificate(ctx->ssl);
1730 if(!server_cert) {
1731 return CURLE_PEER_FAILED_VERIFICATION;
1732 }
1733 result = Curl_ossl_verifyhost(data, cf->conn, server_cert);
1734 X509_free(server_cert);
1735 if(result)
1736 return result;
1737#elif defined(USE_GNUTLS)
1738 result = Curl_gtls_verifyserver(data, ctx->gtls->session,
1739 &cf->conn->ssl_config, &data->set.ssl,
1740 hostname, disp_hostname,
1741 data->set.str[STRING_SSL_PINNEDPUBLICKEY]);
1742 if(result)
1743 return result;
1744#elif defined(USE_WOLFSSL)
1745 if(wolfSSL_check_domain_name(ctx->ssl, snihost) == SSL_FAILURE)
1746 return CURLE_PEER_FAILED_VERIFICATION;
1747#endif
1748 infof(data, "Verified certificate just fine");
1749 }
1750 else
1751 infof(data, "Skipped certificate verification");
1752#ifdef USE_OPENSSL
1753 if(data->set.ssl.certinfo)
1754 /* asked to gather certificate info */
1755 (void)Curl_ossl_certchain(data, ctx->ssl);
1756#endif
1757 return result;
1758}
1759
1760static CURLcode cf_process_ingress(struct Curl_cfilter *cf,
1761 struct Curl_easy *data)
1762{
1763 struct cf_ngtcp2_ctx *ctx = cf->ctx;
1764 ssize_t recvd;
1765 int rv;
1766 uint8_t buf[65536];
1767 int bufsize = (int)sizeof(buf);
1768 size_t pktcount = 0, total_recvd = 0;
1769 struct sockaddr_storage remote_addr;
1770 socklen_t remote_addrlen;
1771 ngtcp2_path path;
1772 ngtcp2_tstamp ts = timestamp();
1773 ngtcp2_pkt_info pi = { 0 };
1774
1775 for(;;) {
1776 remote_addrlen = sizeof(remote_addr);
1777 while((recvd = recvfrom(ctx->q.sockfd, (char *)buf, bufsize, 0,
1778 (struct sockaddr *)&remote_addr,
1779 &remote_addrlen)) == -1 &&
1780 SOCKERRNO == EINTR)
1781 ;
1782 if(recvd == -1) {
1783 if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
1784 DEBUGF(LOG_CF(data, cf, "ingress, recvfrom -> EAGAIN"));
1785 goto out;
1786 }
1787 if(!cf->connected && SOCKERRNO == ECONNREFUSED) {
1788 const char *r_ip;
1789 int r_port;
1790 Curl_cf_socket_peek(cf->next, data, NULL, NULL,
1791 &r_ip, &r_port, NULL, NULL);
1792 failf(data, "ngtcp2: connection to %s port %u refused",
1793 r_ip, r_port);
1794 return CURLE_COULDNT_CONNECT;
1795 }
1796 failf(data, "ngtcp2: recvfrom() unexpectedly returned %zd (errno=%d)",
1797 recvd, SOCKERRNO);
1798 return CURLE_RECV_ERROR;
1799 }
1800
1801 if(recvd > 0 && !ctx->got_first_byte) {
1802 ctx->first_byte_at = Curl_now();
1803 ctx->got_first_byte = TRUE;
1804 }
1805
1806 ++pktcount;
1807 total_recvd += recvd;
1808
1809 ngtcp2_addr_init(&path.local, (struct sockaddr *)&ctx->q.local_addr,
1810 ctx->q.local_addrlen);
1811 ngtcp2_addr_init(&path.remote, (struct sockaddr *)&remote_addr,
1812 remote_addrlen);
1813
1814 rv = ngtcp2_conn_read_pkt(ctx->qconn, &path, &pi, buf, recvd, ts);
1815 if(rv) {
1816 DEBUGF(LOG_CF(data, cf, "ingress, read_pkt -> %s",
1817 ngtcp2_strerror(rv)));
1818 if(!ctx->last_error.error_code) {
1819 if(rv == NGTCP2_ERR_CRYPTO) {
1820 ngtcp2_connection_close_error_set_transport_error_tls_alert(
1821 &ctx->last_error,
1822 ngtcp2_conn_get_tls_alert(ctx->qconn), NULL, 0);
1823 }
1824 else {
1825 ngtcp2_connection_close_error_set_transport_error_liberr(
1826 &ctx->last_error, rv, NULL, 0);
1827 }
1828 }
1829
1830 if(rv == NGTCP2_ERR_CRYPTO)
1831 /* this is a "TLS problem", but a failed certificate verification
1832 is a common reason for this */
1833 return CURLE_PEER_FAILED_VERIFICATION;
1834 return CURLE_RECV_ERROR;
1835 }
1836 }
1837
1838out:
1839 (void)pktcount;
1840 (void)total_recvd;
1841 DEBUGF(LOG_CF(data, cf, "ingress, recvd %zu packets with %zd bytes",
1842 pktcount, total_recvd));
1843 return CURLE_OK;
1844}
1845
1846static CURLcode cf_flush_egress(struct Curl_cfilter *cf,
1847 struct Curl_easy *data)
1848{
1849 struct cf_ngtcp2_ctx *ctx = cf->ctx;
1850 int rv;
1851 size_t sent;
1852 ngtcp2_ssize outlen;
1853 uint8_t *outpos = ctx->q.pktbuf;
1854 size_t max_udp_payload_size =
1855 ngtcp2_conn_get_max_tx_udp_payload_size(ctx->qconn);
1856 size_t path_max_udp_payload_size =
1857 ngtcp2_conn_get_path_max_tx_udp_payload_size(ctx->qconn);
1858 size_t max_pktcnt =
1859 CURLMIN(MAX_PKT_BURST, ctx->q.pktbuflen / max_udp_payload_size);
1860 size_t pktcnt = 0;
1861 size_t gsolen = 0; /* this disables gso until we have a clue */
1862 ngtcp2_path_storage ps;
1863 ngtcp2_tstamp ts = timestamp();
1864 ngtcp2_tstamp expiry;
1865 ngtcp2_duration timeout;
1866 int64_t stream_id;
1867 nghttp3_ssize veccnt;
1868 int fin;
1869 nghttp3_vec vec[16];
1870 ngtcp2_ssize ndatalen;
1871 uint32_t flags;
1872 CURLcode curlcode;
1873
1874 rv = ngtcp2_conn_handle_expiry(ctx->qconn, ts);
1875 if(rv) {
1876 failf(data, "ngtcp2_conn_handle_expiry returned error: %s",
1877 ngtcp2_strerror(rv));
1878 ngtcp2_connection_close_error_set_transport_error_liberr(&ctx->last_error,
1879 rv, NULL, 0);
1880 return CURLE_SEND_ERROR;
1881 }
1882
1883 if(ctx->q.num_blocked_pkt) {
1884 curlcode = vquic_send_blocked_pkt(cf, data, &ctx->q);
1885 if(curlcode) {
1886 if(curlcode == CURLE_AGAIN) {
1887 Curl_expire(data, 1, EXPIRE_QUIC);
1888 return CURLE_OK;
1889 }
1890 return curlcode;
1891 }
1892 }
1893
1894 ngtcp2_path_storage_zero(&ps);
1895
1896 for(;;) {
1897 veccnt = 0;
1898 stream_id = -1;
1899 fin = 0;
1900
1901 if(ctx->h3conn && ngtcp2_conn_get_max_data_left(ctx->qconn)) {
1902 veccnt = nghttp3_conn_writev_stream(ctx->h3conn, &stream_id, &fin, vec,
1903 sizeof(vec) / sizeof(vec[0]));
1904 if(veccnt < 0) {
1905 failf(data, "nghttp3_conn_writev_stream returned error: %s",
1906 nghttp3_strerror((int)veccnt));
1907 ngtcp2_connection_close_error_set_application_error(
1908 &ctx->last_error,
1909 nghttp3_err_infer_quic_app_error_code((int)veccnt), NULL, 0);
1910 return CURLE_SEND_ERROR;
1911 }
1912 }
1913
1914 flags = NGTCP2_WRITE_STREAM_FLAG_MORE |
1915 (fin ? NGTCP2_WRITE_STREAM_FLAG_FIN : 0);
1916 outlen = ngtcp2_conn_writev_stream(ctx->qconn, &ps.path, NULL, outpos,
1917 max_udp_payload_size,
1918 &ndatalen, flags, stream_id,
1919 (const ngtcp2_vec *)vec, veccnt, ts);
1920 if(outlen == 0) {
1921 /* ngtcp2 does not want to send more packets, if the buffer is
1922 * not empty, send that now */
1923 if(outpos != ctx->q.pktbuf) {
1924 curlcode = vquic_send_packet(cf, data, &ctx->q, ctx->q.pktbuf,
1925 outpos - ctx->q.pktbuf, gsolen, &sent);
1926 if(curlcode) {
1927 if(curlcode == CURLE_AGAIN) {
1928 vquic_push_blocked_pkt(cf, &ctx->q, ctx->q.pktbuf + sent,
1929 outpos - ctx->q.pktbuf - sent,
1930 gsolen);
1931 Curl_expire(data, 1, EXPIRE_QUIC);
1932 return CURLE_OK;
1933 }
1934 return curlcode;
1935 }
1936 }
1937 /* done for now */
1938 goto out;
1939 }
1940 if(outlen < 0) {
1941 switch(outlen) {
1942 case NGTCP2_ERR_STREAM_DATA_BLOCKED:
1943 assert(ndatalen == -1);
1944 nghttp3_conn_block_stream(ctx->h3conn, stream_id);
1945 continue;
1946 case NGTCP2_ERR_STREAM_SHUT_WR:
1947 assert(ndatalen == -1);
1948 nghttp3_conn_shutdown_stream_write(ctx->h3conn, stream_id);
1949 continue;
1950 case NGTCP2_ERR_WRITE_MORE:
1951 /* ngtcp2 wants to send more. update the flow of the stream whose data
1952 * is in the buffer and continue */
1953 assert(ndatalen >= 0);
1954 rv = nghttp3_conn_add_write_offset(ctx->h3conn, stream_id, ndatalen);
1955 if(rv) {
1956 failf(data, "nghttp3_conn_add_write_offset returned error: %s\n",
1957 nghttp3_strerror(rv));
1958 return CURLE_SEND_ERROR;
1959 }
1960 continue;
1961 default:
1962 assert(ndatalen == -1);
1963 failf(data, "ngtcp2_conn_writev_stream returned error: %s",
1964 ngtcp2_strerror((int)outlen));
1965 ngtcp2_connection_close_error_set_transport_error_liberr(
1966 &ctx->last_error, (int)outlen, NULL, 0);
1967 return CURLE_SEND_ERROR;
1968 }
1969 }
1970 else if(ndatalen >= 0) {
1971 /* ngtcp2 thinks it has added all it wants. Update the stream */
1972 rv = nghttp3_conn_add_write_offset(ctx->h3conn, stream_id, ndatalen);
1973 if(rv) {
1974 failf(data, "nghttp3_conn_add_write_offset returned error: %s\n",
1975 nghttp3_strerror(rv));
1976 return CURLE_SEND_ERROR;
1977 }
1978 }
1979
1980 /* advance to the end of the buffered packet data */
1981 outpos += outlen;
1982
1983 if(pktcnt == 0) {
1984 /* first packet buffer chunk. use this as gsolen. It's how ngtcp2
1985 * indicates the intended segment size. */
1986 gsolen = outlen;
1987 }
1988 else if((size_t)outlen > gsolen ||
1989 (gsolen > path_max_udp_payload_size && (size_t)outlen != gsolen)) {
1990 /* Packet larger than path_max_udp_payload_size is PMTUD probe
1991 packet and it might not be sent because of EMSGSIZE. Send
1992 them separately to minimize the loss. */
1993 /* send the pktbuf *before* the last addition */
1994 curlcode = vquic_send_packet(cf, data, &ctx->q, ctx->q.pktbuf,
1995 outpos - outlen - ctx->q.pktbuf, gsolen, &sent);
1996 if(curlcode) {
1997 if(curlcode == CURLE_AGAIN) {
1998 /* blocked, add the pktbuf *before* and *at* the last addition
1999 * separately to the blocked packages */
2000 vquic_push_blocked_pkt(cf, &ctx->q, ctx->q.pktbuf + sent,
2001 outpos - outlen - ctx->q.pktbuf - sent, gsolen);
2002 vquic_push_blocked_pkt(cf, &ctx->q, outpos - outlen, outlen, outlen);
2003 Curl_expire(data, 1, EXPIRE_QUIC);
2004 return CURLE_OK;
2005 }
2006 return curlcode;
2007 }
2008 /* send the pktbuf *at* the last addition */
2009 curlcode = vquic_send_packet(cf, data, &ctx->q, outpos - outlen, outlen,
2010 outlen, &sent);
2011 if(curlcode) {
2012 if(curlcode == CURLE_AGAIN) {
2013 assert(0 == sent);
2014 vquic_push_blocked_pkt(cf, &ctx->q, outpos - outlen, outlen, outlen);
2015 Curl_expire(data, 1, EXPIRE_QUIC);
2016 return CURLE_OK;
2017 }
2018 return curlcode;
2019 }
2020 /* pktbuf has been completely sent */
2021 pktcnt = 0;
2022 outpos = ctx->q.pktbuf;
2023 continue;
2024 }
2025
2026 if(++pktcnt >= max_pktcnt || (size_t)outlen < gsolen) {
2027 /* enough packets or last one is shorter than the intended
2028 * segment size, indicating that it is time to send. */
2029 curlcode = vquic_send_packet(cf, data, &ctx->q, ctx->q.pktbuf,
2030 outpos - ctx->q.pktbuf, gsolen, &sent);
2031 if(curlcode) {
2032 if(curlcode == CURLE_AGAIN) {
2033 vquic_push_blocked_pkt(cf, &ctx->q, ctx->q.pktbuf + sent,
2034 outpos - ctx->q.pktbuf - sent, gsolen);
2035 Curl_expire(data, 1, EXPIRE_QUIC);
2036 return CURLE_OK;
2037 }
2038 return curlcode;
2039 }
2040 /* pktbuf has been completely sent */
2041 pktcnt = 0;
2042 outpos = ctx->q.pktbuf;
2043 }
2044 }
2045
2046out:
2047 /* non-errored exit. check when we should run again. */
2048 expiry = ngtcp2_conn_get_expiry(ctx->qconn);
2049 if(expiry != UINT64_MAX) {
2050 if(expiry <= ts) {
2051 timeout = 0;
2052 }
2053 else {
2054 timeout = expiry - ts;
2055 if(timeout % NGTCP2_MILLISECONDS) {
2056 timeout += NGTCP2_MILLISECONDS;
2057 }
2058 }
2059 Curl_expire(data, timeout / NGTCP2_MILLISECONDS, EXPIRE_QUIC);
2060 }
2061
2062 return CURLE_OK;
2063}
2064
2065/*
2066 * Called from transfer.c:data_pending to know if we should keep looping
2067 * to receive more data from the connection.
2068 */
2069static bool cf_ngtcp2_data_pending(struct Curl_cfilter *cf,
2070 const struct Curl_easy *data)
2071{
2072 /* We may have received more data than we're able to hold in the receive
2073 buffer and allocated an overflow buffer. Since it's possible that
2074 there's no more data coming on the socket, we need to keep reading
2075 until the overflow buffer is empty. */
2076 const struct HTTP *stream = data->req.p.http;
2077 (void)cf;
2078 return Curl_dyn_len(&stream->overflow) > 0;
2079}
2080
2081static CURLcode cf_ngtcp2_data_event(struct Curl_cfilter *cf,
2082 struct Curl_easy *data,
2083 int event, int arg1, void *arg2)
2084{
2085 struct cf_ngtcp2_ctx *ctx = cf->ctx;
2086 CURLcode result = CURLE_OK;
2087 struct cf_call_data save;
2088
2089 CF_DATA_SAVE(save, cf, data);
2090 (void)arg1;
2091 (void)arg2;
2092 switch(event) {
2093 case CF_CTRL_DATA_DONE: {
2094 struct HTTP *stream = data->req.p.http;
2095 Curl_dyn_free(&stream->overflow);
2096 free(stream->h3out);
2097 break;
2098 }
2099 case CF_CTRL_DATA_DONE_SEND: {
2100 struct HTTP *stream = data->req.p.http;
2101 stream->upload_done = TRUE;
2102 (void)nghttp3_conn_resume_stream(ctx->h3conn, stream->stream3_id);
2103 break;
2104 }
2105 case CF_CTRL_DATA_IDLE:
2106 if(timestamp() >= ngtcp2_conn_get_expiry(ctx->qconn)) {
2107 if(cf_flush_egress(cf, data)) {
2108 result = CURLE_SEND_ERROR;
2109 }
2110 }
2111 break;
2112 default:
2113 break;
2114 }
2115 CF_DATA_RESTORE(cf, save);
2116 return result;
2117}
2118
2119static void cf_ngtcp2_ctx_clear(struct cf_ngtcp2_ctx *ctx)
2120{
2121 struct cf_call_data save = ctx->call_data;
2122
2123 if(ctx->qlogfd != -1) {
2124 close(ctx->qlogfd);
2125 }
2126#ifdef USE_OPENSSL
2127 if(ctx->ssl)
2128 SSL_free(ctx->ssl);
2129 if(ctx->sslctx)
2130 SSL_CTX_free(ctx->sslctx);
2131#elif defined(USE_GNUTLS)
2132 if(ctx->gtls) {
2133 if(ctx->gtls->cred)
2134 gnutls_certificate_free_credentials(ctx->gtls->cred);
2135 if(ctx->gtls->session)
2136 gnutls_deinit(ctx->gtls->session);
2137 free(ctx->gtls);
2138 }
2139#elif defined(USE_WOLFSSL)
2140 if(ctx->ssl)
2141 wolfSSL_free(ctx->ssl);
2142 if(ctx->sslctx)
2143 wolfSSL_CTX_free(ctx->sslctx);
2144#endif
2145 vquic_ctx_free(&ctx->q);
2146 if(ctx->h3conn)
2147 nghttp3_conn_del(ctx->h3conn);
2148 if(ctx->qconn)
2149 ngtcp2_conn_del(ctx->qconn);
2150
2151 memset(ctx, 0, sizeof(*ctx));
2152 ctx->qlogfd = -1;
2153 ctx->call_data = save;
2154}
2155
2156static void cf_ngtcp2_close(struct Curl_cfilter *cf, struct Curl_easy *data)
2157{
2158 struct cf_ngtcp2_ctx *ctx = cf->ctx;
2159 struct cf_call_data save;
2160
2161 CF_DATA_SAVE(save, cf, data);
2162 if(ctx && ctx->qconn) {
2163 char buffer[NGTCP2_MAX_UDP_PAYLOAD_SIZE];
2164 ngtcp2_tstamp ts;
2165 ngtcp2_ssize rc;
2166
2167 DEBUGF(LOG_CF(data, cf, "close"));
2168 ts = timestamp();
2169 rc = ngtcp2_conn_write_connection_close(ctx->qconn, NULL, /* path */
2170 NULL, /* pkt_info */
2171 (uint8_t *)buffer, sizeof(buffer),
2172 &ctx->last_error, ts);
2173 if(rc > 0) {
2174 while((send(ctx->q.sockfd, buffer, (SEND_TYPE_ARG3)rc, 0) == -1) &&
2175 SOCKERRNO == EINTR);
2176 }
2177
2178 cf_ngtcp2_ctx_clear(ctx);
2179 }
2180
2181 cf->connected = FALSE;
2182 CF_DATA_RESTORE(cf, save);
2183}
2184
2185static void cf_ngtcp2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
2186{
2187 struct cf_ngtcp2_ctx *ctx = cf->ctx;
2188 struct cf_call_data save;
2189
2190 CF_DATA_SAVE(save, cf, data);
2191 DEBUGF(LOG_CF(data, cf, "destroy"));
2192 if(ctx) {
2193 cf_ngtcp2_ctx_clear(ctx);
2194 free(ctx);
2195 }
2196 cf->ctx = NULL;
2197 /* No CF_DATA_RESTORE(cf, save) possible */
2198 (void)save;
2199}
2200
2201/*
2202 * Might be called twice for happy eyeballs.
2203 */
2204static CURLcode cf_connect_start(struct Curl_cfilter *cf,
2205 struct Curl_easy *data)
2206{
2207 struct cf_ngtcp2_ctx *ctx = cf->ctx;
2208 int rc;
2209 int rv;
2210 CURLcode result;
2211 const struct Curl_sockaddr_ex *sockaddr;
2212 int qfd;
2213
2214 ctx->version = NGTCP2_PROTO_VER_MAX;
2215#ifdef USE_OPENSSL
2216 result = quic_ssl_ctx(&ctx->sslctx, cf, data);
2217 if(result)
2218 return result;
2219
2220 result = quic_set_client_cert(cf, data);
2221 if(result)
2222 return result;
2223#elif defined(USE_WOLFSSL)
2224 result = quic_ssl_ctx(&ctx->sslctx, cf, data);
2225 if(result)
2226 return result;
2227#endif
2228
2229 result = quic_init_ssl(cf, data);
2230 if(result)
2231 return result;
2232
2233 ctx->dcid.datalen = NGTCP2_MAX_CIDLEN;
2234 result = Curl_rand(data, ctx->dcid.data, NGTCP2_MAX_CIDLEN);
2235 if(result)
2236 return result;
2237
2238 ctx->scid.datalen = NGTCP2_MAX_CIDLEN;
2239 result = Curl_rand(data, ctx->scid.data, NGTCP2_MAX_CIDLEN);
2240 if(result)
2241 return result;
2242
2243 (void)Curl_qlogdir(data, ctx->scid.data, NGTCP2_MAX_CIDLEN, &qfd);
2244 ctx->qlogfd = qfd; /* -1 if failure above */
2245 quic_settings(ctx, data);
2246
2247 result = vquic_ctx_init(&ctx->q,
2248 NGTCP2_MAX_PMTUD_UDP_PAYLOAD_SIZE * MAX_PKT_BURST);
2249 if(result)
2250 return result;
2251
2252 Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd,
2253 &sockaddr, NULL, NULL, NULL, NULL);
2254 ctx->q.local_addrlen = sizeof(ctx->q.local_addr);
2255 rv = getsockname(ctx->q.sockfd, (struct sockaddr *)&ctx->q.local_addr,
2256 &ctx->q.local_addrlen);
2257 if(rv == -1)
2258 return CURLE_QUIC_CONNECT_ERROR;
2259
2260 ngtcp2_addr_init(&ctx->connected_path.local,
2261 (struct sockaddr *)&ctx->q.local_addr,
2262 ctx->q.local_addrlen);
2263 ngtcp2_addr_init(&ctx->connected_path.remote,
2264 &sockaddr->sa_addr, sockaddr->addrlen);
2265
2266 rc = ngtcp2_conn_client_new(&ctx->qconn, &ctx->dcid, &ctx->scid,
2267 &ctx->connected_path,
2268 NGTCP2_PROTO_VER_V1, &ng_callbacks,
2269 &ctx->settings, &ctx->transport_params,
2270 NULL, cf);
2271 if(rc)
2272 return CURLE_QUIC_CONNECT_ERROR;
2273
2274#ifdef USE_GNUTLS
2275 ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->gtls->session);
2276#else
2277 ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->ssl);
2278#endif
2279
2280 ngtcp2_connection_close_error_default(&ctx->last_error);
2281
2282 ctx->conn_ref.get_conn = get_conn;
2283 ctx->conn_ref.user_data = cf;
2284
2285 return CURLE_OK;
2286}
2287
2288static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf,
2289 struct Curl_easy *data,
2290 bool blocking, bool *done)
2291{
2292 struct cf_ngtcp2_ctx *ctx = cf->ctx;
2293 CURLcode result = CURLE_OK;
2294 struct cf_call_data save;
2295 struct curltime now;
2296
2297 if(cf->connected) {
2298 *done = TRUE;
2299 return CURLE_OK;
2300 }
2301
2302 /* Connect the UDP filter first */
2303 if(!cf->next->connected) {
2304 result = Curl_conn_cf_connect(cf->next, data, blocking, done);
2305 if(result || !*done)
2306 return result;
2307 }
2308
2309 *done = FALSE;
2310 now = Curl_now();
2311
2312 CF_DATA_SAVE(save, cf, data);
2313
2314 if(ctx->reconnect_at.tv_sec && Curl_timediff(now, ctx->reconnect_at) < 0) {
2315 /* Not time yet to attempt the next connect */
2316 DEBUGF(LOG_CF(data, cf, "waiting for reconnect time"));
2317 goto out;
2318 }
2319
2320 if(!ctx->qconn) {
2321 ctx->started_at = now;
2322 result = cf_connect_start(cf, data);
2323 if(result)
2324 goto out;
2325 result = cf_flush_egress(cf, data);
2326 /* we do not expect to be able to recv anything yet */
2327 goto out;
2328 }
2329
2330 result = cf_process_ingress(cf, data);
2331 if(result)
2332 goto out;
2333
2334 result = cf_flush_egress(cf, data);
2335 if(result)
2336 goto out;
2337
2338 if(ngtcp2_conn_get_handshake_completed(ctx->qconn)) {
2339 ctx->handshake_at = now;
2340 DEBUGF(LOG_CF(data, cf, "handshake complete after %dms",
2341 (int)Curl_timediff(now, ctx->started_at)));
2342 result = qng_verify_peer(cf, data);
2343 if(!result) {
2344 DEBUGF(LOG_CF(data, cf, "peer verified"));
2345 cf->connected = TRUE;
2346 cf->conn->alpn = CURL_HTTP_VERSION_3;
2347 *done = TRUE;
2348 connkeep(cf->conn, "HTTP/3 default");
2349 }
2350 }
2351
2352out:
2353 if(result == CURLE_RECV_ERROR && ctx->qconn &&
2354 ngtcp2_conn_is_in_draining_period(ctx->qconn)) {
2355 /* When a QUIC server instance is shutting down, it may send us a
2356 * CONNECTION_CLOSE right away. Our connection then enters the DRAINING
2357 * state.
2358 * This may be a stopping of the service or it may be that the server
2359 * is reloading and a new instance will start serving soon.
2360 * In any case, we tear down our socket and start over with a new one.
2361 * We re-open the underlying UDP cf right now, but do not start
2362 * connecting until called again.
2363 */
2364 int reconn_delay_ms = 200;
2365
2366 DEBUGF(LOG_CF(data, cf, "connect, remote closed, reconnect after %dms",
2367 reconn_delay_ms));
2368 Curl_conn_cf_close(cf->next, data);
2369 cf_ngtcp2_ctx_clear(ctx);
2370 result = Curl_conn_cf_connect(cf->next, data, FALSE, done);
2371 if(!result && *done) {
2372 *done = FALSE;
2373 ctx->reconnect_at = now;
2374 ctx->reconnect_at.tv_usec += reconn_delay_ms * 1000;
2375 Curl_expire(data, reconn_delay_ms, EXPIRE_QUIC);
2376 result = CURLE_OK;
2377 }
2378 }
2379
2380#ifndef CURL_DISABLE_VERBOSE_STRINGS
2381 if(result) {
2382 const char *r_ip;
2383 int r_port;
2384
2385 Curl_cf_socket_peek(cf->next, data, NULL, NULL,
2386 &r_ip, &r_port, NULL, NULL);
2387 infof(data, "QUIC connect to %s port %u failed: %s",
2388 r_ip, r_port, curl_easy_strerror(result));
2389 }
2390#endif
2391 DEBUGF(LOG_CF(data, cf, "connect -> %d, done=%d", result, *done));
2392 CF_DATA_RESTORE(cf, save);
2393 return result;
2394}
2395
2396static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf,
2397 struct Curl_easy *data,
2398 int query, int *pres1, void *pres2)
2399{
2400 struct cf_ngtcp2_ctx *ctx = cf->ctx;
2401 struct cf_call_data save;
2402
2403 switch(query) {
2404 case CF_QUERY_MAX_CONCURRENT: {
2405 const ngtcp2_transport_params *rp;
2406 DEBUGASSERT(pres1);
2407
2408 CF_DATA_SAVE(save, cf, data);
2409 rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn);
2410 if(rp)
2411 *pres1 = (rp->initial_max_streams_bidi > INT_MAX)?
2412 INT_MAX : (int)rp->initial_max_streams_bidi;
2413 else /* not arrived yet? */
2414 *pres1 = Curl_multi_max_concurrent_streams(data->multi);
2415 DEBUGF(LOG_CF(data, cf, "query max_conncurrent -> %d", *pres1));
2416 CF_DATA_RESTORE(cf, save);
2417 return CURLE_OK;
2418 }
2419 case CF_QUERY_CONNECT_REPLY_MS:
2420 if(ctx->got_first_byte) {
2421 timediff_t ms = Curl_timediff(ctx->first_byte_at, ctx->started_at);
2422 *pres1 = (ms < INT_MAX)? (int)ms : INT_MAX;
2423 }
2424 else
2425 *pres1 = -1;
2426 return CURLE_OK;
2427 case CF_QUERY_TIMER_CONNECT: {
2428 struct curltime *when = pres2;
2429 if(ctx->got_first_byte)
2430 *when = ctx->first_byte_at;
2431 return CURLE_OK;
2432 }
2433 case CF_QUERY_TIMER_APPCONNECT: {
2434 struct curltime *when = pres2;
2435 if(cf->connected)
2436 *when = ctx->handshake_at;
2437 return CURLE_OK;
2438 }
2439 default:
2440 break;
2441 }
2442 return cf->next?
2443 cf->next->cft->query(cf->next, data, query, pres1, pres2) :
2444 CURLE_UNKNOWN_OPTION;
2445}
2446
2447static bool cf_ngtcp2_conn_is_alive(struct Curl_cfilter *cf,
2448 struct Curl_easy *data,
2449 bool *input_pending)
2450{
2451 bool alive = TRUE;
2452
2453 *input_pending = FALSE;
2454 if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending))
2455 return FALSE;
2456
2457 if(*input_pending) {
2458 /* This happens before we've sent off a request and the connection is
2459 not in use by any other transfer, there shouldn't be any data here,
2460 only "protocol frames" */
2461 *input_pending = FALSE;
2462 Curl_attach_connection(data, cf->conn);
2463 if(cf_process_ingress(cf, data))
2464 alive = FALSE;
2465 else {
2466 alive = TRUE;
2467 }
2468 Curl_detach_connection(data);
2469 }
2470
2471 return alive;
2472}
2473
2474struct Curl_cftype Curl_cft_http3 = {
2475 "HTTP/3",
2476 CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX,
2477 0,
2478 cf_ngtcp2_destroy,
2479 cf_ngtcp2_connect,
2480 cf_ngtcp2_close,
2481 Curl_cf_def_get_host,
2482 cf_ngtcp2_get_select_socks,
2483 cf_ngtcp2_data_pending,
2484 cf_ngtcp2_send,
2485 cf_ngtcp2_recv,
2486 cf_ngtcp2_data_event,
2487 cf_ngtcp2_conn_is_alive,
2488 Curl_cf_def_conn_keep_alive,
2489 cf_ngtcp2_query,
2490};
2491
2492CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf,
2493 struct Curl_easy *data,
2494 struct connectdata *conn,
2495 const struct Curl_addrinfo *ai)
2496{
2497 struct cf_ngtcp2_ctx *ctx = NULL;
2498 struct Curl_cfilter *cf = NULL, *udp_cf = NULL;
2499 CURLcode result;
2500
2501 (void)data;
2502 ctx = calloc(sizeof(*ctx), 1);
2503 if(!ctx) {
2504 result = CURLE_OUT_OF_MEMORY;
2505 goto out;
2506 }
2507 ctx->qlogfd = -1;
2508 cf_ngtcp2_ctx_clear(ctx);
2509
2510 result = Curl_cf_create(&cf, &Curl_cft_http3, ctx);
2511 if(result)
2512 goto out;
2513
2514 result = Curl_cf_udp_create(&udp_cf, data, conn, ai, TRNSPRT_QUIC);
2515 if(result)
2516 goto out;
2517
2518 cf->conn = conn;
2519 udp_cf->conn = cf->conn;
2520 udp_cf->sockindex = cf->sockindex;
2521 cf->next = udp_cf;
2522
2523out:
2524 *pcf = (!result)? cf : NULL;
2525 if(result) {
2526 if(udp_cf)
2527 Curl_conn_cf_discard(udp_cf, data);
2528 Curl_safefree(cf);
2529 Curl_safefree(ctx);
2530 }
2531 return result;
2532}
2533
2534bool Curl_conn_is_ngtcp2(const struct Curl_easy *data,
2535 const struct connectdata *conn,
2536 int sockindex)
2537{
2538 struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL;
2539
2540 (void)data;
2541 for(; cf; cf = cf->next) {
2542 if(cf->cft == &Curl_cft_http3)
2543 return TRUE;
2544 if(cf->cft->flags & CF_TYPE_IP_CONNECT)
2545 return FALSE;
2546 }
2547 return FALSE;
2548}
2549
2550#endif
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette