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