xref: /curl/lib/vquic/curl_osslq.c (revision 65eb2026)
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_OPENSSL_QUIC) && defined(USE_NGHTTP3)
28 
29 #include <openssl/ssl.h>
30 #include <openssl/bio.h>
31 #include <openssl/err.h>
32 #include <nghttp3/nghttp3.h>
33 
34 #include "urldata.h"
35 #include "hash.h"
36 #include "sendf.h"
37 #include "strdup.h"
38 #include "rand.h"
39 #include "multiif.h"
40 #include "strcase.h"
41 #include "cfilters.h"
42 #include "cf-socket.h"
43 #include "connect.h"
44 #include "progress.h"
45 #include "strerror.h"
46 #include "dynbuf.h"
47 #include "http1.h"
48 #include "select.h"
49 #include "inet_pton.h"
50 #include "vquic.h"
51 #include "vquic_int.h"
52 #include "vquic-tls.h"
53 #include "vtls/keylog.h"
54 #include "vtls/vtls.h"
55 #include "vtls/openssl.h"
56 #include "curl_osslq.h"
57 
58 #include "warnless.h"
59 
60 /* The last 3 #include files should be in this order */
61 #include "curl_printf.h"
62 #include "curl_memory.h"
63 #include "memdebug.h"
64 
65 /* A stream window is the maximum amount we need to buffer for
66  * each active transfer. We use HTTP/3 flow control and only ACK
67  * when we take things out of the buffer.
68  * Chunk size is large enough to take a full DATA frame */
69 #define H3_STREAM_WINDOW_SIZE (128 * 1024)
70 #define H3_STREAM_CHUNK_SIZE   (16 * 1024)
71 /* The pool keeps spares around and half of a full stream window
72  * seems good. More does not seem to improve performance.
73  * The benefit of the pool is that stream buffer to not keep
74  * spares. Memory consumption goes down when streams run empty,
75  * have a large upload done, etc. */
76 #define H3_STREAM_POOL_SPARES \
77           (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE ) / 2
78 /* Receive and Send max number of chunks just follows from the
79  * chunk size and window size */
80 #define H3_STREAM_RECV_CHUNKS \
81           (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE)
82 #define H3_STREAM_SEND_CHUNKS \
83           (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE)
84 
85 #ifndef ARRAYSIZE
86 #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
87 #endif
88 
89 #if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
90 typedef uint32_t sslerr_t;
91 #else
92 typedef unsigned long sslerr_t;
93 #endif
94 
95 
96 /* How to access `call_data` from a cf_osslq filter */
97 #undef CF_CTX_CALL_DATA
98 #define CF_CTX_CALL_DATA(cf)  \
99   ((struct cf_osslq_ctx *)(cf)->ctx)->call_data
100 
101 static CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
102                                     struct Curl_easy *data);
103 
osslq_SSL_ERROR_to_str(int err)104 static const char *osslq_SSL_ERROR_to_str(int err)
105 {
106   switch(err) {
107   case SSL_ERROR_NONE:
108     return "SSL_ERROR_NONE";
109   case SSL_ERROR_SSL:
110     return "SSL_ERROR_SSL";
111   case SSL_ERROR_WANT_READ:
112     return "SSL_ERROR_WANT_READ";
113   case SSL_ERROR_WANT_WRITE:
114     return "SSL_ERROR_WANT_WRITE";
115   case SSL_ERROR_WANT_X509_LOOKUP:
116     return "SSL_ERROR_WANT_X509_LOOKUP";
117   case SSL_ERROR_SYSCALL:
118     return "SSL_ERROR_SYSCALL";
119   case SSL_ERROR_ZERO_RETURN:
120     return "SSL_ERROR_ZERO_RETURN";
121   case SSL_ERROR_WANT_CONNECT:
122     return "SSL_ERROR_WANT_CONNECT";
123   case SSL_ERROR_WANT_ACCEPT:
124     return "SSL_ERROR_WANT_ACCEPT";
125 #if defined(SSL_ERROR_WANT_ASYNC)
126   case SSL_ERROR_WANT_ASYNC:
127     return "SSL_ERROR_WANT_ASYNC";
128 #endif
129 #if defined(SSL_ERROR_WANT_ASYNC_JOB)
130   case SSL_ERROR_WANT_ASYNC_JOB:
131     return "SSL_ERROR_WANT_ASYNC_JOB";
132 #endif
133 #if defined(SSL_ERROR_WANT_EARLY)
134   case SSL_ERROR_WANT_EARLY:
135     return "SSL_ERROR_WANT_EARLY";
136 #endif
137   default:
138     return "SSL_ERROR unknown";
139   }
140 }
141 
142 /* Return error string for last OpenSSL error */
osslq_strerror(unsigned long error,char * buf,size_t size)143 static char *osslq_strerror(unsigned long error, char *buf, size_t size)
144 {
145   DEBUGASSERT(size);
146   *buf = '\0';
147 
148 #if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
149   ERR_error_string_n((uint32_t)error, buf, size);
150 #else
151   ERR_error_string_n(error, buf, size);
152 #endif
153 
154   if(!*buf) {
155     const char *msg = error ? "Unknown error" : "No error";
156     if(strlen(msg) < size)
157       strcpy(buf, msg);
158   }
159 
160   return buf;
161 }
162 
make_bio_addr(BIO_ADDR ** pbio_addr,const struct Curl_sockaddr_ex * addr)163 static CURLcode make_bio_addr(BIO_ADDR **pbio_addr,
164                               const struct Curl_sockaddr_ex *addr)
165 {
166   BIO_ADDR *ba;
167   CURLcode result = CURLE_FAILED_INIT;
168 
169   ba = BIO_ADDR_new();
170   if(!ba) {
171     result = CURLE_OUT_OF_MEMORY;
172     goto out;
173   }
174 
175   switch(addr->family) {
176   case AF_INET: {
177     struct sockaddr_in * const sin =
178       (struct sockaddr_in * const)(void *)&addr->curl_sa_addr;
179     if(!BIO_ADDR_rawmake(ba, AF_INET, &sin->sin_addr,
180                          sizeof(sin->sin_addr), sin->sin_port)) {
181       goto out;
182     }
183     result = CURLE_OK;
184     break;
185   }
186 #ifdef USE_IPV6
187   case AF_INET6: {
188     struct sockaddr_in6 * const sin =
189       (struct sockaddr_in6 * const)(void *)&addr->curl_sa_addr;
190     if(!BIO_ADDR_rawmake(ba, AF_INET6, &sin->sin6_addr,
191                          sizeof(sin->sin6_addr), sin->sin6_port)) {
192     }
193     result = CURLE_OK;
194     break;
195   }
196 #endif /* USE_IPV6 */
197   default:
198     /* sunsupported */
199     DEBUGASSERT(0);
200     break;
201   }
202 
203 out:
204   if(result && ba) {
205     BIO_ADDR_free(ba);
206     ba = NULL;
207   }
208   *pbio_addr = ba;
209   return result;
210 }
211 
212 /* QUIC stream (not necessarily H3) */
213 struct cf_osslq_stream {
214   curl_int64_t id;
215   SSL *ssl;
216   struct bufq recvbuf; /* QUIC war data recv buffer */
217   BIT(recvd_eos);
218   BIT(closed);
219   BIT(reset);
220   BIT(send_blocked);
221 };
222 
cf_osslq_stream_open(struct cf_osslq_stream * s,SSL * conn,uint64_t flags,struct bufc_pool * bufcp,void * user_data)223 static CURLcode cf_osslq_stream_open(struct cf_osslq_stream *s,
224                                      SSL *conn,
225                                      uint64_t flags,
226                                      struct bufc_pool *bufcp,
227                                      void *user_data)
228 {
229   DEBUGASSERT(!s->ssl);
230   Curl_bufq_initp(&s->recvbuf, bufcp, 1, BUFQ_OPT_NONE);
231   s->ssl = SSL_new_stream(conn, flags);
232   if(!s->ssl) {
233     return CURLE_FAILED_INIT;
234   }
235   s->id = (curl_int64_t)SSL_get_stream_id(s->ssl);
236   SSL_set_app_data(s->ssl, user_data);
237   return CURLE_OK;
238 }
239 
cf_osslq_stream_cleanup(struct cf_osslq_stream * s)240 static void cf_osslq_stream_cleanup(struct cf_osslq_stream *s)
241 {
242   if(s->ssl) {
243     SSL_set_app_data(s->ssl, NULL);
244     SSL_free(s->ssl);
245   }
246   Curl_bufq_free(&s->recvbuf);
247   memset(s, 0, sizeof(*s));
248 }
249 
cf_osslq_stream_close(struct cf_osslq_stream * s)250 static void cf_osslq_stream_close(struct cf_osslq_stream *s)
251 {
252   if(s->ssl) {
253     SSL_free(s->ssl);
254     s->ssl = NULL;
255   }
256 }
257 
258 struct cf_osslq_h3conn {
259   nghttp3_conn *conn;
260   nghttp3_settings settings;
261   struct cf_osslq_stream s_ctrl;
262   struct cf_osslq_stream s_qpack_enc;
263   struct cf_osslq_stream s_qpack_dec;
264   struct cf_osslq_stream remote_ctrl[3]; /* uni streams opened by the peer */
265   size_t remote_ctrl_n; /* number of peer streams opened */
266 };
267 
cf_osslq_h3conn_cleanup(struct cf_osslq_h3conn * h3)268 static void cf_osslq_h3conn_cleanup(struct cf_osslq_h3conn *h3)
269 {
270   size_t i;
271 
272   if(h3->conn)
273     nghttp3_conn_del(h3->conn);
274   cf_osslq_stream_cleanup(&h3->s_ctrl);
275   cf_osslq_stream_cleanup(&h3->s_qpack_enc);
276   cf_osslq_stream_cleanup(&h3->s_qpack_dec);
277   for(i = 0; i < h3->remote_ctrl_n; ++i) {
278     cf_osslq_stream_cleanup(&h3->remote_ctrl[i]);
279   }
280 }
281 
282 struct cf_osslq_ctx {
283   struct cf_quic_ctx q;
284   struct ssl_peer peer;
285   struct curl_tls_ctx tls;
286   struct cf_call_data call_data;
287   struct cf_osslq_h3conn h3;
288   struct curltime started_at;        /* time the current attempt started */
289   struct curltime handshake_at;      /* time connect handshake finished */
290   struct curltime first_byte_at;     /* when first byte was recvd */
291   struct bufc_pool stream_bufcp;     /* chunk pool for streams */
292   struct Curl_hash streams;          /* hash `data->mid` to `h3_stream_ctx` */
293   size_t max_stream_window;          /* max flow window for one stream */
294   uint64_t max_idle_ms;              /* max idle time for QUIC connection */
295   BIT(initialized);
296   BIT(got_first_byte);               /* if first byte was received */
297   BIT(x509_store_setup);             /* if x509 store has been set up */
298   BIT(protocol_shutdown);            /* QUIC connection is shut down */
299   BIT(need_recv);                    /* QUIC connection needs to receive */
300   BIT(need_send);                    /* QUIC connection needs to send */
301 };
302 
303 static void h3_stream_hash_free(void *stream);
304 
cf_osslq_ctx_init(struct cf_osslq_ctx * ctx)305 static void cf_osslq_ctx_init(struct cf_osslq_ctx *ctx)
306 {
307   DEBUGASSERT(!ctx->initialized);
308   Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE,
309                   H3_STREAM_POOL_SPARES);
310   Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free);
311   ctx->initialized = TRUE;
312 }
313 
cf_osslq_ctx_free(struct cf_osslq_ctx * ctx)314 static void cf_osslq_ctx_free(struct cf_osslq_ctx *ctx)
315 {
316   if(ctx && ctx->initialized) {
317     Curl_bufcp_free(&ctx->stream_bufcp);
318     Curl_hash_clean(&ctx->streams);
319     Curl_hash_destroy(&ctx->streams);
320     Curl_ssl_peer_cleanup(&ctx->peer);
321   }
322   free(ctx);
323 }
324 
cf_osslq_ctx_close(struct cf_osslq_ctx * ctx)325 static void cf_osslq_ctx_close(struct cf_osslq_ctx *ctx)
326 {
327   struct cf_call_data save = ctx->call_data;
328 
329   cf_osslq_h3conn_cleanup(&ctx->h3);
330   Curl_vquic_tls_cleanup(&ctx->tls);
331   vquic_ctx_free(&ctx->q);
332   ctx->call_data = save;
333 }
334 
cf_osslq_shutdown(struct Curl_cfilter * cf,struct Curl_easy * data,bool * done)335 static CURLcode cf_osslq_shutdown(struct Curl_cfilter *cf,
336                                   struct Curl_easy *data, bool *done)
337 {
338   struct cf_osslq_ctx *ctx = cf->ctx;
339   struct cf_call_data save;
340   CURLcode result = CURLE_OK;
341   int rc;
342 
343   CF_DATA_SAVE(save, cf, data);
344 
345   if(cf->shutdown || ctx->protocol_shutdown) {
346     *done = TRUE;
347     return CURLE_OK;
348   }
349 
350   CF_DATA_SAVE(save, cf, data);
351   *done = FALSE;
352   ctx->need_send = FALSE;
353   ctx->need_recv = FALSE;
354 
355   rc = SSL_shutdown_ex(ctx->tls.ossl.ssl,
356                        SSL_SHUTDOWN_FLAG_NO_BLOCK, NULL, 0);
357   if(rc == 0) {  /* ongoing */
358     CURL_TRC_CF(data, cf, "shutdown ongoing");
359     ctx->need_recv = TRUE;
360     goto out;
361   }
362   else if(rc == 1) {  /* done */
363     CURL_TRC_CF(data, cf, "shutdown finished");
364     *done = TRUE;
365     goto out;
366   }
367   else {
368     long sslerr;
369     char err_buffer[256];
370     int err = SSL_get_error(ctx->tls.ossl.ssl, rc);
371 
372     switch(err) {
373     case SSL_ERROR_NONE:
374     case SSL_ERROR_ZERO_RETURN:
375       CURL_TRC_CF(data, cf, "shutdown not received, but closed");
376       *done = TRUE;
377       goto out;
378     case SSL_ERROR_WANT_READ:
379       /* SSL has send its notify and now wants to read the reply
380        * from the server. We are not really interested in that. */
381       CURL_TRC_CF(data, cf, "shutdown sent, want receive");
382       ctx->need_recv = TRUE;
383       goto out;
384     case SSL_ERROR_WANT_WRITE:
385       CURL_TRC_CF(data, cf, "shutdown send blocked");
386       ctx->need_send = TRUE;
387       goto out;
388     default:
389       /* We give up on this. */
390       sslerr = ERR_get_error();
391       CURL_TRC_CF(data, cf, "shutdown, ignore recv error: '%s', errno %d",
392                   (sslerr ?
393                    osslq_strerror(sslerr, err_buffer, sizeof(err_buffer)) :
394                    osslq_SSL_ERROR_to_str(err)),
395                   SOCKERRNO);
396       *done = TRUE;
397       result = CURLE_OK;
398       goto out;
399     }
400   }
401 out:
402   CF_DATA_RESTORE(cf, save);
403   return result;
404 }
405 
cf_osslq_close(struct Curl_cfilter * cf,struct Curl_easy * data)406 static void cf_osslq_close(struct Curl_cfilter *cf, struct Curl_easy *data)
407 {
408   struct cf_osslq_ctx *ctx = cf->ctx;
409   struct cf_call_data save;
410 
411   CF_DATA_SAVE(save, cf, data);
412   if(ctx && ctx->tls.ossl.ssl) {
413     CURL_TRC_CF(data, cf, "cf_osslq_close()");
414     if(!cf->shutdown && !ctx->protocol_shutdown) {
415       /* last best effort, which OpenSSL calls a "rapid" shutdown. */
416       SSL_shutdown_ex(ctx->tls.ossl.ssl,
417                       (SSL_SHUTDOWN_FLAG_NO_BLOCK | SSL_SHUTDOWN_FLAG_RAPID),
418                       NULL, 0);
419     }
420     cf_osslq_ctx_close(ctx);
421   }
422 
423   cf->connected = FALSE;
424   CF_DATA_RESTORE(cf, save);
425 }
426 
cf_osslq_destroy(struct Curl_cfilter * cf,struct Curl_easy * data)427 static void cf_osslq_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
428 {
429   struct cf_osslq_ctx *ctx = cf->ctx;
430   struct cf_call_data save;
431 
432   CF_DATA_SAVE(save, cf, data);
433   CURL_TRC_CF(data, cf, "destroy");
434   if(ctx) {
435     CURL_TRC_CF(data, cf, "cf_osslq_destroy()");
436     if(ctx->tls.ossl.ssl)
437       cf_osslq_ctx_close(ctx);
438     cf_osslq_ctx_free(ctx);
439   }
440   cf->ctx = NULL;
441   /* No CF_DATA_RESTORE(cf, save) possible */
442   (void)save;
443 }
444 
cf_osslq_h3conn_add_stream(struct cf_osslq_h3conn * h3,SSL * stream_ssl,struct Curl_cfilter * cf,struct Curl_easy * data)445 static CURLcode cf_osslq_h3conn_add_stream(struct cf_osslq_h3conn *h3,
446                                            SSL *stream_ssl,
447                                            struct Curl_cfilter *cf,
448                                            struct Curl_easy *data)
449 {
450   struct cf_osslq_ctx *ctx = cf->ctx;
451   curl_int64_t stream_id = (curl_int64_t)SSL_get_stream_id(stream_ssl);
452 
453   if(h3->remote_ctrl_n >= ARRAYSIZE(h3->remote_ctrl)) {
454     /* rejected, we are full */
455     CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] rejecting remote stream",
456                 stream_id);
457     SSL_free(stream_ssl);
458     return CURLE_FAILED_INIT;
459   }
460   switch(SSL_get_stream_type(stream_ssl)) {
461     case SSL_STREAM_TYPE_READ: {
462       struct cf_osslq_stream *nstream = &h3->remote_ctrl[h3->remote_ctrl_n++];
463       nstream->id = stream_id;
464       nstream->ssl = stream_ssl;
465       Curl_bufq_initp(&nstream->recvbuf, &ctx->stream_bufcp, 1, BUFQ_OPT_NONE);
466       CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] accepted remote uni stream",
467                   stream_id);
468       break;
469     }
470     default:
471       CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] reject remote non-uni-read"
472                   " stream", stream_id);
473       SSL_free(stream_ssl);
474       return CURLE_FAILED_INIT;
475   }
476   return CURLE_OK;
477 
478 }
479 
cf_osslq_ssl_err(struct Curl_cfilter * cf,struct Curl_easy * data,int detail,CURLcode def_result)480 static CURLcode cf_osslq_ssl_err(struct Curl_cfilter *cf,
481                                  struct Curl_easy *data,
482                                  int detail, CURLcode def_result)
483 {
484   struct cf_osslq_ctx *ctx = cf->ctx;
485   CURLcode result = def_result;
486   sslerr_t errdetail;
487   char ebuf[256] = "unknown";
488   const char *err_descr = ebuf;
489   long lerr;
490   int lib;
491   int reason;
492   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
493 
494   errdetail = ERR_get_error();
495   lib = ERR_GET_LIB(errdetail);
496   reason = ERR_GET_REASON(errdetail);
497 
498   if((lib == ERR_LIB_SSL) &&
499      ((reason == SSL_R_CERTIFICATE_VERIFY_FAILED) ||
500       (reason == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED))) {
501     result = CURLE_PEER_FAILED_VERIFICATION;
502 
503     lerr = SSL_get_verify_result(ctx->tls.ossl.ssl);
504     if(lerr != X509_V_OK) {
505       ssl_config->certverifyresult = lerr;
506       msnprintf(ebuf, sizeof(ebuf),
507                 "SSL certificate problem: %s",
508                 X509_verify_cert_error_string(lerr));
509     }
510     else
511       err_descr = "SSL certificate verification failed";
512   }
513 #if defined(SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED)
514   /* SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED is only available on
515      OpenSSL version above v1.1.1, not LibreSSL, BoringSSL, or AWS-LC */
516   else if((lib == ERR_LIB_SSL) &&
517           (reason == SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED)) {
518     /* If client certificate is required, communicate the
519        error to client */
520     result = CURLE_SSL_CLIENTCERT;
521     osslq_strerror(errdetail, ebuf, sizeof(ebuf));
522   }
523 #endif
524   else if((lib == ERR_LIB_SSL) && (reason == SSL_R_PROTOCOL_IS_SHUTDOWN)) {
525     ctx->protocol_shutdown = TRUE;
526     err_descr = "QUIC connection has been shut down";
527     result = def_result;
528   }
529   else {
530     result = def_result;
531     osslq_strerror(errdetail, ebuf, sizeof(ebuf));
532   }
533 
534   /* detail is already set to the SSL error above */
535 
536   /* If we e.g. use SSLv2 request-method and the server does not like us
537    * (RST connection, etc.), OpenSSL gives no explanation whatsoever and
538    * the SO_ERROR is also lost.
539    */
540   if(CURLE_SSL_CONNECT_ERROR == result && errdetail == 0) {
541     char extramsg[80]="";
542     int sockerr = SOCKERRNO;
543     struct ip_quadruple ip;
544 
545     Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip);
546     if(sockerr && detail == SSL_ERROR_SYSCALL)
547       Curl_strerror(sockerr, extramsg, sizeof(extramsg));
548     failf(data, "QUIC connect: %s in connection to %s:%d (%s)",
549           extramsg[0] ? extramsg : osslq_SSL_ERROR_to_str(detail),
550           ctx->peer.dispname, ip.remote_port, ip.remote_ip);
551   }
552   else {
553     /* Could be a CERT problem */
554     failf(data, "%s", err_descr);
555   }
556   return result;
557 }
558 
cf_osslq_verify_peer(struct Curl_cfilter * cf,struct Curl_easy * data)559 static CURLcode cf_osslq_verify_peer(struct Curl_cfilter *cf,
560                                   struct Curl_easy *data)
561 {
562   struct cf_osslq_ctx *ctx = cf->ctx;
563 
564   cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
565   cf->conn->httpversion = 30;
566 
567   return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer);
568 }
569 
570 /**
571  * All about the H3 internals of a stream
572  */
573 struct h3_stream_ctx {
574   struct cf_osslq_stream s;
575   struct bufq sendbuf;   /* h3 request body */
576   struct bufq recvbuf;   /* h3 response body */
577   struct h1_req_parser h1; /* h1 request parsing */
578   size_t sendbuf_len_in_flight; /* sendbuf amount "in flight" */
579   size_t recv_buf_nonflow; /* buffered bytes, not counting for flow control */
580   curl_uint64_t error3; /* HTTP/3 stream error code */
581   curl_off_t upload_left; /* number of request bytes left to upload */
582   curl_off_t download_recvd; /* number of response DATA bytes received */
583   int status_code; /* HTTP status code */
584   bool resp_hds_complete; /* we have a complete, final response */
585   bool closed; /* TRUE on stream close */
586   bool reset;  /* TRUE on stream reset */
587   bool send_closed; /* stream is local closed */
588   BIT(quic_flow_blocked); /* stream is blocked by QUIC flow control */
589 };
590 
591 #define H3_STREAM_CTX(ctx,data)   ((struct h3_stream_ctx *)(\
592             data? Curl_hash_offt_get(&(ctx)->streams, (data)->mid) : NULL))
593 
h3_stream_ctx_free(struct h3_stream_ctx * stream)594 static void h3_stream_ctx_free(struct h3_stream_ctx *stream)
595 {
596   cf_osslq_stream_cleanup(&stream->s);
597   Curl_bufq_free(&stream->sendbuf);
598   Curl_bufq_free(&stream->recvbuf);
599   Curl_h1_req_parse_free(&stream->h1);
600   free(stream);
601 }
602 
h3_stream_hash_free(void * stream)603 static void h3_stream_hash_free(void *stream)
604 {
605   DEBUGASSERT(stream);
606   h3_stream_ctx_free((struct h3_stream_ctx *)stream);
607 }
608 
h3_data_setup(struct Curl_cfilter * cf,struct Curl_easy * data)609 static CURLcode h3_data_setup(struct Curl_cfilter *cf,
610                               struct Curl_easy *data)
611 {
612   struct cf_osslq_ctx *ctx = cf->ctx;
613   struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
614 
615   if(!data)
616     return CURLE_FAILED_INIT;
617 
618   if(stream)
619     return CURLE_OK;
620 
621   stream = calloc(1, sizeof(*stream));
622   if(!stream)
623     return CURLE_OUT_OF_MEMORY;
624 
625   stream->s.id = -1;
626   /* on send, we control how much we put into the buffer */
627   Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp,
628                   H3_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE);
629   stream->sendbuf_len_in_flight = 0;
630   /* on recv, we need a flexible buffer limit since we also write
631    * headers to it that are not counted against the nghttp3 flow limits. */
632   Curl_bufq_initp(&stream->recvbuf, &ctx->stream_bufcp,
633                   H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT);
634   stream->recv_buf_nonflow = 0;
635   Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
636 
637   if(!Curl_hash_offt_set(&ctx->streams, data->mid, stream)) {
638     h3_stream_ctx_free(stream);
639     return CURLE_OUT_OF_MEMORY;
640   }
641 
642   return CURLE_OK;
643 }
644 
h3_data_done(struct Curl_cfilter * cf,struct Curl_easy * data)645 static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
646 {
647   struct cf_osslq_ctx *ctx = cf->ctx;
648   struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
649 
650   (void)cf;
651   if(stream) {
652     CURL_TRC_CF(data, cf, "[%"FMT_PRId64"] easy handle is done",
653                 stream->s.id);
654     if(ctx->h3.conn && !stream->closed) {
655       nghttp3_conn_shutdown_stream_read(ctx->h3.conn, stream->s.id);
656       nghttp3_conn_close_stream(ctx->h3.conn, stream->s.id,
657                                 NGHTTP3_H3_REQUEST_CANCELLED);
658       nghttp3_conn_set_stream_user_data(ctx->h3.conn, stream->s.id, NULL);
659       stream->closed = TRUE;
660     }
661 
662     Curl_hash_offt_remove(&ctx->streams, data->mid);
663   }
664 }
665 
cf_osslq_get_qstream(struct Curl_cfilter * cf,struct Curl_easy * data,int64_t stream_id)666 static struct cf_osslq_stream *cf_osslq_get_qstream(struct Curl_cfilter *cf,
667                                                     struct Curl_easy *data,
668                                                     int64_t stream_id)
669 {
670   struct cf_osslq_ctx *ctx = cf->ctx;
671   struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
672 
673   if(stream && stream->s.id == stream_id) {
674     return &stream->s;
675   }
676   else if(ctx->h3.s_ctrl.id == stream_id) {
677     return &ctx->h3.s_ctrl;
678   }
679   else if(ctx->h3.s_qpack_enc.id == stream_id) {
680     return &ctx->h3.s_qpack_enc;
681   }
682   else if(ctx->h3.s_qpack_dec.id == stream_id) {
683     return &ctx->h3.s_qpack_dec;
684   }
685   else {
686     struct Curl_llist_node *e;
687     DEBUGASSERT(data->multi);
688     for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) {
689       struct Curl_easy *sdata = Curl_node_elem(e);
690       if(sdata->conn != data->conn)
691         continue;
692       stream = H3_STREAM_CTX(ctx, sdata);
693       if(stream && stream->s.id == stream_id) {
694         return &stream->s;
695       }
696     }
697   }
698   return NULL;
699 }
700 
h3_drain_stream(struct Curl_cfilter * cf,struct Curl_easy * data)701 static void h3_drain_stream(struct Curl_cfilter *cf,
702                             struct Curl_easy *data)
703 {
704   struct cf_osslq_ctx *ctx = cf->ctx;
705   struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
706   unsigned char bits;
707 
708   (void)cf;
709   bits = CURL_CSELECT_IN;
710   if(stream && stream->upload_left && !stream->send_closed)
711     bits |= CURL_CSELECT_OUT;
712   if(data->state.select_bits != bits) {
713     data->state.select_bits = bits;
714     Curl_expire(data, 0, EXPIRE_RUN_NOW);
715   }
716 }
717 
h3_data_pause(struct Curl_cfilter * cf,struct Curl_easy * data,bool pause)718 static CURLcode h3_data_pause(struct Curl_cfilter *cf,
719                               struct Curl_easy *data,
720                               bool pause)
721 {
722   if(!pause) {
723     /* unpaused. make it run again right away */
724     h3_drain_stream(cf, data);
725     Curl_expire(data, 0, EXPIRE_RUN_NOW);
726   }
727   return CURLE_OK;
728 }
729 
cb_h3_stream_close(nghttp3_conn * conn,int64_t stream_id,uint64_t app_error_code,void * user_data,void * stream_user_data)730 static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id,
731                               uint64_t app_error_code, void *user_data,
732                               void *stream_user_data)
733 {
734   struct Curl_cfilter *cf = user_data;
735   struct cf_osslq_ctx *ctx = cf->ctx;
736   struct Curl_easy *data = stream_user_data;
737   struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
738   (void)conn;
739   (void)stream_id;
740 
741   /* we might be called by nghttp3 after we already cleaned up */
742   if(!stream)
743     return 0;
744 
745   stream->closed = TRUE;
746   stream->error3 = app_error_code;
747   if(stream->error3 != NGHTTP3_H3_NO_ERROR) {
748     stream->reset = TRUE;
749     stream->send_closed = TRUE;
750     CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] RESET: error %" FMT_PRIu64,
751                 stream->s.id, stream->error3);
752   }
753   else {
754     CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] CLOSED", stream->s.id);
755   }
756   h3_drain_stream(cf, data);
757   return 0;
758 }
759 
760 /*
761  * write_resp_raw() copies response data in raw format to the `data`'s
762   * receive buffer. If not enough space is available, it appends to the
763  * `data`'s overflow buffer.
764  */
write_resp_raw(struct Curl_cfilter * cf,struct Curl_easy * data,const void * mem,size_t memlen,bool flow)765 static CURLcode write_resp_raw(struct Curl_cfilter *cf,
766                                struct Curl_easy *data,
767                                const void *mem, size_t memlen,
768                                bool flow)
769 {
770   struct cf_osslq_ctx *ctx = cf->ctx;
771   struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
772   CURLcode result = CURLE_OK;
773   ssize_t nwritten;
774 
775   (void)cf;
776   if(!stream) {
777     return CURLE_RECV_ERROR;
778   }
779   nwritten = Curl_bufq_write(&stream->recvbuf, mem, memlen, &result);
780   if(nwritten < 0) {
781     return result;
782   }
783 
784   if(!flow)
785     stream->recv_buf_nonflow += (size_t)nwritten;
786 
787   if((size_t)nwritten < memlen) {
788     /* This MUST not happen. Our recbuf is dimensioned to hold the
789      * full max_stream_window and then some for this very reason. */
790     DEBUGASSERT(0);
791     return CURLE_RECV_ERROR;
792   }
793   return result;
794 }
795 
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)796 static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id,
797                            const uint8_t *buf, size_t buflen,
798                            void *user_data, void *stream_user_data)
799 {
800   struct Curl_cfilter *cf = user_data;
801   struct cf_osslq_ctx *ctx = cf->ctx;
802   struct Curl_easy *data = stream_user_data;
803   struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
804   CURLcode result;
805 
806   (void)conn;
807   (void)stream3_id;
808 
809   if(!stream)
810     return NGHTTP3_ERR_CALLBACK_FAILURE;
811 
812   result = write_resp_raw(cf, data, buf, buflen, TRUE);
813   if(result) {
814     CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] DATA len=%zu, ERROR %d",
815                 stream->s.id, buflen, result);
816     return NGHTTP3_ERR_CALLBACK_FAILURE;
817   }
818   stream->download_recvd += (curl_off_t)buflen;
819   CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] DATA len=%zu, total=%zd",
820               stream->s.id, buflen, stream->download_recvd);
821   h3_drain_stream(cf, data);
822   return 0;
823 }
824 
cb_h3_deferred_consume(nghttp3_conn * conn,int64_t stream_id,size_t consumed,void * user_data,void * stream_user_data)825 static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream_id,
826                                   size_t consumed, void *user_data,
827                                   void *stream_user_data)
828 {
829   struct Curl_cfilter *cf = user_data;
830   struct cf_osslq_ctx *ctx = cf->ctx;
831   struct Curl_easy *data = stream_user_data;
832   struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
833 
834   (void)conn;
835   (void)stream_id;
836   if(stream)
837     CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] deferred consume %zu bytes",
838                 stream->s.id, consumed);
839   return 0;
840 }
841 
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)842 static int cb_h3_recv_header(nghttp3_conn *conn, int64_t sid,
843                              int32_t token, nghttp3_rcbuf *name,
844                              nghttp3_rcbuf *value, uint8_t flags,
845                              void *user_data, void *stream_user_data)
846 {
847   struct Curl_cfilter *cf = user_data;
848   curl_int64_t stream_id = sid;
849   struct cf_osslq_ctx *ctx = cf->ctx;
850   nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name);
851   nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value);
852   struct Curl_easy *data = stream_user_data;
853   struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
854   CURLcode result = CURLE_OK;
855   (void)conn;
856   (void)stream_id;
857   (void)token;
858   (void)flags;
859   (void)cf;
860 
861   /* we might have cleaned up this transfer already */
862   if(!stream)
863     return 0;
864 
865   if(token == NGHTTP3_QPACK_TOKEN__STATUS) {
866     char line[14]; /* status line is always 13 characters long */
867     size_t ncopy;
868 
869     result = Curl_http_decode_status(&stream->status_code,
870                                      (const char *)h3val.base, h3val.len);
871     if(result)
872       return -1;
873     ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n",
874                       stream->status_code);
875     CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] status: %s", stream_id, line);
876     result = write_resp_raw(cf, data, line, ncopy, FALSE);
877     if(result) {
878       return -1;
879     }
880   }
881   else {
882     /* store as an HTTP1-style header */
883     CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] header: %.*s: %.*s",
884                 stream_id, (int)h3name.len, h3name.base,
885                 (int)h3val.len, h3val.base);
886     result = write_resp_raw(cf, data, h3name.base, h3name.len, FALSE);
887     if(result) {
888       return -1;
889     }
890     result = write_resp_raw(cf, data, ": ", 2, FALSE);
891     if(result) {
892       return -1;
893     }
894     result = write_resp_raw(cf, data, h3val.base, h3val.len, FALSE);
895     if(result) {
896       return -1;
897     }
898     result = write_resp_raw(cf, data, "\r\n", 2, FALSE);
899     if(result) {
900       return -1;
901     }
902   }
903   return 0;
904 }
905 
cb_h3_end_headers(nghttp3_conn * conn,int64_t sid,int fin,void * user_data,void * stream_user_data)906 static int cb_h3_end_headers(nghttp3_conn *conn, int64_t sid,
907                              int fin, void *user_data, void *stream_user_data)
908 {
909   struct Curl_cfilter *cf = user_data;
910   struct cf_osslq_ctx *ctx = cf->ctx;
911   struct Curl_easy *data = stream_user_data;
912   curl_int64_t stream_id = sid;
913   struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
914   CURLcode result = CURLE_OK;
915   (void)conn;
916   (void)stream_id;
917   (void)fin;
918   (void)cf;
919 
920   if(!stream)
921     return 0;
922   /* add a CRLF only if we have received some headers */
923   result = write_resp_raw(cf, data, "\r\n", 2, FALSE);
924   if(result) {
925     return -1;
926   }
927 
928   CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] end_headers, status=%d",
929               stream_id, stream->status_code);
930   if(stream->status_code / 100 != 1) {
931     stream->resp_hds_complete = TRUE;
932   }
933   h3_drain_stream(cf, data);
934   return 0;
935 }
936 
cb_h3_stop_sending(nghttp3_conn * conn,int64_t sid,uint64_t app_error_code,void * user_data,void * stream_user_data)937 static int cb_h3_stop_sending(nghttp3_conn *conn, int64_t sid,
938                               uint64_t app_error_code, void *user_data,
939                               void *stream_user_data)
940 {
941   struct Curl_cfilter *cf = user_data;
942   struct cf_osslq_ctx *ctx = cf->ctx;
943   struct Curl_easy *data = stream_user_data;
944   curl_int64_t stream_id = sid;
945   struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
946   (void)conn;
947   (void)app_error_code;
948 
949   if(!stream || !stream->s.ssl)
950     return 0;
951 
952   CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] stop_sending", stream_id);
953   cf_osslq_stream_close(&stream->s);
954   return 0;
955 }
956 
cb_h3_reset_stream(nghttp3_conn * conn,int64_t sid,uint64_t app_error_code,void * user_data,void * stream_user_data)957 static int cb_h3_reset_stream(nghttp3_conn *conn, int64_t sid,
958                               uint64_t app_error_code, void *user_data,
959                               void *stream_user_data) {
960   struct Curl_cfilter *cf = user_data;
961   struct cf_osslq_ctx *ctx = cf->ctx;
962   struct Curl_easy *data = stream_user_data;
963   curl_int64_t stream_id = sid;
964   struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
965   int rv;
966   (void)conn;
967 
968   if(stream && stream->s.ssl) {
969     SSL_STREAM_RESET_ARGS args = {0};
970     args.quic_error_code = app_error_code;
971     rv = !SSL_stream_reset(stream->s.ssl, &args, sizeof(args));
972     CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] reset -> %d", stream_id, rv);
973     if(!rv) {
974       return NGHTTP3_ERR_CALLBACK_FAILURE;
975     }
976   }
977   return 0;
978 }
979 
980 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)981 cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id,
982                     nghttp3_vec *vec, size_t veccnt,
983                     uint32_t *pflags, void *user_data,
984                     void *stream_user_data)
985 {
986   struct Curl_cfilter *cf = user_data;
987   struct cf_osslq_ctx *ctx = cf->ctx;
988   struct Curl_easy *data = stream_user_data;
989   struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
990   ssize_t nwritten = 0;
991   size_t nvecs = 0;
992   (void)cf;
993   (void)conn;
994   (void)stream_id;
995   (void)user_data;
996   (void)veccnt;
997 
998   if(!stream)
999     return NGHTTP3_ERR_CALLBACK_FAILURE;
1000   /* nghttp3 keeps references to the sendbuf data until it is ACKed
1001    * by the server (see `cb_h3_acked_req_body()` for updates).
1002    * `sendbuf_len_in_flight` is the amount of bytes in `sendbuf`
1003    * that we have already passed to nghttp3, but which have not been
1004    * ACKed yet.
1005    * Any amount beyond `sendbuf_len_in_flight` we need still to pass
1006    * to nghttp3. Do that now, if we can. */
1007   if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) {
1008     nvecs = 0;
1009     while(nvecs < veccnt &&
1010           Curl_bufq_peek_at(&stream->sendbuf,
1011                             stream->sendbuf_len_in_flight,
1012                             (const unsigned char **)&vec[nvecs].base,
1013                             &vec[nvecs].len)) {
1014       stream->sendbuf_len_in_flight += vec[nvecs].len;
1015       nwritten += vec[nvecs].len;
1016       ++nvecs;
1017     }
1018     DEBUGASSERT(nvecs > 0); /* we SHOULD have been be able to peek */
1019   }
1020 
1021   if(nwritten > 0 && stream->upload_left != -1)
1022     stream->upload_left -= nwritten;
1023 
1024   /* When we stopped sending and everything in `sendbuf` is "in flight",
1025    * we are at the end of the request body. */
1026   if(stream->upload_left == 0) {
1027     *pflags = NGHTTP3_DATA_FLAG_EOF;
1028     stream->send_closed = TRUE;
1029   }
1030   else if(!nwritten) {
1031     /* Not EOF, and nothing to give, we signal WOULDBLOCK. */
1032     CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] read req body -> AGAIN",
1033                 stream->s.id);
1034     return NGHTTP3_ERR_WOULDBLOCK;
1035   }
1036 
1037   CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] read req body -> "
1038               "%d vecs%s with %zu (buffered=%zu, left=%" FMT_OFF_T ")",
1039               stream->s.id, (int)nvecs,
1040               *pflags == NGHTTP3_DATA_FLAG_EOF ? " EOF" : "",
1041               nwritten, Curl_bufq_len(&stream->sendbuf),
1042               stream->upload_left);
1043   return (nghttp3_ssize)nvecs;
1044 }
1045 
cb_h3_acked_stream_data(nghttp3_conn * conn,int64_t stream_id,uint64_t datalen,void * user_data,void * stream_user_data)1046 static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id,
1047                                    uint64_t datalen, void *user_data,
1048                                    void *stream_user_data)
1049 {
1050   struct Curl_cfilter *cf = user_data;
1051   struct cf_osslq_ctx *ctx = cf->ctx;
1052   struct Curl_easy *data = stream_user_data;
1053   struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
1054   size_t skiplen;
1055 
1056   (void)cf;
1057   if(!stream)
1058     return 0;
1059   /* The server acknowledged `datalen` of bytes from our request body.
1060    * This is a delta. We have kept this data in `sendbuf` for
1061    * re-transmissions and can free it now. */
1062   if(datalen >= (uint64_t)stream->sendbuf_len_in_flight)
1063     skiplen = stream->sendbuf_len_in_flight;
1064   else
1065     skiplen = (size_t)datalen;
1066   Curl_bufq_skip(&stream->sendbuf, skiplen);
1067   stream->sendbuf_len_in_flight -= skiplen;
1068 
1069   /* Resume upload processing if we have more data to send */
1070   if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) {
1071     int rv = nghttp3_conn_resume_stream(conn, stream_id);
1072     if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
1073       return NGHTTP3_ERR_CALLBACK_FAILURE;
1074     }
1075   }
1076   return 0;
1077 }
1078 
1079 static nghttp3_callbacks ngh3_callbacks = {
1080   cb_h3_acked_stream_data,
1081   cb_h3_stream_close,
1082   cb_h3_recv_data,
1083   cb_h3_deferred_consume,
1084   NULL, /* begin_headers */
1085   cb_h3_recv_header,
1086   cb_h3_end_headers,
1087   NULL, /* begin_trailers */
1088   cb_h3_recv_header,
1089   NULL, /* end_trailers */
1090   cb_h3_stop_sending,
1091   NULL, /* end_stream */
1092   cb_h3_reset_stream,
1093   NULL, /* shutdown */
1094   NULL /* recv_settings */
1095 };
1096 
cf_osslq_h3conn_init(struct cf_osslq_ctx * ctx,SSL * conn,void * user_data)1097 static CURLcode cf_osslq_h3conn_init(struct cf_osslq_ctx *ctx, SSL *conn,
1098                                      void *user_data)
1099 {
1100   struct cf_osslq_h3conn *h3 = &ctx->h3;
1101   CURLcode result;
1102   int rc;
1103 
1104   nghttp3_settings_default(&h3->settings);
1105   rc = nghttp3_conn_client_new(&h3->conn,
1106                                &ngh3_callbacks,
1107                                &h3->settings,
1108                                nghttp3_mem_default(),
1109                                user_data);
1110   if(rc) {
1111     result = CURLE_OUT_OF_MEMORY;
1112     goto out;
1113   }
1114 
1115   result = cf_osslq_stream_open(&h3->s_ctrl, conn,
1116                                 SSL_STREAM_FLAG_ADVANCE|SSL_STREAM_FLAG_UNI,
1117                                 &ctx->stream_bufcp, NULL);
1118   if(result) {
1119     result = CURLE_QUIC_CONNECT_ERROR;
1120     goto out;
1121   }
1122   result = cf_osslq_stream_open(&h3->s_qpack_enc, conn,
1123                                 SSL_STREAM_FLAG_ADVANCE|SSL_STREAM_FLAG_UNI,
1124                                 &ctx->stream_bufcp, NULL);
1125   if(result) {
1126     result = CURLE_QUIC_CONNECT_ERROR;
1127     goto out;
1128   }
1129   result = cf_osslq_stream_open(&h3->s_qpack_dec, conn,
1130                                 SSL_STREAM_FLAG_ADVANCE|SSL_STREAM_FLAG_UNI,
1131                                 &ctx->stream_bufcp, NULL);
1132   if(result) {
1133     result = CURLE_QUIC_CONNECT_ERROR;
1134     goto out;
1135   }
1136 
1137   rc = nghttp3_conn_bind_control_stream(h3->conn, h3->s_ctrl.id);
1138   if(rc) {
1139     result = CURLE_QUIC_CONNECT_ERROR;
1140     goto out;
1141   }
1142   rc = nghttp3_conn_bind_qpack_streams(h3->conn, h3->s_qpack_enc.id,
1143                                        h3->s_qpack_dec.id);
1144   if(rc) {
1145     result = CURLE_QUIC_CONNECT_ERROR;
1146     goto out;
1147   }
1148 
1149   result = CURLE_OK;
1150 out:
1151   return result;
1152 }
1153 
cf_osslq_ctx_start(struct Curl_cfilter * cf,struct Curl_easy * data)1154 static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf,
1155                                    struct Curl_easy *data)
1156 {
1157   struct cf_osslq_ctx *ctx = cf->ctx;
1158   CURLcode result;
1159   int rv;
1160   const struct Curl_sockaddr_ex *peer_addr = NULL;
1161   BIO *bio = NULL;
1162   BIO_ADDR *baddr = NULL;
1163 
1164   DEBUGASSERT(ctx->initialized);
1165   result = Curl_ssl_peer_init(&ctx->peer, cf, TRNSPRT_QUIC);
1166   if(result)
1167     goto out;
1168 
1169 #define H3_ALPN "\x2h3"
1170   result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer,
1171                                H3_ALPN, sizeof(H3_ALPN) - 1,
1172                                NULL, NULL, NULL);
1173   if(result)
1174     goto out;
1175 
1176   result = vquic_ctx_init(&ctx->q);
1177   if(result)
1178     goto out;
1179 
1180   result = CURLE_QUIC_CONNECT_ERROR;
1181   Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, &peer_addr, NULL);
1182   if(!peer_addr)
1183     goto out;
1184 
1185   ctx->q.local_addrlen = sizeof(ctx->q.local_addr);
1186   rv = getsockname(ctx->q.sockfd, (struct sockaddr *)&ctx->q.local_addr,
1187                    &ctx->q.local_addrlen);
1188   if(rv == -1)
1189     goto out;
1190 
1191   result = make_bio_addr(&baddr, peer_addr);
1192   if(result) {
1193     failf(data, "error creating BIO_ADDR from sockaddr");
1194     goto out;
1195   }
1196 
1197   /* Type conversions, see #12861: OpenSSL wants an `int`, but on 64-bit
1198    * Win32 systems, Microsoft defines SOCKET as `unsigned long long`.
1199    */
1200 #if defined(_WIN32) && !defined(__LWIP_OPT_H__) && !defined(LWIP_HDR_OPT_H)
1201   if(ctx->q.sockfd > INT_MAX) {
1202     failf(data, "Windows socket identifier larger than MAX_INT, "
1203           "unable to set in OpenSSL dgram API.");
1204     result = CURLE_QUIC_CONNECT_ERROR;
1205     goto out;
1206   }
1207   bio = BIO_new_dgram((int)ctx->q.sockfd, BIO_NOCLOSE);
1208 #else
1209   bio = BIO_new_dgram(ctx->q.sockfd, BIO_NOCLOSE);
1210 #endif
1211   if(!bio) {
1212     result = CURLE_OUT_OF_MEMORY;
1213     goto out;
1214   }
1215 
1216   if(!SSL_set1_initial_peer_addr(ctx->tls.ossl.ssl, baddr)) {
1217     failf(data, "failed to set the initial peer address");
1218     result = CURLE_FAILED_INIT;
1219     goto out;
1220   }
1221   if(!SSL_set_blocking_mode(ctx->tls.ossl.ssl, 0)) {
1222     failf(data, "failed to turn off blocking mode");
1223     result = CURLE_FAILED_INIT;
1224     goto out;
1225   }
1226 
1227 #ifdef SSL_VALUE_QUIC_IDLE_TIMEOUT
1228   /* Added in OpenSSL v3.3.x */
1229   if(!SSL_set_feature_request_uint(ctx->tls.ossl.ssl,
1230                                    SSL_VALUE_QUIC_IDLE_TIMEOUT,
1231                                    CURL_QUIC_MAX_IDLE_MS)) {
1232     CURL_TRC_CF(data, cf, "error setting idle timeout, ");
1233     result = CURLE_FAILED_INIT;
1234     goto out;
1235   }
1236 #endif
1237 
1238   SSL_set_bio(ctx->tls.ossl.ssl, bio, bio);
1239   bio = NULL;
1240   SSL_set_connect_state(ctx->tls.ossl.ssl);
1241   SSL_set_incoming_stream_policy(ctx->tls.ossl.ssl,
1242                                  SSL_INCOMING_STREAM_POLICY_ACCEPT, 0);
1243   /* setup the H3 things on top of the QUIC connection */
1244   result = cf_osslq_h3conn_init(ctx, ctx->tls.ossl.ssl, cf);
1245 
1246 out:
1247   if(bio)
1248     BIO_free(bio);
1249   if(baddr)
1250     BIO_ADDR_free(baddr);
1251   CURL_TRC_CF(data, cf, "QUIC tls init -> %d", result);
1252   return result;
1253 }
1254 
1255 struct h3_quic_recv_ctx {
1256   struct Curl_cfilter *cf;
1257   struct Curl_easy *data;
1258   struct cf_osslq_stream *s;
1259 };
1260 
h3_quic_recv(void * reader_ctx,unsigned char * buf,size_t len,CURLcode * err)1261 static ssize_t h3_quic_recv(void *reader_ctx,
1262                             unsigned char *buf, size_t len,
1263                             CURLcode *err)
1264 {
1265   struct h3_quic_recv_ctx *x = reader_ctx;
1266   size_t nread;
1267   int rv;
1268 
1269   *err = CURLE_OK;
1270   rv = SSL_read_ex(x->s->ssl, buf, len, &nread);
1271   if(rv <= 0) {
1272     int detail = SSL_get_error(x->s->ssl, rv);
1273     if(detail == SSL_ERROR_WANT_READ || detail == SSL_ERROR_WANT_WRITE) {
1274       *err = CURLE_AGAIN;
1275       return -1;
1276     }
1277     else if(detail == SSL_ERROR_ZERO_RETURN) {
1278       CURL_TRC_CF(x->data, x->cf, "[%" FMT_PRId64 "] h3_quic_recv -> EOS",
1279                   x->s->id);
1280       x->s->recvd_eos = TRUE;
1281       return 0;
1282     }
1283     else if(SSL_get_stream_read_state(x->s->ssl) ==
1284             SSL_STREAM_STATE_RESET_REMOTE) {
1285       uint64_t app_error_code = NGHTTP3_H3_NO_ERROR;
1286       SSL_get_stream_read_error_code(x->s->ssl, &app_error_code);
1287       CURL_TRC_CF(x->data, x->cf, "[%" FMT_PRId64 "] h3_quic_recv -> RESET, "
1288                   "rv=%d, app_err=%" FMT_PRIu64,
1289                   x->s->id, rv, (curl_uint64_t)app_error_code);
1290       if(app_error_code != NGHTTP3_H3_NO_ERROR) {
1291         x->s->reset = TRUE;
1292       }
1293       x->s->recvd_eos = TRUE;
1294       return 0;
1295     }
1296     else {
1297       *err = cf_osslq_ssl_err(x->cf, x->data, detail, CURLE_RECV_ERROR);
1298       return -1;
1299     }
1300   }
1301   return (ssize_t)nread;
1302 }
1303 
cf_osslq_stream_recv(struct cf_osslq_stream * s,struct Curl_cfilter * cf,struct Curl_easy * data)1304 static CURLcode cf_osslq_stream_recv(struct cf_osslq_stream *s,
1305                                      struct Curl_cfilter *cf,
1306                                      struct Curl_easy *data)
1307 {
1308   struct cf_osslq_ctx *ctx = cf->ctx;
1309   CURLcode result = CURLE_OK;
1310   ssize_t nread;
1311   struct h3_quic_recv_ctx x;
1312   bool eagain = FALSE;
1313   size_t total_recv_len = 0;
1314 
1315   DEBUGASSERT(s);
1316   if(s->closed)
1317     return CURLE_OK;
1318 
1319   x.cf = cf;
1320   x.data = data;
1321   x.s = s;
1322   while(s->ssl && !s->closed && !eagain &&
1323         (total_recv_len < H3_STREAM_CHUNK_SIZE)) {
1324     if(Curl_bufq_is_empty(&s->recvbuf) && !s->recvd_eos) {
1325       while(!eagain && !s->recvd_eos && !Curl_bufq_is_full(&s->recvbuf)) {
1326         nread = Curl_bufq_sipn(&s->recvbuf, 0, h3_quic_recv, &x, &result);
1327         if(nread < 0) {
1328           if(result != CURLE_AGAIN)
1329             goto out;
1330           result = CURLE_OK;
1331           eagain = TRUE;
1332         }
1333       }
1334     }
1335 
1336     /* Forward what we have to nghttp3 */
1337     if(!Curl_bufq_is_empty(&s->recvbuf)) {
1338       const unsigned char *buf;
1339       size_t blen;
1340 
1341       while(Curl_bufq_peek(&s->recvbuf, &buf, &blen)) {
1342         nread = nghttp3_conn_read_stream(ctx->h3.conn, s->id,
1343                                          buf, blen, 0);
1344         CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] forward %zu bytes "
1345                     "to nghttp3 -> %zd", s->id, blen, nread);
1346         if(nread < 0) {
1347           failf(data, "nghttp3_conn_read_stream(len=%zu) error: %s",
1348                 blen, nghttp3_strerror((int)nread));
1349           result = CURLE_RECV_ERROR;
1350           goto out;
1351         }
1352         /* success, `nread` is the flow for QUIC to count as "consumed",
1353          * not sure how that will work with OpenSSL. Anyways, without error,
1354          * all data that we passed is not owned by nghttp3. */
1355         Curl_bufq_skip(&s->recvbuf, blen);
1356         total_recv_len += blen;
1357       }
1358     }
1359 
1360     /* When we forwarded everything, handle RESET/EOS */
1361     if(Curl_bufq_is_empty(&s->recvbuf) && !s->closed) {
1362       int rv;
1363       result = CURLE_OK;
1364       if(s->reset) {
1365         uint64_t app_error;
1366         if(!SSL_get_stream_read_error_code(s->ssl, &app_error)) {
1367           failf(data, "SSL_get_stream_read_error_code returned error");
1368           result = CURLE_RECV_ERROR;
1369           goto out;
1370         }
1371         rv = nghttp3_conn_close_stream(ctx->h3.conn, s->id, app_error);
1372         s->closed = TRUE;
1373         if(rv < 0 && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
1374           failf(data, "nghttp3_conn_close_stream returned error: %s",
1375                 nghttp3_strerror(rv));
1376           result = CURLE_RECV_ERROR;
1377           goto out;
1378         }
1379       }
1380       else if(s->recvd_eos) {
1381         rv = nghttp3_conn_close_stream(ctx->h3.conn, s->id,
1382                                        NGHTTP3_H3_NO_ERROR);
1383         s->closed = TRUE;
1384         CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] close nghttp3 stream -> %d",
1385                     s->id, rv);
1386         if(rv < 0 && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
1387           failf(data, "nghttp3_conn_close_stream returned error: %s",
1388                 nghttp3_strerror(rv));
1389           result = CURLE_RECV_ERROR;
1390           goto out;
1391         }
1392       }
1393     }
1394   }
1395 out:
1396   if(result)
1397     CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_osslq_stream_recv -> %d",
1398                 s->id, result);
1399   return result;
1400 }
1401 
cf_progress_ingress(struct Curl_cfilter * cf,struct Curl_easy * data)1402 static CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
1403                                     struct Curl_easy *data)
1404 {
1405   struct cf_osslq_ctx *ctx = cf->ctx;
1406   CURLcode result = CURLE_OK;
1407 
1408   if(!ctx->tls.ossl.ssl)
1409     goto out;
1410 
1411   ERR_clear_error();
1412 
1413   /* 1. Check for new incoming streams */
1414   while(1) {
1415     SSL *snew = SSL_accept_stream(ctx->tls.ossl.ssl,
1416                                   SSL_ACCEPT_STREAM_NO_BLOCK);
1417     if(!snew)
1418       break;
1419 
1420     (void)cf_osslq_h3conn_add_stream(&ctx->h3, snew, cf, data);
1421   }
1422 
1423   if(!SSL_handle_events(ctx->tls.ossl.ssl)) {
1424     int detail = SSL_get_error(ctx->tls.ossl.ssl, 0);
1425     result = cf_osslq_ssl_err(cf, data, detail, CURLE_RECV_ERROR);
1426   }
1427 
1428   if(ctx->h3.conn) {
1429     size_t i;
1430     for(i = 0; i < ctx->h3.remote_ctrl_n; ++i) {
1431       result = cf_osslq_stream_recv(&ctx->h3.remote_ctrl[i], cf, data);
1432       if(result)
1433         goto out;
1434     }
1435   }
1436 
1437   if(ctx->h3.conn) {
1438     struct Curl_llist_node *e;
1439     struct h3_stream_ctx *stream;
1440     /* PULL all open streams */
1441     DEBUGASSERT(data->multi);
1442     for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) {
1443       struct Curl_easy *sdata = Curl_node_elem(e);
1444       if(sdata->conn == data->conn && CURL_WANT_RECV(sdata)) {
1445         stream = H3_STREAM_CTX(ctx, sdata);
1446         if(stream && !stream->closed &&
1447            !Curl_bufq_is_full(&stream->recvbuf)) {
1448           result = cf_osslq_stream_recv(&stream->s, cf, sdata);
1449           if(result)
1450             goto out;
1451         }
1452       }
1453     }
1454   }
1455 
1456 out:
1457   CURL_TRC_CF(data, cf, "progress_ingress -> %d", result);
1458   return result;
1459 }
1460 
1461 /* Iterate over all streams and check if blocked can be unblocked */
cf_osslq_check_and_unblock(struct Curl_cfilter * cf,struct Curl_easy * data)1462 static CURLcode cf_osslq_check_and_unblock(struct Curl_cfilter *cf,
1463                                            struct Curl_easy *data)
1464 {
1465   struct cf_osslq_ctx *ctx = cf->ctx;
1466   struct h3_stream_ctx *stream;
1467 
1468   if(ctx->h3.conn) {
1469     struct Curl_llist_node *e;
1470     for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) {
1471       struct Curl_easy *sdata = Curl_node_elem(e);
1472       if(sdata->conn == data->conn) {
1473         stream = H3_STREAM_CTX(ctx, sdata);
1474         if(stream && stream->s.ssl && stream->s.send_blocked &&
1475            !SSL_want_write(stream->s.ssl)) {
1476           nghttp3_conn_unblock_stream(ctx->h3.conn, stream->s.id);
1477           stream->s.send_blocked = FALSE;
1478           h3_drain_stream(cf, sdata);
1479           CURL_TRC_CF(sdata, cf, "unblocked");
1480         }
1481       }
1482     }
1483   }
1484   return CURLE_OK;
1485 }
1486 
h3_send_streams(struct Curl_cfilter * cf,struct Curl_easy * data)1487 static CURLcode h3_send_streams(struct Curl_cfilter *cf,
1488                                 struct Curl_easy *data)
1489 {
1490   struct cf_osslq_ctx *ctx = cf->ctx;
1491   CURLcode result = CURLE_OK;
1492 
1493   if(!ctx->tls.ossl.ssl || !ctx->h3.conn)
1494     goto out;
1495 
1496   for(;;) {
1497     struct cf_osslq_stream *s = NULL;
1498     nghttp3_vec vec[16];
1499     nghttp3_ssize n, i;
1500     int64_t stream_id;
1501     size_t written;
1502     int eos, ok, rv;
1503     size_t total_len, acked_len = 0;
1504     bool blocked = FALSE, eos_written = FALSE;
1505 
1506     n = nghttp3_conn_writev_stream(ctx->h3.conn, &stream_id, &eos,
1507                                    vec, ARRAYSIZE(vec));
1508     if(n < 0) {
1509       failf(data, "nghttp3_conn_writev_stream returned error: %s",
1510             nghttp3_strerror((int)n));
1511       result = CURLE_SEND_ERROR;
1512       goto out;
1513     }
1514     if(stream_id < 0) {
1515       result = CURLE_OK;
1516       goto out;
1517     }
1518 
1519     /* Get the stream for this data */
1520     s = cf_osslq_get_qstream(cf, data, stream_id);
1521     if(!s) {
1522       failf(data, "nghttp3_conn_writev_stream gave unknown stream %"
1523             FMT_PRId64, (curl_int64_t)stream_id);
1524       result = CURLE_SEND_ERROR;
1525       goto out;
1526     }
1527     /* Now write the data to the stream's SSL*, it may not all fit! */
1528     DEBUGASSERT(s->id == stream_id);
1529     for(i = 0, total_len = 0; i < n; ++i) {
1530       total_len += vec[i].len;
1531     }
1532     for(i = 0; (i < n) && !blocked; ++i) {
1533       /* Without stream->s.ssl, we closed that already, so
1534        * pretend the write did succeed. */
1535       uint64_t flags = (eos && ((i + 1) == n)) ? SSL_WRITE_FLAG_CONCLUDE : 0;
1536       written = vec[i].len;
1537       ok = !s->ssl || SSL_write_ex2(s->ssl, vec[i].base, vec[i].len, flags,
1538                                    &written);
1539       if(ok && flags & SSL_WRITE_FLAG_CONCLUDE)
1540         eos_written = TRUE;
1541       if(ok) {
1542         /* As OpenSSL buffers the data, we count this as acknowledged
1543          * from nghttp3's point of view */
1544         CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] send %zu bytes to QUIC ok",
1545                     s->id, vec[i].len);
1546         acked_len += vec[i].len;
1547       }
1548       else {
1549         int detail = SSL_get_error(s->ssl, 0);
1550         switch(detail) {
1551         case SSL_ERROR_WANT_WRITE:
1552         case SSL_ERROR_WANT_READ:
1553           /* QUIC blocked us from writing more */
1554           CURL_TRC_CF(data, cf, "[%"FMT_PRId64 "] send %zu bytes to "
1555                       "QUIC blocked", s->id, vec[i].len);
1556           written = 0;
1557           nghttp3_conn_block_stream(ctx->h3.conn, s->id);
1558           s->send_blocked = blocked = TRUE;
1559           break;
1560         default:
1561           failf(data, "[%"FMT_PRId64 "] send %zu bytes to QUIC, SSL error %d",
1562                 s->id, vec[i].len, detail);
1563           result = cf_osslq_ssl_err(cf, data, detail, CURLE_HTTP3);
1564           goto out;
1565         }
1566       }
1567     }
1568 
1569     if(acked_len > 0 || (eos && !s->send_blocked)) {
1570       /* Since QUIC buffers the data written internally, we can tell
1571        * nghttp3 that it can move forward on it */
1572       ctx->q.last_io = Curl_now();
1573       rv = nghttp3_conn_add_write_offset(ctx->h3.conn, s->id, acked_len);
1574       if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
1575         failf(data, "nghttp3_conn_add_write_offset returned error: %s\n",
1576               nghttp3_strerror(rv));
1577         result = CURLE_SEND_ERROR;
1578         goto out;
1579       }
1580       rv = nghttp3_conn_add_ack_offset(ctx->h3.conn, s->id, acked_len);
1581       if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
1582         failf(data, "nghttp3_conn_add_ack_offset returned error: %s\n",
1583               nghttp3_strerror(rv));
1584         result = CURLE_SEND_ERROR;
1585         goto out;
1586       }
1587       CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] forwarded %zu/%zu h3 bytes "
1588                   "to QUIC, eos=%d", s->id, acked_len, total_len, eos);
1589     }
1590 
1591     if(eos && !s->send_blocked && !eos_written) {
1592       /* wrote everything and H3 indicates end of stream */
1593       CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] closing QUIC stream", s->id);
1594       SSL_stream_conclude(s->ssl, 0);
1595     }
1596   }
1597 
1598 out:
1599   CURL_TRC_CF(data, cf, "h3_send_streams -> %d", result);
1600   return result;
1601 }
1602 
cf_progress_egress(struct Curl_cfilter * cf,struct Curl_easy * data)1603 static CURLcode cf_progress_egress(struct Curl_cfilter *cf,
1604                                    struct Curl_easy *data)
1605 {
1606   struct cf_osslq_ctx *ctx = cf->ctx;
1607   CURLcode result = CURLE_OK;
1608 
1609   if(!ctx->tls.ossl.ssl)
1610     goto out;
1611 
1612   ERR_clear_error();
1613   result = h3_send_streams(cf, data);
1614   if(result)
1615     goto out;
1616 
1617   if(!SSL_handle_events(ctx->tls.ossl.ssl)) {
1618     int detail = SSL_get_error(ctx->tls.ossl.ssl, 0);
1619     result = cf_osslq_ssl_err(cf, data, detail, CURLE_SEND_ERROR);
1620   }
1621 
1622   result = cf_osslq_check_and_unblock(cf, data);
1623 
1624 out:
1625   CURL_TRC_CF(data, cf, "progress_egress -> %d", result);
1626   return result;
1627 }
1628 
check_and_set_expiry(struct Curl_cfilter * cf,struct Curl_easy * data)1629 static CURLcode check_and_set_expiry(struct Curl_cfilter *cf,
1630                                      struct Curl_easy *data)
1631 {
1632   struct cf_osslq_ctx *ctx = cf->ctx;
1633   CURLcode result = CURLE_OK;
1634   struct timeval tv;
1635   timediff_t timeoutms;
1636   int is_infinite = 1;
1637 
1638   if(ctx->tls.ossl.ssl &&
1639      SSL_get_event_timeout(ctx->tls.ossl.ssl, &tv, &is_infinite) &&
1640      !is_infinite) {
1641     timeoutms = curlx_tvtoms(&tv);
1642     /* QUIC want to be called again latest at the returned timeout */
1643     if(timeoutms <= 0) {
1644       result = cf_progress_ingress(cf, data);
1645       if(result)
1646         goto out;
1647       result = cf_progress_egress(cf, data);
1648       if(result)
1649         goto out;
1650       if(SSL_get_event_timeout(ctx->tls.ossl.ssl, &tv, &is_infinite)) {
1651         timeoutms = curlx_tvtoms(&tv);
1652       }
1653     }
1654     if(!is_infinite) {
1655       Curl_expire(data, timeoutms, EXPIRE_QUIC);
1656       CURL_TRC_CF(data, cf, "QUIC expiry in %ldms", (long)timeoutms);
1657     }
1658   }
1659 out:
1660   return result;
1661 }
1662 
cf_osslq_connect(struct Curl_cfilter * cf,struct Curl_easy * data,bool blocking,bool * done)1663 static CURLcode cf_osslq_connect(struct Curl_cfilter *cf,
1664                                  struct Curl_easy *data,
1665                                  bool blocking, bool *done)
1666 {
1667   struct cf_osslq_ctx *ctx = cf->ctx;
1668   CURLcode result = CURLE_OK;
1669   struct cf_call_data save;
1670   struct curltime now;
1671   int err;
1672 
1673   if(cf->connected) {
1674     *done = TRUE;
1675     return CURLE_OK;
1676   }
1677 
1678   /* Connect the UDP filter first */
1679   if(!cf->next->connected) {
1680     result = Curl_conn_cf_connect(cf->next, data, blocking, done);
1681     if(result || !*done)
1682       return result;
1683   }
1684 
1685   *done = FALSE;
1686   now = Curl_now();
1687   CF_DATA_SAVE(save, cf, data);
1688 
1689   if(!ctx->tls.ossl.ssl) {
1690     ctx->started_at = now;
1691     result = cf_osslq_ctx_start(cf, data);
1692     if(result)
1693       goto out;
1694   }
1695 
1696   if(!ctx->got_first_byte) {
1697     int readable = SOCKET_READABLE(ctx->q.sockfd, 0);
1698     if(readable > 0 && (readable & CURL_CSELECT_IN)) {
1699       ctx->got_first_byte = TRUE;
1700       ctx->first_byte_at = Curl_now();
1701     }
1702   }
1703 
1704   /* Since OpenSSL does its own send/recv internally, we may miss the
1705    * moment to populate the x509 store right before the server response.
1706    * Do it instead before we start the handshake, at the loss of the
1707    * time to set this up. */
1708   result = Curl_vquic_tls_before_recv(&ctx->tls, cf, data);
1709   if(result)
1710     goto out;
1711 
1712   ERR_clear_error();
1713   err = SSL_do_handshake(ctx->tls.ossl.ssl);
1714 
1715   if(err == 1) {
1716     /* connected */
1717     ctx->handshake_at = now;
1718     ctx->q.last_io = now;
1719     CURL_TRC_CF(data, cf, "handshake complete after %dms",
1720                (int)Curl_timediff(now, ctx->started_at));
1721     result = cf_osslq_verify_peer(cf, data);
1722     if(!result) {
1723       CURL_TRC_CF(data, cf, "peer verified");
1724       cf->connected = TRUE;
1725       cf->conn->alpn = CURL_HTTP_VERSION_3;
1726       *done = TRUE;
1727       connkeep(cf->conn, "HTTP/3 default");
1728     }
1729   }
1730   else {
1731     int detail = SSL_get_error(ctx->tls.ossl.ssl, err);
1732     switch(detail) {
1733     case SSL_ERROR_WANT_READ:
1734       ctx->q.last_io = now;
1735       CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_RECV");
1736       goto out;
1737     case SSL_ERROR_WANT_WRITE:
1738       ctx->q.last_io = now;
1739       CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_SEND");
1740       result = CURLE_OK;
1741       goto out;
1742 #ifdef SSL_ERROR_WANT_ASYNC
1743     case SSL_ERROR_WANT_ASYNC:
1744       ctx->q.last_io = now;
1745       CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_ASYNC");
1746       result = CURLE_OK;
1747       goto out;
1748 #endif
1749 #ifdef SSL_ERROR_WANT_RETRY_VERIFY
1750     case SSL_ERROR_WANT_RETRY_VERIFY:
1751       result = CURLE_OK;
1752       goto out;
1753 #endif
1754     default:
1755       result = cf_osslq_ssl_err(cf, data, detail, CURLE_COULDNT_CONNECT);
1756       goto out;
1757     }
1758   }
1759 
1760 out:
1761   if(result == CURLE_RECV_ERROR && ctx->tls.ossl.ssl &&
1762      ctx->protocol_shutdown) {
1763     /* When a QUIC server instance is shutting down, it may send us a
1764      * CONNECTION_CLOSE right away. Our connection then enters the DRAINING
1765      * state. The CONNECT may work in the near future again. Indicate
1766      * that as a "weird" reply. */
1767     result = CURLE_WEIRD_SERVER_REPLY;
1768   }
1769 
1770 #ifndef CURL_DISABLE_VERBOSE_STRINGS
1771   if(result) {
1772     struct ip_quadruple ip;
1773 
1774     Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip);
1775     infof(data, "QUIC connect to %s port %u failed: %s",
1776           ip.remote_ip, ip.remote_port, curl_easy_strerror(result));
1777   }
1778 #endif
1779   if(!result)
1780     result = check_and_set_expiry(cf, data);
1781   if(result || *done)
1782     CURL_TRC_CF(data, cf, "connect -> %d, done=%d", result, *done);
1783   CF_DATA_RESTORE(cf, save);
1784   return result;
1785 }
1786 
h3_stream_open(struct Curl_cfilter * cf,struct Curl_easy * data,const void * buf,size_t len,CURLcode * err)1787 static ssize_t h3_stream_open(struct Curl_cfilter *cf,
1788                               struct Curl_easy *data,
1789                               const void *buf, size_t len,
1790                               CURLcode *err)
1791 {
1792   struct cf_osslq_ctx *ctx = cf->ctx;
1793   struct h3_stream_ctx *stream = NULL;
1794   struct dynhds h2_headers;
1795   size_t nheader;
1796   nghttp3_nv *nva = NULL;
1797   int rc = 0;
1798   unsigned int i;
1799   ssize_t nwritten = -1;
1800   nghttp3_data_reader reader;
1801   nghttp3_data_reader *preader = NULL;
1802 
1803   Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST);
1804 
1805   *err = h3_data_setup(cf, data);
1806   if(*err)
1807     goto out;
1808   stream = H3_STREAM_CTX(ctx, data);
1809   DEBUGASSERT(stream);
1810   if(!stream) {
1811     *err = CURLE_FAILED_INIT;
1812     goto out;
1813   }
1814 
1815   nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err);
1816   if(nwritten < 0)
1817     goto out;
1818   if(!stream->h1.done) {
1819     /* need more data */
1820     goto out;
1821   }
1822   DEBUGASSERT(stream->h1.req);
1823 
1824   *err = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data);
1825   if(*err) {
1826     nwritten = -1;
1827     goto out;
1828   }
1829   /* no longer needed */
1830   Curl_h1_req_parse_free(&stream->h1);
1831 
1832   nheader = Curl_dynhds_count(&h2_headers);
1833   nva = malloc(sizeof(nghttp3_nv) * nheader);
1834   if(!nva) {
1835     *err = CURLE_OUT_OF_MEMORY;
1836     nwritten = -1;
1837     goto out;
1838   }
1839 
1840   for(i = 0; i < nheader; ++i) {
1841     struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i);
1842     nva[i].name = (unsigned char *)e->name;
1843     nva[i].namelen = e->namelen;
1844     nva[i].value = (unsigned char *)e->value;
1845     nva[i].valuelen = e->valuelen;
1846     nva[i].flags = NGHTTP3_NV_FLAG_NONE;
1847   }
1848 
1849   DEBUGASSERT(stream->s.id == -1);
1850   *err = cf_osslq_stream_open(&stream->s, ctx->tls.ossl.ssl, 0,
1851                               &ctx->stream_bufcp, data);
1852   if(*err) {
1853     failf(data, "cannot get bidi streams");
1854     *err = CURLE_SEND_ERROR;
1855     goto out;
1856   }
1857 
1858   switch(data->state.httpreq) {
1859   case HTTPREQ_POST:
1860   case HTTPREQ_POST_FORM:
1861   case HTTPREQ_POST_MIME:
1862   case HTTPREQ_PUT:
1863     /* known request body size or -1 */
1864     if(data->state.infilesize != -1)
1865       stream->upload_left = data->state.infilesize;
1866     else
1867       /* data sending without specifying the data amount up front */
1868       stream->upload_left = -1; /* unknown */
1869     break;
1870   default:
1871     /* there is not request body */
1872     stream->upload_left = 0; /* no request body */
1873     break;
1874   }
1875 
1876   stream->send_closed = (stream->upload_left == 0);
1877   if(!stream->send_closed) {
1878     reader.read_data = cb_h3_read_req_body;
1879     preader = &reader;
1880   }
1881 
1882   rc = nghttp3_conn_submit_request(ctx->h3.conn, stream->s.id,
1883                                    nva, nheader, preader, data);
1884   if(rc) {
1885     switch(rc) {
1886     case NGHTTP3_ERR_CONN_CLOSING:
1887       CURL_TRC_CF(data, cf, "h3sid[%"FMT_PRId64"] failed to send, "
1888                   "connection is closing", stream->s.id);
1889       break;
1890     default:
1891       CURL_TRC_CF(data, cf, "h3sid[%"FMT_PRId64 "] failed to send -> %d (%s)",
1892                   stream->s.id, rc, nghttp3_strerror(rc));
1893       break;
1894     }
1895     *err = CURLE_SEND_ERROR;
1896     nwritten = -1;
1897     goto out;
1898   }
1899 
1900   if(Curl_trc_is_verbose(data)) {
1901     infof(data, "[HTTP/3] [%" FMT_PRId64 "] OPENED stream for %s",
1902           stream->s.id, data->state.url);
1903     for(i = 0; i < nheader; ++i) {
1904       infof(data, "[HTTP/3] [%" FMT_PRId64 "] [%.*s: %.*s]",
1905             stream->s.id,
1906             (int)nva[i].namelen, nva[i].name,
1907             (int)nva[i].valuelen, nva[i].value);
1908     }
1909   }
1910 
1911 out:
1912   free(nva);
1913   Curl_dynhds_free(&h2_headers);
1914   return nwritten;
1915 }
1916 
cf_osslq_send(struct Curl_cfilter * cf,struct Curl_easy * data,const void * buf,size_t len,bool eos,CURLcode * err)1917 static ssize_t cf_osslq_send(struct Curl_cfilter *cf, struct Curl_easy *data,
1918                              const void *buf, size_t len, bool eos,
1919                              CURLcode *err)
1920 {
1921   struct cf_osslq_ctx *ctx = cf->ctx;
1922   struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
1923   struct cf_call_data save;
1924   ssize_t nwritten;
1925   CURLcode result;
1926 
1927   (void)eos; /* TODO: use to end stream */
1928   CF_DATA_SAVE(save, cf, data);
1929   DEBUGASSERT(cf->connected);
1930   DEBUGASSERT(ctx->tls.ossl.ssl);
1931   DEBUGASSERT(ctx->h3.conn);
1932   *err = CURLE_OK;
1933 
1934   result = cf_progress_ingress(cf, data);
1935   if(result) {
1936     *err = result;
1937     nwritten = -1;
1938     goto out;
1939   }
1940 
1941   result = cf_progress_egress(cf, data);
1942   if(result) {
1943     *err = result;
1944     nwritten = -1;
1945     goto out;
1946   }
1947 
1948   if(!stream || stream->s.id < 0) {
1949     nwritten = h3_stream_open(cf, data, buf, len, err);
1950     if(nwritten < 0) {
1951       CURL_TRC_CF(data, cf, "failed to open stream -> %d", *err);
1952       goto out;
1953     }
1954     stream = H3_STREAM_CTX(ctx, data);
1955   }
1956   else if(stream->closed) {
1957     if(stream->resp_hds_complete) {
1958       /* Server decided to close the stream after having sent us a final
1959        * response. This is valid if it is not interested in the request
1960        * body. This happens on 30x or 40x responses.
1961        * We silently discard the data sent, since this is not a transport
1962        * error situation. */
1963       CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] discarding data"
1964                   "on closed stream with response", stream->s.id);
1965       *err = CURLE_OK;
1966       nwritten = (ssize_t)len;
1967       goto out;
1968     }
1969     CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] send_body(len=%zu) "
1970                 "-> stream closed", stream->s.id, len);
1971     *err = CURLE_HTTP3;
1972     nwritten = -1;
1973     goto out;
1974   }
1975   else {
1976     nwritten = Curl_bufq_write(&stream->sendbuf, buf, len, err);
1977     CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_send, add to "
1978                 "sendbuf(len=%zu) -> %zd, %d",
1979                 stream->s.id, len, nwritten, *err);
1980     if(nwritten < 0) {
1981       goto out;
1982     }
1983 
1984     (void)nghttp3_conn_resume_stream(ctx->h3.conn, stream->s.id);
1985   }
1986 
1987   result = cf_progress_egress(cf, data);
1988   if(result) {
1989     *err = result;
1990     nwritten = -1;
1991   }
1992 
1993 out:
1994   result = check_and_set_expiry(cf, data);
1995   CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_send(len=%zu) -> %zd, %d",
1996               stream ? stream->s.id : -1, len, nwritten, *err);
1997   CF_DATA_RESTORE(cf, save);
1998   return nwritten;
1999 }
2000 
recv_closed_stream(struct Curl_cfilter * cf,struct Curl_easy * data,struct h3_stream_ctx * stream,CURLcode * err)2001 static ssize_t recv_closed_stream(struct Curl_cfilter *cf,
2002                                   struct Curl_easy *data,
2003                                   struct h3_stream_ctx *stream,
2004                                   CURLcode *err)
2005 {
2006   ssize_t nread = -1;
2007 
2008   (void)cf;
2009   if(stream->reset) {
2010     failf(data,
2011           "HTTP/3 stream %" FMT_PRId64 " reset by server",
2012           stream->s.id);
2013     *err = data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP3;
2014     goto out;
2015   }
2016   else if(!stream->resp_hds_complete) {
2017     failf(data,
2018           "HTTP/3 stream %" FMT_PRId64
2019           " was closed cleanly, but before getting"
2020           " all response header fields, treated as error",
2021           stream->s.id);
2022     *err = CURLE_HTTP3;
2023     goto out;
2024   }
2025   *err = CURLE_OK;
2026   nread = 0;
2027 
2028 out:
2029   return nread;
2030 }
2031 
cf_osslq_recv(struct Curl_cfilter * cf,struct Curl_easy * data,char * buf,size_t len,CURLcode * err)2032 static ssize_t cf_osslq_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
2033                              char *buf, size_t len, CURLcode *err)
2034 {
2035   struct cf_osslq_ctx *ctx = cf->ctx;
2036   struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
2037   ssize_t nread = -1;
2038   struct cf_call_data save;
2039   CURLcode result;
2040 
2041   (void)ctx;
2042   CF_DATA_SAVE(save, cf, data);
2043   DEBUGASSERT(cf->connected);
2044   DEBUGASSERT(ctx);
2045   DEBUGASSERT(ctx->tls.ossl.ssl);
2046   DEBUGASSERT(ctx->h3.conn);
2047   *err = CURLE_OK;
2048 
2049   if(!stream) {
2050     *err = CURLE_RECV_ERROR;
2051     goto out;
2052   }
2053 
2054   if(!Curl_bufq_is_empty(&stream->recvbuf)) {
2055     nread = Curl_bufq_read(&stream->recvbuf,
2056                            (unsigned char *)buf, len, err);
2057     if(nread < 0) {
2058       CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] read recvbuf(len=%zu) "
2059                   "-> %zd, %d", stream->s.id, len, nread, *err);
2060       goto out;
2061     }
2062   }
2063 
2064   result = cf_progress_ingress(cf, data);
2065   if(result) {
2066     *err = result;
2067     nread = -1;
2068     goto out;
2069   }
2070 
2071   /* recvbuf had nothing before, maybe after progressing ingress? */
2072   if(nread < 0 && !Curl_bufq_is_empty(&stream->recvbuf)) {
2073     nread = Curl_bufq_read(&stream->recvbuf,
2074                            (unsigned char *)buf, len, err);
2075     if(nread < 0) {
2076       CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] read recvbuf(len=%zu) "
2077                   "-> %zd, %d", stream->s.id, len, nread, *err);
2078       goto out;
2079     }
2080   }
2081 
2082   if(nread > 0) {
2083     h3_drain_stream(cf, data);
2084   }
2085   else {
2086     if(stream->closed) {
2087       nread = recv_closed_stream(cf, data, stream, err);
2088       goto out;
2089     }
2090     *err = CURLE_AGAIN;
2091     nread = -1;
2092   }
2093 
2094 out:
2095   if(cf_progress_egress(cf, data)) {
2096     *err = CURLE_SEND_ERROR;
2097     nread = -1;
2098   }
2099   else {
2100     CURLcode result2 = check_and_set_expiry(cf, data);
2101     if(result2) {
2102       *err = result2;
2103       nread = -1;
2104     }
2105   }
2106   CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_recv(len=%zu) -> %zd, %d",
2107               stream ? stream->s.id : -1, len, nread, *err);
2108   CF_DATA_RESTORE(cf, save);
2109   return nread;
2110 }
2111 
2112 /*
2113  * Called from transfer.c:data_pending to know if we should keep looping
2114  * to receive more data from the connection.
2115  */
cf_osslq_data_pending(struct Curl_cfilter * cf,const struct Curl_easy * data)2116 static bool cf_osslq_data_pending(struct Curl_cfilter *cf,
2117                                   const struct Curl_easy *data)
2118 {
2119   struct cf_osslq_ctx *ctx = cf->ctx;
2120   const struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
2121   (void)cf;
2122   return stream && !Curl_bufq_is_empty(&stream->recvbuf);
2123 }
2124 
cf_osslq_data_event(struct Curl_cfilter * cf,struct Curl_easy * data,int event,int arg1,void * arg2)2125 static CURLcode cf_osslq_data_event(struct Curl_cfilter *cf,
2126                                     struct Curl_easy *data,
2127                                     int event, int arg1, void *arg2)
2128 {
2129   struct cf_osslq_ctx *ctx = cf->ctx;
2130   CURLcode result = CURLE_OK;
2131   struct cf_call_data save;
2132 
2133   CF_DATA_SAVE(save, cf, data);
2134   (void)arg1;
2135   (void)arg2;
2136   switch(event) {
2137   case CF_CTRL_DATA_SETUP:
2138     break;
2139   case CF_CTRL_DATA_PAUSE:
2140     result = h3_data_pause(cf, data, (arg1 != 0));
2141     break;
2142   case CF_CTRL_DATA_DETACH:
2143     h3_data_done(cf, data);
2144     break;
2145   case CF_CTRL_DATA_DONE:
2146     h3_data_done(cf, data);
2147     break;
2148   case CF_CTRL_DATA_DONE_SEND: {
2149     struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
2150     if(stream && !stream->send_closed) {
2151       stream->send_closed = TRUE;
2152       stream->upload_left = Curl_bufq_len(&stream->sendbuf) -
2153         stream->sendbuf_len_in_flight;
2154       (void)nghttp3_conn_resume_stream(ctx->h3.conn, stream->s.id);
2155     }
2156     break;
2157   }
2158   case CF_CTRL_DATA_IDLE: {
2159     struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
2160     CURL_TRC_CF(data, cf, "data idle");
2161     if(stream && !stream->closed) {
2162       result = check_and_set_expiry(cf, data);
2163     }
2164     break;
2165   }
2166   default:
2167     break;
2168   }
2169   CF_DATA_RESTORE(cf, save);
2170   return result;
2171 }
2172 
cf_osslq_conn_is_alive(struct Curl_cfilter * cf,struct Curl_easy * data,bool * input_pending)2173 static bool cf_osslq_conn_is_alive(struct Curl_cfilter *cf,
2174                                    struct Curl_easy *data,
2175                                    bool *input_pending)
2176 {
2177   struct cf_osslq_ctx *ctx = cf->ctx;
2178   bool alive = FALSE;
2179   struct cf_call_data save;
2180 
2181   CF_DATA_SAVE(save, cf, data);
2182   *input_pending = FALSE;
2183   if(!ctx->tls.ossl.ssl)
2184     goto out;
2185 
2186 #ifdef SSL_VALUE_QUIC_IDLE_TIMEOUT
2187   /* Added in OpenSSL v3.3.x */
2188   {
2189     timediff_t idletime;
2190     uint64_t idle_ms = ctx->max_idle_ms;
2191     if(!SSL_get_value_uint(ctx->tls.ossl.ssl,
2192                            SSL_VALUE_CLASS_FEATURE_NEGOTIATED,
2193                            SSL_VALUE_QUIC_IDLE_TIMEOUT, &idle_ms)) {
2194       CURL_TRC_CF(data, cf, "error getting negotiated idle timeout, "
2195                   "assume connection is dead.");
2196       goto out;
2197     }
2198     CURL_TRC_CF(data, cf, "negotiated idle timeout: %zums", (size_t)idle_ms);
2199     idletime = Curl_timediff(Curl_now(), ctx->q.last_io);
2200     if(idletime > 0 && (uint64_t)idletime > idle_ms)
2201       goto out;
2202   }
2203 
2204 #endif
2205 
2206   if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending))
2207     goto out;
2208 
2209   alive = TRUE;
2210   if(*input_pending) {
2211     CURLcode result;
2212     /* This happens before we have sent off a request and the connection is
2213        not in use by any other transfer, there should not be any data here,
2214        only "protocol frames" */
2215     *input_pending = FALSE;
2216     result = cf_progress_ingress(cf, data);
2217     CURL_TRC_CF(data, cf, "is_alive, progress ingress -> %d", result);
2218     alive = result ? FALSE : TRUE;
2219   }
2220 
2221 out:
2222   CF_DATA_RESTORE(cf, save);
2223   return alive;
2224 }
2225 
cf_osslq_adjust_pollset(struct Curl_cfilter * cf,struct Curl_easy * data,struct easy_pollset * ps)2226 static void cf_osslq_adjust_pollset(struct Curl_cfilter *cf,
2227                                     struct Curl_easy *data,
2228                                     struct easy_pollset *ps)
2229 {
2230   struct cf_osslq_ctx *ctx = cf->ctx;
2231 
2232   if(!ctx->tls.ossl.ssl) {
2233     /* NOP */
2234   }
2235   else if(!cf->connected) {
2236     /* during handshake, transfer has not started yet. we always
2237      * add our socket for polling if SSL wants to send/recv */
2238     Curl_pollset_set(data, ps, ctx->q.sockfd,
2239                      SSL_net_read_desired(ctx->tls.ossl.ssl),
2240                      SSL_net_write_desired(ctx->tls.ossl.ssl));
2241   }
2242   else {
2243     /* once connected, we only modify the socket if it is present.
2244      * this avoids adding it for paused transfers. */
2245     bool want_recv, want_send;
2246     Curl_pollset_check(data, ps, ctx->q.sockfd, &want_recv, &want_send);
2247     if(want_recv || want_send) {
2248       Curl_pollset_set(data, ps, ctx->q.sockfd,
2249                        SSL_net_read_desired(ctx->tls.ossl.ssl),
2250                        SSL_net_write_desired(ctx->tls.ossl.ssl));
2251     }
2252     else if(ctx->need_recv || ctx->need_send) {
2253       Curl_pollset_set(data, ps, ctx->q.sockfd,
2254                        ctx->need_recv, ctx->need_send);
2255     }
2256   }
2257 }
2258 
cf_osslq_query(struct Curl_cfilter * cf,struct Curl_easy * data,int query,int * pres1,void * pres2)2259 static CURLcode cf_osslq_query(struct Curl_cfilter *cf,
2260                                struct Curl_easy *data,
2261                                int query, int *pres1, void *pres2)
2262 {
2263   struct cf_osslq_ctx *ctx = cf->ctx;
2264 
2265   switch(query) {
2266   case CF_QUERY_MAX_CONCURRENT: {
2267 #ifdef SSL_VALUE_QUIC_STREAM_BIDI_LOCAL_AVAIL
2268     /* Added in OpenSSL v3.3.x */
2269     uint64_t v;
2270     if(!SSL_get_value_uint(ctx->tls.ossl.ssl, SSL_VALUE_CLASS_GENERIC,
2271                            SSL_VALUE_QUIC_STREAM_BIDI_LOCAL_AVAIL, &v)) {
2272       CURL_TRC_CF(data, cf, "error getting available local bidi streams");
2273       return CURLE_HTTP3;
2274     }
2275     /* we report avail + in_use */
2276     v += CONN_INUSE(cf->conn);
2277     *pres1 = (v > INT_MAX) ? INT_MAX : (int)v;
2278 #else
2279     *pres1 = 100;
2280 #endif
2281     CURL_TRC_CF(data, cf, "query max_conncurrent -> %d", *pres1);
2282     return CURLE_OK;
2283   }
2284   case CF_QUERY_CONNECT_REPLY_MS:
2285     if(ctx->got_first_byte) {
2286       timediff_t ms = Curl_timediff(ctx->first_byte_at, ctx->started_at);
2287       *pres1 = (ms < INT_MAX) ? (int)ms : INT_MAX;
2288     }
2289     else
2290       *pres1 = -1;
2291     return CURLE_OK;
2292   case CF_QUERY_TIMER_CONNECT: {
2293     struct curltime *when = pres2;
2294     if(ctx->got_first_byte)
2295       *when = ctx->first_byte_at;
2296     return CURLE_OK;
2297   }
2298   case CF_QUERY_TIMER_APPCONNECT: {
2299     struct curltime *when = pres2;
2300     if(cf->connected)
2301       *when = ctx->handshake_at;
2302     return CURLE_OK;
2303   }
2304   default:
2305     break;
2306   }
2307   return cf->next ?
2308     cf->next->cft->query(cf->next, data, query, pres1, pres2) :
2309     CURLE_UNKNOWN_OPTION;
2310 }
2311 
2312 struct Curl_cftype Curl_cft_http3 = {
2313   "HTTP/3",
2314   CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX,
2315   0,
2316   cf_osslq_destroy,
2317   cf_osslq_connect,
2318   cf_osslq_close,
2319   cf_osslq_shutdown,
2320   Curl_cf_def_get_host,
2321   cf_osslq_adjust_pollset,
2322   cf_osslq_data_pending,
2323   cf_osslq_send,
2324   cf_osslq_recv,
2325   cf_osslq_data_event,
2326   cf_osslq_conn_is_alive,
2327   Curl_cf_def_conn_keep_alive,
2328   cf_osslq_query,
2329 };
2330 
Curl_cf_osslq_create(struct Curl_cfilter ** pcf,struct Curl_easy * data,struct connectdata * conn,const struct Curl_addrinfo * ai)2331 CURLcode Curl_cf_osslq_create(struct Curl_cfilter **pcf,
2332                               struct Curl_easy *data,
2333                               struct connectdata *conn,
2334                               const struct Curl_addrinfo *ai)
2335 {
2336   struct cf_osslq_ctx *ctx = NULL;
2337   struct Curl_cfilter *cf = NULL, *udp_cf = NULL;
2338   CURLcode result;
2339 
2340   (void)data;
2341   ctx = calloc(1, sizeof(*ctx));
2342   if(!ctx) {
2343     result = CURLE_OUT_OF_MEMORY;
2344     goto out;
2345   }
2346   cf_osslq_ctx_init(ctx);
2347 
2348   result = Curl_cf_create(&cf, &Curl_cft_http3, ctx);
2349   if(result)
2350     goto out;
2351 
2352   result = Curl_cf_udp_create(&udp_cf, data, conn, ai, TRNSPRT_QUIC);
2353   if(result)
2354     goto out;
2355 
2356   cf->conn = conn;
2357   udp_cf->conn = cf->conn;
2358   udp_cf->sockindex = cf->sockindex;
2359   cf->next = udp_cf;
2360 
2361 out:
2362   *pcf = (!result) ? cf : NULL;
2363   if(result) {
2364     if(udp_cf)
2365       Curl_conn_cf_discard_sub(cf, udp_cf, data, TRUE);
2366     Curl_safefree(cf);
2367     cf_osslq_ctx_free(ctx);
2368   }
2369   return result;
2370 }
2371 
Curl_conn_is_osslq(const struct Curl_easy * data,const struct connectdata * conn,int sockindex)2372 bool Curl_conn_is_osslq(const struct Curl_easy *data,
2373                         const struct connectdata *conn,
2374                         int sockindex)
2375 {
2376   struct Curl_cfilter *cf = conn ? conn->cfilter[sockindex] : NULL;
2377 
2378   (void)data;
2379   for(; cf; cf = cf->next) {
2380     if(cf->cft == &Curl_cft_http3)
2381       return TRUE;
2382     if(cf->cft->flags & CF_TYPE_IP_CONNECT)
2383       return FALSE;
2384   }
2385   return FALSE;
2386 }
2387 
2388 /*
2389  * Store ngtcp2 version info in this buffer.
2390  */
Curl_osslq_ver(char * p,size_t len)2391 void Curl_osslq_ver(char *p, size_t len)
2392 {
2393   const nghttp3_info *ht3 = nghttp3_version(0);
2394   (void)msnprintf(p, len, "nghttp3/%s", ht3->version_str);
2395 }
2396 
2397 #endif /* USE_OPENSSL_QUIC && USE_NGHTTP3 */
2398