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