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