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