VirtualBox

source: vbox/trunk/src/libs/curl-7.87.0/lib/vquic/ngtcp2.c@ 98697

Last change on this file since 98697 was 98326, checked in by vboxsync, 2 years ago

curl-7.87.0: Applied and adjusted our curl changes to 7.83.1. bugref:10356

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