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