comparison mupdf-source/thirdparty/curl/lib/vquic/ngtcp2.c @ 2:b50eed0cc0ef upstream

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.haxx.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 #include <openssl/err.h>
30 #include "urldata.h"
31 #include "sendf.h"
32 #include "strdup.h"
33 #include "rand.h"
34 #include "ngtcp2.h"
35 #include "multiif.h"
36 #include "strcase.h"
37 #include "connect.h"
38 #include "strerror.h"
39
40 /* The last 3 #include files should be in this order */
41 #include "curl_printf.h"
42 #include "curl_memory.h"
43 #include "memdebug.h"
44
45 /* #define DEBUG_NGTCP2 */
46 #define DEBUG_HTTP3
47 #ifdef DEBUG_HTTP3
48 #define H3BUGF(x) x
49 #else
50 #define H3BUGF(x) do { } WHILE_FALSE
51 #endif
52
53 /*
54 * This holds outgoing HTTP/3 stream data that is used by nghttp3 until acked.
55 * It is used as a circular buffer. Add new bytes at the end until it reaches
56 * the far end, then start over at index 0 again.
57 */
58
59 #define H3_SEND_SIZE (20*1024)
60 struct h3out {
61 uint8_t buf[H3_SEND_SIZE];
62 size_t used; /* number of bytes used in the buffer */
63 size_t windex; /* index in the buffer where to start writing the next
64 data block */
65 };
66
67 #define QUIC_MAX_STREAMS (256*1024)
68 #define QUIC_MAX_DATA (1*1024*1024)
69 #define QUIC_IDLE_TIMEOUT 60000 /* milliseconds */
70 #define QUIC_CIPHERS \
71 "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \
72 "POLY1305_SHA256:TLS_AES_128_CCM_SHA256"
73 #define QUIC_GROUPS "P-256:X25519:P-384:P-521"
74
75 static CURLcode ng_process_ingress(struct connectdata *conn,
76 curl_socket_t sockfd,
77 struct quicsocket *qs);
78 static CURLcode ng_flush_egress(struct connectdata *conn, int sockfd,
79 struct quicsocket *qs);
80 static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id,
81 size_t datalen, void *user_data,
82 void *stream_user_data);
83
84 static ngtcp2_tstamp timestamp(void)
85 {
86 struct curltime ct = Curl_now();
87 return ct.tv_sec * NGTCP2_SECONDS + ct.tv_usec * NGTCP2_MICROSECONDS;
88 }
89
90 #ifdef DEBUG_NGTCP2
91 static void quic_printf(void *user_data, const char *fmt, ...)
92 {
93 va_list ap;
94 (void)user_data; /* TODO, use this to do infof() instead long-term */
95 va_start(ap, fmt);
96 vfprintf(stderr, fmt, ap);
97 va_end(ap);
98 fprintf(stderr, "\n");
99 }
100 #endif
101
102 static ngtcp2_crypto_level
103 quic_from_ossl_level(OSSL_ENCRYPTION_LEVEL ossl_level)
104 {
105 switch(ossl_level) {
106 case ssl_encryption_initial:
107 return NGTCP2_CRYPTO_LEVEL_INITIAL;
108 case ssl_encryption_early_data:
109 return NGTCP2_CRYPTO_LEVEL_EARLY;
110 case ssl_encryption_handshake:
111 return NGTCP2_CRYPTO_LEVEL_HANDSHAKE;
112 case ssl_encryption_application:
113 return NGTCP2_CRYPTO_LEVEL_APP;
114 default:
115 assert(0);
116 }
117 }
118
119 static int setup_initial_crypto_context(struct quicsocket *qs)
120 {
121 const ngtcp2_cid *dcid = ngtcp2_conn_get_dcid(qs->qconn);
122
123 if(ngtcp2_crypto_derive_and_install_initial_key(
124 qs->qconn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, dcid,
125 NGTCP2_CRYPTO_SIDE_CLIENT) != 0)
126 return -1;
127
128 return 0;
129 }
130
131 static void quic_settings(ngtcp2_settings *s,
132 uint64_t stream_buffer_size)
133 {
134 ngtcp2_settings_default(s);
135 #ifdef DEBUG_NGTCP2
136 s->log_printf = quic_printf;
137 #else
138 s->log_printf = NULL;
139 #endif
140 s->initial_ts = timestamp();
141 s->max_stream_data_bidi_local = stream_buffer_size;
142 s->max_stream_data_bidi_remote = QUIC_MAX_STREAMS;
143 s->max_stream_data_uni = QUIC_MAX_STREAMS;
144 s->max_data = QUIC_MAX_DATA;
145 s->max_streams_bidi = 1;
146 s->max_streams_uni = 3;
147 s->idle_timeout = QUIC_IDLE_TIMEOUT;
148 }
149
150 static FILE *keylog_file; /* not thread-safe */
151 static void keylog_callback(const SSL *ssl, const char *line)
152 {
153 (void)ssl;
154 fputs(line, keylog_file);
155 fputc('\n', keylog_file);
156 fflush(keylog_file);
157 }
158
159 static int init_ngh3_conn(struct quicsocket *qs);
160
161 static int quic_set_encryption_secrets(SSL *ssl,
162 OSSL_ENCRYPTION_LEVEL ossl_level,
163 const uint8_t *rx_secret,
164 const uint8_t *tx_secret,
165 size_t secretlen)
166 {
167 struct quicsocket *qs = (struct quicsocket *)SSL_get_app_data(ssl);
168 int level = quic_from_ossl_level(ossl_level);
169
170 if(ngtcp2_crypto_derive_and_install_key(
171 qs->qconn, ssl, NULL, NULL, NULL, NULL, NULL, NULL, level, rx_secret,
172 tx_secret, secretlen, NGTCP2_CRYPTO_SIDE_CLIENT) != 0)
173 return 0;
174
175 if(level == NGTCP2_CRYPTO_LEVEL_APP && init_ngh3_conn(qs) != CURLE_OK)
176 return 0;
177
178 return 1;
179 }
180
181 static int quic_add_handshake_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL ossl_level,
182 const uint8_t *data, size_t len)
183 {
184 struct quicsocket *qs = (struct quicsocket *)SSL_get_app_data(ssl);
185 struct quic_handshake *crypto_data;
186 ngtcp2_crypto_level level = quic_from_ossl_level(ossl_level);
187 int rv;
188
189 crypto_data = &qs->client_crypto_data[level];
190 if(crypto_data->buf == NULL) {
191 crypto_data->buf = malloc(4096);
192 crypto_data->alloclen = 4096;
193 /* TODO Explode if malloc failed */
194 }
195
196 /* TODO Just pretend that handshake does not grow more than 4KiB for
197 now */
198 assert(crypto_data->len + len <= crypto_data->alloclen);
199
200 memcpy(&crypto_data->buf[crypto_data->len], data, len);
201 crypto_data->len += len;
202
203 rv = ngtcp2_conn_submit_crypto_data(
204 qs->qconn, level, (uint8_t *)(&crypto_data->buf[crypto_data->len] - len),
205 len);
206 if(rv) {
207 fprintf(stderr, "write_client_handshake failed\n");
208 }
209 assert(0 == rv);
210
211 return 1;
212 }
213
214 static int quic_flush_flight(SSL *ssl)
215 {
216 (void)ssl;
217 return 1;
218 }
219
220 static int quic_send_alert(SSL *ssl, enum ssl_encryption_level_t level,
221 uint8_t alert)
222 {
223 struct quicsocket *qs = (struct quicsocket *)SSL_get_app_data(ssl);
224 (void)level;
225
226 qs->tls_alert = alert;
227 return 1;
228 }
229
230 static SSL_QUIC_METHOD quic_method = {quic_set_encryption_secrets,
231 quic_add_handshake_data,
232 quic_flush_flight, quic_send_alert};
233
234 static SSL_CTX *quic_ssl_ctx(struct Curl_easy *data)
235 {
236 SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method());
237 const char *keylog_filename;
238
239 SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION);
240 SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION);
241
242 SSL_CTX_set_default_verify_paths(ssl_ctx);
243
244 if(SSL_CTX_set_ciphersuites(ssl_ctx, QUIC_CIPHERS) != 1) {
245 failf(data, "SSL_CTX_set_ciphersuites: %s",
246 ERR_error_string(ERR_get_error(), NULL));
247 return NULL;
248 }
249
250 if(SSL_CTX_set1_groups_list(ssl_ctx, QUIC_GROUPS) != 1) {
251 failf(data, "SSL_CTX_set1_groups_list failed");
252 return NULL;
253 }
254
255 SSL_CTX_set_quic_method(ssl_ctx, &quic_method);
256
257 keylog_filename = getenv("SSLKEYLOGFILE");
258 if(keylog_filename) {
259 keylog_file = fopen(keylog_filename, "wb");
260 if(keylog_file) {
261 SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
262 }
263 }
264
265 return ssl_ctx;
266 }
267
268 /** SSL callbacks ***/
269
270 static int quic_init_ssl(struct quicsocket *qs)
271 {
272 const uint8_t *alpn = NULL;
273 size_t alpnlen = 0;
274 /* this will need some attention when HTTPS proxy over QUIC get fixed */
275 const char * const hostname = qs->conn->host.name;
276
277 if(qs->ssl)
278 SSL_free(qs->ssl);
279
280 qs->ssl = SSL_new(qs->sslctx);
281
282 SSL_set_app_data(qs->ssl, qs);
283 SSL_set_connect_state(qs->ssl);
284
285 switch(qs->version) {
286 #ifdef NGTCP2_PROTO_VER
287 case NGTCP2_PROTO_VER:
288 alpn = (const uint8_t *)NGTCP2_ALPN_H3;
289 alpnlen = sizeof(NGTCP2_ALPN_H3) - 1;
290 break;
291 #endif
292 }
293 if(alpn)
294 SSL_set_alpn_protos(qs->ssl, alpn, (int)alpnlen);
295
296 /* set SNI */
297 SSL_set_tlsext_host_name(qs->ssl, hostname);
298 return 0;
299 }
300
301 static int cb_initial(ngtcp2_conn *quic, void *user_data)
302 {
303 struct quicsocket *qs = (struct quicsocket *)user_data;
304
305 if(ngtcp2_crypto_read_write_crypto_data(
306 quic, qs->ssl, NGTCP2_CRYPTO_LEVEL_INITIAL, NULL, 0) != 0)
307 return NGTCP2_ERR_CALLBACK_FAILURE;
308
309 return 0;
310 }
311
312 static int
313 cb_recv_crypto_data(ngtcp2_conn *tconn, ngtcp2_crypto_level crypto_level,
314 uint64_t offset,
315 const uint8_t *data, size_t datalen,
316 void *user_data)
317 {
318 struct quicsocket *qs = (struct quicsocket *)user_data;
319 (void)offset;
320
321 if(ngtcp2_crypto_read_write_crypto_data(tconn, qs->ssl, crypto_level, data,
322 datalen) != 0)
323 return NGTCP2_ERR_CRYPTO;
324
325 return 0;
326 }
327
328 static int cb_handshake_completed(ngtcp2_conn *tconn, void *user_data)
329 {
330 struct quicsocket *qs = (struct quicsocket *)user_data;
331 (void)tconn;
332 infof(qs->conn->data, "QUIC handshake is completed\n");
333
334 return 0;
335 }
336
337 static int cb_recv_stream_data(ngtcp2_conn *tconn, int64_t stream_id,
338 int fin, uint64_t offset,
339 const uint8_t *buf, size_t buflen,
340 void *user_data, void *stream_user_data)
341 {
342 struct quicsocket *qs = (struct quicsocket *)user_data;
343 ssize_t nconsumed;
344 (void)offset;
345 (void)stream_user_data;
346
347 infof(qs->conn->data, "Received %ld bytes data on stream %u\n",
348 buflen, stream_id);
349
350 nconsumed =
351 nghttp3_conn_read_stream(qs->h3conn, stream_id, buf, buflen, fin);
352 if(nconsumed < 0) {
353 failf(qs->conn->data, "nghttp3_conn_read_stream returned error: %s\n",
354 nghttp3_strerror((int)nconsumed));
355 return NGTCP2_ERR_CALLBACK_FAILURE;
356 }
357
358 ngtcp2_conn_extend_max_stream_offset(tconn, stream_id, nconsumed);
359 ngtcp2_conn_extend_max_offset(tconn, nconsumed);
360
361 return 0;
362 }
363
364 static int
365 cb_acked_stream_data_offset(ngtcp2_conn *tconn, int64_t stream_id,
366 uint64_t offset, size_t datalen, void *user_data,
367 void *stream_user_data)
368 {
369 struct quicsocket *qs = (struct quicsocket *)user_data;
370 int rv;
371 (void)stream_id;
372 (void)tconn;
373 (void)offset;
374 (void)datalen;
375 (void)stream_user_data;
376
377 rv = nghttp3_conn_add_ack_offset(qs->h3conn, stream_id, datalen);
378 if(rv != 0) {
379 failf(qs->conn->data, "nghttp3_conn_add_ack_offset returned error: %s\n",
380 nghttp3_strerror(rv));
381 return NGTCP2_ERR_CALLBACK_FAILURE;
382 }
383
384 return 0;
385 }
386
387 static int cb_stream_close(ngtcp2_conn *tconn, int64_t stream_id,
388 uint64_t app_error_code,
389 void *user_data, void *stream_user_data)
390 {
391 struct quicsocket *qs = (struct quicsocket *)user_data;
392 int rv;
393 (void)tconn;
394 (void)stream_user_data;
395 /* stream is closed... */
396
397 rv = nghttp3_conn_close_stream(qs->h3conn, stream_id,
398 app_error_code);
399 if(rv != 0) {
400 failf(qs->conn->data, "nghttp3_conn_close_stream returned error: %s\n",
401 nghttp3_strerror(rv));
402 return NGTCP2_ERR_CALLBACK_FAILURE;
403 }
404
405 return 0;
406 }
407
408 static int cb_stream_reset(ngtcp2_conn *tconn, int64_t stream_id,
409 uint64_t final_size, uint64_t app_error_code,
410 void *user_data, void *stream_user_data)
411 {
412 struct quicsocket *qs = (struct quicsocket *)user_data;
413 int rv;
414 (void)tconn;
415 (void)final_size;
416 (void)app_error_code;
417 (void)stream_user_data;
418
419 rv = nghttp3_conn_reset_stream(qs->h3conn, stream_id);
420 if(rv != 0) {
421 failf(qs->conn->data, "nghttp3_conn_reset_stream returned error: %s\n",
422 nghttp3_strerror(rv));
423 return NGTCP2_ERR_CALLBACK_FAILURE;
424 }
425
426 return 0;
427 }
428
429 static int cb_recv_retry(ngtcp2_conn *tconn, const ngtcp2_pkt_hd *hd,
430 const ngtcp2_pkt_retry *retry, void *user_data)
431 {
432 /* Re-generate handshake secrets here because connection ID might change. */
433 struct quicsocket *qs = (struct quicsocket *)user_data;
434 (void)tconn;
435 (void)hd;
436 (void)retry;
437
438 setup_initial_crypto_context(qs);
439
440 return 0;
441 }
442
443 static int cb_extend_max_local_streams_bidi(ngtcp2_conn *tconn,
444 uint64_t max_streams,
445 void *user_data)
446 {
447 (void)tconn;
448 (void)max_streams;
449 (void)user_data;
450
451 return 0;
452 }
453
454 static int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t stream_id,
455 uint64_t max_data, void *user_data,
456 void *stream_user_data)
457 {
458 struct quicsocket *qs = (struct quicsocket *)user_data;
459 int rv;
460 (void)tconn;
461 (void)max_data;
462 (void)stream_user_data;
463
464 rv = nghttp3_conn_unblock_stream(qs->h3conn, stream_id);
465 if(rv != 0) {
466 failf(qs->conn->data, "nghttp3_conn_unblock_stream returned error: %s\n",
467 nghttp3_strerror(rv));
468 return NGTCP2_ERR_CALLBACK_FAILURE;
469 }
470
471 return 0;
472 }
473
474 static int cb_get_new_connection_id(ngtcp2_conn *tconn, ngtcp2_cid *cid,
475 uint8_t *token, size_t cidlen,
476 void *user_data)
477 {
478 struct quicsocket *qs = (struct quicsocket *)user_data;
479 CURLcode result;
480 (void)tconn;
481
482 result = Curl_rand(qs->conn->data, cid->data, cidlen);
483 if(result)
484 return NGTCP2_ERR_CALLBACK_FAILURE;
485 cid->datalen = cidlen;
486
487 result = Curl_rand(qs->conn->data, token, NGTCP2_STATELESS_RESET_TOKENLEN);
488 if(result)
489 return NGTCP2_ERR_CALLBACK_FAILURE;
490
491 return 0;
492 }
493
494 static ngtcp2_conn_callbacks ng_callbacks = {
495 cb_initial,
496 NULL, /* recv_client_initial */
497 cb_recv_crypto_data,
498 cb_handshake_completed,
499 NULL, /* recv_version_negotiation */
500 ngtcp2_crypto_encrypt_cb,
501 ngtcp2_crypto_decrypt_cb,
502 ngtcp2_crypto_hp_mask_cb,
503 cb_recv_stream_data,
504 NULL, /* acked_crypto_offset */
505 cb_acked_stream_data_offset,
506 NULL, /* stream_open */
507 cb_stream_close,
508 NULL, /* recv_stateless_reset */
509 cb_recv_retry,
510 cb_extend_max_local_streams_bidi,
511 NULL, /* extend_max_local_streams_uni */
512 NULL, /* rand */
513 cb_get_new_connection_id,
514 NULL, /* remove_connection_id */
515 NULL, /* update_key */
516 NULL, /* path_validation */
517 NULL, /* select_preferred_addr */
518 cb_stream_reset,
519 NULL, /* extend_max_remote_streams_bidi */
520 NULL, /* extend_max_remote_streams_uni */
521 cb_extend_max_stream_data,
522 };
523
524 /*
525 * Might be called twice for happy eyeballs.
526 */
527 CURLcode Curl_quic_connect(struct connectdata *conn,
528 curl_socket_t sockfd,
529 int sockindex,
530 const struct sockaddr *addr,
531 socklen_t addrlen)
532 {
533 int rc;
534 int rv;
535 CURLcode result;
536 ngtcp2_path path; /* TODO: this must be initialized properly */
537 struct Curl_easy *data = conn->data;
538 struct quicsocket *qs = &conn->hequic[sockindex];
539 char ipbuf[40];
540 long port;
541 uint8_t paramsbuf[64];
542 ngtcp2_transport_params params;
543 ssize_t nwrite;
544
545 qs->conn = conn;
546
547 /* extract the used address as a string */
548 if(!Curl_addr2string((struct sockaddr*)addr, addrlen, ipbuf, &port)) {
549 char buffer[STRERROR_LEN];
550 failf(data, "ssrem inet_ntop() failed with errno %d: %s",
551 errno, Curl_strerror(errno, buffer, sizeof(buffer)));
552 return CURLE_BAD_FUNCTION_ARGUMENT;
553 }
554
555 infof(data, "Connect socket %d over QUIC to %s:%ld\n",
556 sockfd, ipbuf, port);
557
558 qs->version = NGTCP2_PROTO_VER;
559 qs->sslctx = quic_ssl_ctx(data);
560 if(!qs->sslctx)
561 return CURLE_FAILED_INIT; /* TODO: better return code */
562
563 if(quic_init_ssl(qs))
564 return CURLE_FAILED_INIT; /* TODO: better return code */
565
566 qs->dcid.datalen = NGTCP2_MAX_CIDLEN;
567 result = Curl_rand(data, qs->dcid.data, NGTCP2_MAX_CIDLEN);
568 if(result)
569 return result;
570
571 qs->scid.datalen = NGTCP2_MAX_CIDLEN;
572 result = Curl_rand(data, qs->scid.data, NGTCP2_MAX_CIDLEN);
573 if(result)
574 return result;
575
576 quic_settings(&qs->settings, data->set.buffer_size);
577
578 qs->local_addrlen = sizeof(qs->local_addr);
579 rv = getsockname(sockfd, (struct sockaddr *)&qs->local_addr,
580 &qs->local_addrlen);
581 if(rv == -1)
582 return CURLE_FAILED_INIT;
583
584 ngtcp2_addr_init(&path.local, (uint8_t *)&qs->local_addr, qs->local_addrlen,
585 NULL);
586 ngtcp2_addr_init(&path.remote, (uint8_t*)addr, addrlen, NULL);
587
588 #ifdef NGTCP2_PROTO_VER
589 #define QUICVER NGTCP2_PROTO_VER
590 #else
591 #error "unsupported ngtcp2 version"
592 #endif
593 rc = ngtcp2_conn_client_new(&qs->qconn, &qs->dcid, &qs->scid, &path, QUICVER,
594 &ng_callbacks, &qs->settings, NULL, qs);
595 if(rc)
596 return CURLE_FAILED_INIT; /* TODO: create a QUIC error code */
597
598 ngtcp2_conn_get_local_transport_params(qs->qconn, &params);
599 nwrite = ngtcp2_encode_transport_params(
600 paramsbuf, sizeof(paramsbuf), NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO,
601 &params);
602 if(nwrite < 0) {
603 fprintf(stderr, "ngtcp2_encode_transport_params: %s\n",
604 ngtcp2_strerror((int)nwrite));
605
606 return CURLE_FAILED_INIT;
607 }
608
609 if(!SSL_set_quic_transport_params(qs->ssl, paramsbuf, nwrite))
610 return CURLE_FAILED_INIT;
611
612 rc = setup_initial_crypto_context(qs);
613 if(rc)
614 return CURLE_FAILED_INIT; /* TODO: better return code */
615
616 return CURLE_OK;
617 }
618
619 /*
620 * Store ngtp2 version info in this buffer, Prefix with a space. Return total
621 * length written.
622 */
623 int Curl_quic_ver(char *p, size_t len)
624 {
625 ngtcp2_info *ng2 = ngtcp2_version(0);
626 nghttp3_info *ht3 = nghttp3_version(0);
627 return msnprintf(p, len, " ngtcp2/%s nghttp3/%s",
628 ng2->version_str, ht3->version_str);
629 }
630
631 static int ng_getsock(struct connectdata *conn, curl_socket_t *socks)
632 {
633 struct SingleRequest *k = &conn->data->req;
634 int bitmap = GETSOCK_BLANK;
635
636 socks[0] = conn->sock[FIRSTSOCKET];
637
638 /* in a HTTP/2 connection we can basically always get a frame so we should
639 always be ready for one */
640 bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
641
642 /* we're still uploading or the HTTP/2 layer wants to send data */
643 if((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND)
644 bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
645
646 return bitmap;
647 }
648
649 static int ng_perform_getsock(const struct connectdata *conn,
650 curl_socket_t *socks)
651 {
652 return ng_getsock((struct connectdata *)conn, socks);
653 }
654
655 static CURLcode ng_disconnect(struct connectdata *conn,
656 bool dead_connection)
657 {
658 (void)conn;
659 (void)dead_connection;
660 return CURLE_OK;
661 }
662
663 static unsigned int ng_conncheck(struct connectdata *conn,
664 unsigned int checks_to_perform)
665 {
666 (void)conn;
667 (void)checks_to_perform;
668 return CONNRESULT_NONE;
669 }
670
671 static const struct Curl_handler Curl_handler_http3 = {
672 "HTTPS", /* scheme */
673 ZERO_NULL, /* setup_connection */
674 Curl_http, /* do_it */
675 Curl_http_done, /* done */
676 ZERO_NULL, /* do_more */
677 ZERO_NULL, /* connect_it */
678 ZERO_NULL, /* connecting */
679 ZERO_NULL, /* doing */
680 ng_getsock, /* proto_getsock */
681 ng_getsock, /* doing_getsock */
682 ZERO_NULL, /* domore_getsock */
683 ng_perform_getsock, /* perform_getsock */
684 ng_disconnect, /* disconnect */
685 ZERO_NULL, /* readwrite */
686 ng_conncheck, /* connection_check */
687 PORT_HTTP, /* defport */
688 CURLPROTO_HTTPS, /* protocol */
689 PROTOPT_SSL | PROTOPT_STREAM /* flags */
690 };
691
692 static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id,
693 uint64_t app_error_code, void *user_data,
694 void *stream_user_data)
695 {
696 struct Curl_easy *data = stream_user_data;
697 struct HTTP *stream = data->req.protop;
698 (void)conn;
699 (void)stream_id;
700 (void)app_error_code;
701 (void)user_data;
702 fprintf(stderr, "cb_h3_stream_close CALLED\n");
703
704 stream->closed = TRUE;
705 Curl_expire(data, 0, EXPIRE_QUIC);
706 return 0;
707 }
708
709 static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream_id,
710 const uint8_t *buf, size_t buflen,
711 void *user_data, void *stream_user_data)
712 {
713 struct quicsocket *qs = user_data;
714 size_t ncopy;
715 struct Curl_easy *data = stream_user_data;
716 struct HTTP *stream = data->req.protop;
717 (void)conn;
718 fprintf(stderr, "cb_h3_recv_data CALLED with %d bytes\n", buflen);
719
720 /* TODO: this needs to be handled properly */
721 DEBUGASSERT(buflen <= stream->len);
722
723 ncopy = CURLMIN(stream->len, buflen);
724 memcpy(stream->mem, buf, ncopy);
725 stream->len -= ncopy;
726 stream->memlen += ncopy;
727 #if 0 /* extra debugging of incoming h3 data */
728 fprintf(stderr, "!! Copies %zd bytes to %p (total %zd)\n",
729 ncopy, stream->mem, stream->memlen);
730 {
731 size_t i;
732 for(i = 0; i < ncopy; i++) {
733 fprintf(stderr, "!! data[%d]: %02x '%c'\n", i, buf[i], buf[i]);
734 }
735 }
736 #endif
737 stream->mem += ncopy;
738
739 ngtcp2_conn_extend_max_stream_offset(qs->qconn, stream_id, buflen);
740 ngtcp2_conn_extend_max_offset(qs->qconn, buflen);
741
742 return 0;
743 }
744
745 static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream_id,
746 size_t consumed, void *user_data,
747 void *stream_user_data)
748 {
749 struct quicsocket *qs = user_data;
750 (void)conn;
751 (void)stream_user_data;
752 fprintf(stderr, "cb_h3_deferred_consume CALLED\n");
753
754 ngtcp2_conn_extend_max_stream_offset(qs->qconn, stream_id, consumed);
755 ngtcp2_conn_extend_max_offset(qs->qconn, consumed);
756
757 return 0;
758 }
759
760 /* Decode HTTP status code. Returns -1 if no valid status code was
761 decoded. (duplicate from http2.c) */
762 static int decode_status_code(const uint8_t *value, size_t len)
763 {
764 int i;
765 int res;
766
767 if(len != 3) {
768 return -1;
769 }
770
771 res = 0;
772
773 for(i = 0; i < 3; ++i) {
774 char c = value[i];
775
776 if(c < '0' || c > '9') {
777 return -1;
778 }
779
780 res *= 10;
781 res += c - '0';
782 }
783
784 return res;
785 }
786
787 static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id,
788 void *user_data, void *stream_user_data)
789 {
790 struct Curl_easy *data = stream_user_data;
791 struct HTTP *stream = data->req.protop;
792 (void)conn;
793 (void)stream_id;
794 (void)user_data;
795
796 if(stream->memlen >= 2) {
797 memcpy(stream->mem, "\r\n", 2);
798 stream->len -= 2;
799 stream->memlen += 2;
800 stream->mem += 2;
801 }
802 return 0;
803 }
804
805 static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id,
806 int32_t token, nghttp3_rcbuf *name,
807 nghttp3_rcbuf *value, uint8_t flags,
808 void *user_data, void *stream_user_data)
809 {
810 nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name);
811 nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value);
812 struct Curl_easy *data = stream_user_data;
813 struct HTTP *stream = data->req.protop;
814 size_t ncopy;
815 (void)conn;
816 (void)stream_id;
817 (void)token;
818 (void)flags;
819 (void)user_data;
820
821 fprintf(stderr, "cb_h3_recv_header called!\n");
822
823 if(h3name.len == sizeof(":status") - 1 &&
824 !memcmp(":status", h3name.base, h3name.len)) {
825 int status = decode_status_code(h3val.base, h3val.len);
826 DEBUGASSERT(status != -1);
827 msnprintf(stream->mem, stream->len, "HTTP/3 %03d \r\n", status);
828 }
829 else {
830 /* store as a HTTP1-style header */
831 msnprintf(stream->mem, stream->len, "%.*s: %.*s\n",
832 h3name.len, h3name.base, h3val.len, h3val.base);
833 }
834
835 ncopy = strlen(stream->mem);
836 stream->len -= ncopy;
837 stream->memlen += ncopy;
838 stream->mem += ncopy;
839 return 0;
840 }
841
842 static int cb_h3_send_stop_sending(nghttp3_conn *conn, int64_t stream_id,
843 uint64_t app_error_code,
844 void *user_data,
845 void *stream_user_data)
846 {
847 (void)conn;
848 (void)stream_id;
849 (void)app_error_code;
850 (void)user_data;
851 (void)stream_user_data;
852 fprintf(stderr, "cb_h3_send_stop_sending CALLED\n");
853 return 0;
854 }
855
856 static nghttp3_conn_callbacks ngh3_callbacks = {
857 cb_h3_acked_stream_data, /* acked_stream_data */
858 cb_h3_stream_close,
859 cb_h3_recv_data,
860 cb_h3_deferred_consume,
861 NULL, /* begin_headers */
862 cb_h3_recv_header,
863 cb_h3_end_headers,
864 NULL, /* begin_trailers */
865 cb_h3_recv_header,
866 NULL, /* end_trailers */
867 NULL, /* http_begin_push_promise */
868 NULL, /* http_recv_push_promise */
869 NULL, /* http_end_push_promise */
870 NULL, /* http_cancel_push */
871 cb_h3_send_stop_sending,
872 NULL, /* push_stream */
873 NULL, /* end_stream */
874 };
875
876 static int init_ngh3_conn(struct quicsocket *qs)
877 {
878 CURLcode result;
879 int rc;
880 int64_t ctrl_stream_id, qpack_enc_stream_id, qpack_dec_stream_id;
881
882 if(ngtcp2_conn_get_max_local_streams_uni(qs->qconn) < 3) {
883 failf(qs->conn->data, "too few available QUIC streams");
884 return CURLE_FAILED_INIT;
885 }
886
887 nghttp3_conn_settings_default(&qs->h3settings);
888
889 rc = nghttp3_conn_client_new(&qs->h3conn,
890 &ngh3_callbacks,
891 &qs->h3settings,
892 nghttp3_mem_default(),
893 qs);
894 if(rc) {
895 result = CURLE_OUT_OF_MEMORY;
896 goto fail;
897 }
898
899 rc = ngtcp2_conn_open_uni_stream(qs->qconn, &ctrl_stream_id, NULL);
900 if(rc) {
901 result = CURLE_FAILED_INIT;
902 goto fail;
903 }
904
905 rc = nghttp3_conn_bind_control_stream(qs->h3conn, ctrl_stream_id);
906 if(rc) {
907 result = CURLE_FAILED_INIT;
908 goto fail;
909 }
910
911 rc = ngtcp2_conn_open_uni_stream(qs->qconn, &qpack_enc_stream_id, NULL);
912 if(rc) {
913 result = CURLE_FAILED_INIT;
914 goto fail;
915 }
916
917 rc = ngtcp2_conn_open_uni_stream(qs->qconn, &qpack_dec_stream_id, NULL);
918 if(rc) {
919 result = CURLE_FAILED_INIT;
920 goto fail;
921 }
922
923 rc = nghttp3_conn_bind_qpack_streams(qs->h3conn, qpack_enc_stream_id,
924 qpack_dec_stream_id);
925 if(rc) {
926 result = CURLE_FAILED_INIT;
927 goto fail;
928 }
929
930 return CURLE_OK;
931 fail:
932
933 return result;
934 }
935
936 static Curl_recv ngh3_stream_recv;
937 static Curl_send ngh3_stream_send;
938
939 /* incoming data frames on the h3 stream */
940 static ssize_t ngh3_stream_recv(struct connectdata *conn,
941 int sockindex,
942 char *buf,
943 size_t buffersize,
944 CURLcode *curlcode)
945 {
946 curl_socket_t sockfd = conn->sock[sockindex];
947 struct HTTP *stream = conn->data->req.protop;
948 struct quicsocket *qs = conn->quic;
949
950 fprintf(stderr, "ngh3_stream_recv CALLED (easy %p, socket %d)\n",
951 conn->data, sockfd);
952
953 if(!stream->memlen) {
954 /* remember where to store incoming data for this stream and how big the
955 buffer is */
956 stream->mem = buf;
957 stream->len = buffersize;
958 }
959 /* else, there's data in the buffer already */
960
961 if(ng_process_ingress(conn, sockfd, qs)) {
962 *curlcode = CURLE_RECV_ERROR;
963 return -1;
964 }
965 if(ng_flush_egress(conn, sockfd, qs)) {
966 *curlcode = CURLE_SEND_ERROR;
967 return -1;
968 }
969
970 if(stream->memlen) {
971 ssize_t memlen = stream->memlen;
972 /* data arrived */
973 *curlcode = CURLE_OK;
974 /* reset to allow more data to come */
975 stream->memlen = 0;
976 stream->mem = buf;
977 stream->len = buffersize;
978 H3BUGF(infof(conn->data, "!! ngh3_stream_recv returns %zd bytes at %p\n",
979 memlen, buf));
980 return memlen;
981 }
982
983 if(stream->closed) {
984 *curlcode = CURLE_OK;
985 return 0;
986 }
987
988 infof(conn->data, "ngh3_stream_recv returns 0 bytes and EAGAIN\n");
989 *curlcode = CURLE_AGAIN;
990 return -1;
991 }
992
993 /* this amount of data has now been acked on this stream */
994 static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id,
995 size_t datalen, void *user_data,
996 void *stream_user_data)
997 {
998 struct Curl_easy *data = stream_user_data;
999 struct HTTP *stream = data->req.protop;
1000 (void)conn;
1001 (void)stream_id;
1002 (void)user_data;
1003
1004 if(!data->set.postfields) {
1005 stream->h3out->used -= datalen;
1006 fprintf(stderr, "cb_h3_acked_stream_data, %zd bytes, %zd left unacked\n",
1007 datalen, stream->h3out->used);
1008 DEBUGASSERT(stream->h3out->used < H3_SEND_SIZE);
1009 }
1010 return 0;
1011 }
1012
1013 static int cb_h3_readfunction(nghttp3_conn *conn, int64_t stream_id,
1014 const uint8_t **pdata,
1015 size_t *pdatalen, uint32_t *pflags,
1016 void *user_data, void *stream_user_data)
1017 {
1018 struct Curl_easy *data = stream_user_data;
1019 size_t nread;
1020 struct HTTP *stream = data->req.protop;
1021 (void)conn;
1022 (void)stream_id;
1023 (void)user_data;
1024
1025 if(data->set.postfields) {
1026 *pdata = data->set.postfields;
1027 *pdatalen = data->state.infilesize;
1028 *pflags = NGHTTP3_DATA_FLAG_EOF;
1029 return 0;
1030 }
1031
1032 nread = CURLMIN(stream->upload_len, H3_SEND_SIZE - stream->h3out->used);
1033 if(nread > 0) {
1034 /* nghttp3 wants us to hold on to the data until it tells us it is okay to
1035 delete it. Append the data at the end of the h3out buffer. Since we can
1036 only return consecutive data, copy the amount that fits and the next
1037 part comes in next invoke. */
1038 struct h3out *out = stream->h3out;
1039 if(nread + out->windex > H3_SEND_SIZE)
1040 nread = H3_SEND_SIZE - out->windex;
1041
1042 memcpy(&out->buf[out->windex], stream->upload_mem, nread);
1043 out->windex += nread;
1044 out->used += nread;
1045
1046 /* that's the chunk we return to nghttp3 */
1047 *pdata = &out->buf[out->windex];
1048 *pdatalen = nread;
1049
1050 if(out->windex == H3_SEND_SIZE)
1051 out->windex = 0; /* wrap */
1052 stream->upload_mem += nread;
1053 stream->upload_len -= nread;
1054 if(data->state.infilesize != -1) {
1055 stream->upload_left -= nread;
1056 if(!stream->upload_left)
1057 *pflags = NGHTTP3_DATA_FLAG_EOF;
1058 }
1059 fprintf(stderr, "cb_h3_readfunction %zd bytes%s (at %zd unacked)\n",
1060 nread, *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"",
1061 out->used);
1062 }
1063 if(stream->upload_done && !stream->upload_len &&
1064 (stream->upload_left <= 0)) {
1065 H3BUGF(infof(data, "!!!!!!!!! cb_h3_readfunction sets EOF\n"));
1066 *pdata = NULL;
1067 *pdatalen = 0;
1068 *pflags = NGHTTP3_DATA_FLAG_EOF;
1069 }
1070 else if(!nread) {
1071 *pdatalen = 0;
1072 return NGHTTP3_ERR_WOULDBLOCK;
1073 }
1074 return 0;
1075 }
1076
1077 /* Index where :authority header field will appear in request header
1078 field list. */
1079 #define AUTHORITY_DST_IDX 3
1080
1081 static CURLcode http_request(struct connectdata *conn, const void *mem,
1082 size_t len)
1083 {
1084 struct HTTP *stream = conn->data->req.protop;
1085 size_t nheader;
1086 size_t i;
1087 size_t authority_idx;
1088 char *hdbuf = (char *)mem;
1089 char *end, *line_end;
1090 struct quicsocket *qs = conn->quic;
1091 CURLcode result = CURLE_OK;
1092 struct Curl_easy *data = conn->data;
1093 nghttp3_nv *nva = NULL;
1094 int64_t stream3_id;
1095 int rc;
1096 struct h3out *h3out = NULL;
1097
1098 rc = ngtcp2_conn_open_bidi_stream(qs->qconn, &stream3_id, NULL);
1099 if(rc) {
1100 failf(conn->data, "can get bidi streams");
1101 result = CURLE_SEND_ERROR;
1102 goto fail;
1103 }
1104
1105 stream->stream3_id = stream3_id;
1106 stream->h3req = TRUE; /* senf off! */
1107
1108 /* Calculate number of headers contained in [mem, mem + len). Assumes a
1109 correctly generated HTTP header field block. */
1110 nheader = 0;
1111 for(i = 1; i < len; ++i) {
1112 if(hdbuf[i] == '\n' && hdbuf[i - 1] == '\r') {
1113 ++nheader;
1114 ++i;
1115 }
1116 }
1117 if(nheader < 2)
1118 goto fail;
1119
1120 /* We counted additional 2 \r\n in the first and last line. We need 3
1121 new headers: :method, :path and :scheme. Therefore we need one
1122 more space. */
1123 nheader += 1;
1124 nva = malloc(sizeof(nghttp3_nv) * nheader);
1125 if(!nva) {
1126 result = CURLE_OUT_OF_MEMORY;
1127 goto fail;
1128 }
1129
1130 /* Extract :method, :path from request line
1131 We do line endings with CRLF so checking for CR is enough */
1132 line_end = memchr(hdbuf, '\r', len);
1133 if(!line_end) {
1134 result = CURLE_BAD_FUNCTION_ARGUMENT; /* internal error */
1135 goto fail;
1136 }
1137
1138 /* Method does not contain spaces */
1139 end = memchr(hdbuf, ' ', line_end - hdbuf);
1140 if(!end || end == hdbuf)
1141 goto fail;
1142 nva[0].name = (unsigned char *)":method";
1143 nva[0].namelen = strlen((char *)nva[0].name);
1144 nva[0].value = (unsigned char *)hdbuf;
1145 nva[0].valuelen = (size_t)(end - hdbuf);
1146 nva[0].flags = NGHTTP3_NV_FLAG_NONE;
1147
1148 hdbuf = end + 1;
1149
1150 /* Path may contain spaces so scan backwards */
1151 end = NULL;
1152 for(i = (size_t)(line_end - hdbuf); i; --i) {
1153 if(hdbuf[i - 1] == ' ') {
1154 end = &hdbuf[i - 1];
1155 break;
1156 }
1157 }
1158 if(!end || end == hdbuf)
1159 goto fail;
1160 nva[1].name = (unsigned char *)":path";
1161 nva[1].namelen = strlen((char *)nva[1].name);
1162 nva[1].value = (unsigned char *)hdbuf;
1163 nva[1].valuelen = (size_t)(end - hdbuf);
1164 nva[1].flags = NGHTTP3_NV_FLAG_NONE;
1165
1166 nva[2].name = (unsigned char *)":scheme";
1167 nva[2].namelen = strlen((char *)nva[2].name);
1168 if(conn->handler->flags & PROTOPT_SSL)
1169 nva[2].value = (unsigned char *)"https";
1170 else
1171 nva[2].value = (unsigned char *)"http";
1172 nva[2].valuelen = strlen((char *)nva[2].value);
1173 nva[2].flags = NGHTTP3_NV_FLAG_NONE;
1174
1175
1176 authority_idx = 0;
1177 i = 3;
1178 while(i < nheader) {
1179 size_t hlen;
1180
1181 hdbuf = line_end + 2;
1182
1183 /* check for next CR, but only within the piece of data left in the given
1184 buffer */
1185 line_end = memchr(hdbuf, '\r', len - (hdbuf - (char *)mem));
1186 if(!line_end || (line_end == hdbuf))
1187 goto fail;
1188
1189 /* header continuation lines are not supported */
1190 if(*hdbuf == ' ' || *hdbuf == '\t')
1191 goto fail;
1192
1193 for(end = hdbuf; end < line_end && *end != ':'; ++end)
1194 ;
1195 if(end == hdbuf || end == line_end)
1196 goto fail;
1197 hlen = end - hdbuf;
1198
1199 if(hlen == 4 && strncasecompare("host", hdbuf, 4)) {
1200 authority_idx = i;
1201 nva[i].name = (unsigned char *)":authority";
1202 nva[i].namelen = strlen((char *)nva[i].name);
1203 }
1204 else {
1205 nva[i].name = (unsigned char *)hdbuf;
1206 nva[i].namelen = (size_t)(end - hdbuf);
1207 }
1208 nva[i].flags = NGHTTP3_NV_FLAG_NONE;
1209 hdbuf = end + 1;
1210 while(*hdbuf == ' ' || *hdbuf == '\t')
1211 ++hdbuf;
1212 end = line_end;
1213
1214 #if 0 /* This should probably go in more or less like this */
1215 switch(inspect_header((const char *)nva[i].name, nva[i].namelen, hdbuf,
1216 end - hdbuf)) {
1217 case HEADERINST_IGNORE:
1218 /* skip header fields prohibited by HTTP/2 specification. */
1219 --nheader;
1220 continue;
1221 case HEADERINST_TE_TRAILERS:
1222 nva[i].value = (uint8_t*)"trailers";
1223 nva[i].value_len = sizeof("trailers") - 1;
1224 break;
1225 default:
1226 nva[i].value = (unsigned char *)hdbuf;
1227 nva[i].value_len = (size_t)(end - hdbuf);
1228 }
1229 #endif
1230 nva[i].value = (unsigned char *)hdbuf;
1231 nva[i].valuelen = (size_t)(end - hdbuf);
1232 nva[i].flags = NGHTTP3_NV_FLAG_NONE;
1233
1234 ++i;
1235 }
1236
1237 /* :authority must come before non-pseudo header fields */
1238 if(authority_idx != 0 && authority_idx != AUTHORITY_DST_IDX) {
1239 nghttp3_nv authority = nva[authority_idx];
1240 for(i = authority_idx; i > AUTHORITY_DST_IDX; --i) {
1241 nva[i] = nva[i - 1];
1242 }
1243 nva[i] = authority;
1244 }
1245
1246 /* Warn stream may be rejected if cumulative length of headers is too
1247 large. */
1248 #define MAX_ACC 60000 /* <64KB to account for some overhead */
1249 {
1250 size_t acc = 0;
1251 for(i = 0; i < nheader; ++i)
1252 acc += nva[i].namelen + nva[i].valuelen;
1253
1254 if(acc > MAX_ACC) {
1255 infof(data, "http_request: Warning: The cumulative length of all "
1256 "headers exceeds %zu bytes and that could cause the "
1257 "stream to be rejected.\n", MAX_ACC);
1258 }
1259 }
1260
1261 switch(data->set.httpreq) {
1262 case HTTPREQ_POST:
1263 case HTTPREQ_POST_FORM:
1264 case HTTPREQ_POST_MIME:
1265 case HTTPREQ_PUT: {
1266 nghttp3_data_reader data_reader;
1267 if(data->state.infilesize != -1)
1268 stream->upload_left = data->state.infilesize;
1269 else
1270 /* data sending without specifying the data amount up front */
1271 stream->upload_left = -1; /* unknown, but not zero */
1272
1273 data_reader.read_data = cb_h3_readfunction;
1274
1275 h3out = calloc(sizeof(struct h3out), 1);
1276 if(!h3out) {
1277 result = CURLE_OUT_OF_MEMORY;
1278 goto fail;
1279 }
1280 stream->h3out = h3out;
1281
1282 rc = nghttp3_conn_submit_request(qs->h3conn, stream->stream3_id,
1283 nva, nheader, &data_reader,
1284 conn->data);
1285 if(rc) {
1286 result = CURLE_SEND_ERROR;
1287 goto fail;
1288 }
1289 break;
1290 }
1291 default:
1292 stream->upload_left = 0; /* nothing left to send */
1293 rc = nghttp3_conn_submit_request(qs->h3conn, stream->stream3_id,
1294 nva, nheader,
1295 NULL, /* no body! */
1296 conn->data);
1297 if(rc) {
1298 result = CURLE_SEND_ERROR;
1299 goto fail;
1300 }
1301 break;
1302 }
1303
1304 Curl_safefree(nva);
1305
1306 infof(data, "Using HTTP/3 Stream ID: %x (easy handle %p)\n",
1307 stream3_id, (void *)data);
1308
1309 return CURLE_OK;
1310
1311 fail:
1312 free(nva);
1313 return result;
1314 }
1315 static ssize_t ngh3_stream_send(struct connectdata *conn,
1316 int sockindex,
1317 const void *mem,
1318 size_t len,
1319 CURLcode *curlcode)
1320 {
1321 ssize_t sent;
1322 struct quicsocket *qs = conn->quic;
1323 curl_socket_t sockfd = conn->sock[sockindex];
1324 struct HTTP *stream = conn->data->req.protop;
1325
1326 if(!stream->h3req) {
1327 CURLcode result = http_request(conn, mem, len);
1328 if(result) {
1329 *curlcode = CURLE_SEND_ERROR;
1330 return -1;
1331 }
1332 sent = len;
1333 }
1334 else {
1335 fprintf(stderr, "ngh3_stream_send() wants to send %zd bytes\n", len);
1336 if(!stream->upload_len) {
1337 stream->upload_mem = mem;
1338 stream->upload_len = len;
1339 (void)nghttp3_conn_resume_stream(qs->h3conn, stream->stream3_id);
1340 sent = len;
1341 }
1342 else {
1343 *curlcode = CURLE_AGAIN;
1344 return -1;
1345 }
1346 }
1347
1348 if(ng_flush_egress(conn, sockfd, qs)) {
1349 *curlcode = CURLE_SEND_ERROR;
1350 return -1;
1351 }
1352
1353 *curlcode = CURLE_OK;
1354 return sent;
1355 }
1356
1357 static void ng_has_connected(struct connectdata *conn, int tempindex)
1358 {
1359 conn->recv[FIRSTSOCKET] = ngh3_stream_recv;
1360 conn->send[FIRSTSOCKET] = ngh3_stream_send;
1361 conn->handler = &Curl_handler_http3;
1362 conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
1363 conn->httpversion = 30;
1364 conn->bundle->multiuse = BUNDLE_MULTIPLEX;
1365 conn->quic = &conn->hequic[tempindex];
1366 DEBUGF(infof(conn->data, "ngtcp2 established connection!\n"));
1367 }
1368
1369 /*
1370 * There can be multiple connection attempts going on in parallel.
1371 */
1372 CURLcode Curl_quic_is_connected(struct connectdata *conn,
1373 int sockindex,
1374 bool *done)
1375 {
1376 CURLcode result;
1377 struct quicsocket *qs = &conn->hequic[sockindex];
1378 curl_socket_t sockfd = conn->tempsock[sockindex];
1379
1380 result = ng_process_ingress(conn, sockfd, qs);
1381 if(result)
1382 return result;
1383
1384 result = ng_flush_egress(conn, sockfd, qs);
1385 if(result)
1386 return result;
1387
1388 if(ngtcp2_conn_get_handshake_completed(qs->qconn)) {
1389 *done = TRUE;
1390 ng_has_connected(conn, sockindex);
1391 }
1392
1393 return result;
1394 }
1395
1396 static CURLcode ng_process_ingress(struct connectdata *conn, int sockfd,
1397 struct quicsocket *qs)
1398 {
1399 ssize_t recvd;
1400 int rv;
1401 uint8_t buf[65536];
1402 size_t bufsize = sizeof(buf);
1403 struct sockaddr_storage remote_addr;
1404 socklen_t remote_addrlen;
1405 ngtcp2_path path;
1406 ngtcp2_tstamp ts = timestamp();
1407
1408 for(;;) {
1409 remote_addrlen = sizeof(remote_addr);
1410 while((recvd = recvfrom(sockfd, buf, bufsize, MSG_DONTWAIT,
1411 (struct sockaddr *)&remote_addr,
1412 &remote_addrlen)) == -1 &&
1413 errno == EINTR)
1414 ;
1415 if(recvd == -1) {
1416 if(errno == EAGAIN || errno == EWOULDBLOCK)
1417 break;
1418
1419 failf(conn->data, "ngtcp2: recvfrom() unexpectedly returned %d", recvd);
1420 return CURLE_RECV_ERROR;
1421 }
1422
1423 ngtcp2_addr_init(&path.local, (uint8_t *)&qs->local_addr,
1424 qs->local_addrlen, NULL);
1425 ngtcp2_addr_init(&path.remote, (uint8_t *)&remote_addr, remote_addrlen,
1426 NULL);
1427
1428 rv = ngtcp2_conn_read_pkt(qs->qconn, &path, buf, recvd, ts);
1429 if(rv != 0) {
1430 /* TODO Send CONNECTION_CLOSE if possible */
1431 return CURLE_RECV_ERROR;
1432 }
1433 }
1434
1435 return CURLE_OK;
1436 }
1437
1438 static CURLcode ng_flush_egress(struct connectdata *conn, int sockfd,
1439 struct quicsocket *qs)
1440 {
1441 int rv;
1442 ssize_t sent;
1443 ssize_t outlen;
1444 uint8_t out[NGTCP2_MAX_PKTLEN_IPV4];
1445 size_t pktlen;
1446 ngtcp2_path_storage ps;
1447 ngtcp2_tstamp ts = timestamp();
1448 struct sockaddr_storage remote_addr;
1449 ngtcp2_tstamp expiry;
1450 ngtcp2_duration timeout;
1451 int64_t stream_id;
1452 ssize_t veccnt;
1453 int fin;
1454 nghttp3_vec vec[16];
1455 ssize_t ndatalen;
1456
1457 switch(qs->local_addr.ss_family) {
1458 case AF_INET:
1459 pktlen = NGTCP2_MAX_PKTLEN_IPV4;
1460 break;
1461 case AF_INET6:
1462 pktlen = NGTCP2_MAX_PKTLEN_IPV6;
1463 break;
1464 default:
1465 assert(0);
1466 }
1467
1468 rv = ngtcp2_conn_handle_expiry(qs->qconn, ts);
1469 if(rv != 0) {
1470 failf(conn->data, "ngtcp2_conn_handle_expiry returned error: %s\n",
1471 ngtcp2_strerror(rv));
1472 return CURLE_SEND_ERROR;
1473 }
1474
1475 ngtcp2_path_storage_zero(&ps);
1476
1477 for(;;) {
1478 outlen = -1;
1479 if(qs->h3conn && ngtcp2_conn_get_max_data_left(qs->qconn)) {
1480 veccnt = nghttp3_conn_writev_stream(qs->h3conn, &stream_id, &fin, vec,
1481 sizeof(vec) / sizeof(vec[0]));
1482 if(veccnt < 0) {
1483 failf(conn->data, "nghttp3_conn_writev_stream returned error: %s\n",
1484 nghttp3_strerror((int)veccnt));
1485 return CURLE_SEND_ERROR;
1486 }
1487 else if(veccnt > 0) {
1488 outlen =
1489 ngtcp2_conn_writev_stream(qs->qconn, &ps.path,
1490 out, pktlen, &ndatalen,
1491 NGTCP2_WRITE_STREAM_FLAG_MORE,
1492 stream_id, fin,
1493 (const ngtcp2_vec *)vec, veccnt, ts);
1494 if(outlen == 0) {
1495 break;
1496 }
1497 if(outlen < 0) {
1498 if(outlen == NGTCP2_ERR_STREAM_DATA_BLOCKED ||
1499 outlen == NGTCP2_ERR_STREAM_SHUT_WR) {
1500 rv = nghttp3_conn_block_stream(qs->h3conn, stream_id);
1501 if(rv != 0) {
1502 failf(conn->data,
1503 "nghttp3_conn_block_stream returned error: %s\n",
1504 nghttp3_strerror(rv));
1505 return CURLE_SEND_ERROR;
1506 }
1507 continue;
1508 }
1509 else if(outlen == NGTCP2_ERR_WRITE_STREAM_MORE) {
1510 assert(ndatalen > 0);
1511 rv = nghttp3_conn_add_write_offset(qs->h3conn, stream_id,
1512 ndatalen);
1513 if(rv != 0) {
1514 failf(conn->data,
1515 "nghttp3_conn_add_write_offset returned error: %s\n",
1516 nghttp3_strerror(rv));
1517 return CURLE_SEND_ERROR;
1518 }
1519 continue;
1520 }
1521 else {
1522 failf(conn->data, "ngtcp2_conn_writev_stream returned error: %s\n",
1523 ngtcp2_strerror((int)outlen));
1524 return CURLE_SEND_ERROR;
1525 }
1526 }
1527 else if(ndatalen > 0) {
1528 rv = nghttp3_conn_add_write_offset(qs->h3conn, stream_id, ndatalen);
1529 if(rv != 0) {
1530 failf(conn->data,
1531 "nghttp3_conn_add_write_offset returned error: %s\n",
1532 nghttp3_strerror(rv));
1533 return CURLE_SEND_ERROR;
1534 }
1535 }
1536 }
1537 }
1538 if(outlen < 0) {
1539 outlen = ngtcp2_conn_write_pkt(qs->qconn, &ps.path, out, pktlen, ts);
1540 if(outlen < 0) {
1541 failf(conn->data, "ngtcp2_conn_write_pkt returned error: %s\n",
1542 ngtcp2_strerror((int)outlen));
1543 return CURLE_SEND_ERROR;
1544 }
1545 if(outlen == 0)
1546 break;
1547 }
1548
1549 memcpy(&remote_addr, ps.path.remote.addr, ps.path.remote.addrlen);
1550 while((sent = sendto(sockfd, out, outlen, MSG_DONTWAIT,
1551 (struct sockaddr *)&remote_addr,
1552 (socklen_t)ps.path.remote.addrlen)) == -1 &&
1553 errno == EINTR)
1554 ;
1555
1556 if(sent == -1) {
1557 if(errno == EAGAIN || errno == EWOULDBLOCK) {
1558 /* TODO Cache packet */
1559 break;
1560 }
1561 else {
1562 failf(conn->data, "sendto() returned %zd (errno %d)\n", sent,
1563 SOCKERRNO);
1564 return CURLE_SEND_ERROR;
1565 }
1566 }
1567 }
1568
1569 expiry = ngtcp2_conn_get_expiry(qs->qconn);
1570 if(expiry != UINT64_MAX) {
1571 if(expiry <= ts) {
1572 timeout = NGTCP2_MILLISECONDS;
1573 }
1574 else {
1575 timeout = expiry - ts;
1576 }
1577 Curl_expire(conn->data, timeout / NGTCP2_MILLISECONDS, EXPIRE_QUIC);
1578 }
1579
1580 return CURLE_OK;
1581 }
1582
1583 /*
1584 * Called from transfer.c:done_sending when we stop HTTP/3 uploading.
1585 */
1586 CURLcode Curl_quic_done_sending(struct connectdata *conn)
1587 {
1588 if(conn->handler == &Curl_handler_http3) {
1589 /* only for HTTP/3 transfers */
1590 struct HTTP *stream = conn->data->req.protop;
1591 struct quicsocket *qs = conn->quic;
1592 fprintf(stderr, "!!! Curl_quic_done_sending stream %zu\n",
1593 stream->stream3_id);
1594 stream->upload_done = TRUE;
1595 (void)nghttp3_conn_resume_stream(qs->h3conn, stream->stream3_id);
1596 }
1597
1598 return CURLE_OK;
1599 }
1600 #endif