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