VirtualBox

source: vbox/trunk/src/libs/curl-7.83.1/lib/vquic/ngtcp2.c@ 97623

Last change on this file since 97623 was 95312, checked in by vboxsync, 3 years ago

libs/{curl,libxml2}: OSE export fixes, bugref:8515

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

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