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