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