1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 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.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 * SPDX-License-Identifier: curl
22 *
23 ***************************************************************************/
24
25 #include "curl_setup.h"
26
27 #if defined(USE_NGTCP2) && defined(USE_NGHTTP3)
28 #include <ngtcp2/ngtcp2.h>
29 #include <nghttp3/nghttp3.h>
30
31 #ifdef USE_OPENSSL
32 #include <openssl/err.h>
33 #if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
34 #include <ngtcp2/ngtcp2_crypto_boringssl.h>
35 #else
36 #include <ngtcp2/ngtcp2_crypto_quictls.h>
37 #endif
38 #include "vtls/openssl.h"
39 #elif defined(USE_GNUTLS)
40 #include <ngtcp2/ngtcp2_crypto_gnutls.h>
41 #include "vtls/gtls.h"
42 #elif defined(USE_WOLFSSL)
43 #include <ngtcp2/ngtcp2_crypto_wolfssl.h>
44 #include "vtls/wolfssl.h"
45 #endif
46
47 #include "urldata.h"
48 #include "hash.h"
49 #include "sendf.h"
50 #include "strdup.h"
51 #include "rand.h"
52 #include "multiif.h"
53 #include "strcase.h"
54 #include "cfilters.h"
55 #include "cf-socket.h"
56 #include "connect.h"
57 #include "progress.h"
58 #include "strerror.h"
59 #include "dynbuf.h"
60 #include "http1.h"
61 #include "select.h"
62 #include "inet_pton.h"
63 #include "transfer.h"
64 #include "vquic.h"
65 #include "vquic_int.h"
66 #include "vquic-tls.h"
67 #include "vtls/keylog.h"
68 #include "vtls/vtls.h"
69 #include "curl_ngtcp2.h"
70
71 #include "warnless.h"
72
73 /* The last 3 #include files should be in this order */
74 #include "curl_printf.h"
75 #include "curl_memory.h"
76 #include "memdebug.h"
77
78
79 #define QUIC_MAX_STREAMS (256*1024)
80 #define QUIC_MAX_DATA (1*1024*1024)
81 #define QUIC_HANDSHAKE_TIMEOUT (10*NGTCP2_SECONDS)
82
83 /* A stream window is the maximum amount we need to buffer for
84 * each active transfer. We use HTTP/3 flow control and only ACK
85 * when we take things out of the buffer.
86 * Chunk size is large enough to take a full DATA frame */
87 #define H3_STREAM_WINDOW_SIZE (128 * 1024)
88 #define H3_STREAM_CHUNK_SIZE (16 * 1024)
89 /* The pool keeps spares around and half of a full stream windows
90 * seems good. More does not seem to improve performance.
91 * The benefit of the pool is that stream buffer to not keep
92 * spares. Memory consumption goes down when streams run empty,
93 * have a large upload done, etc. */
94 #define H3_STREAM_POOL_SPARES \
95 (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE ) / 2
96 /* Receive and Send max number of chunks just follows from the
97 * chunk size and window size */
98 #define H3_STREAM_RECV_CHUNKS \
99 (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE)
100 #define H3_STREAM_SEND_CHUNKS \
101 (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE)
102
103
104 /*
105 * Store ngtcp2 version info in this buffer.
106 */
Curl_ngtcp2_ver(char * p,size_t len)107 void Curl_ngtcp2_ver(char *p, size_t len)
108 {
109 const ngtcp2_info *ng2 = ngtcp2_version(0);
110 const nghttp3_info *ht3 = nghttp3_version(0);
111 (void)msnprintf(p, len, "ngtcp2/%s nghttp3/%s",
112 ng2->version_str, ht3->version_str);
113 }
114
115 struct cf_ngtcp2_ctx {
116 struct cf_quic_ctx q;
117 struct ssl_peer peer;
118 struct curl_tls_ctx tls;
119 ngtcp2_path connected_path;
120 ngtcp2_conn *qconn;
121 ngtcp2_cid dcid;
122 ngtcp2_cid scid;
123 uint32_t version;
124 ngtcp2_settings settings;
125 ngtcp2_transport_params transport_params;
126 ngtcp2_ccerr last_error;
127 ngtcp2_crypto_conn_ref conn_ref;
128 struct cf_call_data call_data;
129 nghttp3_conn *h3conn;
130 nghttp3_settings h3settings;
131 struct curltime started_at; /* time the current attempt started */
132 struct curltime handshake_at; /* time connect handshake finished */
133 struct bufc_pool stream_bufcp; /* chunk pool for streams */
134 struct dynbuf scratch; /* temp buffer for header construction */
135 struct Curl_hash streams; /* hash `data->mid` to `h3_stream_ctx` */
136 size_t max_stream_window; /* max flow window for one stream */
137 uint64_t max_idle_ms; /* max idle time for QUIC connection */
138 uint64_t used_bidi_streams; /* bidi streams we have opened */
139 uint64_t max_bidi_streams; /* max bidi streams we can open */
140 int qlogfd;
141 BIT(initialized);
142 BIT(shutdown_started); /* queued shutdown packets */
143 };
144
145 /* How to access `call_data` from a cf_ngtcp2 filter */
146 #undef CF_CTX_CALL_DATA
147 #define CF_CTX_CALL_DATA(cf) \
148 ((struct cf_ngtcp2_ctx *)(cf)->ctx)->call_data
149
150 static void h3_stream_hash_free(void *stream);
151
cf_ngtcp2_ctx_init(struct cf_ngtcp2_ctx * ctx)152 static void cf_ngtcp2_ctx_init(struct cf_ngtcp2_ctx *ctx)
153 {
154 DEBUGASSERT(!ctx->initialized);
155 ctx->qlogfd = -1;
156 ctx->version = NGTCP2_PROTO_VER_MAX;
157 ctx->max_stream_window = H3_STREAM_WINDOW_SIZE;
158 ctx->max_idle_ms = CURL_QUIC_MAX_IDLE_MS;
159 Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE,
160 H3_STREAM_POOL_SPARES);
161 Curl_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER);
162 Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free);
163 ctx->initialized = TRUE;
164 }
165
cf_ngtcp2_ctx_free(struct cf_ngtcp2_ctx * ctx)166 static void cf_ngtcp2_ctx_free(struct cf_ngtcp2_ctx *ctx)
167 {
168 if(ctx && ctx->initialized) {
169 Curl_bufcp_free(&ctx->stream_bufcp);
170 Curl_dyn_free(&ctx->scratch);
171 Curl_hash_clean(&ctx->streams);
172 Curl_hash_destroy(&ctx->streams);
173 Curl_ssl_peer_cleanup(&ctx->peer);
174 }
175 free(ctx);
176 }
177
178 struct pkt_io_ctx;
179 static CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
180 struct Curl_easy *data,
181 struct pkt_io_ctx *pktx);
182 static CURLcode cf_progress_egress(struct Curl_cfilter *cf,
183 struct Curl_easy *data,
184 struct pkt_io_ctx *pktx);
185
186 /**
187 * All about the H3 internals of a stream
188 */
189 struct h3_stream_ctx {
190 curl_int64_t id; /* HTTP/3 protocol identifier */
191 struct bufq sendbuf; /* h3 request body */
192 struct h1_req_parser h1; /* h1 request parsing */
193 size_t sendbuf_len_in_flight; /* sendbuf amount "in flight" */
194 curl_uint64_t error3; /* HTTP/3 stream error code */
195 curl_off_t upload_left; /* number of request bytes left to upload */
196 int status_code; /* HTTP status code */
197 CURLcode xfer_result; /* result from xfer_resp_write(_hd) */
198 bool resp_hds_complete; /* we have a complete, final response */
199 bool closed; /* TRUE on stream close */
200 bool reset; /* TRUE on stream reset */
201 bool send_closed; /* stream is local closed */
202 BIT(quic_flow_blocked); /* stream is blocked by QUIC flow control */
203 };
204
205 #define H3_STREAM_CTX(ctx,data) ((struct h3_stream_ctx *)(\
206 data? Curl_hash_offt_get(&(ctx)->streams, (data)->mid) : NULL))
207 #define H3_STREAM_CTX_ID(ctx,id) ((struct h3_stream_ctx *)(\
208 Curl_hash_offt_get(&(ctx)->streams, (id))))
209
h3_stream_ctx_free(struct h3_stream_ctx * stream)210 static void h3_stream_ctx_free(struct h3_stream_ctx *stream)
211 {
212 Curl_bufq_free(&stream->sendbuf);
213 Curl_h1_req_parse_free(&stream->h1);
214 free(stream);
215 }
216
h3_stream_hash_free(void * stream)217 static void h3_stream_hash_free(void *stream)
218 {
219 DEBUGASSERT(stream);
220 h3_stream_ctx_free((struct h3_stream_ctx *)stream);
221 }
222
h3_data_setup(struct Curl_cfilter * cf,struct Curl_easy * data)223 static CURLcode h3_data_setup(struct Curl_cfilter *cf,
224 struct Curl_easy *data)
225 {
226 struct cf_ngtcp2_ctx *ctx = cf->ctx;
227 struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
228
229 if(!data)
230 return CURLE_FAILED_INIT;
231
232 if(stream)
233 return CURLE_OK;
234
235 stream = calloc(1, sizeof(*stream));
236 if(!stream)
237 return CURLE_OUT_OF_MEMORY;
238
239 stream->id = -1;
240 /* on send, we control how much we put into the buffer */
241 Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp,
242 H3_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE);
243 stream->sendbuf_len_in_flight = 0;
244 Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
245
246 if(!Curl_hash_offt_set(&ctx->streams, data->mid, stream)) {
247 h3_stream_ctx_free(stream);
248 return CURLE_OUT_OF_MEMORY;
249 }
250
251 return CURLE_OK;
252 }
253
cf_ngtcp2_stream_close(struct Curl_cfilter * cf,struct Curl_easy * data,struct h3_stream_ctx * stream)254 static void cf_ngtcp2_stream_close(struct Curl_cfilter *cf,
255 struct Curl_easy *data,
256 struct h3_stream_ctx *stream)
257 {
258 struct cf_ngtcp2_ctx *ctx = cf->ctx;
259 DEBUGASSERT(data);
260 DEBUGASSERT(stream);
261 if(!stream->closed && ctx->qconn && ctx->h3conn) {
262 CURLcode result;
263
264 nghttp3_conn_set_stream_user_data(ctx->h3conn, stream->id, NULL);
265 ngtcp2_conn_set_stream_user_data(ctx->qconn, stream->id, NULL);
266 stream->closed = TRUE;
267 (void)ngtcp2_conn_shutdown_stream(ctx->qconn, 0, stream->id,
268 NGHTTP3_H3_REQUEST_CANCELLED);
269 result = cf_progress_egress(cf, data, NULL);
270 if(result)
271 CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cancel stream -> %d",
272 stream->id, result);
273 }
274 }
275
h3_data_done(struct Curl_cfilter * cf,struct Curl_easy * data)276 static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
277 {
278 struct cf_ngtcp2_ctx *ctx = cf->ctx;
279 struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
280 (void)cf;
281 if(stream) {
282 CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] easy handle is done",
283 stream->id);
284 cf_ngtcp2_stream_close(cf, data, stream);
285 Curl_hash_offt_remove(&ctx->streams, data->mid);
286 }
287 }
288
get_stream_easy(struct Curl_cfilter * cf,struct Curl_easy * data,int64_t stream_id,struct h3_stream_ctx ** pstream)289 static struct Curl_easy *get_stream_easy(struct Curl_cfilter *cf,
290 struct Curl_easy *data,
291 int64_t stream_id,
292 struct h3_stream_ctx **pstream)
293 {
294 struct cf_ngtcp2_ctx *ctx = cf->ctx;
295 struct h3_stream_ctx *stream;
296
297 (void)cf;
298 stream = H3_STREAM_CTX(ctx, data);
299 if(stream && stream->id == stream_id) {
300 *pstream = stream;
301 return data;
302 }
303 else {
304 struct Curl_llist_node *e;
305 DEBUGASSERT(data->multi);
306 for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) {
307 struct Curl_easy *sdata = Curl_node_elem(e);
308 if(sdata->conn != data->conn)
309 continue;
310 stream = H3_STREAM_CTX(ctx, sdata);
311 if(stream && stream->id == stream_id) {
312 *pstream = stream;
313 return sdata;
314 }
315 }
316 }
317 *pstream = NULL;
318 return NULL;
319 }
320
h3_drain_stream(struct Curl_cfilter * cf,struct Curl_easy * data)321 static void h3_drain_stream(struct Curl_cfilter *cf,
322 struct Curl_easy *data)
323 {
324 struct cf_ngtcp2_ctx *ctx = cf->ctx;
325 struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
326 unsigned char bits;
327
328 (void)cf;
329 bits = CURL_CSELECT_IN;
330 if(stream && stream->upload_left && !stream->send_closed)
331 bits |= CURL_CSELECT_OUT;
332 if(data->state.select_bits != bits) {
333 data->state.select_bits = bits;
334 Curl_expire(data, 0, EXPIRE_RUN_NOW);
335 }
336 }
337
338 /* ngtcp2 default congestion controller does not perform pacing. Limit
339 the maximum packet burst to MAX_PKT_BURST packets. */
340 #define MAX_PKT_BURST 10
341
342 struct pkt_io_ctx {
343 struct Curl_cfilter *cf;
344 struct Curl_easy *data;
345 ngtcp2_tstamp ts;
346 ngtcp2_path_storage ps;
347 };
348
pktx_update_time(struct pkt_io_ctx * pktx,struct Curl_cfilter * cf)349 static void pktx_update_time(struct pkt_io_ctx *pktx,
350 struct Curl_cfilter *cf)
351 {
352 struct cf_ngtcp2_ctx *ctx = cf->ctx;
353
354 vquic_ctx_update_time(&ctx->q);
355 pktx->ts = (ngtcp2_tstamp)ctx->q.last_op.tv_sec * NGTCP2_SECONDS +
356 (ngtcp2_tstamp)ctx->q.last_op.tv_usec * NGTCP2_MICROSECONDS;
357 }
358
pktx_init(struct pkt_io_ctx * pktx,struct Curl_cfilter * cf,struct Curl_easy * data)359 static void pktx_init(struct pkt_io_ctx *pktx,
360 struct Curl_cfilter *cf,
361 struct Curl_easy *data)
362 {
363 pktx->cf = cf;
364 pktx->data = data;
365 ngtcp2_path_storage_zero(&pktx->ps);
366 pktx_update_time(pktx, cf);
367 }
368
369 static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id,
370 uint64_t datalen, void *user_data,
371 void *stream_user_data);
372
get_conn(ngtcp2_crypto_conn_ref * conn_ref)373 static ngtcp2_conn *get_conn(ngtcp2_crypto_conn_ref *conn_ref)
374 {
375 struct Curl_cfilter *cf = conn_ref->user_data;
376 struct cf_ngtcp2_ctx *ctx = cf->ctx;
377 return ctx->qconn;
378 }
379
380 #ifdef DEBUG_NGTCP2
quic_printf(void * user_data,const char * fmt,...)381 static void quic_printf(void *user_data, const char *fmt, ...)
382 {
383 struct Curl_cfilter *cf = user_data;
384 struct cf_ngtcp2_ctx *ctx = cf->ctx;
385
386 (void)ctx; /* TODO: need an easy handle to infof() message */
387 va_list ap;
388 va_start(ap, fmt);
389 vfprintf(stderr, fmt, ap);
390 va_end(ap);
391 fprintf(stderr, "\n");
392 }
393 #endif
394
qlog_callback(void * user_data,uint32_t flags,const void * data,size_t datalen)395 static void qlog_callback(void *user_data, uint32_t flags,
396 const void *data, size_t datalen)
397 {
398 struct Curl_cfilter *cf = user_data;
399 struct cf_ngtcp2_ctx *ctx = cf->ctx;
400 (void)flags;
401 if(ctx->qlogfd != -1) {
402 ssize_t rc = write(ctx->qlogfd, data, datalen);
403 if(rc == -1) {
404 /* on write error, stop further write attempts */
405 close(ctx->qlogfd);
406 ctx->qlogfd = -1;
407 }
408 }
409
410 }
411
quic_settings(struct cf_ngtcp2_ctx * ctx,struct Curl_easy * data,struct pkt_io_ctx * pktx)412 static void quic_settings(struct cf_ngtcp2_ctx *ctx,
413 struct Curl_easy *data,
414 struct pkt_io_ctx *pktx)
415 {
416 ngtcp2_settings *s = &ctx->settings;
417 ngtcp2_transport_params *t = &ctx->transport_params;
418
419 ngtcp2_settings_default(s);
420 ngtcp2_transport_params_default(t);
421 #ifdef DEBUG_NGTCP2
422 s->log_printf = quic_printf;
423 #else
424 s->log_printf = NULL;
425 #endif
426
427 (void)data;
428 s->initial_ts = pktx->ts;
429 s->handshake_timeout = QUIC_HANDSHAKE_TIMEOUT;
430 s->max_window = 100 * ctx->max_stream_window;
431 s->max_stream_window = 10 * ctx->max_stream_window;
432
433 t->initial_max_data = 10 * ctx->max_stream_window;
434 t->initial_max_stream_data_bidi_local = ctx->max_stream_window;
435 t->initial_max_stream_data_bidi_remote = ctx->max_stream_window;
436 t->initial_max_stream_data_uni = ctx->max_stream_window;
437 t->initial_max_streams_bidi = QUIC_MAX_STREAMS;
438 t->initial_max_streams_uni = QUIC_MAX_STREAMS;
439 t->max_idle_timeout = (ctx->max_idle_ms * NGTCP2_MILLISECONDS);
440 if(ctx->qlogfd != -1) {
441 s->qlog_write = qlog_callback;
442 }
443 }
444
445 static CURLcode init_ngh3_conn(struct Curl_cfilter *cf);
446
cb_handshake_completed(ngtcp2_conn * tconn,void * user_data)447 static int cb_handshake_completed(ngtcp2_conn *tconn, void *user_data)
448 {
449 (void)user_data;
450 (void)tconn;
451 return 0;
452 }
453
454 static void cf_ngtcp2_conn_close(struct Curl_cfilter *cf,
455 struct Curl_easy *data);
456
cf_ngtcp2_err_is_fatal(int code)457 static bool cf_ngtcp2_err_is_fatal(int code)
458 {
459 return (NGTCP2_ERR_FATAL >= code) ||
460 (NGTCP2_ERR_DROP_CONN == code) ||
461 (NGTCP2_ERR_IDLE_CLOSE == code);
462 }
463
cf_ngtcp2_err_set(struct Curl_cfilter * cf,struct Curl_easy * data,int code)464 static void cf_ngtcp2_err_set(struct Curl_cfilter *cf,
465 struct Curl_easy *data, int code)
466 {
467 struct cf_ngtcp2_ctx *ctx = cf->ctx;
468 if(!ctx->last_error.error_code) {
469 if(NGTCP2_ERR_CRYPTO == code) {
470 ngtcp2_ccerr_set_tls_alert(&ctx->last_error,
471 ngtcp2_conn_get_tls_alert(ctx->qconn),
472 NULL, 0);
473 }
474 else {
475 ngtcp2_ccerr_set_liberr(&ctx->last_error, code, NULL, 0);
476 }
477 }
478 if(cf_ngtcp2_err_is_fatal(code))
479 cf_ngtcp2_conn_close(cf, data);
480 }
481
cf_ngtcp2_h3_err_is_fatal(int code)482 static bool cf_ngtcp2_h3_err_is_fatal(int code)
483 {
484 return (NGHTTP3_ERR_FATAL >= code) ||
485 (NGHTTP3_ERR_H3_CLOSED_CRITICAL_STREAM == code);
486 }
487
cf_ngtcp2_h3_err_set(struct Curl_cfilter * cf,struct Curl_easy * data,int code)488 static void cf_ngtcp2_h3_err_set(struct Curl_cfilter *cf,
489 struct Curl_easy *data, int code)
490 {
491 struct cf_ngtcp2_ctx *ctx = cf->ctx;
492 if(!ctx->last_error.error_code) {
493 ngtcp2_ccerr_set_application_error(&ctx->last_error,
494 nghttp3_err_infer_quic_app_error_code(code), NULL, 0);
495 }
496 if(cf_ngtcp2_h3_err_is_fatal(code))
497 cf_ngtcp2_conn_close(cf, data);
498 }
499
cb_recv_stream_data(ngtcp2_conn * tconn,uint32_t flags,int64_t sid,uint64_t offset,const uint8_t * buf,size_t buflen,void * user_data,void * stream_user_data)500 static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags,
501 int64_t sid, uint64_t offset,
502 const uint8_t *buf, size_t buflen,
503 void *user_data, void *stream_user_data)
504 {
505 struct Curl_cfilter *cf = user_data;
506 struct cf_ngtcp2_ctx *ctx = cf->ctx;
507 curl_int64_t stream_id = (curl_int64_t)sid;
508 nghttp3_ssize nconsumed;
509 int fin = (flags & NGTCP2_STREAM_DATA_FLAG_FIN) ? 1 : 0;
510 struct Curl_easy *data = stream_user_data;
511 (void)offset;
512 (void)data;
513
514 nconsumed =
515 nghttp3_conn_read_stream(ctx->h3conn, stream_id, buf, buflen, fin);
516 if(!data)
517 data = CF_DATA_CURRENT(cf);
518 if(data)
519 CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] read_stream(len=%zu) -> %zd",
520 stream_id, buflen, nconsumed);
521 if(nconsumed < 0) {
522 struct h3_stream_ctx *stream = H3_STREAM_CTX_ID(ctx, stream_id);
523 if(data && stream) {
524 CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] error on known stream, "
525 "reset=%d, closed=%d",
526 stream_id, stream->reset, stream->closed);
527 }
528 return NGTCP2_ERR_CALLBACK_FAILURE;
529 }
530
531 /* number of bytes inside buflen which consists of framing overhead
532 * including QPACK HEADERS. In other words, it does not consume payload of
533 * DATA frame. */
534 ngtcp2_conn_extend_max_stream_offset(tconn, stream_id, (uint64_t)nconsumed);
535 ngtcp2_conn_extend_max_offset(tconn, (uint64_t)nconsumed);
536
537 return 0;
538 }
539
540 static int
cb_acked_stream_data_offset(ngtcp2_conn * tconn,int64_t stream_id,uint64_t offset,uint64_t datalen,void * user_data,void * stream_user_data)541 cb_acked_stream_data_offset(ngtcp2_conn *tconn, int64_t stream_id,
542 uint64_t offset, uint64_t datalen, void *user_data,
543 void *stream_user_data)
544 {
545 struct Curl_cfilter *cf = user_data;
546 struct cf_ngtcp2_ctx *ctx = cf->ctx;
547 int rv;
548 (void)stream_id;
549 (void)tconn;
550 (void)offset;
551 (void)datalen;
552 (void)stream_user_data;
553
554 rv = nghttp3_conn_add_ack_offset(ctx->h3conn, stream_id, datalen);
555 if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
556 return NGTCP2_ERR_CALLBACK_FAILURE;
557 }
558
559 return 0;
560 }
561
cb_stream_close(ngtcp2_conn * tconn,uint32_t flags,int64_t sid,uint64_t app_error_code,void * user_data,void * stream_user_data)562 static int cb_stream_close(ngtcp2_conn *tconn, uint32_t flags,
563 int64_t sid, uint64_t app_error_code,
564 void *user_data, void *stream_user_data)
565 {
566 struct Curl_cfilter *cf = user_data;
567 struct cf_ngtcp2_ctx *ctx = cf->ctx;
568 struct Curl_easy *data = stream_user_data;
569 curl_int64_t stream_id = (curl_int64_t)sid;
570 int rv;
571
572 (void)tconn;
573 /* stream is closed... */
574 if(!data)
575 data = CF_DATA_CURRENT(cf);
576 if(!data)
577 return NGTCP2_ERR_CALLBACK_FAILURE;
578
579 if(!(flags & NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET)) {
580 app_error_code = NGHTTP3_H3_NO_ERROR;
581 }
582
583 rv = nghttp3_conn_close_stream(ctx->h3conn, stream_id, app_error_code);
584 CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] quic close(app_error=%"
585 FMT_PRIu64 ") -> %d", stream_id, (curl_uint64_t)app_error_code,
586 rv);
587 if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
588 cf_ngtcp2_h3_err_set(cf, data, rv);
589 return NGTCP2_ERR_CALLBACK_FAILURE;
590 }
591
592 return 0;
593 }
594
cb_stream_reset(ngtcp2_conn * tconn,int64_t sid,uint64_t final_size,uint64_t app_error_code,void * user_data,void * stream_user_data)595 static int cb_stream_reset(ngtcp2_conn *tconn, int64_t sid,
596 uint64_t final_size, uint64_t app_error_code,
597 void *user_data, void *stream_user_data)
598 {
599 struct Curl_cfilter *cf = user_data;
600 struct cf_ngtcp2_ctx *ctx = cf->ctx;
601 curl_int64_t stream_id = (curl_int64_t)sid;
602 struct Curl_easy *data = stream_user_data;
603 int rv;
604 (void)tconn;
605 (void)final_size;
606 (void)app_error_code;
607 (void)data;
608
609 rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id);
610 CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] reset -> %d", stream_id, rv);
611 if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
612 return NGTCP2_ERR_CALLBACK_FAILURE;
613 }
614
615 return 0;
616 }
617
cb_stream_stop_sending(ngtcp2_conn * tconn,int64_t stream_id,uint64_t app_error_code,void * user_data,void * stream_user_data)618 static int cb_stream_stop_sending(ngtcp2_conn *tconn, int64_t stream_id,
619 uint64_t app_error_code, void *user_data,
620 void *stream_user_data)
621 {
622 struct Curl_cfilter *cf = user_data;
623 struct cf_ngtcp2_ctx *ctx = cf->ctx;
624 int rv;
625 (void)tconn;
626 (void)app_error_code;
627 (void)stream_user_data;
628
629 rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id);
630 if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
631 return NGTCP2_ERR_CALLBACK_FAILURE;
632 }
633
634 return 0;
635 }
636
cb_extend_max_local_streams_bidi(ngtcp2_conn * tconn,uint64_t max_streams,void * user_data)637 static int cb_extend_max_local_streams_bidi(ngtcp2_conn *tconn,
638 uint64_t max_streams,
639 void *user_data)
640 {
641 struct Curl_cfilter *cf = user_data;
642 struct cf_ngtcp2_ctx *ctx = cf->ctx;
643 struct Curl_easy *data = CF_DATA_CURRENT(cf);
644
645 (void)tconn;
646 ctx->max_bidi_streams = max_streams;
647 if(data)
648 CURL_TRC_CF(data, cf, "max bidi streams now %" FMT_PRIu64
649 ", used %" FMT_PRIu64, (curl_uint64_t)ctx->max_bidi_streams,
650 (curl_uint64_t)ctx->used_bidi_streams);
651 return 0;
652 }
653
cb_extend_max_stream_data(ngtcp2_conn * tconn,int64_t sid,uint64_t max_data,void * user_data,void * stream_user_data)654 static int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t sid,
655 uint64_t max_data, void *user_data,
656 void *stream_user_data)
657 {
658 struct Curl_cfilter *cf = user_data;
659 struct cf_ngtcp2_ctx *ctx = cf->ctx;
660 curl_int64_t stream_id = (curl_int64_t)sid;
661 struct Curl_easy *data = CF_DATA_CURRENT(cf);
662 struct Curl_easy *s_data;
663 struct h3_stream_ctx *stream;
664 int rv;
665 (void)tconn;
666 (void)max_data;
667 (void)stream_user_data;
668
669 rv = nghttp3_conn_unblock_stream(ctx->h3conn, stream_id);
670 if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
671 return NGTCP2_ERR_CALLBACK_FAILURE;
672 }
673 s_data = get_stream_easy(cf, data, stream_id, &stream);
674 if(s_data && stream && stream->quic_flow_blocked) {
675 CURL_TRC_CF(s_data, cf, "[%" FMT_PRId64 "] unblock quic flow", stream_id);
676 stream->quic_flow_blocked = FALSE;
677 h3_drain_stream(cf, s_data);
678 }
679 return 0;
680 }
681
cb_rand(uint8_t * dest,size_t destlen,const ngtcp2_rand_ctx * rand_ctx)682 static void cb_rand(uint8_t *dest, size_t destlen,
683 const ngtcp2_rand_ctx *rand_ctx)
684 {
685 CURLcode result;
686 (void)rand_ctx;
687
688 result = Curl_rand(NULL, dest, destlen);
689 if(result) {
690 /* cb_rand is only used for non-cryptographic context. If Curl_rand
691 failed, just fill 0 and call it *random*. */
692 memset(dest, 0, destlen);
693 }
694 }
695
cb_get_new_connection_id(ngtcp2_conn * tconn,ngtcp2_cid * cid,uint8_t * token,size_t cidlen,void * user_data)696 static int cb_get_new_connection_id(ngtcp2_conn *tconn, ngtcp2_cid *cid,
697 uint8_t *token, size_t cidlen,
698 void *user_data)
699 {
700 CURLcode result;
701 (void)tconn;
702 (void)user_data;
703
704 result = Curl_rand(NULL, cid->data, cidlen);
705 if(result)
706 return NGTCP2_ERR_CALLBACK_FAILURE;
707 cid->datalen = cidlen;
708
709 result = Curl_rand(NULL, token, NGTCP2_STATELESS_RESET_TOKENLEN);
710 if(result)
711 return NGTCP2_ERR_CALLBACK_FAILURE;
712
713 return 0;
714 }
715
cb_recv_rx_key(ngtcp2_conn * tconn,ngtcp2_encryption_level level,void * user_data)716 static int cb_recv_rx_key(ngtcp2_conn *tconn, ngtcp2_encryption_level level,
717 void *user_data)
718 {
719 struct Curl_cfilter *cf = user_data;
720 (void)tconn;
721
722 if(level != NGTCP2_ENCRYPTION_LEVEL_1RTT) {
723 return 0;
724 }
725
726 if(init_ngh3_conn(cf) != CURLE_OK) {
727 return NGTCP2_ERR_CALLBACK_FAILURE;
728 }
729
730 return 0;
731 }
732
733 #if defined(_MSC_VER) && defined(_DLL)
734 # pragma warning(push)
735 # pragma warning(disable:4232) /* MSVC extension, dllimport identity */
736 #endif
737
738 static ngtcp2_callbacks ng_callbacks = {
739 ngtcp2_crypto_client_initial_cb,
740 NULL, /* recv_client_initial */
741 ngtcp2_crypto_recv_crypto_data_cb,
742 cb_handshake_completed,
743 NULL, /* recv_version_negotiation */
744 ngtcp2_crypto_encrypt_cb,
745 ngtcp2_crypto_decrypt_cb,
746 ngtcp2_crypto_hp_mask_cb,
747 cb_recv_stream_data,
748 cb_acked_stream_data_offset,
749 NULL, /* stream_open */
750 cb_stream_close,
751 NULL, /* recv_stateless_reset */
752 ngtcp2_crypto_recv_retry_cb,
753 cb_extend_max_local_streams_bidi,
754 NULL, /* extend_max_local_streams_uni */
755 cb_rand,
756 cb_get_new_connection_id,
757 NULL, /* remove_connection_id */
758 ngtcp2_crypto_update_key_cb, /* update_key */
759 NULL, /* path_validation */
760 NULL, /* select_preferred_addr */
761 cb_stream_reset,
762 NULL, /* extend_max_remote_streams_bidi */
763 NULL, /* extend_max_remote_streams_uni */
764 cb_extend_max_stream_data,
765 NULL, /* dcid_status */
766 NULL, /* handshake_confirmed */
767 NULL, /* recv_new_token */
768 ngtcp2_crypto_delete_crypto_aead_ctx_cb,
769 ngtcp2_crypto_delete_crypto_cipher_ctx_cb,
770 NULL, /* recv_datagram */
771 NULL, /* ack_datagram */
772 NULL, /* lost_datagram */
773 ngtcp2_crypto_get_path_challenge_data_cb,
774 cb_stream_stop_sending,
775 NULL, /* version_negotiation */
776 cb_recv_rx_key,
777 NULL, /* recv_tx_key */
778 NULL, /* early_data_rejected */
779 };
780
781 #if defined(_MSC_VER) && defined(_DLL)
782 # pragma warning(pop)
783 #endif
784
785 /**
786 * Connection maintenance like timeouts on packet ACKs etc. are done by us, not
787 * the OS like for TCP. POLL events on the socket therefore are not
788 * sufficient.
789 * ngtcp2 tells us when it wants to be invoked again. We handle that via
790 * the `Curl_expire()` mechanisms.
791 */
check_and_set_expiry(struct Curl_cfilter * cf,struct Curl_easy * data,struct pkt_io_ctx * pktx)792 static CURLcode check_and_set_expiry(struct Curl_cfilter *cf,
793 struct Curl_easy *data,
794 struct pkt_io_ctx *pktx)
795 {
796 struct cf_ngtcp2_ctx *ctx = cf->ctx;
797 struct pkt_io_ctx local_pktx;
798 ngtcp2_tstamp expiry;
799
800 if(!pktx) {
801 pktx_init(&local_pktx, cf, data);
802 pktx = &local_pktx;
803 }
804 else {
805 pktx_update_time(pktx, cf);
806 }
807
808 expiry = ngtcp2_conn_get_expiry(ctx->qconn);
809 if(expiry != UINT64_MAX) {
810 if(expiry <= pktx->ts) {
811 CURLcode result;
812 int rv = ngtcp2_conn_handle_expiry(ctx->qconn, pktx->ts);
813 if(rv) {
814 failf(data, "ngtcp2_conn_handle_expiry returned error: %s",
815 ngtcp2_strerror(rv));
816 cf_ngtcp2_err_set(cf, data, rv);
817 return CURLE_SEND_ERROR;
818 }
819 result = cf_progress_ingress(cf, data, pktx);
820 if(result)
821 return result;
822 result = cf_progress_egress(cf, data, pktx);
823 if(result)
824 return result;
825 /* ask again, things might have changed */
826 expiry = ngtcp2_conn_get_expiry(ctx->qconn);
827 }
828
829 if(expiry > pktx->ts) {
830 ngtcp2_duration timeout = expiry - pktx->ts;
831 if(timeout % NGTCP2_MILLISECONDS) {
832 timeout += NGTCP2_MILLISECONDS;
833 }
834 Curl_expire(data, (timediff_t)(timeout / NGTCP2_MILLISECONDS),
835 EXPIRE_QUIC);
836 }
837 }
838 return CURLE_OK;
839 }
840
cf_ngtcp2_adjust_pollset(struct Curl_cfilter * cf,struct Curl_easy * data,struct easy_pollset * ps)841 static void cf_ngtcp2_adjust_pollset(struct Curl_cfilter *cf,
842 struct Curl_easy *data,
843 struct easy_pollset *ps)
844 {
845 struct cf_ngtcp2_ctx *ctx = cf->ctx;
846 bool want_recv, want_send;
847
848 if(!ctx->qconn)
849 return;
850
851 Curl_pollset_check(data, ps, ctx->q.sockfd, &want_recv, &want_send);
852 if(!want_send && !Curl_bufq_is_empty(&ctx->q.sendbuf))
853 want_send = TRUE;
854
855 if(want_recv || want_send) {
856 struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
857 struct cf_call_data save;
858 bool c_exhaust, s_exhaust;
859
860 CF_DATA_SAVE(save, cf, data);
861 c_exhaust = want_send && (!ngtcp2_conn_get_cwnd_left(ctx->qconn) ||
862 !ngtcp2_conn_get_max_data_left(ctx->qconn));
863 s_exhaust = want_send && stream && stream->id >= 0 &&
864 stream->quic_flow_blocked;
865 want_recv = (want_recv || c_exhaust || s_exhaust);
866 want_send = (!s_exhaust && want_send) ||
867 !Curl_bufq_is_empty(&ctx->q.sendbuf);
868
869 Curl_pollset_set(data, ps, ctx->q.sockfd, want_recv, want_send);
870 CF_DATA_RESTORE(cf, save);
871 }
872 }
873
cb_h3_stream_close(nghttp3_conn * conn,int64_t sid,uint64_t app_error_code,void * user_data,void * stream_user_data)874 static int cb_h3_stream_close(nghttp3_conn *conn, int64_t sid,
875 uint64_t app_error_code, void *user_data,
876 void *stream_user_data)
877 {
878 struct Curl_cfilter *cf = user_data;
879 struct cf_ngtcp2_ctx *ctx = cf->ctx;
880 struct Curl_easy *data = stream_user_data;
881 curl_int64_t stream_id = (curl_int64_t)sid;
882 struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
883 (void)conn;
884 (void)stream_id;
885
886 /* we might be called by nghttp3 after we already cleaned up */
887 if(!stream)
888 return 0;
889
890 stream->closed = TRUE;
891 stream->error3 = (curl_uint64_t)app_error_code;
892 if(stream->error3 != NGHTTP3_H3_NO_ERROR) {
893 stream->reset = TRUE;
894 stream->send_closed = TRUE;
895 CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] RESET: error %" FMT_PRIu64,
896 stream->id, stream->error3);
897 }
898 else {
899 CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] CLOSED", stream->id);
900 }
901 h3_drain_stream(cf, data);
902 return 0;
903 }
904
h3_xfer_write_resp_hd(struct Curl_cfilter * cf,struct Curl_easy * data,struct h3_stream_ctx * stream,const char * buf,size_t blen,bool eos)905 static void h3_xfer_write_resp_hd(struct Curl_cfilter *cf,
906 struct Curl_easy *data,
907 struct h3_stream_ctx *stream,
908 const char *buf, size_t blen, bool eos)
909 {
910
911 /* If we already encountered an error, skip further writes */
912 if(!stream->xfer_result) {
913 stream->xfer_result = Curl_xfer_write_resp_hd(data, buf, blen, eos);
914 if(stream->xfer_result)
915 CURL_TRC_CF(data, cf, "[%"FMT_PRId64"] error %d writing %zu "
916 "bytes of headers", stream->id, stream->xfer_result, blen);
917 }
918 }
919
h3_xfer_write_resp(struct Curl_cfilter * cf,struct Curl_easy * data,struct h3_stream_ctx * stream,const char * buf,size_t blen,bool eos)920 static void h3_xfer_write_resp(struct Curl_cfilter *cf,
921 struct Curl_easy *data,
922 struct h3_stream_ctx *stream,
923 const char *buf, size_t blen, bool eos)
924 {
925
926 /* If we already encountered an error, skip further writes */
927 if(!stream->xfer_result) {
928 stream->xfer_result = Curl_xfer_write_resp(data, buf, blen, eos);
929 /* If the transfer write is errored, we do not want any more data */
930 if(stream->xfer_result) {
931 CURL_TRC_CF(data, cf, "[%"FMT_PRId64"] error %d writing %zu bytes "
932 "of data", stream->id, stream->xfer_result, blen);
933 }
934 }
935 }
936
cb_h3_recv_data(nghttp3_conn * conn,int64_t stream3_id,const uint8_t * buf,size_t blen,void * user_data,void * stream_user_data)937 static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id,
938 const uint8_t *buf, size_t blen,
939 void *user_data, void *stream_user_data)
940 {
941 struct Curl_cfilter *cf = user_data;
942 struct cf_ngtcp2_ctx *ctx = cf->ctx;
943 struct Curl_easy *data = stream_user_data;
944 struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
945
946 (void)conn;
947 (void)stream3_id;
948
949 if(!stream)
950 return NGHTTP3_ERR_CALLBACK_FAILURE;
951
952 h3_xfer_write_resp(cf, data, stream, (char *)buf, blen, FALSE);
953 if(blen) {
954 CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] ACK %zu bytes of DATA",
955 stream->id, blen);
956 ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream->id, blen);
957 ngtcp2_conn_extend_max_offset(ctx->qconn, blen);
958 }
959 CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] DATA len=%zu", stream->id, blen);
960 return 0;
961 }
962
cb_h3_deferred_consume(nghttp3_conn * conn,int64_t stream3_id,size_t consumed,void * user_data,void * stream_user_data)963 static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream3_id,
964 size_t consumed, void *user_data,
965 void *stream_user_data)
966 {
967 struct Curl_cfilter *cf = user_data;
968 struct cf_ngtcp2_ctx *ctx = cf->ctx;
969 (void)conn;
970 (void)stream_user_data;
971
972 /* nghttp3 has consumed bytes on the QUIC stream and we need to
973 * tell the QUIC connection to increase its flow control */
974 ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream3_id, consumed);
975 ngtcp2_conn_extend_max_offset(ctx->qconn, consumed);
976 return 0;
977 }
978
cb_h3_end_headers(nghttp3_conn * conn,int64_t sid,int fin,void * user_data,void * stream_user_data)979 static int cb_h3_end_headers(nghttp3_conn *conn, int64_t sid,
980 int fin, void *user_data, void *stream_user_data)
981 {
982 struct Curl_cfilter *cf = user_data;
983 struct cf_ngtcp2_ctx *ctx = cf->ctx;
984 struct Curl_easy *data = stream_user_data;
985 curl_int64_t stream_id = (curl_int64_t)sid;
986 struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
987 (void)conn;
988 (void)stream_id;
989 (void)fin;
990 (void)cf;
991
992 if(!stream)
993 return 0;
994 /* add a CRLF only if we have received some headers */
995 h3_xfer_write_resp_hd(cf, data, stream, STRCONST("\r\n"), stream->closed);
996
997 CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] end_headers, status=%d",
998 stream_id, stream->status_code);
999 if(stream->status_code / 100 != 1) {
1000 stream->resp_hds_complete = TRUE;
1001 }
1002 h3_drain_stream(cf, data);
1003 return 0;
1004 }
1005
cb_h3_recv_header(nghttp3_conn * conn,int64_t sid,int32_t token,nghttp3_rcbuf * name,nghttp3_rcbuf * value,uint8_t flags,void * user_data,void * stream_user_data)1006 static int cb_h3_recv_header(nghttp3_conn *conn, int64_t sid,
1007 int32_t token, nghttp3_rcbuf *name,
1008 nghttp3_rcbuf *value, uint8_t flags,
1009 void *user_data, void *stream_user_data)
1010 {
1011 struct Curl_cfilter *cf = user_data;
1012 struct cf_ngtcp2_ctx *ctx = cf->ctx;
1013 curl_int64_t stream_id = (curl_int64_t)sid;
1014 nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name);
1015 nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value);
1016 struct Curl_easy *data = stream_user_data;
1017 struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
1018 CURLcode result = CURLE_OK;
1019 (void)conn;
1020 (void)stream_id;
1021 (void)token;
1022 (void)flags;
1023 (void)cf;
1024
1025 /* we might have cleaned up this transfer already */
1026 if(!stream)
1027 return 0;
1028
1029 if(token == NGHTTP3_QPACK_TOKEN__STATUS) {
1030
1031 result = Curl_http_decode_status(&stream->status_code,
1032 (const char *)h3val.base, h3val.len);
1033 if(result)
1034 return -1;
1035 Curl_dyn_reset(&ctx->scratch);
1036 result = Curl_dyn_addn(&ctx->scratch, STRCONST("HTTP/3 "));
1037 if(!result)
1038 result = Curl_dyn_addn(&ctx->scratch,
1039 (const char *)h3val.base, h3val.len);
1040 if(!result)
1041 result = Curl_dyn_addn(&ctx->scratch, STRCONST(" \r\n"));
1042 if(!result)
1043 h3_xfer_write_resp_hd(cf, data, stream, Curl_dyn_ptr(&ctx->scratch),
1044 Curl_dyn_len(&ctx->scratch), FALSE);
1045 CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] status: %s",
1046 stream_id, Curl_dyn_ptr(&ctx->scratch));
1047 if(result) {
1048 return -1;
1049 }
1050 }
1051 else {
1052 /* store as an HTTP1-style header */
1053 CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] header: %.*s: %.*s",
1054 stream_id, (int)h3name.len, h3name.base,
1055 (int)h3val.len, h3val.base);
1056 Curl_dyn_reset(&ctx->scratch);
1057 result = Curl_dyn_addn(&ctx->scratch,
1058 (const char *)h3name.base, h3name.len);
1059 if(!result)
1060 result = Curl_dyn_addn(&ctx->scratch, STRCONST(": "));
1061 if(!result)
1062 result = Curl_dyn_addn(&ctx->scratch,
1063 (const char *)h3val.base, h3val.len);
1064 if(!result)
1065 result = Curl_dyn_addn(&ctx->scratch, STRCONST("\r\n"));
1066 if(!result)
1067 h3_xfer_write_resp_hd(cf, data, stream, Curl_dyn_ptr(&ctx->scratch),
1068 Curl_dyn_len(&ctx->scratch), FALSE);
1069 }
1070 return 0;
1071 }
1072
cb_h3_stop_sending(nghttp3_conn * conn,int64_t stream_id,uint64_t app_error_code,void * user_data,void * stream_user_data)1073 static int cb_h3_stop_sending(nghttp3_conn *conn, int64_t stream_id,
1074 uint64_t app_error_code, void *user_data,
1075 void *stream_user_data)
1076 {
1077 struct Curl_cfilter *cf = user_data;
1078 struct cf_ngtcp2_ctx *ctx = cf->ctx;
1079 int rv;
1080 (void)conn;
1081 (void)stream_user_data;
1082
1083 rv = ngtcp2_conn_shutdown_stream_read(ctx->qconn, 0, stream_id,
1084 app_error_code);
1085 if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) {
1086 return NGHTTP3_ERR_CALLBACK_FAILURE;
1087 }
1088
1089 return 0;
1090 }
1091
cb_h3_reset_stream(nghttp3_conn * conn,int64_t sid,uint64_t app_error_code,void * user_data,void * stream_user_data)1092 static int cb_h3_reset_stream(nghttp3_conn *conn, int64_t sid,
1093 uint64_t app_error_code, void *user_data,
1094 void *stream_user_data) {
1095 struct Curl_cfilter *cf = user_data;
1096 struct cf_ngtcp2_ctx *ctx = cf->ctx;
1097 curl_int64_t stream_id = (curl_int64_t)sid;
1098 struct Curl_easy *data = stream_user_data;
1099 int rv;
1100 (void)conn;
1101 (void)data;
1102
1103 rv = ngtcp2_conn_shutdown_stream_write(ctx->qconn, 0, stream_id,
1104 app_error_code);
1105 CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] reset -> %d", stream_id, rv);
1106 if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) {
1107 return NGHTTP3_ERR_CALLBACK_FAILURE;
1108 }
1109
1110 return 0;
1111 }
1112
1113 static nghttp3_callbacks ngh3_callbacks = {
1114 cb_h3_acked_req_body, /* acked_stream_data */
1115 cb_h3_stream_close,
1116 cb_h3_recv_data,
1117 cb_h3_deferred_consume,
1118 NULL, /* begin_headers */
1119 cb_h3_recv_header,
1120 cb_h3_end_headers,
1121 NULL, /* begin_trailers */
1122 cb_h3_recv_header,
1123 NULL, /* end_trailers */
1124 cb_h3_stop_sending,
1125 NULL, /* end_stream */
1126 cb_h3_reset_stream,
1127 NULL, /* shutdown */
1128 NULL /* recv_settings */
1129 };
1130
init_ngh3_conn(struct Curl_cfilter * cf)1131 static CURLcode init_ngh3_conn(struct Curl_cfilter *cf)
1132 {
1133 struct cf_ngtcp2_ctx *ctx = cf->ctx;
1134 CURLcode result;
1135 int rc;
1136 int64_t ctrl_stream_id, qpack_enc_stream_id, qpack_dec_stream_id;
1137
1138 if(ngtcp2_conn_get_streams_uni_left(ctx->qconn) < 3) {
1139 return CURLE_QUIC_CONNECT_ERROR;
1140 }
1141
1142 nghttp3_settings_default(&ctx->h3settings);
1143
1144 rc = nghttp3_conn_client_new(&ctx->h3conn,
1145 &ngh3_callbacks,
1146 &ctx->h3settings,
1147 nghttp3_mem_default(),
1148 cf);
1149 if(rc) {
1150 result = CURLE_OUT_OF_MEMORY;
1151 goto fail;
1152 }
1153
1154 rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &ctrl_stream_id, NULL);
1155 if(rc) {
1156 result = CURLE_QUIC_CONNECT_ERROR;
1157 goto fail;
1158 }
1159
1160 rc = nghttp3_conn_bind_control_stream(ctx->h3conn, ctrl_stream_id);
1161 if(rc) {
1162 result = CURLE_QUIC_CONNECT_ERROR;
1163 goto fail;
1164 }
1165
1166 rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &qpack_enc_stream_id, NULL);
1167 if(rc) {
1168 result = CURLE_QUIC_CONNECT_ERROR;
1169 goto fail;
1170 }
1171
1172 rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &qpack_dec_stream_id, NULL);
1173 if(rc) {
1174 result = CURLE_QUIC_CONNECT_ERROR;
1175 goto fail;
1176 }
1177
1178 rc = nghttp3_conn_bind_qpack_streams(ctx->h3conn, qpack_enc_stream_id,
1179 qpack_dec_stream_id);
1180 if(rc) {
1181 result = CURLE_QUIC_CONNECT_ERROR;
1182 goto fail;
1183 }
1184
1185 return CURLE_OK;
1186 fail:
1187
1188 return result;
1189 }
1190
recv_closed_stream(struct Curl_cfilter * cf,struct Curl_easy * data,struct h3_stream_ctx * stream,CURLcode * err)1191 static ssize_t recv_closed_stream(struct Curl_cfilter *cf,
1192 struct Curl_easy *data,
1193 struct h3_stream_ctx *stream,
1194 CURLcode *err)
1195 {
1196 ssize_t nread = -1;
1197
1198 (void)cf;
1199 if(stream->reset) {
1200 failf(data, "HTTP/3 stream %" FMT_PRId64 " reset by server", stream->id);
1201 *err = data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP3;
1202 goto out;
1203 }
1204 else if(!stream->resp_hds_complete) {
1205 failf(data,
1206 "HTTP/3 stream %" FMT_PRId64 " was closed cleanly, but before "
1207 "getting all response header fields, treated as error",
1208 stream->id);
1209 *err = CURLE_HTTP3;
1210 goto out;
1211 }
1212 *err = CURLE_OK;
1213 nread = 0;
1214
1215 out:
1216 return nread;
1217 }
1218
1219 /* incoming data frames on the h3 stream */
cf_ngtcp2_recv(struct Curl_cfilter * cf,struct Curl_easy * data,char * buf,size_t blen,CURLcode * err)1220 static ssize_t cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
1221 char *buf, size_t blen, CURLcode *err)
1222 {
1223 struct cf_ngtcp2_ctx *ctx = cf->ctx;
1224 struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
1225 ssize_t nread = -1;
1226 struct cf_call_data save;
1227 struct pkt_io_ctx pktx;
1228
1229 (void)ctx;
1230 (void)buf;
1231
1232 CF_DATA_SAVE(save, cf, data);
1233 DEBUGASSERT(cf->connected);
1234 DEBUGASSERT(ctx);
1235 DEBUGASSERT(ctx->qconn);
1236 DEBUGASSERT(ctx->h3conn);
1237 *err = CURLE_OK;
1238
1239 pktx_init(&pktx, cf, data);
1240
1241 if(!stream || ctx->shutdown_started) {
1242 *err = CURLE_RECV_ERROR;
1243 goto out;
1244 }
1245
1246 if(cf_progress_ingress(cf, data, &pktx)) {
1247 *err = CURLE_RECV_ERROR;
1248 nread = -1;
1249 goto out;
1250 }
1251
1252 if(stream->xfer_result) {
1253 CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] xfer write failed", stream->id);
1254 cf_ngtcp2_stream_close(cf, data, stream);
1255 *err = stream->xfer_result;
1256 nread = -1;
1257 goto out;
1258 }
1259 else if(stream->closed) {
1260 nread = recv_closed_stream(cf, data, stream, err);
1261 goto out;
1262 }
1263 *err = CURLE_AGAIN;
1264 nread = -1;
1265
1266 out:
1267 if(cf_progress_egress(cf, data, &pktx)) {
1268 *err = CURLE_SEND_ERROR;
1269 nread = -1;
1270 }
1271 else {
1272 CURLcode result2 = check_and_set_expiry(cf, data, &pktx);
1273 if(result2) {
1274 *err = result2;
1275 nread = -1;
1276 }
1277 }
1278 CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_recv(blen=%zu) -> %zd, %d",
1279 stream ? stream->id : -1, blen, nread, *err);
1280 CF_DATA_RESTORE(cf, save);
1281 return nread;
1282 }
1283
cb_h3_acked_req_body(nghttp3_conn * conn,int64_t stream_id,uint64_t datalen,void * user_data,void * stream_user_data)1284 static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id,
1285 uint64_t datalen, void *user_data,
1286 void *stream_user_data)
1287 {
1288 struct Curl_cfilter *cf = user_data;
1289 struct cf_ngtcp2_ctx *ctx = cf->ctx;
1290 struct Curl_easy *data = stream_user_data;
1291 struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
1292 size_t skiplen;
1293
1294 (void)cf;
1295 if(!stream)
1296 return 0;
1297 /* The server acknowledged `datalen` of bytes from our request body.
1298 * This is a delta. We have kept this data in `sendbuf` for
1299 * re-transmissions and can free it now. */
1300 if(datalen >= (uint64_t)stream->sendbuf_len_in_flight)
1301 skiplen = stream->sendbuf_len_in_flight;
1302 else
1303 skiplen = (size_t)datalen;
1304 Curl_bufq_skip(&stream->sendbuf, skiplen);
1305 stream->sendbuf_len_in_flight -= skiplen;
1306
1307 /* Resume upload processing if we have more data to send */
1308 if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) {
1309 int rv = nghttp3_conn_resume_stream(conn, stream_id);
1310 if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
1311 return NGHTTP3_ERR_CALLBACK_FAILURE;
1312 }
1313 }
1314 return 0;
1315 }
1316
1317 static nghttp3_ssize
cb_h3_read_req_body(nghttp3_conn * conn,int64_t stream_id,nghttp3_vec * vec,size_t veccnt,uint32_t * pflags,void * user_data,void * stream_user_data)1318 cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id,
1319 nghttp3_vec *vec, size_t veccnt,
1320 uint32_t *pflags, void *user_data,
1321 void *stream_user_data)
1322 {
1323 struct Curl_cfilter *cf = user_data;
1324 struct cf_ngtcp2_ctx *ctx = cf->ctx;
1325 struct Curl_easy *data = stream_user_data;
1326 struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
1327 ssize_t nwritten = 0;
1328 size_t nvecs = 0;
1329 (void)cf;
1330 (void)conn;
1331 (void)stream_id;
1332 (void)user_data;
1333 (void)veccnt;
1334
1335 if(!stream)
1336 return NGHTTP3_ERR_CALLBACK_FAILURE;
1337 /* nghttp3 keeps references to the sendbuf data until it is ACKed
1338 * by the server (see `cb_h3_acked_req_body()` for updates).
1339 * `sendbuf_len_in_flight` is the amount of bytes in `sendbuf`
1340 * that we have already passed to nghttp3, but which have not been
1341 * ACKed yet.
1342 * Any amount beyond `sendbuf_len_in_flight` we need still to pass
1343 * to nghttp3. Do that now, if we can. */
1344 if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) {
1345 nvecs = 0;
1346 while(nvecs < veccnt &&
1347 Curl_bufq_peek_at(&stream->sendbuf,
1348 stream->sendbuf_len_in_flight,
1349 (const unsigned char **)&vec[nvecs].base,
1350 &vec[nvecs].len)) {
1351 stream->sendbuf_len_in_flight += vec[nvecs].len;
1352 nwritten += vec[nvecs].len;
1353 ++nvecs;
1354 }
1355 DEBUGASSERT(nvecs > 0); /* we SHOULD have been be able to peek */
1356 }
1357
1358 if(nwritten > 0 && stream->upload_left != -1)
1359 stream->upload_left -= nwritten;
1360
1361 /* When we stopped sending and everything in `sendbuf` is "in flight",
1362 * we are at the end of the request body. */
1363 if(stream->upload_left == 0) {
1364 *pflags = NGHTTP3_DATA_FLAG_EOF;
1365 stream->send_closed = TRUE;
1366 }
1367 else if(!nwritten) {
1368 /* Not EOF, and nothing to give, we signal WOULDBLOCK. */
1369 CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] read req body -> AGAIN",
1370 stream->id);
1371 return NGHTTP3_ERR_WOULDBLOCK;
1372 }
1373
1374 CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] read req body -> "
1375 "%d vecs%s with %zu (buffered=%zu, left=%" FMT_OFF_T ")",
1376 stream->id, (int)nvecs,
1377 *pflags == NGHTTP3_DATA_FLAG_EOF ? " EOF" : "",
1378 nwritten, Curl_bufq_len(&stream->sendbuf),
1379 stream->upload_left);
1380 return (nghttp3_ssize)nvecs;
1381 }
1382
1383 /* Index where :authority header field will appear in request header
1384 field list. */
1385 #define AUTHORITY_DST_IDX 3
1386
h3_stream_open(struct Curl_cfilter * cf,struct Curl_easy * data,const void * buf,size_t len,CURLcode * err)1387 static ssize_t h3_stream_open(struct Curl_cfilter *cf,
1388 struct Curl_easy *data,
1389 const void *buf, size_t len,
1390 CURLcode *err)
1391 {
1392 struct cf_ngtcp2_ctx *ctx = cf->ctx;
1393 struct h3_stream_ctx *stream = NULL;
1394 int64_t sid;
1395 struct dynhds h2_headers;
1396 size_t nheader;
1397 nghttp3_nv *nva = NULL;
1398 int rc = 0;
1399 unsigned int i;
1400 ssize_t nwritten = -1;
1401 nghttp3_data_reader reader;
1402 nghttp3_data_reader *preader = NULL;
1403
1404 Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST);
1405
1406 *err = h3_data_setup(cf, data);
1407 if(*err)
1408 goto out;
1409 stream = H3_STREAM_CTX(ctx, data);
1410 DEBUGASSERT(stream);
1411 if(!stream) {
1412 *err = CURLE_FAILED_INIT;
1413 goto out;
1414 }
1415
1416 nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err);
1417 if(nwritten < 0)
1418 goto out;
1419 if(!stream->h1.done) {
1420 /* need more data */
1421 goto out;
1422 }
1423 DEBUGASSERT(stream->h1.req);
1424
1425 *err = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data);
1426 if(*err) {
1427 nwritten = -1;
1428 goto out;
1429 }
1430 /* no longer needed */
1431 Curl_h1_req_parse_free(&stream->h1);
1432
1433 nheader = Curl_dynhds_count(&h2_headers);
1434 nva = malloc(sizeof(nghttp3_nv) * nheader);
1435 if(!nva) {
1436 *err = CURLE_OUT_OF_MEMORY;
1437 nwritten = -1;
1438 goto out;
1439 }
1440
1441 for(i = 0; i < nheader; ++i) {
1442 struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i);
1443 nva[i].name = (unsigned char *)e->name;
1444 nva[i].namelen = e->namelen;
1445 nva[i].value = (unsigned char *)e->value;
1446 nva[i].valuelen = e->valuelen;
1447 nva[i].flags = NGHTTP3_NV_FLAG_NONE;
1448 }
1449
1450 rc = ngtcp2_conn_open_bidi_stream(ctx->qconn, &sid, data);
1451 if(rc) {
1452 failf(data, "can get bidi streams");
1453 *err = CURLE_SEND_ERROR;
1454 nwritten = -1;
1455 goto out;
1456 }
1457 stream->id = (curl_int64_t)sid;
1458 ++ctx->used_bidi_streams;
1459
1460 switch(data->state.httpreq) {
1461 case HTTPREQ_POST:
1462 case HTTPREQ_POST_FORM:
1463 case HTTPREQ_POST_MIME:
1464 case HTTPREQ_PUT:
1465 /* known request body size or -1 */
1466 if(data->state.infilesize != -1)
1467 stream->upload_left = data->state.infilesize;
1468 else
1469 /* data sending without specifying the data amount up front */
1470 stream->upload_left = -1; /* unknown */
1471 break;
1472 default:
1473 /* there is not request body */
1474 stream->upload_left = 0; /* no request body */
1475 break;
1476 }
1477
1478 stream->send_closed = (stream->upload_left == 0);
1479 if(!stream->send_closed) {
1480 reader.read_data = cb_h3_read_req_body;
1481 preader = &reader;
1482 }
1483
1484 rc = nghttp3_conn_submit_request(ctx->h3conn, stream->id,
1485 nva, nheader, preader, data);
1486 if(rc) {
1487 switch(rc) {
1488 case NGHTTP3_ERR_CONN_CLOSING:
1489 CURL_TRC_CF(data, cf, "h3sid[%" FMT_PRId64 "] failed to send, "
1490 "connection is closing", stream->id);
1491 break;
1492 default:
1493 CURL_TRC_CF(data, cf, "h3sid[%" FMT_PRId64 "] failed to send -> "
1494 "%d (%s)", stream->id, rc, nghttp3_strerror(rc));
1495 break;
1496 }
1497 *err = CURLE_SEND_ERROR;
1498 nwritten = -1;
1499 goto out;
1500 }
1501
1502 if(Curl_trc_is_verbose(data)) {
1503 infof(data, "[HTTP/3] [%" FMT_PRId64 "] OPENED stream for %s",
1504 stream->id, data->state.url);
1505 for(i = 0; i < nheader; ++i) {
1506 infof(data, "[HTTP/3] [%" FMT_PRId64 "] [%.*s: %.*s]", stream->id,
1507 (int)nva[i].namelen, nva[i].name,
1508 (int)nva[i].valuelen, nva[i].value);
1509 }
1510 }
1511
1512 out:
1513 free(nva);
1514 Curl_dynhds_free(&h2_headers);
1515 return nwritten;
1516 }
1517
cf_ngtcp2_send(struct Curl_cfilter * cf,struct Curl_easy * data,const void * buf,size_t len,bool eos,CURLcode * err)1518 static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
1519 const void *buf, size_t len, bool eos,
1520 CURLcode *err)
1521 {
1522 struct cf_ngtcp2_ctx *ctx = cf->ctx;
1523 struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
1524 ssize_t sent = 0;
1525 struct cf_call_data save;
1526 struct pkt_io_ctx pktx;
1527 CURLcode result;
1528
1529 CF_DATA_SAVE(save, cf, data);
1530 DEBUGASSERT(cf->connected);
1531 DEBUGASSERT(ctx->qconn);
1532 DEBUGASSERT(ctx->h3conn);
1533 pktx_init(&pktx, cf, data);
1534 *err = CURLE_OK;
1535
1536 (void)eos; /* TODO: use for stream EOF and block handling */
1537 result = cf_progress_ingress(cf, data, &pktx);
1538 if(result) {
1539 *err = result;
1540 sent = -1;
1541 }
1542
1543 if(!stream || stream->id < 0) {
1544 if(ctx->shutdown_started) {
1545 CURL_TRC_CF(data, cf, "cannot open stream on closed connection");
1546 *err = CURLE_SEND_ERROR;
1547 sent = -1;
1548 goto out;
1549 }
1550 sent = h3_stream_open(cf, data, buf, len, err);
1551 if(sent < 0) {
1552 CURL_TRC_CF(data, cf, "failed to open stream -> %d", *err);
1553 goto out;
1554 }
1555 stream = H3_STREAM_CTX(ctx, data);
1556 }
1557 else if(stream->xfer_result) {
1558 CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] xfer write failed", stream->id);
1559 cf_ngtcp2_stream_close(cf, data, stream);
1560 *err = stream->xfer_result;
1561 sent = -1;
1562 goto out;
1563 }
1564 else if(stream->closed) {
1565 if(stream->resp_hds_complete) {
1566 /* Server decided to close the stream after having sent us a final
1567 * response. This is valid if it is not interested in the request
1568 * body. This happens on 30x or 40x responses.
1569 * We silently discard the data sent, since this is not a transport
1570 * error situation. */
1571 CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] discarding data"
1572 "on closed stream with response", stream->id);
1573 *err = CURLE_OK;
1574 sent = (ssize_t)len;
1575 goto out;
1576 }
1577 CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] send_body(len=%zu) "
1578 "-> stream closed", stream->id, len);
1579 *err = CURLE_HTTP3;
1580 sent = -1;
1581 goto out;
1582 }
1583 else if(ctx->shutdown_started) {
1584 CURL_TRC_CF(data, cf, "cannot send on closed connection");
1585 *err = CURLE_SEND_ERROR;
1586 sent = -1;
1587 goto out;
1588 }
1589 else {
1590 sent = Curl_bufq_write(&stream->sendbuf, buf, len, err);
1591 CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_send, add to "
1592 "sendbuf(len=%zu) -> %zd, %d",
1593 stream->id, len, sent, *err);
1594 if(sent < 0) {
1595 goto out;
1596 }
1597
1598 (void)nghttp3_conn_resume_stream(ctx->h3conn, stream->id);
1599 }
1600
1601 result = cf_progress_egress(cf, data, &pktx);
1602 if(result) {
1603 *err = result;
1604 sent = -1;
1605 }
1606
1607 out:
1608 result = check_and_set_expiry(cf, data, &pktx);
1609 if(result) {
1610 *err = result;
1611 sent = -1;
1612 }
1613 CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_send(len=%zu) -> %zd, %d",
1614 stream ? stream->id : -1, len, sent, *err);
1615 CF_DATA_RESTORE(cf, save);
1616 return sent;
1617 }
1618
qng_verify_peer(struct Curl_cfilter * cf,struct Curl_easy * data)1619 static CURLcode qng_verify_peer(struct Curl_cfilter *cf,
1620 struct Curl_easy *data)
1621 {
1622 struct cf_ngtcp2_ctx *ctx = cf->ctx;
1623
1624 cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
1625 cf->conn->httpversion = 30;
1626
1627 return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer);
1628 }
1629
recv_pkt(const unsigned char * pkt,size_t pktlen,struct sockaddr_storage * remote_addr,socklen_t remote_addrlen,int ecn,void * userp)1630 static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen,
1631 struct sockaddr_storage *remote_addr,
1632 socklen_t remote_addrlen, int ecn,
1633 void *userp)
1634 {
1635 struct pkt_io_ctx *pktx = userp;
1636 struct cf_ngtcp2_ctx *ctx = pktx->cf->ctx;
1637 ngtcp2_pkt_info pi;
1638 ngtcp2_path path;
1639 int rv;
1640
1641 ngtcp2_addr_init(&path.local, (struct sockaddr *)&ctx->q.local_addr,
1642 (socklen_t)ctx->q.local_addrlen);
1643 ngtcp2_addr_init(&path.remote, (struct sockaddr *)remote_addr,
1644 remote_addrlen);
1645 pi.ecn = (uint8_t)ecn;
1646
1647 rv = ngtcp2_conn_read_pkt(ctx->qconn, &path, &pi, pkt, pktlen, pktx->ts);
1648 if(rv) {
1649 CURL_TRC_CF(pktx->data, pktx->cf, "ingress, read_pkt -> %s (%d)",
1650 ngtcp2_strerror(rv), rv);
1651 cf_ngtcp2_err_set(pktx->cf, pktx->data, rv);
1652
1653 if(rv == NGTCP2_ERR_CRYPTO)
1654 /* this is a "TLS problem", but a failed certificate verification
1655 is a common reason for this */
1656 return CURLE_PEER_FAILED_VERIFICATION;
1657 return CURLE_RECV_ERROR;
1658 }
1659
1660 return CURLE_OK;
1661 }
1662
cf_progress_ingress(struct Curl_cfilter * cf,struct Curl_easy * data,struct pkt_io_ctx * pktx)1663 static CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
1664 struct Curl_easy *data,
1665 struct pkt_io_ctx *pktx)
1666 {
1667 struct cf_ngtcp2_ctx *ctx = cf->ctx;
1668 struct pkt_io_ctx local_pktx;
1669 CURLcode result = CURLE_OK;
1670
1671 if(!pktx) {
1672 pktx_init(&local_pktx, cf, data);
1673 pktx = &local_pktx;
1674 }
1675
1676 result = Curl_vquic_tls_before_recv(&ctx->tls, cf, data);
1677 if(result)
1678 return result;
1679
1680 return vquic_recv_packets(cf, data, &ctx->q, 1000, recv_pkt, pktx);
1681 }
1682
1683 /**
1684 * Read a network packet to send from ngtcp2 into `buf`.
1685 * Return number of bytes written or -1 with *err set.
1686 */
read_pkt_to_send(void * userp,unsigned char * buf,size_t buflen,CURLcode * err)1687 static ssize_t read_pkt_to_send(void *userp,
1688 unsigned char *buf, size_t buflen,
1689 CURLcode *err)
1690 {
1691 struct pkt_io_ctx *x = userp;
1692 struct cf_ngtcp2_ctx *ctx = x->cf->ctx;
1693 nghttp3_vec vec[16];
1694 nghttp3_ssize veccnt;
1695 ngtcp2_ssize ndatalen;
1696 uint32_t flags;
1697 int64_t stream_id;
1698 int fin;
1699 ssize_t nwritten, n;
1700 veccnt = 0;
1701 stream_id = -1;
1702 fin = 0;
1703
1704 /* ngtcp2 may want to put several frames from different streams into
1705 * this packet. `NGTCP2_WRITE_STREAM_FLAG_MORE` tells it to do so.
1706 * When `NGTCP2_ERR_WRITE_MORE` is returned, we *need* to make
1707 * another iteration.
1708 * When ngtcp2 is happy (because it has no other frame that would fit
1709 * or it has nothing more to send), it returns the total length
1710 * of the assembled packet. This may be 0 if there was nothing to send. */
1711 nwritten = 0;
1712 *err = CURLE_OK;
1713 for(;;) {
1714
1715 if(ctx->h3conn && ngtcp2_conn_get_max_data_left(ctx->qconn)) {
1716 veccnt = nghttp3_conn_writev_stream(ctx->h3conn, &stream_id, &fin, vec,
1717 sizeof(vec) / sizeof(vec[0]));
1718 if(veccnt < 0) {
1719 failf(x->data, "nghttp3_conn_writev_stream returned error: %s",
1720 nghttp3_strerror((int)veccnt));
1721 cf_ngtcp2_h3_err_set(x->cf, x->data, (int)veccnt);
1722 *err = CURLE_SEND_ERROR;
1723 return -1;
1724 }
1725 }
1726
1727 flags = NGTCP2_WRITE_STREAM_FLAG_MORE |
1728 (fin ? NGTCP2_WRITE_STREAM_FLAG_FIN : 0);
1729 n = ngtcp2_conn_writev_stream(ctx->qconn, &x->ps.path,
1730 NULL, buf, buflen,
1731 &ndatalen, flags, stream_id,
1732 (const ngtcp2_vec *)vec, veccnt, x->ts);
1733 if(n == 0) {
1734 /* nothing to send */
1735 *err = CURLE_AGAIN;
1736 nwritten = -1;
1737 goto out;
1738 }
1739 else if(n < 0) {
1740 switch(n) {
1741 case NGTCP2_ERR_STREAM_DATA_BLOCKED: {
1742 struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, x->data);
1743 DEBUGASSERT(ndatalen == -1);
1744 nghttp3_conn_block_stream(ctx->h3conn, stream_id);
1745 CURL_TRC_CF(x->data, x->cf, "[%" FMT_PRId64 "] block quic flow",
1746 (curl_int64_t)stream_id);
1747 DEBUGASSERT(stream);
1748 if(stream)
1749 stream->quic_flow_blocked = TRUE;
1750 n = 0;
1751 break;
1752 }
1753 case NGTCP2_ERR_STREAM_SHUT_WR:
1754 DEBUGASSERT(ndatalen == -1);
1755 nghttp3_conn_shutdown_stream_write(ctx->h3conn, stream_id);
1756 n = 0;
1757 break;
1758 case NGTCP2_ERR_WRITE_MORE:
1759 /* ngtcp2 wants to send more. update the flow of the stream whose data
1760 * is in the buffer and continue */
1761 DEBUGASSERT(ndatalen >= 0);
1762 n = 0;
1763 break;
1764 default:
1765 DEBUGASSERT(ndatalen == -1);
1766 failf(x->data, "ngtcp2_conn_writev_stream returned error: %s",
1767 ngtcp2_strerror((int)n));
1768 cf_ngtcp2_err_set(x->cf, x->data, (int)n);
1769 *err = CURLE_SEND_ERROR;
1770 nwritten = -1;
1771 goto out;
1772 }
1773 }
1774
1775 if(ndatalen >= 0) {
1776 /* we add the amount of data bytes to the flow windows */
1777 int rv = nghttp3_conn_add_write_offset(ctx->h3conn, stream_id, ndatalen);
1778 if(rv) {
1779 failf(x->data, "nghttp3_conn_add_write_offset returned error: %s\n",
1780 nghttp3_strerror(rv));
1781 return CURLE_SEND_ERROR;
1782 }
1783 }
1784
1785 if(n > 0) {
1786 /* packet assembled, leave */
1787 nwritten = n;
1788 goto out;
1789 }
1790 }
1791 out:
1792 return nwritten;
1793 }
1794
cf_progress_egress(struct Curl_cfilter * cf,struct Curl_easy * data,struct pkt_io_ctx * pktx)1795 static CURLcode cf_progress_egress(struct Curl_cfilter *cf,
1796 struct Curl_easy *data,
1797 struct pkt_io_ctx *pktx)
1798 {
1799 struct cf_ngtcp2_ctx *ctx = cf->ctx;
1800 ssize_t nread;
1801 size_t max_payload_size, path_max_payload_size, max_pktcnt;
1802 size_t pktcnt = 0;
1803 size_t gsolen = 0; /* this disables gso until we have a clue */
1804 CURLcode curlcode;
1805 struct pkt_io_ctx local_pktx;
1806
1807 if(!pktx) {
1808 pktx_init(&local_pktx, cf, data);
1809 pktx = &local_pktx;
1810 }
1811 else {
1812 pktx_update_time(pktx, cf);
1813 ngtcp2_path_storage_zero(&pktx->ps);
1814 }
1815
1816 curlcode = vquic_flush(cf, data, &ctx->q);
1817 if(curlcode) {
1818 if(curlcode == CURLE_AGAIN) {
1819 Curl_expire(data, 1, EXPIRE_QUIC);
1820 return CURLE_OK;
1821 }
1822 return curlcode;
1823 }
1824
1825 /* In UDP, there is a maximum theoretical packet paload length and
1826 * a minimum payload length that is "guaranteed" to work.
1827 * To detect if this minimum payload can be increased, ngtcp2 sends
1828 * now and then a packet payload larger than the minimum. It that
1829 * is ACKed by the peer, both parties know that it works and
1830 * the subsequent packets can use a larger one.
1831 * This is called PMTUD (Path Maximum Transmission Unit Discovery).
1832 * Since a PMTUD might be rejected right on send, we do not want it
1833 * be followed by other packets of lesser size. Because those would
1834 * also fail then. So, if we detect a PMTUD while buffering, we flush.
1835 */
1836 max_payload_size = ngtcp2_conn_get_max_tx_udp_payload_size(ctx->qconn);
1837 path_max_payload_size =
1838 ngtcp2_conn_get_path_max_tx_udp_payload_size(ctx->qconn);
1839 /* maximum number of packets buffered before we flush to the socket */
1840 max_pktcnt = CURLMIN(MAX_PKT_BURST,
1841 ctx->q.sendbuf.chunk_size / max_payload_size);
1842
1843 for(;;) {
1844 /* add the next packet to send, if any, to our buffer */
1845 nread = Curl_bufq_sipn(&ctx->q.sendbuf, max_payload_size,
1846 read_pkt_to_send, pktx, &curlcode);
1847 if(nread < 0) {
1848 if(curlcode != CURLE_AGAIN)
1849 return curlcode;
1850 /* Nothing more to add, flush and leave */
1851 curlcode = vquic_send(cf, data, &ctx->q, gsolen);
1852 if(curlcode) {
1853 if(curlcode == CURLE_AGAIN) {
1854 Curl_expire(data, 1, EXPIRE_QUIC);
1855 return CURLE_OK;
1856 }
1857 return curlcode;
1858 }
1859 goto out;
1860 }
1861
1862 DEBUGASSERT(nread > 0);
1863 if(pktcnt == 0) {
1864 /* first packet in buffer. This is either of a known, "good"
1865 * payload size or it is a PMTUD. We will see. */
1866 gsolen = (size_t)nread;
1867 }
1868 else if((size_t)nread > gsolen ||
1869 (gsolen > path_max_payload_size && (size_t)nread != gsolen)) {
1870 /* The just added packet is a PMTUD *or* the one(s) before the
1871 * just added were PMTUD and the last one is smaller.
1872 * Flush the buffer before the last add. */
1873 curlcode = vquic_send_tail_split(cf, data, &ctx->q,
1874 gsolen, nread, nread);
1875 if(curlcode) {
1876 if(curlcode == CURLE_AGAIN) {
1877 Curl_expire(data, 1, EXPIRE_QUIC);
1878 return CURLE_OK;
1879 }
1880 return curlcode;
1881 }
1882 pktcnt = 0;
1883 continue;
1884 }
1885
1886 if(++pktcnt >= max_pktcnt || (size_t)nread < gsolen) {
1887 /* Reached MAX_PKT_BURST *or*
1888 * the capacity of our buffer *or*
1889 * last add was shorter than the previous ones, flush */
1890 curlcode = vquic_send(cf, data, &ctx->q, gsolen);
1891 if(curlcode) {
1892 if(curlcode == CURLE_AGAIN) {
1893 Curl_expire(data, 1, EXPIRE_QUIC);
1894 return CURLE_OK;
1895 }
1896 return curlcode;
1897 }
1898 /* pktbuf has been completely sent */
1899 pktcnt = 0;
1900 }
1901 }
1902
1903 out:
1904 return CURLE_OK;
1905 }
1906
1907 /*
1908 * Called from transfer.c:data_pending to know if we should keep looping
1909 * to receive more data from the connection.
1910 */
cf_ngtcp2_data_pending(struct Curl_cfilter * cf,const struct Curl_easy * data)1911 static bool cf_ngtcp2_data_pending(struct Curl_cfilter *cf,
1912 const struct Curl_easy *data)
1913 {
1914 (void)cf;
1915 (void)data;
1916 return FALSE;
1917 }
1918
h3_data_pause(struct Curl_cfilter * cf,struct Curl_easy * data,bool pause)1919 static CURLcode h3_data_pause(struct Curl_cfilter *cf,
1920 struct Curl_easy *data,
1921 bool pause)
1922 {
1923 /* TODO: there seems right now no API in ngtcp2 to shrink/enlarge
1924 * the streams windows. As we do in HTTP/2. */
1925 if(!pause) {
1926 h3_drain_stream(cf, data);
1927 Curl_expire(data, 0, EXPIRE_RUN_NOW);
1928 }
1929 return CURLE_OK;
1930 }
1931
cf_ngtcp2_data_event(struct Curl_cfilter * cf,struct Curl_easy * data,int event,int arg1,void * arg2)1932 static CURLcode cf_ngtcp2_data_event(struct Curl_cfilter *cf,
1933 struct Curl_easy *data,
1934 int event, int arg1, void *arg2)
1935 {
1936 struct cf_ngtcp2_ctx *ctx = cf->ctx;
1937 CURLcode result = CURLE_OK;
1938 struct cf_call_data save;
1939
1940 CF_DATA_SAVE(save, cf, data);
1941 (void)arg1;
1942 (void)arg2;
1943 switch(event) {
1944 case CF_CTRL_DATA_SETUP:
1945 break;
1946 case CF_CTRL_DATA_PAUSE:
1947 result = h3_data_pause(cf, data, (arg1 != 0));
1948 break;
1949 case CF_CTRL_DATA_DETACH:
1950 h3_data_done(cf, data);
1951 break;
1952 case CF_CTRL_DATA_DONE:
1953 h3_data_done(cf, data);
1954 break;
1955 case CF_CTRL_DATA_DONE_SEND: {
1956 struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
1957 if(stream && !stream->send_closed) {
1958 stream->send_closed = TRUE;
1959 stream->upload_left = Curl_bufq_len(&stream->sendbuf) -
1960 stream->sendbuf_len_in_flight;
1961 (void)nghttp3_conn_resume_stream(ctx->h3conn, stream->id);
1962 }
1963 break;
1964 }
1965 case CF_CTRL_DATA_IDLE: {
1966 struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
1967 CURL_TRC_CF(data, cf, "data idle");
1968 if(stream && !stream->closed) {
1969 result = check_and_set_expiry(cf, data, NULL);
1970 if(result)
1971 CURL_TRC_CF(data, cf, "data idle, check_and_set_expiry -> %d", result);
1972 }
1973 break;
1974 }
1975 default:
1976 break;
1977 }
1978 CF_DATA_RESTORE(cf, save);
1979 return result;
1980 }
1981
cf_ngtcp2_ctx_close(struct cf_ngtcp2_ctx * ctx)1982 static void cf_ngtcp2_ctx_close(struct cf_ngtcp2_ctx *ctx)
1983 {
1984 struct cf_call_data save = ctx->call_data;
1985
1986 if(!ctx->initialized)
1987 return;
1988 if(ctx->qlogfd != -1) {
1989 close(ctx->qlogfd);
1990 }
1991 ctx->qlogfd = -1;
1992 Curl_vquic_tls_cleanup(&ctx->tls);
1993 vquic_ctx_free(&ctx->q);
1994 if(ctx->h3conn)
1995 nghttp3_conn_del(ctx->h3conn);
1996 if(ctx->qconn)
1997 ngtcp2_conn_del(ctx->qconn);
1998 ctx->call_data = save;
1999 }
2000
cf_ngtcp2_shutdown(struct Curl_cfilter * cf,struct Curl_easy * data,bool * done)2001 static CURLcode cf_ngtcp2_shutdown(struct Curl_cfilter *cf,
2002 struct Curl_easy *data, bool *done)
2003 {
2004 struct cf_ngtcp2_ctx *ctx = cf->ctx;
2005 struct cf_call_data save;
2006 struct pkt_io_ctx pktx;
2007 CURLcode result = CURLE_OK;
2008
2009 if(cf->shutdown || !ctx->qconn) {
2010 *done = TRUE;
2011 return CURLE_OK;
2012 }
2013
2014 CF_DATA_SAVE(save, cf, data);
2015 *done = FALSE;
2016 pktx_init(&pktx, cf, data);
2017
2018 if(!ctx->shutdown_started) {
2019 char buffer[NGTCP2_MAX_UDP_PAYLOAD_SIZE];
2020 ngtcp2_ssize nwritten;
2021
2022 if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) {
2023 CURL_TRC_CF(data, cf, "shutdown, flushing sendbuf");
2024 result = cf_progress_egress(cf, data, &pktx);
2025 if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) {
2026 CURL_TRC_CF(data, cf, "sending shutdown packets blocked");
2027 result = CURLE_OK;
2028 goto out;
2029 }
2030 else if(result) {
2031 CURL_TRC_CF(data, cf, "shutdown, error %d flushing sendbuf", result);
2032 *done = TRUE;
2033 goto out;
2034 }
2035 }
2036
2037 ctx->shutdown_started = TRUE;
2038 nwritten = ngtcp2_conn_write_connection_close(
2039 ctx->qconn, NULL, /* path */
2040 NULL, /* pkt_info */
2041 (uint8_t *)buffer, sizeof(buffer),
2042 &ctx->last_error, pktx.ts);
2043 CURL_TRC_CF(data, cf, "start shutdown(err_type=%d, err_code=%"
2044 FMT_PRIu64 ") -> %d", ctx->last_error.type,
2045 (curl_uint64_t)ctx->last_error.error_code, (int)nwritten);
2046 if(nwritten > 0) {
2047 Curl_bufq_write(&ctx->q.sendbuf, (const unsigned char *)buffer,
2048 (size_t)nwritten, &result);
2049 if(result) {
2050 CURL_TRC_CF(data, cf, "error %d adding shutdown packets to sendbuf, "
2051 "aborting shutdown", result);
2052 goto out;
2053 }
2054 ctx->q.no_gso = TRUE;
2055 ctx->q.gsolen = (size_t)nwritten;
2056 ctx->q.split_len = 0;
2057 }
2058 }
2059
2060 if(!Curl_bufq_is_empty(&ctx->q.sendbuf)) {
2061 CURL_TRC_CF(data, cf, "shutdown, flushing egress");
2062 result = vquic_flush(cf, data, &ctx->q);
2063 if(result == CURLE_AGAIN) {
2064 CURL_TRC_CF(data, cf, "sending shutdown packets blocked");
2065 result = CURLE_OK;
2066 goto out;
2067 }
2068 else if(result) {
2069 CURL_TRC_CF(data, cf, "shutdown, error %d flushing sendbuf", result);
2070 *done = TRUE;
2071 goto out;
2072 }
2073 }
2074
2075 if(Curl_bufq_is_empty(&ctx->q.sendbuf)) {
2076 /* Sent everything off. ngtcp2 seems to have no support for graceful
2077 * shutdowns. So, we are done. */
2078 CURL_TRC_CF(data, cf, "shutdown completely sent off, done");
2079 *done = TRUE;
2080 result = CURLE_OK;
2081 }
2082 out:
2083 CF_DATA_RESTORE(cf, save);
2084 return result;
2085 }
2086
cf_ngtcp2_conn_close(struct Curl_cfilter * cf,struct Curl_easy * data)2087 static void cf_ngtcp2_conn_close(struct Curl_cfilter *cf,
2088 struct Curl_easy *data)
2089 {
2090 bool done;
2091 cf_ngtcp2_shutdown(cf, data, &done);
2092 }
2093
cf_ngtcp2_close(struct Curl_cfilter * cf,struct Curl_easy * data)2094 static void cf_ngtcp2_close(struct Curl_cfilter *cf, struct Curl_easy *data)
2095 {
2096 struct cf_ngtcp2_ctx *ctx = cf->ctx;
2097 struct cf_call_data save;
2098
2099 CF_DATA_SAVE(save, cf, data);
2100 if(ctx && ctx->qconn) {
2101 cf_ngtcp2_conn_close(cf, data);
2102 cf_ngtcp2_ctx_close(ctx);
2103 CURL_TRC_CF(data, cf, "close");
2104 }
2105 cf->connected = FALSE;
2106 CF_DATA_RESTORE(cf, save);
2107 }
2108
cf_ngtcp2_destroy(struct Curl_cfilter * cf,struct Curl_easy * data)2109 static void cf_ngtcp2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
2110 {
2111 CURL_TRC_CF(data, cf, "destroy");
2112 if(cf->ctx) {
2113 cf_ngtcp2_ctx_free(cf->ctx);
2114 cf->ctx = NULL;
2115 }
2116 }
2117
2118 #ifdef USE_OPENSSL
2119 /* The "new session" callback must return zero if the session can be removed
2120 * or non-zero if the session has been put into the session cache.
2121 */
quic_ossl_new_session_cb(SSL * ssl,SSL_SESSION * ssl_sessionid)2122 static int quic_ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
2123 {
2124 struct Curl_cfilter *cf;
2125 struct cf_ngtcp2_ctx *ctx;
2126 struct Curl_easy *data;
2127 ngtcp2_crypto_conn_ref *cref;
2128
2129 cref = (ngtcp2_crypto_conn_ref *)SSL_get_app_data(ssl);
2130 cf = cref ? cref->user_data : NULL;
2131 ctx = cf ? cf->ctx : NULL;
2132 data = cf ? CF_DATA_CURRENT(cf) : NULL;
2133 if(cf && data && ctx) {
2134 Curl_ossl_add_session(cf, data, &ctx->peer, ssl_sessionid);
2135 return 1;
2136 }
2137 return 0;
2138 }
2139 #endif /* USE_OPENSSL */
2140
2141 #ifdef USE_GNUTLS
quic_gtls_handshake_cb(gnutls_session_t session,unsigned int htype,unsigned when,unsigned int incoming,const gnutls_datum_t * msg)2142 static int quic_gtls_handshake_cb(gnutls_session_t session, unsigned int htype,
2143 unsigned when, unsigned int incoming,
2144 const gnutls_datum_t *msg)
2145 {
2146 ngtcp2_crypto_conn_ref *conn_ref = gnutls_session_get_ptr(session);
2147 struct Curl_cfilter *cf = conn_ref ? conn_ref->user_data : NULL;
2148 struct cf_ngtcp2_ctx *ctx = cf ? cf->ctx : NULL;
2149
2150 (void)msg;
2151 (void)incoming;
2152 if(when && cf && ctx) { /* after message has been processed */
2153 struct Curl_easy *data = CF_DATA_CURRENT(cf);
2154 DEBUGASSERT(data);
2155 if(data) {
2156 CURL_TRC_CF(data, cf, "handshake: %s message type %d",
2157 incoming ? "incoming" : "outgoing", htype);
2158 }
2159 switch(htype) {
2160 case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET: {
2161 (void)Curl_gtls_update_session_id(cf, data, session, &ctx->peer, "h3");
2162 break;
2163 }
2164 default:
2165 break;
2166 }
2167 }
2168 return 0;
2169 }
2170 #endif /* USE_GNUTLS */
2171
2172 #ifdef USE_WOLFSSL
wssl_quic_new_session_cb(WOLFSSL * ssl,WOLFSSL_SESSION * session)2173 static int wssl_quic_new_session_cb(WOLFSSL *ssl, WOLFSSL_SESSION *session)
2174 {
2175 ngtcp2_crypto_conn_ref *conn_ref = wolfSSL_get_app_data(ssl);
2176 struct Curl_cfilter *cf = conn_ref ? conn_ref->user_data : NULL;
2177
2178 DEBUGASSERT(cf != NULL);
2179 if(cf && session) {
2180 struct cf_ngtcp2_ctx *ctx = cf->ctx;
2181 struct Curl_easy *data = CF_DATA_CURRENT(cf);
2182 DEBUGASSERT(data);
2183 if(data && ctx) {
2184 (void)wssl_cache_session(cf, data, &ctx->peer, session);
2185 }
2186 }
2187 return 0;
2188 }
2189 #endif /* USE_WOLFSSL */
2190
tls_ctx_setup(struct Curl_cfilter * cf,struct Curl_easy * data,void * user_data)2191 static CURLcode tls_ctx_setup(struct Curl_cfilter *cf,
2192 struct Curl_easy *data,
2193 void *user_data)
2194 {
2195 struct curl_tls_ctx *ctx = user_data;
2196 struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
2197
2198 #ifdef USE_OPENSSL
2199 #if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
2200 if(ngtcp2_crypto_boringssl_configure_client_context(ctx->ossl.ssl_ctx)
2201 != 0) {
2202 failf(data, "ngtcp2_crypto_boringssl_configure_client_context failed");
2203 return CURLE_FAILED_INIT;
2204 }
2205 #else
2206 if(ngtcp2_crypto_quictls_configure_client_context(ctx->ossl.ssl_ctx) != 0) {
2207 failf(data, "ngtcp2_crypto_quictls_configure_client_context failed");
2208 return CURLE_FAILED_INIT;
2209 }
2210 #endif /* !OPENSSL_IS_BORINGSSL && !OPENSSL_IS_AWSLC */
2211 if(ssl_config->primary.cache_session) {
2212 /* Enable the session cache because it is a prerequisite for the
2213 * "new session" callback. Use the "external storage" mode to prevent
2214 * OpenSSL from creating an internal session cache.
2215 */
2216 SSL_CTX_set_session_cache_mode(ctx->ossl.ssl_ctx,
2217 SSL_SESS_CACHE_CLIENT |
2218 SSL_SESS_CACHE_NO_INTERNAL);
2219 SSL_CTX_sess_set_new_cb(ctx->ossl.ssl_ctx, quic_ossl_new_session_cb);
2220 }
2221
2222 #elif defined(USE_GNUTLS)
2223 if(ngtcp2_crypto_gnutls_configure_client_session(ctx->gtls.session) != 0) {
2224 failf(data, "ngtcp2_crypto_gnutls_configure_client_session failed");
2225 return CURLE_FAILED_INIT;
2226 }
2227 if(ssl_config->primary.cache_session) {
2228 gnutls_handshake_set_hook_function(ctx->gtls.session,
2229 GNUTLS_HANDSHAKE_ANY, GNUTLS_HOOK_POST,
2230 quic_gtls_handshake_cb);
2231 }
2232
2233 #elif defined(USE_WOLFSSL)
2234 if(ngtcp2_crypto_wolfssl_configure_client_context(ctx->wssl.ctx) != 0) {
2235 failf(data, "ngtcp2_crypto_wolfssl_configure_client_context failed");
2236 return CURLE_FAILED_INIT;
2237 }
2238 if(ssl_config->primary.cache_session) {
2239 /* Register to get notified when a new session is received */
2240 wolfSSL_CTX_sess_set_new_cb(ctx->wssl.ctx, wssl_quic_new_session_cb);
2241 }
2242 #endif
2243 return CURLE_OK;
2244 }
2245
2246 /*
2247 * Might be called twice for happy eyeballs.
2248 */
cf_connect_start(struct Curl_cfilter * cf,struct Curl_easy * data,struct pkt_io_ctx * pktx)2249 static CURLcode cf_connect_start(struct Curl_cfilter *cf,
2250 struct Curl_easy *data,
2251 struct pkt_io_ctx *pktx)
2252 {
2253 struct cf_ngtcp2_ctx *ctx = cf->ctx;
2254 int rc;
2255 int rv;
2256 CURLcode result;
2257 const struct Curl_sockaddr_ex *sockaddr = NULL;
2258 int qfd;
2259
2260 DEBUGASSERT(ctx->initialized);
2261 result = Curl_ssl_peer_init(&ctx->peer, cf, TRNSPRT_QUIC);
2262 if(result)
2263 return result;
2264
2265 #define H3_ALPN "\x2h3\x5h3-29"
2266 result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer,
2267 H3_ALPN, sizeof(H3_ALPN) - 1,
2268 tls_ctx_setup, &ctx->tls, &ctx->conn_ref);
2269 if(result)
2270 return result;
2271
2272 #ifdef USE_OPENSSL
2273 SSL_set_quic_use_legacy_codepoint(ctx->tls.ossl.ssl, 0);
2274 #endif
2275
2276 ctx->dcid.datalen = NGTCP2_MAX_CIDLEN;
2277 result = Curl_rand(data, ctx->dcid.data, NGTCP2_MAX_CIDLEN);
2278 if(result)
2279 return result;
2280
2281 ctx->scid.datalen = NGTCP2_MAX_CIDLEN;
2282 result = Curl_rand(data, ctx->scid.data, NGTCP2_MAX_CIDLEN);
2283 if(result)
2284 return result;
2285
2286 (void)Curl_qlogdir(data, ctx->scid.data, NGTCP2_MAX_CIDLEN, &qfd);
2287 ctx->qlogfd = qfd; /* -1 if failure above */
2288 quic_settings(ctx, data, pktx);
2289
2290 result = vquic_ctx_init(&ctx->q);
2291 if(result)
2292 return result;
2293
2294 Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, &sockaddr, NULL);
2295 if(!sockaddr)
2296 return CURLE_QUIC_CONNECT_ERROR;
2297 ctx->q.local_addrlen = sizeof(ctx->q.local_addr);
2298 rv = getsockname(ctx->q.sockfd, (struct sockaddr *)&ctx->q.local_addr,
2299 &ctx->q.local_addrlen);
2300 if(rv == -1)
2301 return CURLE_QUIC_CONNECT_ERROR;
2302
2303 ngtcp2_addr_init(&ctx->connected_path.local,
2304 (struct sockaddr *)&ctx->q.local_addr,
2305 ctx->q.local_addrlen);
2306 ngtcp2_addr_init(&ctx->connected_path.remote,
2307 &sockaddr->curl_sa_addr, (socklen_t)sockaddr->addrlen);
2308
2309 rc = ngtcp2_conn_client_new(&ctx->qconn, &ctx->dcid, &ctx->scid,
2310 &ctx->connected_path,
2311 NGTCP2_PROTO_VER_V1, &ng_callbacks,
2312 &ctx->settings, &ctx->transport_params,
2313 NULL, cf);
2314 if(rc)
2315 return CURLE_QUIC_CONNECT_ERROR;
2316
2317 #ifdef USE_OPENSSL
2318 ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.ossl.ssl);
2319 #elif defined(USE_GNUTLS)
2320 ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.gtls.session);
2321 #elif defined(USE_WOLFSSL)
2322 ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.wssl.handle);
2323 #else
2324 #error "ngtcp2 TLS backend not defined"
2325 #endif
2326
2327 ngtcp2_ccerr_default(&ctx->last_error);
2328
2329 ctx->conn_ref.get_conn = get_conn;
2330 ctx->conn_ref.user_data = cf;
2331
2332 return CURLE_OK;
2333 }
2334
cf_ngtcp2_connect(struct Curl_cfilter * cf,struct Curl_easy * data,bool blocking,bool * done)2335 static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf,
2336 struct Curl_easy *data,
2337 bool blocking, bool *done)
2338 {
2339 struct cf_ngtcp2_ctx *ctx = cf->ctx;
2340 CURLcode result = CURLE_OK;
2341 struct cf_call_data save;
2342 struct curltime now;
2343 struct pkt_io_ctx pktx;
2344
2345 if(cf->connected) {
2346 *done = TRUE;
2347 return CURLE_OK;
2348 }
2349
2350 /* Connect the UDP filter first */
2351 if(!cf->next->connected) {
2352 result = Curl_conn_cf_connect(cf->next, data, blocking, done);
2353 if(result || !*done)
2354 return result;
2355 }
2356
2357 *done = FALSE;
2358 now = Curl_now();
2359 pktx_init(&pktx, cf, data);
2360
2361 CF_DATA_SAVE(save, cf, data);
2362
2363 if(!ctx->qconn) {
2364 ctx->started_at = now;
2365 result = cf_connect_start(cf, data, &pktx);
2366 if(result)
2367 goto out;
2368 result = cf_progress_egress(cf, data, &pktx);
2369 /* we do not expect to be able to recv anything yet */
2370 goto out;
2371 }
2372
2373 result = cf_progress_ingress(cf, data, &pktx);
2374 if(result)
2375 goto out;
2376
2377 result = cf_progress_egress(cf, data, &pktx);
2378 if(result)
2379 goto out;
2380
2381 if(ngtcp2_conn_get_handshake_completed(ctx->qconn)) {
2382 ctx->handshake_at = now;
2383 CURL_TRC_CF(data, cf, "handshake complete after %dms",
2384 (int)Curl_timediff(now, ctx->started_at));
2385 result = qng_verify_peer(cf, data);
2386 if(!result) {
2387 CURL_TRC_CF(data, cf, "peer verified");
2388 cf->connected = TRUE;
2389 cf->conn->alpn = CURL_HTTP_VERSION_3;
2390 *done = TRUE;
2391 connkeep(cf->conn, "HTTP/3 default");
2392 }
2393 }
2394
2395 out:
2396 if(result == CURLE_RECV_ERROR && ctx->qconn &&
2397 ngtcp2_conn_in_draining_period(ctx->qconn)) {
2398 /* When a QUIC server instance is shutting down, it may send us a
2399 * CONNECTION_CLOSE right away. Our connection then enters the DRAINING
2400 * state. The CONNECT may work in the near future again. Indicate
2401 * that as a "weird" reply. */
2402 result = CURLE_WEIRD_SERVER_REPLY;
2403 }
2404
2405 #ifndef CURL_DISABLE_VERBOSE_STRINGS
2406 if(result) {
2407 struct ip_quadruple ip;
2408
2409 Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip);
2410 infof(data, "QUIC connect to %s port %u failed: %s",
2411 ip.remote_ip, ip.remote_port, curl_easy_strerror(result));
2412 }
2413 #endif
2414 if(!result && ctx->qconn) {
2415 result = check_and_set_expiry(cf, data, &pktx);
2416 }
2417 if(result || *done)
2418 CURL_TRC_CF(data, cf, "connect -> %d, done=%d", result, *done);
2419 CF_DATA_RESTORE(cf, save);
2420 return result;
2421 }
2422
cf_ngtcp2_query(struct Curl_cfilter * cf,struct Curl_easy * data,int query,int * pres1,void * pres2)2423 static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf,
2424 struct Curl_easy *data,
2425 int query, int *pres1, void *pres2)
2426 {
2427 struct cf_ngtcp2_ctx *ctx = cf->ctx;
2428 struct cf_call_data save;
2429
2430 switch(query) {
2431 case CF_QUERY_MAX_CONCURRENT: {
2432 DEBUGASSERT(pres1);
2433 CF_DATA_SAVE(save, cf, data);
2434 /* Set after transport params arrived and continually updated
2435 * by callback. QUIC counts the number over the lifetime of the
2436 * connection, ever increasing.
2437 * We count the *open* transfers plus the budget for new ones. */
2438 if(!ctx->qconn || ctx->shutdown_started) {
2439 *pres1 = 0;
2440 }
2441 else if(ctx->max_bidi_streams) {
2442 uint64_t avail_bidi_streams = 0;
2443 uint64_t max_streams = CONN_INUSE(cf->conn);
2444 if(ctx->max_bidi_streams > ctx->used_bidi_streams)
2445 avail_bidi_streams = ctx->max_bidi_streams - ctx->used_bidi_streams;
2446 max_streams += avail_bidi_streams;
2447 *pres1 = (max_streams > INT_MAX) ? INT_MAX : (int)max_streams;
2448 }
2449 else /* transport params not arrived yet? take our default. */
2450 *pres1 = (int)Curl_multi_max_concurrent_streams(data->multi);
2451 CURL_TRC_CF(data, cf, "query conn[%" FMT_OFF_T "]: "
2452 "MAX_CONCURRENT -> %d (%zu in use)",
2453 cf->conn->connection_id, *pres1, CONN_INUSE(cf->conn));
2454 CF_DATA_RESTORE(cf, save);
2455 return CURLE_OK;
2456 }
2457 case CF_QUERY_CONNECT_REPLY_MS:
2458 if(ctx->q.got_first_byte) {
2459 timediff_t ms = Curl_timediff(ctx->q.first_byte_at, ctx->started_at);
2460 *pres1 = (ms < INT_MAX) ? (int)ms : INT_MAX;
2461 }
2462 else
2463 *pres1 = -1;
2464 return CURLE_OK;
2465 case CF_QUERY_TIMER_CONNECT: {
2466 struct curltime *when = pres2;
2467 if(ctx->q.got_first_byte)
2468 *when = ctx->q.first_byte_at;
2469 return CURLE_OK;
2470 }
2471 case CF_QUERY_TIMER_APPCONNECT: {
2472 struct curltime *when = pres2;
2473 if(cf->connected)
2474 *when = ctx->handshake_at;
2475 return CURLE_OK;
2476 }
2477 default:
2478 break;
2479 }
2480 return cf->next ?
2481 cf->next->cft->query(cf->next, data, query, pres1, pres2) :
2482 CURLE_UNKNOWN_OPTION;
2483 }
2484
cf_ngtcp2_conn_is_alive(struct Curl_cfilter * cf,struct Curl_easy * data,bool * input_pending)2485 static bool cf_ngtcp2_conn_is_alive(struct Curl_cfilter *cf,
2486 struct Curl_easy *data,
2487 bool *input_pending)
2488 {
2489 struct cf_ngtcp2_ctx *ctx = cf->ctx;
2490 bool alive = FALSE;
2491 const ngtcp2_transport_params *rp;
2492 struct cf_call_data save;
2493
2494 CF_DATA_SAVE(save, cf, data);
2495 *input_pending = FALSE;
2496 if(!ctx->qconn || ctx->shutdown_started)
2497 goto out;
2498
2499 /* Both sides of the QUIC connection announce they max idle times in
2500 * the transport parameters. Look at the minimum of both and if
2501 * we exceed this, regard the connection as dead. The other side
2502 * may have completely purged it and will no longer respond
2503 * to any packets from us. */
2504 rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn);
2505 if(rp) {
2506 timediff_t idletime;
2507 uint64_t idle_ms = ctx->max_idle_ms;
2508
2509 if(rp->max_idle_timeout &&
2510 (rp->max_idle_timeout / NGTCP2_MILLISECONDS) < idle_ms)
2511 idle_ms = (rp->max_idle_timeout / NGTCP2_MILLISECONDS);
2512 idletime = Curl_timediff(Curl_now(), ctx->q.last_io);
2513 if(idletime > 0 && (uint64_t)idletime > idle_ms)
2514 goto out;
2515 }
2516
2517 if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending))
2518 goto out;
2519
2520 alive = TRUE;
2521 if(*input_pending) {
2522 CURLcode result;
2523 /* This happens before we have sent off a request and the connection is
2524 not in use by any other transfer, there should not be any data here,
2525 only "protocol frames" */
2526 *input_pending = FALSE;
2527 result = cf_progress_ingress(cf, data, NULL);
2528 CURL_TRC_CF(data, cf, "is_alive, progress ingress -> %d", result);
2529 alive = result ? FALSE : TRUE;
2530 }
2531
2532 out:
2533 CF_DATA_RESTORE(cf, save);
2534 return alive;
2535 }
2536
2537 struct Curl_cftype Curl_cft_http3 = {
2538 "HTTP/3",
2539 CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX,
2540 0,
2541 cf_ngtcp2_destroy,
2542 cf_ngtcp2_connect,
2543 cf_ngtcp2_close,
2544 cf_ngtcp2_shutdown,
2545 Curl_cf_def_get_host,
2546 cf_ngtcp2_adjust_pollset,
2547 cf_ngtcp2_data_pending,
2548 cf_ngtcp2_send,
2549 cf_ngtcp2_recv,
2550 cf_ngtcp2_data_event,
2551 cf_ngtcp2_conn_is_alive,
2552 Curl_cf_def_conn_keep_alive,
2553 cf_ngtcp2_query,
2554 };
2555
Curl_cf_ngtcp2_create(struct Curl_cfilter ** pcf,struct Curl_easy * data,struct connectdata * conn,const struct Curl_addrinfo * ai)2556 CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf,
2557 struct Curl_easy *data,
2558 struct connectdata *conn,
2559 const struct Curl_addrinfo *ai)
2560 {
2561 struct cf_ngtcp2_ctx *ctx = NULL;
2562 struct Curl_cfilter *cf = NULL, *udp_cf = NULL;
2563 CURLcode result;
2564
2565 (void)data;
2566 ctx = calloc(1, sizeof(*ctx));
2567 if(!ctx) {
2568 result = CURLE_OUT_OF_MEMORY;
2569 goto out;
2570 }
2571 cf_ngtcp2_ctx_init(ctx);
2572
2573 result = Curl_cf_create(&cf, &Curl_cft_http3, ctx);
2574 if(result)
2575 goto out;
2576
2577 result = Curl_cf_udp_create(&udp_cf, data, conn, ai, TRNSPRT_QUIC);
2578 if(result)
2579 goto out;
2580
2581 cf->conn = conn;
2582 udp_cf->conn = cf->conn;
2583 udp_cf->sockindex = cf->sockindex;
2584 cf->next = udp_cf;
2585
2586 out:
2587 *pcf = (!result) ? cf : NULL;
2588 if(result) {
2589 if(udp_cf)
2590 Curl_conn_cf_discard_sub(cf, udp_cf, data, TRUE);
2591 Curl_safefree(cf);
2592 cf_ngtcp2_ctx_free(ctx);
2593 }
2594 return result;
2595 }
2596
Curl_conn_is_ngtcp2(const struct Curl_easy * data,const struct connectdata * conn,int sockindex)2597 bool Curl_conn_is_ngtcp2(const struct Curl_easy *data,
2598 const struct connectdata *conn,
2599 int sockindex)
2600 {
2601 struct Curl_cfilter *cf = conn ? conn->cfilter[sockindex] : NULL;
2602
2603 (void)data;
2604 for(; cf; cf = cf->next) {
2605 if(cf->cft == &Curl_cft_http3)
2606 return TRUE;
2607 if(cf->cft->flags & CF_TYPE_IP_CONNECT)
2608 return FALSE;
2609 }
2610 return FALSE;
2611 }
2612
2613 #endif
2614