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