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