quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

curl_osslq.c (73678B)


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