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