113498266Sopenharmony_ci/*************************************************************************** 213498266Sopenharmony_ci * _ _ ____ _ 313498266Sopenharmony_ci * Project ___| | | | _ \| | 413498266Sopenharmony_ci * / __| | | | |_) | | 513498266Sopenharmony_ci * | (__| |_| | _ <| |___ 613498266Sopenharmony_ci * \___|\___/|_| \_\_____| 713498266Sopenharmony_ci * 813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 913498266Sopenharmony_ci * 1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which 1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms 1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html. 1313498266Sopenharmony_ci * 1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell 1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is 1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file. 1713498266Sopenharmony_ci * 1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 1913498266Sopenharmony_ci * KIND, either express or implied. 2013498266Sopenharmony_ci * 2113498266Sopenharmony_ci * SPDX-License-Identifier: curl 2213498266Sopenharmony_ci * 2313498266Sopenharmony_ci ***************************************************************************/ 2413498266Sopenharmony_ci 2513498266Sopenharmony_ci#include "curl_setup.h" 2613498266Sopenharmony_ci 2713498266Sopenharmony_ci#if defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3) 2813498266Sopenharmony_ci 2913498266Sopenharmony_ci#include <openssl/ssl.h> 3013498266Sopenharmony_ci#include <openssl/bio.h> 3113498266Sopenharmony_ci#include <openssl/err.h> 3213498266Sopenharmony_ci#include <nghttp3/nghttp3.h> 3313498266Sopenharmony_ci 3413498266Sopenharmony_ci#include "urldata.h" 3513498266Sopenharmony_ci#include "sendf.h" 3613498266Sopenharmony_ci#include "strdup.h" 3713498266Sopenharmony_ci#include "rand.h" 3813498266Sopenharmony_ci#include "multiif.h" 3913498266Sopenharmony_ci#include "strcase.h" 4013498266Sopenharmony_ci#include "cfilters.h" 4113498266Sopenharmony_ci#include "cf-socket.h" 4213498266Sopenharmony_ci#include "connect.h" 4313498266Sopenharmony_ci#include "progress.h" 4413498266Sopenharmony_ci#include "strerror.h" 4513498266Sopenharmony_ci#include "dynbuf.h" 4613498266Sopenharmony_ci#include "http1.h" 4713498266Sopenharmony_ci#include "select.h" 4813498266Sopenharmony_ci#include "inet_pton.h" 4913498266Sopenharmony_ci#include "vquic.h" 5013498266Sopenharmony_ci#include "vquic_int.h" 5113498266Sopenharmony_ci#include "vquic-tls.h" 5213498266Sopenharmony_ci#include "vtls/keylog.h" 5313498266Sopenharmony_ci#include "vtls/vtls.h" 5413498266Sopenharmony_ci#include "vtls/openssl.h" 5513498266Sopenharmony_ci#include "curl_osslq.h" 5613498266Sopenharmony_ci 5713498266Sopenharmony_ci#include "warnless.h" 5813498266Sopenharmony_ci 5913498266Sopenharmony_ci/* The last 3 #include files should be in this order */ 6013498266Sopenharmony_ci#include "curl_printf.h" 6113498266Sopenharmony_ci#include "curl_memory.h" 6213498266Sopenharmony_ci#include "memdebug.h" 6313498266Sopenharmony_ci 6413498266Sopenharmony_ci/* A stream window is the maximum amount we need to buffer for 6513498266Sopenharmony_ci * each active transfer. We use HTTP/3 flow control and only ACK 6613498266Sopenharmony_ci * when we take things out of the buffer. 6713498266Sopenharmony_ci * Chunk size is large enough to take a full DATA frame */ 6813498266Sopenharmony_ci#define H3_STREAM_WINDOW_SIZE (128 * 1024) 6913498266Sopenharmony_ci#define H3_STREAM_CHUNK_SIZE (16 * 1024) 7013498266Sopenharmony_ci/* The pool keeps spares around and half of a full stream windows 7113498266Sopenharmony_ci * seems good. More does not seem to improve performance. 7213498266Sopenharmony_ci * The benefit of the pool is that stream buffer to not keep 7313498266Sopenharmony_ci * spares. So memory consumption goes down when streams run empty, 7413498266Sopenharmony_ci * have a large upload done, etc. */ 7513498266Sopenharmony_ci#define H3_STREAM_POOL_SPARES \ 7613498266Sopenharmony_ci (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE ) / 2 7713498266Sopenharmony_ci/* Receive and Send max number of chunks just follows from the 7813498266Sopenharmony_ci * chunk size and window size */ 7913498266Sopenharmony_ci#define H3_STREAM_RECV_CHUNKS \ 8013498266Sopenharmony_ci (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE) 8113498266Sopenharmony_ci#define H3_STREAM_SEND_CHUNKS \ 8213498266Sopenharmony_ci (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE) 8313498266Sopenharmony_ci 8413498266Sopenharmony_ci#ifndef ARRAYSIZE 8513498266Sopenharmony_ci#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0])) 8613498266Sopenharmony_ci#endif 8713498266Sopenharmony_ci 8813498266Sopenharmony_ci#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) 8913498266Sopenharmony_citypedef uint32_t sslerr_t; 9013498266Sopenharmony_ci#else 9113498266Sopenharmony_citypedef unsigned long sslerr_t; 9213498266Sopenharmony_ci#endif 9313498266Sopenharmony_ci 9413498266Sopenharmony_ci 9513498266Sopenharmony_ci/* How to access `call_data` from a cf_osslq filter */ 9613498266Sopenharmony_ci#undef CF_CTX_CALL_DATA 9713498266Sopenharmony_ci#define CF_CTX_CALL_DATA(cf) \ 9813498266Sopenharmony_ci ((struct cf_osslq_ctx *)(cf)->ctx)->call_data 9913498266Sopenharmony_ci 10013498266Sopenharmony_cistatic CURLcode cf_progress_ingress(struct Curl_cfilter *cf, 10113498266Sopenharmony_ci struct Curl_easy *data); 10213498266Sopenharmony_ci 10313498266Sopenharmony_cistatic const char *SSL_ERROR_to_str(int err) 10413498266Sopenharmony_ci{ 10513498266Sopenharmony_ci switch(err) { 10613498266Sopenharmony_ci case SSL_ERROR_NONE: 10713498266Sopenharmony_ci return "SSL_ERROR_NONE"; 10813498266Sopenharmony_ci case SSL_ERROR_SSL: 10913498266Sopenharmony_ci return "SSL_ERROR_SSL"; 11013498266Sopenharmony_ci case SSL_ERROR_WANT_READ: 11113498266Sopenharmony_ci return "SSL_ERROR_WANT_READ"; 11213498266Sopenharmony_ci case SSL_ERROR_WANT_WRITE: 11313498266Sopenharmony_ci return "SSL_ERROR_WANT_WRITE"; 11413498266Sopenharmony_ci case SSL_ERROR_WANT_X509_LOOKUP: 11513498266Sopenharmony_ci return "SSL_ERROR_WANT_X509_LOOKUP"; 11613498266Sopenharmony_ci case SSL_ERROR_SYSCALL: 11713498266Sopenharmony_ci return "SSL_ERROR_SYSCALL"; 11813498266Sopenharmony_ci case SSL_ERROR_ZERO_RETURN: 11913498266Sopenharmony_ci return "SSL_ERROR_ZERO_RETURN"; 12013498266Sopenharmony_ci case SSL_ERROR_WANT_CONNECT: 12113498266Sopenharmony_ci return "SSL_ERROR_WANT_CONNECT"; 12213498266Sopenharmony_ci case SSL_ERROR_WANT_ACCEPT: 12313498266Sopenharmony_ci return "SSL_ERROR_WANT_ACCEPT"; 12413498266Sopenharmony_ci#if defined(SSL_ERROR_WANT_ASYNC) 12513498266Sopenharmony_ci case SSL_ERROR_WANT_ASYNC: 12613498266Sopenharmony_ci return "SSL_ERROR_WANT_ASYNC"; 12713498266Sopenharmony_ci#endif 12813498266Sopenharmony_ci#if defined(SSL_ERROR_WANT_ASYNC_JOB) 12913498266Sopenharmony_ci case SSL_ERROR_WANT_ASYNC_JOB: 13013498266Sopenharmony_ci return "SSL_ERROR_WANT_ASYNC_JOB"; 13113498266Sopenharmony_ci#endif 13213498266Sopenharmony_ci#if defined(SSL_ERROR_WANT_EARLY) 13313498266Sopenharmony_ci case SSL_ERROR_WANT_EARLY: 13413498266Sopenharmony_ci return "SSL_ERROR_WANT_EARLY"; 13513498266Sopenharmony_ci#endif 13613498266Sopenharmony_ci default: 13713498266Sopenharmony_ci return "SSL_ERROR unknown"; 13813498266Sopenharmony_ci } 13913498266Sopenharmony_ci} 14013498266Sopenharmony_ci 14113498266Sopenharmony_ci/* Return error string for last OpenSSL error */ 14213498266Sopenharmony_cistatic char *ossl_strerror(unsigned long error, char *buf, size_t size) 14313498266Sopenharmony_ci{ 14413498266Sopenharmony_ci DEBUGASSERT(size); 14513498266Sopenharmony_ci *buf = '\0'; 14613498266Sopenharmony_ci 14713498266Sopenharmony_ci#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) 14813498266Sopenharmony_ci ERR_error_string_n((uint32_t)error, buf, size); 14913498266Sopenharmony_ci#else 15013498266Sopenharmony_ci ERR_error_string_n(error, buf, size); 15113498266Sopenharmony_ci#endif 15213498266Sopenharmony_ci 15313498266Sopenharmony_ci if(!*buf) { 15413498266Sopenharmony_ci const char *msg = error ? "Unknown error" : "No error"; 15513498266Sopenharmony_ci if(strlen(msg) < size) 15613498266Sopenharmony_ci strcpy(buf, msg); 15713498266Sopenharmony_ci } 15813498266Sopenharmony_ci 15913498266Sopenharmony_ci return buf; 16013498266Sopenharmony_ci} 16113498266Sopenharmony_ci 16213498266Sopenharmony_cistatic CURLcode make_bio_addr(BIO_ADDR **pbio_addr, 16313498266Sopenharmony_ci const struct Curl_sockaddr_ex *addr) 16413498266Sopenharmony_ci{ 16513498266Sopenharmony_ci BIO_ADDR *ba; 16613498266Sopenharmony_ci CURLcode result = CURLE_FAILED_INIT; 16713498266Sopenharmony_ci 16813498266Sopenharmony_ci ba = BIO_ADDR_new(); 16913498266Sopenharmony_ci if(!ba) { 17013498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 17113498266Sopenharmony_ci goto out; 17213498266Sopenharmony_ci } 17313498266Sopenharmony_ci 17413498266Sopenharmony_ci switch(addr->family) { 17513498266Sopenharmony_ci case AF_INET: { 17613498266Sopenharmony_ci struct sockaddr_in * const sin = 17713498266Sopenharmony_ci (struct sockaddr_in * const)(void *)&addr->sa_addr; 17813498266Sopenharmony_ci if(!BIO_ADDR_rawmake(ba, AF_INET, &sin->sin_addr, 17913498266Sopenharmony_ci sizeof(sin->sin_addr), sin->sin_port)) { 18013498266Sopenharmony_ci goto out; 18113498266Sopenharmony_ci } 18213498266Sopenharmony_ci result = CURLE_OK; 18313498266Sopenharmony_ci break; 18413498266Sopenharmony_ci } 18513498266Sopenharmony_ci#ifdef ENABLE_IPV6 18613498266Sopenharmony_ci case AF_INET6: { 18713498266Sopenharmony_ci struct sockaddr_in6 * const sin = 18813498266Sopenharmony_ci (struct sockaddr_in6 * const)(void *)&addr->sa_addr; 18913498266Sopenharmony_ci if(!BIO_ADDR_rawmake(ba, AF_INET6, &sin->sin6_addr, 19013498266Sopenharmony_ci sizeof(sin->sin6_addr), sin->sin6_port)) { 19113498266Sopenharmony_ci } 19213498266Sopenharmony_ci result = CURLE_OK; 19313498266Sopenharmony_ci break; 19413498266Sopenharmony_ci } 19513498266Sopenharmony_ci#endif /* ENABLE_IPV6 */ 19613498266Sopenharmony_ci default: 19713498266Sopenharmony_ci /* sunsupported */ 19813498266Sopenharmony_ci DEBUGASSERT(0); 19913498266Sopenharmony_ci break; 20013498266Sopenharmony_ci } 20113498266Sopenharmony_ci 20213498266Sopenharmony_ciout: 20313498266Sopenharmony_ci if(result && ba) { 20413498266Sopenharmony_ci BIO_ADDR_free(ba); 20513498266Sopenharmony_ci ba = NULL; 20613498266Sopenharmony_ci } 20713498266Sopenharmony_ci *pbio_addr = ba; 20813498266Sopenharmony_ci return result; 20913498266Sopenharmony_ci} 21013498266Sopenharmony_ci 21113498266Sopenharmony_ci/* QUIC stream (not necessarily H3) */ 21213498266Sopenharmony_cistruct cf_osslq_stream { 21313498266Sopenharmony_ci int64_t id; 21413498266Sopenharmony_ci SSL *ssl; 21513498266Sopenharmony_ci struct bufq recvbuf; /* QUIC war data recv buffer */ 21613498266Sopenharmony_ci BIT(recvd_eos); 21713498266Sopenharmony_ci BIT(closed); 21813498266Sopenharmony_ci BIT(reset); 21913498266Sopenharmony_ci BIT(send_blocked); 22013498266Sopenharmony_ci}; 22113498266Sopenharmony_ci 22213498266Sopenharmony_cistatic CURLcode cf_osslq_stream_open(struct cf_osslq_stream *s, 22313498266Sopenharmony_ci SSL *conn, 22413498266Sopenharmony_ci uint64_t flags, 22513498266Sopenharmony_ci struct bufc_pool *bufcp, 22613498266Sopenharmony_ci void *user_data) 22713498266Sopenharmony_ci{ 22813498266Sopenharmony_ci DEBUGASSERT(!s->ssl); 22913498266Sopenharmony_ci Curl_bufq_initp(&s->recvbuf, bufcp, 1, BUFQ_OPT_NONE); 23013498266Sopenharmony_ci s->ssl = SSL_new_stream(conn, flags); 23113498266Sopenharmony_ci if(!s->ssl) { 23213498266Sopenharmony_ci return CURLE_FAILED_INIT; 23313498266Sopenharmony_ci } 23413498266Sopenharmony_ci s->id = SSL_get_stream_id(s->ssl); 23513498266Sopenharmony_ci SSL_set_app_data(s->ssl, user_data); 23613498266Sopenharmony_ci return CURLE_OK; 23713498266Sopenharmony_ci} 23813498266Sopenharmony_ci 23913498266Sopenharmony_cistatic void cf_osslq_stream_cleanup(struct cf_osslq_stream *s) 24013498266Sopenharmony_ci{ 24113498266Sopenharmony_ci if(s->ssl) { 24213498266Sopenharmony_ci SSL_set_app_data(s->ssl, NULL); 24313498266Sopenharmony_ci SSL_free(s->ssl); 24413498266Sopenharmony_ci } 24513498266Sopenharmony_ci Curl_bufq_free(&s->recvbuf); 24613498266Sopenharmony_ci memset(s, 0, sizeof(*s)); 24713498266Sopenharmony_ci} 24813498266Sopenharmony_ci 24913498266Sopenharmony_cistatic void cf_osslq_stream_close(struct cf_osslq_stream *s) 25013498266Sopenharmony_ci{ 25113498266Sopenharmony_ci if(s->ssl) { 25213498266Sopenharmony_ci SSL_free(s->ssl); 25313498266Sopenharmony_ci s->ssl = NULL; 25413498266Sopenharmony_ci } 25513498266Sopenharmony_ci} 25613498266Sopenharmony_ci 25713498266Sopenharmony_cistruct cf_osslq_h3conn { 25813498266Sopenharmony_ci nghttp3_conn *conn; 25913498266Sopenharmony_ci nghttp3_settings settings; 26013498266Sopenharmony_ci struct cf_osslq_stream s_ctrl; 26113498266Sopenharmony_ci struct cf_osslq_stream s_qpack_enc; 26213498266Sopenharmony_ci struct cf_osslq_stream s_qpack_dec; 26313498266Sopenharmony_ci struct cf_osslq_stream remote_ctrl[3]; /* uni streams opened by the peer */ 26413498266Sopenharmony_ci size_t remote_ctrl_n; /* number of peer streams opened */ 26513498266Sopenharmony_ci}; 26613498266Sopenharmony_ci 26713498266Sopenharmony_cistatic void cf_osslq_h3conn_cleanup(struct cf_osslq_h3conn *h3) 26813498266Sopenharmony_ci{ 26913498266Sopenharmony_ci size_t i; 27013498266Sopenharmony_ci 27113498266Sopenharmony_ci if(h3->conn) 27213498266Sopenharmony_ci nghttp3_conn_del(h3->conn); 27313498266Sopenharmony_ci cf_osslq_stream_cleanup(&h3->s_ctrl); 27413498266Sopenharmony_ci cf_osslq_stream_cleanup(&h3->s_qpack_enc); 27513498266Sopenharmony_ci cf_osslq_stream_cleanup(&h3->s_qpack_dec); 27613498266Sopenharmony_ci for(i = 0; i < h3->remote_ctrl_n; ++i) { 27713498266Sopenharmony_ci cf_osslq_stream_cleanup(&h3->remote_ctrl[i]); 27813498266Sopenharmony_ci } 27913498266Sopenharmony_ci} 28013498266Sopenharmony_ci 28113498266Sopenharmony_cistruct cf_osslq_ctx { 28213498266Sopenharmony_ci struct cf_quic_ctx q; 28313498266Sopenharmony_ci struct ssl_peer peer; 28413498266Sopenharmony_ci struct quic_tls_ctx tls; 28513498266Sopenharmony_ci struct cf_call_data call_data; 28613498266Sopenharmony_ci struct cf_osslq_h3conn h3; 28713498266Sopenharmony_ci struct curltime started_at; /* time the current attempt started */ 28813498266Sopenharmony_ci struct curltime handshake_at; /* time connect handshake finished */ 28913498266Sopenharmony_ci struct curltime first_byte_at; /* when first byte was recvd */ 29013498266Sopenharmony_ci struct curltime reconnect_at; /* time the next attempt should start */ 29113498266Sopenharmony_ci struct bufc_pool stream_bufcp; /* chunk pool for streams */ 29213498266Sopenharmony_ci size_t max_stream_window; /* max flow window for one stream */ 29313498266Sopenharmony_ci uint64_t max_idle_ms; /* max idle time for QUIC connection */ 29413498266Sopenharmony_ci BIT(got_first_byte); /* if first byte was received */ 29513498266Sopenharmony_ci#ifdef USE_OPENSSL 29613498266Sopenharmony_ci BIT(x509_store_setup); /* if x509 store has been set up */ 29713498266Sopenharmony_ci BIT(protocol_shutdown); /* QUIC connection is shut down */ 29813498266Sopenharmony_ci#endif 29913498266Sopenharmony_ci}; 30013498266Sopenharmony_ci 30113498266Sopenharmony_cistatic void cf_osslq_ctx_clear(struct cf_osslq_ctx *ctx) 30213498266Sopenharmony_ci{ 30313498266Sopenharmony_ci struct cf_call_data save = ctx->call_data; 30413498266Sopenharmony_ci 30513498266Sopenharmony_ci cf_osslq_h3conn_cleanup(&ctx->h3); 30613498266Sopenharmony_ci Curl_vquic_tls_cleanup(&ctx->tls); 30713498266Sopenharmony_ci vquic_ctx_free(&ctx->q); 30813498266Sopenharmony_ci Curl_bufcp_free(&ctx->stream_bufcp); 30913498266Sopenharmony_ci Curl_ssl_peer_cleanup(&ctx->peer); 31013498266Sopenharmony_ci 31113498266Sopenharmony_ci memset(ctx, 0, sizeof(*ctx)); 31213498266Sopenharmony_ci ctx->call_data = save; 31313498266Sopenharmony_ci} 31413498266Sopenharmony_ci 31513498266Sopenharmony_cistatic void cf_osslq_close(struct Curl_cfilter *cf, struct Curl_easy *data) 31613498266Sopenharmony_ci{ 31713498266Sopenharmony_ci struct cf_osslq_ctx *ctx = cf->ctx; 31813498266Sopenharmony_ci struct cf_call_data save; 31913498266Sopenharmony_ci 32013498266Sopenharmony_ci CF_DATA_SAVE(save, cf, data); 32113498266Sopenharmony_ci if(ctx && ctx->tls.ssl) { 32213498266Sopenharmony_ci /* TODO: send connection close */ 32313498266Sopenharmony_ci CURL_TRC_CF(data, cf, "cf_osslq_close()"); 32413498266Sopenharmony_ci cf_osslq_ctx_clear(ctx); 32513498266Sopenharmony_ci } 32613498266Sopenharmony_ci 32713498266Sopenharmony_ci cf->connected = FALSE; 32813498266Sopenharmony_ci CF_DATA_RESTORE(cf, save); 32913498266Sopenharmony_ci} 33013498266Sopenharmony_ci 33113498266Sopenharmony_cistatic void cf_osslq_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) 33213498266Sopenharmony_ci{ 33313498266Sopenharmony_ci struct cf_osslq_ctx *ctx = cf->ctx; 33413498266Sopenharmony_ci struct cf_call_data save; 33513498266Sopenharmony_ci 33613498266Sopenharmony_ci CF_DATA_SAVE(save, cf, data); 33713498266Sopenharmony_ci CURL_TRC_CF(data, cf, "destroy"); 33813498266Sopenharmony_ci if(ctx) { 33913498266Sopenharmony_ci CURL_TRC_CF(data, cf, "cf_osslq_destroy()"); 34013498266Sopenharmony_ci cf_osslq_ctx_clear(ctx); 34113498266Sopenharmony_ci free(ctx); 34213498266Sopenharmony_ci } 34313498266Sopenharmony_ci cf->ctx = NULL; 34413498266Sopenharmony_ci /* No CF_DATA_RESTORE(cf, save) possible */ 34513498266Sopenharmony_ci (void)save; 34613498266Sopenharmony_ci} 34713498266Sopenharmony_ci 34813498266Sopenharmony_cistatic CURLcode cf_osslq_h3conn_add_stream(struct cf_osslq_h3conn *h3, 34913498266Sopenharmony_ci SSL *stream_ssl, 35013498266Sopenharmony_ci struct Curl_cfilter *cf, 35113498266Sopenharmony_ci struct Curl_easy *data) 35213498266Sopenharmony_ci{ 35313498266Sopenharmony_ci struct cf_osslq_ctx *ctx = cf->ctx; 35413498266Sopenharmony_ci int64_t stream_id = SSL_get_stream_id(stream_ssl); 35513498266Sopenharmony_ci 35613498266Sopenharmony_ci if(h3->remote_ctrl_n >= ARRAYSIZE(h3->remote_ctrl)) { 35713498266Sopenharmony_ci /* rejected, we are full */ 35813498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] rejecting additional remote stream", 35913498266Sopenharmony_ci stream_id); 36013498266Sopenharmony_ci SSL_free(stream_ssl); 36113498266Sopenharmony_ci return CURLE_FAILED_INIT; 36213498266Sopenharmony_ci } 36313498266Sopenharmony_ci switch(SSL_get_stream_type(stream_ssl)) { 36413498266Sopenharmony_ci case SSL_STREAM_TYPE_READ: { 36513498266Sopenharmony_ci struct cf_osslq_stream *nstream = &h3->remote_ctrl[h3->remote_ctrl_n++]; 36613498266Sopenharmony_ci nstream->id = stream_id; 36713498266Sopenharmony_ci nstream->ssl = stream_ssl; 36813498266Sopenharmony_ci Curl_bufq_initp(&nstream->recvbuf, &ctx->stream_bufcp, 1, BUFQ_OPT_NONE); 36913498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] accepted new remote uni stream", 37013498266Sopenharmony_ci stream_id); 37113498266Sopenharmony_ci break; 37213498266Sopenharmony_ci } 37313498266Sopenharmony_ci default: 37413498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] rejecting remote non-uni-read" 37513498266Sopenharmony_ci " stream", stream_id); 37613498266Sopenharmony_ci SSL_free(stream_ssl); 37713498266Sopenharmony_ci return CURLE_FAILED_INIT; 37813498266Sopenharmony_ci } 37913498266Sopenharmony_ci return CURLE_OK; 38013498266Sopenharmony_ci 38113498266Sopenharmony_ci} 38213498266Sopenharmony_ci 38313498266Sopenharmony_cistatic CURLcode cf_osslq_ssl_err(struct Curl_cfilter *cf, 38413498266Sopenharmony_ci struct Curl_easy *data, 38513498266Sopenharmony_ci int detail, CURLcode def_result) 38613498266Sopenharmony_ci{ 38713498266Sopenharmony_ci struct cf_osslq_ctx *ctx = cf->ctx; 38813498266Sopenharmony_ci CURLcode result = def_result; 38913498266Sopenharmony_ci sslerr_t errdetail; 39013498266Sopenharmony_ci char ebuf[256] = "unknown"; 39113498266Sopenharmony_ci const char *err_descr = ebuf; 39213498266Sopenharmony_ci long lerr; 39313498266Sopenharmony_ci int lib; 39413498266Sopenharmony_ci int reason; 39513498266Sopenharmony_ci struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); 39613498266Sopenharmony_ci 39713498266Sopenharmony_ci errdetail = ERR_get_error(); 39813498266Sopenharmony_ci lib = ERR_GET_LIB(errdetail); 39913498266Sopenharmony_ci reason = ERR_GET_REASON(errdetail); 40013498266Sopenharmony_ci 40113498266Sopenharmony_ci if((lib == ERR_LIB_SSL) && 40213498266Sopenharmony_ci ((reason == SSL_R_CERTIFICATE_VERIFY_FAILED) || 40313498266Sopenharmony_ci (reason == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED))) { 40413498266Sopenharmony_ci result = CURLE_PEER_FAILED_VERIFICATION; 40513498266Sopenharmony_ci 40613498266Sopenharmony_ci lerr = SSL_get_verify_result(ctx->tls.ssl); 40713498266Sopenharmony_ci if(lerr != X509_V_OK) { 40813498266Sopenharmony_ci ssl_config->certverifyresult = lerr; 40913498266Sopenharmony_ci msnprintf(ebuf, sizeof(ebuf), 41013498266Sopenharmony_ci "SSL certificate problem: %s", 41113498266Sopenharmony_ci X509_verify_cert_error_string(lerr)); 41213498266Sopenharmony_ci } 41313498266Sopenharmony_ci else 41413498266Sopenharmony_ci err_descr = "SSL certificate verification failed"; 41513498266Sopenharmony_ci } 41613498266Sopenharmony_ci#if defined(SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED) 41713498266Sopenharmony_ci /* SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED is only available on 41813498266Sopenharmony_ci OpenSSL version above v1.1.1, not LibreSSL, BoringSSL, or AWS-LC */ 41913498266Sopenharmony_ci else if((lib == ERR_LIB_SSL) && 42013498266Sopenharmony_ci (reason == SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED)) { 42113498266Sopenharmony_ci /* If client certificate is required, communicate the 42213498266Sopenharmony_ci error to client */ 42313498266Sopenharmony_ci result = CURLE_SSL_CLIENTCERT; 42413498266Sopenharmony_ci ossl_strerror(errdetail, ebuf, sizeof(ebuf)); 42513498266Sopenharmony_ci } 42613498266Sopenharmony_ci#endif 42713498266Sopenharmony_ci else if((lib == ERR_LIB_SSL) && (reason == SSL_R_PROTOCOL_IS_SHUTDOWN)) { 42813498266Sopenharmony_ci ctx->protocol_shutdown = TRUE; 42913498266Sopenharmony_ci err_descr = "QUIC connectin has been shut down"; 43013498266Sopenharmony_ci result = def_result; 43113498266Sopenharmony_ci } 43213498266Sopenharmony_ci else { 43313498266Sopenharmony_ci result = def_result; 43413498266Sopenharmony_ci ossl_strerror(errdetail, ebuf, sizeof(ebuf)); 43513498266Sopenharmony_ci } 43613498266Sopenharmony_ci 43713498266Sopenharmony_ci /* detail is already set to the SSL error above */ 43813498266Sopenharmony_ci 43913498266Sopenharmony_ci /* If we e.g. use SSLv2 request-method and the server doesn't like us 44013498266Sopenharmony_ci * (RST connection, etc.), OpenSSL gives no explanation whatsoever and 44113498266Sopenharmony_ci * the SO_ERROR is also lost. 44213498266Sopenharmony_ci */ 44313498266Sopenharmony_ci if(CURLE_SSL_CONNECT_ERROR == result && errdetail == 0) { 44413498266Sopenharmony_ci char extramsg[80]=""; 44513498266Sopenharmony_ci int sockerr = SOCKERRNO; 44613498266Sopenharmony_ci const char *r_ip = NULL; 44713498266Sopenharmony_ci int r_port = 0; 44813498266Sopenharmony_ci 44913498266Sopenharmony_ci Curl_cf_socket_peek(cf->next, data, NULL, NULL, 45013498266Sopenharmony_ci &r_ip, &r_port, NULL, NULL); 45113498266Sopenharmony_ci if(sockerr && detail == SSL_ERROR_SYSCALL) 45213498266Sopenharmony_ci Curl_strerror(sockerr, extramsg, sizeof(extramsg)); 45313498266Sopenharmony_ci failf(data, "QUIC connect: %s in connection to %s:%d (%s)", 45413498266Sopenharmony_ci extramsg[0] ? extramsg : SSL_ERROR_to_str(detail), 45513498266Sopenharmony_ci ctx->peer.dispname, r_port, r_ip); 45613498266Sopenharmony_ci } 45713498266Sopenharmony_ci else { 45813498266Sopenharmony_ci /* Could be a CERT problem */ 45913498266Sopenharmony_ci failf(data, "%s", err_descr); 46013498266Sopenharmony_ci } 46113498266Sopenharmony_ci return result; 46213498266Sopenharmony_ci} 46313498266Sopenharmony_ci 46413498266Sopenharmony_cistatic CURLcode cf_osslq_verify_peer(struct Curl_cfilter *cf, 46513498266Sopenharmony_ci struct Curl_easy *data) 46613498266Sopenharmony_ci{ 46713498266Sopenharmony_ci struct cf_osslq_ctx *ctx = cf->ctx; 46813498266Sopenharmony_ci 46913498266Sopenharmony_ci cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ 47013498266Sopenharmony_ci cf->conn->httpversion = 30; 47113498266Sopenharmony_ci cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX; 47213498266Sopenharmony_ci 47313498266Sopenharmony_ci return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer); 47413498266Sopenharmony_ci} 47513498266Sopenharmony_ci 47613498266Sopenharmony_ci/** 47713498266Sopenharmony_ci * All about the H3 internals of a stream 47813498266Sopenharmony_ci */ 47913498266Sopenharmony_cistruct h3_stream_ctx { 48013498266Sopenharmony_ci struct cf_osslq_stream s; 48113498266Sopenharmony_ci struct bufq sendbuf; /* h3 request body */ 48213498266Sopenharmony_ci struct bufq recvbuf; /* h3 response body */ 48313498266Sopenharmony_ci struct h1_req_parser h1; /* h1 request parsing */ 48413498266Sopenharmony_ci size_t sendbuf_len_in_flight; /* sendbuf amount "in flight" */ 48513498266Sopenharmony_ci size_t upload_blocked_len; /* the amount written last and EGAINed */ 48613498266Sopenharmony_ci size_t recv_buf_nonflow; /* buffered bytes, not counting for flow control */ 48713498266Sopenharmony_ci uint64_t error3; /* HTTP/3 stream error code */ 48813498266Sopenharmony_ci curl_off_t upload_left; /* number of request bytes left to upload */ 48913498266Sopenharmony_ci curl_off_t download_recvd; /* number of response DATA bytes received */ 49013498266Sopenharmony_ci int status_code; /* HTTP status code */ 49113498266Sopenharmony_ci bool resp_hds_complete; /* we have a complete, final response */ 49213498266Sopenharmony_ci bool closed; /* TRUE on stream close */ 49313498266Sopenharmony_ci bool reset; /* TRUE on stream reset */ 49413498266Sopenharmony_ci bool send_closed; /* stream is local closed */ 49513498266Sopenharmony_ci BIT(quic_flow_blocked); /* stream is blocked by QUIC flow control */ 49613498266Sopenharmony_ci}; 49713498266Sopenharmony_ci 49813498266Sopenharmony_ci#define H3_STREAM_CTX(d) ((struct h3_stream_ctx *)(((d) && (d)->req.p.http)? \ 49913498266Sopenharmony_ci ((struct HTTP *)(d)->req.p.http)->h3_ctx \ 50013498266Sopenharmony_ci : NULL)) 50113498266Sopenharmony_ci#define H3_STREAM_LCTX(d) ((struct HTTP *)(d)->req.p.http)->h3_ctx 50213498266Sopenharmony_ci#define H3_STREAM_ID(d) (H3_STREAM_CTX(d)? \ 50313498266Sopenharmony_ci H3_STREAM_CTX(d)->s.id : -2) 50413498266Sopenharmony_ci 50513498266Sopenharmony_cistatic CURLcode h3_data_setup(struct Curl_cfilter *cf, 50613498266Sopenharmony_ci struct Curl_easy *data) 50713498266Sopenharmony_ci{ 50813498266Sopenharmony_ci struct cf_osslq_ctx *ctx = cf->ctx; 50913498266Sopenharmony_ci struct h3_stream_ctx *stream = H3_STREAM_CTX(data); 51013498266Sopenharmony_ci 51113498266Sopenharmony_ci if(!data || !data->req.p.http) { 51213498266Sopenharmony_ci failf(data, "initialization failure, transfer not http initialized"); 51313498266Sopenharmony_ci return CURLE_FAILED_INIT; 51413498266Sopenharmony_ci } 51513498266Sopenharmony_ci 51613498266Sopenharmony_ci if(stream) 51713498266Sopenharmony_ci return CURLE_OK; 51813498266Sopenharmony_ci 51913498266Sopenharmony_ci stream = calloc(1, sizeof(*stream)); 52013498266Sopenharmony_ci if(!stream) 52113498266Sopenharmony_ci return CURLE_OUT_OF_MEMORY; 52213498266Sopenharmony_ci 52313498266Sopenharmony_ci stream->s.id = -1; 52413498266Sopenharmony_ci /* on send, we control how much we put into the buffer */ 52513498266Sopenharmony_ci Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp, 52613498266Sopenharmony_ci H3_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE); 52713498266Sopenharmony_ci stream->sendbuf_len_in_flight = 0; 52813498266Sopenharmony_ci /* on recv, we need a flexible buffer limit since we also write 52913498266Sopenharmony_ci * headers to it that are not counted against the nghttp3 flow limits. */ 53013498266Sopenharmony_ci Curl_bufq_initp(&stream->recvbuf, &ctx->stream_bufcp, 53113498266Sopenharmony_ci H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); 53213498266Sopenharmony_ci stream->recv_buf_nonflow = 0; 53313498266Sopenharmony_ci Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); 53413498266Sopenharmony_ci 53513498266Sopenharmony_ci H3_STREAM_LCTX(data) = stream; 53613498266Sopenharmony_ci return CURLE_OK; 53713498266Sopenharmony_ci} 53813498266Sopenharmony_ci 53913498266Sopenharmony_cistatic void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) 54013498266Sopenharmony_ci{ 54113498266Sopenharmony_ci struct cf_osslq_ctx *ctx = cf->ctx; 54213498266Sopenharmony_ci struct h3_stream_ctx *stream = H3_STREAM_CTX(data); 54313498266Sopenharmony_ci 54413498266Sopenharmony_ci (void)cf; 54513498266Sopenharmony_ci if(stream) { 54613498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%"PRId64"] easy handle is done", stream->s.id); 54713498266Sopenharmony_ci if(ctx->h3.conn && !stream->closed) { 54813498266Sopenharmony_ci nghttp3_conn_shutdown_stream_read(ctx->h3.conn, stream->s.id); 54913498266Sopenharmony_ci nghttp3_conn_close_stream(ctx->h3.conn, stream->s.id, 55013498266Sopenharmony_ci NGHTTP3_H3_REQUEST_CANCELLED); 55113498266Sopenharmony_ci nghttp3_conn_set_stream_user_data(ctx->h3.conn, stream->s.id, NULL); 55213498266Sopenharmony_ci stream->closed = TRUE; 55313498266Sopenharmony_ci } 55413498266Sopenharmony_ci 55513498266Sopenharmony_ci cf_osslq_stream_cleanup(&stream->s); 55613498266Sopenharmony_ci Curl_bufq_free(&stream->sendbuf); 55713498266Sopenharmony_ci Curl_bufq_free(&stream->recvbuf); 55813498266Sopenharmony_ci Curl_h1_req_parse_free(&stream->h1); 55913498266Sopenharmony_ci free(stream); 56013498266Sopenharmony_ci H3_STREAM_LCTX(data) = NULL; 56113498266Sopenharmony_ci } 56213498266Sopenharmony_ci} 56313498266Sopenharmony_ci 56413498266Sopenharmony_cistatic struct cf_osslq_stream *cf_osslq_get_qstream(struct Curl_cfilter *cf, 56513498266Sopenharmony_ci struct Curl_easy *data, 56613498266Sopenharmony_ci int64_t stream_id) 56713498266Sopenharmony_ci{ 56813498266Sopenharmony_ci struct cf_osslq_ctx *ctx = cf->ctx; 56913498266Sopenharmony_ci struct h3_stream_ctx *stream = H3_STREAM_CTX(data); 57013498266Sopenharmony_ci struct Curl_easy *sdata; 57113498266Sopenharmony_ci 57213498266Sopenharmony_ci if(stream && stream->s.id == stream_id) { 57313498266Sopenharmony_ci return &stream->s; 57413498266Sopenharmony_ci } 57513498266Sopenharmony_ci else if(ctx->h3.s_ctrl.id == stream_id) { 57613498266Sopenharmony_ci return &ctx->h3.s_ctrl; 57713498266Sopenharmony_ci } 57813498266Sopenharmony_ci else if(ctx->h3.s_qpack_enc.id == stream_id) { 57913498266Sopenharmony_ci return &ctx->h3.s_qpack_enc; 58013498266Sopenharmony_ci } 58113498266Sopenharmony_ci else if(ctx->h3.s_qpack_dec.id == stream_id) { 58213498266Sopenharmony_ci return &ctx->h3.s_qpack_dec; 58313498266Sopenharmony_ci } 58413498266Sopenharmony_ci else { 58513498266Sopenharmony_ci DEBUGASSERT(data->multi); 58613498266Sopenharmony_ci for(sdata = data->multi->easyp; sdata; sdata = sdata->next) { 58713498266Sopenharmony_ci if((sdata->conn == data->conn) && H3_STREAM_ID(sdata) == stream_id) { 58813498266Sopenharmony_ci stream = H3_STREAM_CTX(sdata); 58913498266Sopenharmony_ci return stream? &stream->s : NULL; 59013498266Sopenharmony_ci } 59113498266Sopenharmony_ci } 59213498266Sopenharmony_ci } 59313498266Sopenharmony_ci return NULL; 59413498266Sopenharmony_ci} 59513498266Sopenharmony_ci 59613498266Sopenharmony_cistatic void h3_drain_stream(struct Curl_cfilter *cf, 59713498266Sopenharmony_ci struct Curl_easy *data) 59813498266Sopenharmony_ci{ 59913498266Sopenharmony_ci struct h3_stream_ctx *stream = H3_STREAM_CTX(data); 60013498266Sopenharmony_ci unsigned char bits; 60113498266Sopenharmony_ci 60213498266Sopenharmony_ci (void)cf; 60313498266Sopenharmony_ci bits = CURL_CSELECT_IN; 60413498266Sopenharmony_ci if(stream && stream->upload_left && !stream->send_closed) 60513498266Sopenharmony_ci bits |= CURL_CSELECT_OUT; 60613498266Sopenharmony_ci if(data->state.select_bits != bits) { 60713498266Sopenharmony_ci data->state.select_bits = bits; 60813498266Sopenharmony_ci Curl_expire(data, 0, EXPIRE_RUN_NOW); 60913498266Sopenharmony_ci } 61013498266Sopenharmony_ci} 61113498266Sopenharmony_ci 61213498266Sopenharmony_cistatic CURLcode h3_data_pause(struct Curl_cfilter *cf, 61313498266Sopenharmony_ci struct Curl_easy *data, 61413498266Sopenharmony_ci bool pause) 61513498266Sopenharmony_ci{ 61613498266Sopenharmony_ci if(!pause) { 61713498266Sopenharmony_ci /* unpaused. make it run again right away */ 61813498266Sopenharmony_ci h3_drain_stream(cf, data); 61913498266Sopenharmony_ci Curl_expire(data, 0, EXPIRE_RUN_NOW); 62013498266Sopenharmony_ci } 62113498266Sopenharmony_ci return CURLE_OK; 62213498266Sopenharmony_ci} 62313498266Sopenharmony_ci 62413498266Sopenharmony_cistatic int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id, 62513498266Sopenharmony_ci uint64_t app_error_code, void *user_data, 62613498266Sopenharmony_ci void *stream_user_data) 62713498266Sopenharmony_ci{ 62813498266Sopenharmony_ci struct Curl_cfilter *cf = user_data; 62913498266Sopenharmony_ci struct Curl_easy *data = stream_user_data; 63013498266Sopenharmony_ci struct h3_stream_ctx *stream = H3_STREAM_CTX(data); 63113498266Sopenharmony_ci (void)conn; 63213498266Sopenharmony_ci (void)stream_id; 63313498266Sopenharmony_ci 63413498266Sopenharmony_ci /* we might be called by nghttp3 after we already cleaned up */ 63513498266Sopenharmony_ci if(!stream) 63613498266Sopenharmony_ci return 0; 63713498266Sopenharmony_ci 63813498266Sopenharmony_ci stream->closed = TRUE; 63913498266Sopenharmony_ci stream->error3 = app_error_code; 64013498266Sopenharmony_ci if(stream->error3 != NGHTTP3_H3_NO_ERROR) { 64113498266Sopenharmony_ci stream->reset = TRUE; 64213498266Sopenharmony_ci stream->send_closed = TRUE; 64313498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] RESET: error %" PRId64, 64413498266Sopenharmony_ci stream->s.id, stream->error3); 64513498266Sopenharmony_ci } 64613498266Sopenharmony_ci else { 64713498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] CLOSED", stream->s.id); 64813498266Sopenharmony_ci } 64913498266Sopenharmony_ci h3_drain_stream(cf, data); 65013498266Sopenharmony_ci return 0; 65113498266Sopenharmony_ci} 65213498266Sopenharmony_ci 65313498266Sopenharmony_ci/* 65413498266Sopenharmony_ci * write_resp_raw() copies response data in raw format to the `data`'s 65513498266Sopenharmony_ci * receive buffer. If not enough space is available, it appends to the 65613498266Sopenharmony_ci * `data`'s overflow buffer. 65713498266Sopenharmony_ci */ 65813498266Sopenharmony_cistatic CURLcode write_resp_raw(struct Curl_cfilter *cf, 65913498266Sopenharmony_ci struct Curl_easy *data, 66013498266Sopenharmony_ci const void *mem, size_t memlen, 66113498266Sopenharmony_ci bool flow) 66213498266Sopenharmony_ci{ 66313498266Sopenharmony_ci struct h3_stream_ctx *stream = H3_STREAM_CTX(data); 66413498266Sopenharmony_ci CURLcode result = CURLE_OK; 66513498266Sopenharmony_ci ssize_t nwritten; 66613498266Sopenharmony_ci 66713498266Sopenharmony_ci (void)cf; 66813498266Sopenharmony_ci if(!stream) { 66913498266Sopenharmony_ci return CURLE_RECV_ERROR; 67013498266Sopenharmony_ci } 67113498266Sopenharmony_ci nwritten = Curl_bufq_write(&stream->recvbuf, mem, memlen, &result); 67213498266Sopenharmony_ci if(nwritten < 0) { 67313498266Sopenharmony_ci return result; 67413498266Sopenharmony_ci } 67513498266Sopenharmony_ci 67613498266Sopenharmony_ci if(!flow) 67713498266Sopenharmony_ci stream->recv_buf_nonflow += (size_t)nwritten; 67813498266Sopenharmony_ci 67913498266Sopenharmony_ci if((size_t)nwritten < memlen) { 68013498266Sopenharmony_ci /* This MUST not happen. Our recbuf is dimensioned to hold the 68113498266Sopenharmony_ci * full max_stream_window and then some for this very reason. */ 68213498266Sopenharmony_ci DEBUGASSERT(0); 68313498266Sopenharmony_ci return CURLE_RECV_ERROR; 68413498266Sopenharmony_ci } 68513498266Sopenharmony_ci return result; 68613498266Sopenharmony_ci} 68713498266Sopenharmony_ci 68813498266Sopenharmony_cistatic int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id, 68913498266Sopenharmony_ci const uint8_t *buf, size_t buflen, 69013498266Sopenharmony_ci void *user_data, void *stream_user_data) 69113498266Sopenharmony_ci{ 69213498266Sopenharmony_ci struct Curl_cfilter *cf = user_data; 69313498266Sopenharmony_ci struct Curl_easy *data = stream_user_data; 69413498266Sopenharmony_ci struct h3_stream_ctx *stream = H3_STREAM_CTX(data); 69513498266Sopenharmony_ci CURLcode result; 69613498266Sopenharmony_ci 69713498266Sopenharmony_ci (void)conn; 69813498266Sopenharmony_ci (void)stream3_id; 69913498266Sopenharmony_ci 70013498266Sopenharmony_ci if(!stream) 70113498266Sopenharmony_ci return NGHTTP3_ERR_CALLBACK_FAILURE; 70213498266Sopenharmony_ci 70313498266Sopenharmony_ci result = write_resp_raw(cf, data, buf, buflen, TRUE); 70413498266Sopenharmony_ci if(result) { 70513498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu, ERROR receiving %d", 70613498266Sopenharmony_ci stream->s.id, buflen, result); 70713498266Sopenharmony_ci return NGHTTP3_ERR_CALLBACK_FAILURE; 70813498266Sopenharmony_ci } 70913498266Sopenharmony_ci stream->download_recvd += (curl_off_t)buflen; 71013498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu, total=%zd", 71113498266Sopenharmony_ci stream->s.id, buflen, stream->download_recvd); 71213498266Sopenharmony_ci h3_drain_stream(cf, data); 71313498266Sopenharmony_ci return 0; 71413498266Sopenharmony_ci} 71513498266Sopenharmony_ci 71613498266Sopenharmony_cistatic int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream_id, 71713498266Sopenharmony_ci size_t consumed, void *user_data, 71813498266Sopenharmony_ci void *stream_user_data) 71913498266Sopenharmony_ci{ 72013498266Sopenharmony_ci struct Curl_cfilter *cf = user_data; 72113498266Sopenharmony_ci struct Curl_easy *data = stream_user_data; 72213498266Sopenharmony_ci struct h3_stream_ctx *stream = H3_STREAM_CTX(data); 72313498266Sopenharmony_ci 72413498266Sopenharmony_ci (void)conn; 72513498266Sopenharmony_ci (void)stream_id; 72613498266Sopenharmony_ci if(stream) 72713498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] deferred consume %zu bytes", 72813498266Sopenharmony_ci stream->s.id, consumed); 72913498266Sopenharmony_ci return 0; 73013498266Sopenharmony_ci} 73113498266Sopenharmony_ci 73213498266Sopenharmony_cistatic int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id, 73313498266Sopenharmony_ci int32_t token, nghttp3_rcbuf *name, 73413498266Sopenharmony_ci nghttp3_rcbuf *value, uint8_t flags, 73513498266Sopenharmony_ci void *user_data, void *stream_user_data) 73613498266Sopenharmony_ci{ 73713498266Sopenharmony_ci struct Curl_cfilter *cf = user_data; 73813498266Sopenharmony_ci nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name); 73913498266Sopenharmony_ci nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value); 74013498266Sopenharmony_ci struct Curl_easy *data = stream_user_data; 74113498266Sopenharmony_ci struct h3_stream_ctx *stream = H3_STREAM_CTX(data); 74213498266Sopenharmony_ci CURLcode result = CURLE_OK; 74313498266Sopenharmony_ci (void)conn; 74413498266Sopenharmony_ci (void)stream_id; 74513498266Sopenharmony_ci (void)token; 74613498266Sopenharmony_ci (void)flags; 74713498266Sopenharmony_ci (void)cf; 74813498266Sopenharmony_ci 74913498266Sopenharmony_ci /* we might have cleaned up this transfer already */ 75013498266Sopenharmony_ci if(!stream) 75113498266Sopenharmony_ci return 0; 75213498266Sopenharmony_ci 75313498266Sopenharmony_ci if(token == NGHTTP3_QPACK_TOKEN__STATUS) { 75413498266Sopenharmony_ci char line[14]; /* status line is always 13 characters long */ 75513498266Sopenharmony_ci size_t ncopy; 75613498266Sopenharmony_ci 75713498266Sopenharmony_ci result = Curl_http_decode_status(&stream->status_code, 75813498266Sopenharmony_ci (const char *)h3val.base, h3val.len); 75913498266Sopenharmony_ci if(result) 76013498266Sopenharmony_ci return -1; 76113498266Sopenharmony_ci ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n", 76213498266Sopenharmony_ci stream->status_code); 76313498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] status: %s", stream_id, line); 76413498266Sopenharmony_ci result = write_resp_raw(cf, data, line, ncopy, FALSE); 76513498266Sopenharmony_ci if(result) { 76613498266Sopenharmony_ci return -1; 76713498266Sopenharmony_ci } 76813498266Sopenharmony_ci } 76913498266Sopenharmony_ci else { 77013498266Sopenharmony_ci /* store as an HTTP1-style header */ 77113498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] header: %.*s: %.*s", 77213498266Sopenharmony_ci stream_id, (int)h3name.len, h3name.base, 77313498266Sopenharmony_ci (int)h3val.len, h3val.base); 77413498266Sopenharmony_ci result = write_resp_raw(cf, data, h3name.base, h3name.len, FALSE); 77513498266Sopenharmony_ci if(result) { 77613498266Sopenharmony_ci return -1; 77713498266Sopenharmony_ci } 77813498266Sopenharmony_ci result = write_resp_raw(cf, data, ": ", 2, FALSE); 77913498266Sopenharmony_ci if(result) { 78013498266Sopenharmony_ci return -1; 78113498266Sopenharmony_ci } 78213498266Sopenharmony_ci result = write_resp_raw(cf, data, h3val.base, h3val.len, FALSE); 78313498266Sopenharmony_ci if(result) { 78413498266Sopenharmony_ci return -1; 78513498266Sopenharmony_ci } 78613498266Sopenharmony_ci result = write_resp_raw(cf, data, "\r\n", 2, FALSE); 78713498266Sopenharmony_ci if(result) { 78813498266Sopenharmony_ci return -1; 78913498266Sopenharmony_ci } 79013498266Sopenharmony_ci } 79113498266Sopenharmony_ci return 0; 79213498266Sopenharmony_ci} 79313498266Sopenharmony_ci 79413498266Sopenharmony_cistatic int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id, 79513498266Sopenharmony_ci int fin, void *user_data, void *stream_user_data) 79613498266Sopenharmony_ci{ 79713498266Sopenharmony_ci struct Curl_cfilter *cf = user_data; 79813498266Sopenharmony_ci struct Curl_easy *data = stream_user_data; 79913498266Sopenharmony_ci struct h3_stream_ctx *stream = H3_STREAM_CTX(data); 80013498266Sopenharmony_ci CURLcode result = CURLE_OK; 80113498266Sopenharmony_ci (void)conn; 80213498266Sopenharmony_ci (void)stream_id; 80313498266Sopenharmony_ci (void)fin; 80413498266Sopenharmony_ci (void)cf; 80513498266Sopenharmony_ci 80613498266Sopenharmony_ci if(!stream) 80713498266Sopenharmony_ci return 0; 80813498266Sopenharmony_ci /* add a CRLF only if we've received some headers */ 80913498266Sopenharmony_ci result = write_resp_raw(cf, data, "\r\n", 2, FALSE); 81013498266Sopenharmony_ci if(result) { 81113498266Sopenharmony_ci return -1; 81213498266Sopenharmony_ci } 81313498266Sopenharmony_ci 81413498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] end_headers, status=%d", 81513498266Sopenharmony_ci stream_id, stream->status_code); 81613498266Sopenharmony_ci if(stream->status_code / 100 != 1) { 81713498266Sopenharmony_ci stream->resp_hds_complete = TRUE; 81813498266Sopenharmony_ci } 81913498266Sopenharmony_ci h3_drain_stream(cf, data); 82013498266Sopenharmony_ci return 0; 82113498266Sopenharmony_ci} 82213498266Sopenharmony_ci 82313498266Sopenharmony_cistatic int cb_h3_stop_sending(nghttp3_conn *conn, int64_t stream_id, 82413498266Sopenharmony_ci uint64_t app_error_code, void *user_data, 82513498266Sopenharmony_ci void *stream_user_data) 82613498266Sopenharmony_ci{ 82713498266Sopenharmony_ci struct Curl_cfilter *cf = user_data; 82813498266Sopenharmony_ci struct Curl_easy *data = stream_user_data; 82913498266Sopenharmony_ci struct h3_stream_ctx *stream = H3_STREAM_CTX(data); 83013498266Sopenharmony_ci (void)conn; 83113498266Sopenharmony_ci (void)app_error_code; 83213498266Sopenharmony_ci 83313498266Sopenharmony_ci if(!stream || !stream->s.ssl) 83413498266Sopenharmony_ci return 0; 83513498266Sopenharmony_ci 83613498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] stop_sending", stream_id); 83713498266Sopenharmony_ci cf_osslq_stream_close(&stream->s); 83813498266Sopenharmony_ci return 0; 83913498266Sopenharmony_ci} 84013498266Sopenharmony_ci 84113498266Sopenharmony_cistatic int cb_h3_reset_stream(nghttp3_conn *conn, int64_t stream_id, 84213498266Sopenharmony_ci uint64_t app_error_code, void *user_data, 84313498266Sopenharmony_ci void *stream_user_data) { 84413498266Sopenharmony_ci struct Curl_cfilter *cf = user_data; 84513498266Sopenharmony_ci struct Curl_easy *data = stream_user_data; 84613498266Sopenharmony_ci struct h3_stream_ctx *stream = H3_STREAM_CTX(data); 84713498266Sopenharmony_ci int rv; 84813498266Sopenharmony_ci (void)conn; 84913498266Sopenharmony_ci 85013498266Sopenharmony_ci if(stream && stream->s.ssl) { 85113498266Sopenharmony_ci SSL_STREAM_RESET_ARGS args = {0}; 85213498266Sopenharmony_ci args.quic_error_code = app_error_code; 85313498266Sopenharmony_ci rv = !SSL_stream_reset(stream->s.ssl, &args, sizeof(args)); 85413498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d", stream_id, rv); 85513498266Sopenharmony_ci if(!rv) { 85613498266Sopenharmony_ci return NGHTTP3_ERR_CALLBACK_FAILURE; 85713498266Sopenharmony_ci } 85813498266Sopenharmony_ci } 85913498266Sopenharmony_ci return 0; 86013498266Sopenharmony_ci} 86113498266Sopenharmony_ci 86213498266Sopenharmony_cistatic nghttp3_ssize 86313498266Sopenharmony_cicb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id, 86413498266Sopenharmony_ci nghttp3_vec *vec, size_t veccnt, 86513498266Sopenharmony_ci uint32_t *pflags, void *user_data, 86613498266Sopenharmony_ci void *stream_user_data) 86713498266Sopenharmony_ci{ 86813498266Sopenharmony_ci struct Curl_cfilter *cf = user_data; 86913498266Sopenharmony_ci struct Curl_easy *data = stream_user_data; 87013498266Sopenharmony_ci struct h3_stream_ctx *stream = H3_STREAM_CTX(data); 87113498266Sopenharmony_ci ssize_t nwritten = 0; 87213498266Sopenharmony_ci size_t nvecs = 0; 87313498266Sopenharmony_ci (void)cf; 87413498266Sopenharmony_ci (void)conn; 87513498266Sopenharmony_ci (void)stream_id; 87613498266Sopenharmony_ci (void)user_data; 87713498266Sopenharmony_ci (void)veccnt; 87813498266Sopenharmony_ci 87913498266Sopenharmony_ci if(!stream) 88013498266Sopenharmony_ci return NGHTTP3_ERR_CALLBACK_FAILURE; 88113498266Sopenharmony_ci /* nghttp3 keeps references to the sendbuf data until it is ACKed 88213498266Sopenharmony_ci * by the server (see `cb_h3_acked_req_body()` for updates). 88313498266Sopenharmony_ci * `sendbuf_len_in_flight` is the amount of bytes in `sendbuf` 88413498266Sopenharmony_ci * that we have already passed to nghttp3, but which have not been 88513498266Sopenharmony_ci * ACKed yet. 88613498266Sopenharmony_ci * Any amount beyond `sendbuf_len_in_flight` we need still to pass 88713498266Sopenharmony_ci * to nghttp3. Do that now, if we can. */ 88813498266Sopenharmony_ci if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) { 88913498266Sopenharmony_ci nvecs = 0; 89013498266Sopenharmony_ci while(nvecs < veccnt && 89113498266Sopenharmony_ci Curl_bufq_peek_at(&stream->sendbuf, 89213498266Sopenharmony_ci stream->sendbuf_len_in_flight, 89313498266Sopenharmony_ci (const unsigned char **)&vec[nvecs].base, 89413498266Sopenharmony_ci &vec[nvecs].len)) { 89513498266Sopenharmony_ci stream->sendbuf_len_in_flight += vec[nvecs].len; 89613498266Sopenharmony_ci nwritten += vec[nvecs].len; 89713498266Sopenharmony_ci ++nvecs; 89813498266Sopenharmony_ci } 89913498266Sopenharmony_ci DEBUGASSERT(nvecs > 0); /* we SHOULD have been be able to peek */ 90013498266Sopenharmony_ci } 90113498266Sopenharmony_ci 90213498266Sopenharmony_ci if(nwritten > 0 && stream->upload_left != -1) 90313498266Sopenharmony_ci stream->upload_left -= nwritten; 90413498266Sopenharmony_ci 90513498266Sopenharmony_ci /* When we stopped sending and everything in `sendbuf` is "in flight", 90613498266Sopenharmony_ci * we are at the end of the request body. */ 90713498266Sopenharmony_ci if(stream->upload_left == 0) { 90813498266Sopenharmony_ci *pflags = NGHTTP3_DATA_FLAG_EOF; 90913498266Sopenharmony_ci stream->send_closed = TRUE; 91013498266Sopenharmony_ci } 91113498266Sopenharmony_ci else if(!nwritten) { 91213498266Sopenharmony_ci /* Not EOF, and nothing to give, we signal WOULDBLOCK. */ 91313498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> AGAIN", 91413498266Sopenharmony_ci stream->s.id); 91513498266Sopenharmony_ci return NGHTTP3_ERR_WOULDBLOCK; 91613498266Sopenharmony_ci } 91713498266Sopenharmony_ci 91813498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> " 91913498266Sopenharmony_ci "%d vecs%s with %zu (buffered=%zu, left=%" 92013498266Sopenharmony_ci CURL_FORMAT_CURL_OFF_T ")", 92113498266Sopenharmony_ci stream->s.id, (int)nvecs, 92213498266Sopenharmony_ci *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"", 92313498266Sopenharmony_ci nwritten, Curl_bufq_len(&stream->sendbuf), 92413498266Sopenharmony_ci stream->upload_left); 92513498266Sopenharmony_ci return (nghttp3_ssize)nvecs; 92613498266Sopenharmony_ci} 92713498266Sopenharmony_ci 92813498266Sopenharmony_cistatic int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id, 92913498266Sopenharmony_ci uint64_t datalen, void *user_data, 93013498266Sopenharmony_ci void *stream_user_data) 93113498266Sopenharmony_ci{ 93213498266Sopenharmony_ci struct Curl_cfilter *cf = user_data; 93313498266Sopenharmony_ci struct Curl_easy *data = stream_user_data; 93413498266Sopenharmony_ci struct h3_stream_ctx *stream = H3_STREAM_CTX(data); 93513498266Sopenharmony_ci size_t skiplen; 93613498266Sopenharmony_ci 93713498266Sopenharmony_ci (void)cf; 93813498266Sopenharmony_ci if(!stream) 93913498266Sopenharmony_ci return 0; 94013498266Sopenharmony_ci /* The server acknowledged `datalen` of bytes from our request body. 94113498266Sopenharmony_ci * This is a delta. We have kept this data in `sendbuf` for 94213498266Sopenharmony_ci * re-transmissions and can free it now. */ 94313498266Sopenharmony_ci if(datalen >= (uint64_t)stream->sendbuf_len_in_flight) 94413498266Sopenharmony_ci skiplen = stream->sendbuf_len_in_flight; 94513498266Sopenharmony_ci else 94613498266Sopenharmony_ci skiplen = (size_t)datalen; 94713498266Sopenharmony_ci Curl_bufq_skip(&stream->sendbuf, skiplen); 94813498266Sopenharmony_ci stream->sendbuf_len_in_flight -= skiplen; 94913498266Sopenharmony_ci 95013498266Sopenharmony_ci /* Everything ACKed, we resume upload processing */ 95113498266Sopenharmony_ci if(!stream->sendbuf_len_in_flight) { 95213498266Sopenharmony_ci int rv = nghttp3_conn_resume_stream(conn, stream_id); 95313498266Sopenharmony_ci if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { 95413498266Sopenharmony_ci return NGHTTP3_ERR_CALLBACK_FAILURE; 95513498266Sopenharmony_ci } 95613498266Sopenharmony_ci } 95713498266Sopenharmony_ci return 0; 95813498266Sopenharmony_ci} 95913498266Sopenharmony_ci 96013498266Sopenharmony_cistatic nghttp3_callbacks ngh3_callbacks = { 96113498266Sopenharmony_ci cb_h3_acked_stream_data, 96213498266Sopenharmony_ci cb_h3_stream_close, 96313498266Sopenharmony_ci cb_h3_recv_data, 96413498266Sopenharmony_ci cb_h3_deferred_consume, 96513498266Sopenharmony_ci NULL, /* begin_headers */ 96613498266Sopenharmony_ci cb_h3_recv_header, 96713498266Sopenharmony_ci cb_h3_end_headers, 96813498266Sopenharmony_ci NULL, /* begin_trailers */ 96913498266Sopenharmony_ci cb_h3_recv_header, 97013498266Sopenharmony_ci NULL, /* end_trailers */ 97113498266Sopenharmony_ci cb_h3_stop_sending, 97213498266Sopenharmony_ci NULL, /* end_stream */ 97313498266Sopenharmony_ci cb_h3_reset_stream, 97413498266Sopenharmony_ci NULL, /* shutdown */ 97513498266Sopenharmony_ci NULL /* recv_settings */ 97613498266Sopenharmony_ci}; 97713498266Sopenharmony_ci 97813498266Sopenharmony_cistatic CURLcode cf_osslq_h3conn_init(struct cf_osslq_ctx *ctx, SSL *conn, 97913498266Sopenharmony_ci void *user_data) 98013498266Sopenharmony_ci{ 98113498266Sopenharmony_ci struct cf_osslq_h3conn *h3 = &ctx->h3; 98213498266Sopenharmony_ci CURLcode result; 98313498266Sopenharmony_ci int rc; 98413498266Sopenharmony_ci 98513498266Sopenharmony_ci nghttp3_settings_default(&h3->settings); 98613498266Sopenharmony_ci rc = nghttp3_conn_client_new(&h3->conn, 98713498266Sopenharmony_ci &ngh3_callbacks, 98813498266Sopenharmony_ci &h3->settings, 98913498266Sopenharmony_ci nghttp3_mem_default(), 99013498266Sopenharmony_ci user_data); 99113498266Sopenharmony_ci if(rc) { 99213498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 99313498266Sopenharmony_ci goto out; 99413498266Sopenharmony_ci } 99513498266Sopenharmony_ci 99613498266Sopenharmony_ci result = cf_osslq_stream_open(&h3->s_ctrl, conn, 99713498266Sopenharmony_ci SSL_STREAM_FLAG_ADVANCE|SSL_STREAM_FLAG_UNI, 99813498266Sopenharmony_ci &ctx->stream_bufcp, NULL); 99913498266Sopenharmony_ci if(result) { 100013498266Sopenharmony_ci result = CURLE_QUIC_CONNECT_ERROR; 100113498266Sopenharmony_ci goto out; 100213498266Sopenharmony_ci } 100313498266Sopenharmony_ci result = cf_osslq_stream_open(&h3->s_qpack_enc, conn, 100413498266Sopenharmony_ci SSL_STREAM_FLAG_ADVANCE|SSL_STREAM_FLAG_UNI, 100513498266Sopenharmony_ci &ctx->stream_bufcp, NULL); 100613498266Sopenharmony_ci if(result) { 100713498266Sopenharmony_ci result = CURLE_QUIC_CONNECT_ERROR; 100813498266Sopenharmony_ci goto out; 100913498266Sopenharmony_ci } 101013498266Sopenharmony_ci result = cf_osslq_stream_open(&h3->s_qpack_dec, conn, 101113498266Sopenharmony_ci SSL_STREAM_FLAG_ADVANCE|SSL_STREAM_FLAG_UNI, 101213498266Sopenharmony_ci &ctx->stream_bufcp, NULL); 101313498266Sopenharmony_ci if(result) { 101413498266Sopenharmony_ci result = CURLE_QUIC_CONNECT_ERROR; 101513498266Sopenharmony_ci goto out; 101613498266Sopenharmony_ci } 101713498266Sopenharmony_ci 101813498266Sopenharmony_ci rc = nghttp3_conn_bind_control_stream(h3->conn, h3->s_ctrl.id); 101913498266Sopenharmony_ci if(rc) { 102013498266Sopenharmony_ci result = CURLE_QUIC_CONNECT_ERROR; 102113498266Sopenharmony_ci goto out; 102213498266Sopenharmony_ci } 102313498266Sopenharmony_ci rc = nghttp3_conn_bind_qpack_streams(h3->conn, h3->s_qpack_enc.id, 102413498266Sopenharmony_ci h3->s_qpack_dec.id); 102513498266Sopenharmony_ci if(rc) { 102613498266Sopenharmony_ci result = CURLE_QUIC_CONNECT_ERROR; 102713498266Sopenharmony_ci goto out; 102813498266Sopenharmony_ci } 102913498266Sopenharmony_ci 103013498266Sopenharmony_ci result = CURLE_OK; 103113498266Sopenharmony_ciout: 103213498266Sopenharmony_ci return result; 103313498266Sopenharmony_ci} 103413498266Sopenharmony_ci 103513498266Sopenharmony_cistatic CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf, 103613498266Sopenharmony_ci struct Curl_easy *data) 103713498266Sopenharmony_ci{ 103813498266Sopenharmony_ci struct cf_osslq_ctx *ctx = cf->ctx; 103913498266Sopenharmony_ci CURLcode result; 104013498266Sopenharmony_ci int rv; 104113498266Sopenharmony_ci const struct Curl_sockaddr_ex *peer_addr = NULL; 104213498266Sopenharmony_ci int peer_port; 104313498266Sopenharmony_ci BIO *bio = NULL; 104413498266Sopenharmony_ci BIO_ADDR *baddr = NULL; 104513498266Sopenharmony_ci 104613498266Sopenharmony_ci Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE, 104713498266Sopenharmony_ci H3_STREAM_POOL_SPARES); 104813498266Sopenharmony_ci result = Curl_ssl_peer_init(&ctx->peer, cf); 104913498266Sopenharmony_ci if(result) 105013498266Sopenharmony_ci goto out; 105113498266Sopenharmony_ci 105213498266Sopenharmony_ci#define H3_ALPN "\x2h3" 105313498266Sopenharmony_ci result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer, 105413498266Sopenharmony_ci H3_ALPN, sizeof(H3_ALPN) - 1, 105513498266Sopenharmony_ci NULL, NULL); 105613498266Sopenharmony_ci if(result) 105713498266Sopenharmony_ci goto out; 105813498266Sopenharmony_ci 105913498266Sopenharmony_ci result = vquic_ctx_init(&ctx->q); 106013498266Sopenharmony_ci if(result) 106113498266Sopenharmony_ci goto out; 106213498266Sopenharmony_ci 106313498266Sopenharmony_ci result = CURLE_QUIC_CONNECT_ERROR; 106413498266Sopenharmony_ci Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, 106513498266Sopenharmony_ci &peer_addr, NULL, &peer_port, NULL, NULL); 106613498266Sopenharmony_ci if(!peer_addr) 106713498266Sopenharmony_ci goto out; 106813498266Sopenharmony_ci 106913498266Sopenharmony_ci ctx->q.local_addrlen = sizeof(ctx->q.local_addr); 107013498266Sopenharmony_ci rv = getsockname(ctx->q.sockfd, (struct sockaddr *)&ctx->q.local_addr, 107113498266Sopenharmony_ci &ctx->q.local_addrlen); 107213498266Sopenharmony_ci if(rv == -1) 107313498266Sopenharmony_ci goto out; 107413498266Sopenharmony_ci 107513498266Sopenharmony_ci result = make_bio_addr(&baddr, peer_addr); 107613498266Sopenharmony_ci if(result) { 107713498266Sopenharmony_ci failf(data, "error creating BIO_ADDR from sockaddr"); 107813498266Sopenharmony_ci goto out; 107913498266Sopenharmony_ci } 108013498266Sopenharmony_ci 108113498266Sopenharmony_ci bio = BIO_new_dgram(ctx->q.sockfd, BIO_NOCLOSE); 108213498266Sopenharmony_ci if(!bio) { 108313498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 108413498266Sopenharmony_ci goto out; 108513498266Sopenharmony_ci } 108613498266Sopenharmony_ci 108713498266Sopenharmony_ci if(!SSL_set1_initial_peer_addr(ctx->tls.ssl, baddr)) { 108813498266Sopenharmony_ci failf(data, "failed to set the initial peer address"); 108913498266Sopenharmony_ci result = CURLE_FAILED_INIT; 109013498266Sopenharmony_ci goto out; 109113498266Sopenharmony_ci } 109213498266Sopenharmony_ci if(!SSL_set_blocking_mode(ctx->tls.ssl, 0)) { 109313498266Sopenharmony_ci failf(data, "failed to turn off blocking mode"); 109413498266Sopenharmony_ci result = CURLE_FAILED_INIT; 109513498266Sopenharmony_ci goto out; 109613498266Sopenharmony_ci } 109713498266Sopenharmony_ci 109813498266Sopenharmony_ci SSL_set_bio(ctx->tls.ssl, bio, bio); 109913498266Sopenharmony_ci bio = NULL; 110013498266Sopenharmony_ci SSL_set_connect_state(ctx->tls.ssl); 110113498266Sopenharmony_ci SSL_set_incoming_stream_policy(ctx->tls.ssl, 110213498266Sopenharmony_ci SSL_INCOMING_STREAM_POLICY_ACCEPT, 0); 110313498266Sopenharmony_ci /* setup the H3 things on top of the QUIC connection */ 110413498266Sopenharmony_ci result = cf_osslq_h3conn_init(ctx, ctx->tls.ssl, cf); 110513498266Sopenharmony_ci 110613498266Sopenharmony_ciout: 110713498266Sopenharmony_ci if(bio) 110813498266Sopenharmony_ci BIO_free(bio); 110913498266Sopenharmony_ci if(baddr) 111013498266Sopenharmony_ci BIO_ADDR_free(baddr); 111113498266Sopenharmony_ci CURL_TRC_CF(data, cf, "QUIC tls init -> %d", result); 111213498266Sopenharmony_ci return result; 111313498266Sopenharmony_ci} 111413498266Sopenharmony_ci 111513498266Sopenharmony_cistruct h3_quic_recv_ctx { 111613498266Sopenharmony_ci struct Curl_cfilter *cf; 111713498266Sopenharmony_ci struct Curl_easy *data; 111813498266Sopenharmony_ci struct cf_osslq_stream *s; 111913498266Sopenharmony_ci}; 112013498266Sopenharmony_ci 112113498266Sopenharmony_cistatic ssize_t h3_quic_recv(void *reader_ctx, 112213498266Sopenharmony_ci unsigned char *buf, size_t len, 112313498266Sopenharmony_ci CURLcode *err) 112413498266Sopenharmony_ci{ 112513498266Sopenharmony_ci struct h3_quic_recv_ctx *x = reader_ctx; 112613498266Sopenharmony_ci size_t nread; 112713498266Sopenharmony_ci int rv; 112813498266Sopenharmony_ci 112913498266Sopenharmony_ci *err = CURLE_OK; 113013498266Sopenharmony_ci rv = SSL_read_ex(x->s->ssl, buf, len, &nread); 113113498266Sopenharmony_ci if(rv <= 0) { 113213498266Sopenharmony_ci int detail = SSL_get_error(x->s->ssl, rv); 113313498266Sopenharmony_ci if(detail == SSL_ERROR_WANT_READ || detail == SSL_ERROR_WANT_WRITE) { 113413498266Sopenharmony_ci *err = CURLE_AGAIN; 113513498266Sopenharmony_ci return -1; 113613498266Sopenharmony_ci } 113713498266Sopenharmony_ci else if(detail == SSL_ERROR_ZERO_RETURN) { 113813498266Sopenharmony_ci CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] h3_quic_recv -> EOS", 113913498266Sopenharmony_ci x->s->id); 114013498266Sopenharmony_ci x->s->recvd_eos = TRUE; 114113498266Sopenharmony_ci return 0; 114213498266Sopenharmony_ci } 114313498266Sopenharmony_ci else if(SSL_get_stream_read_state(x->s->ssl) == 114413498266Sopenharmony_ci SSL_STREAM_STATE_RESET_REMOTE) { 114513498266Sopenharmony_ci uint64_t app_error_code = NGHTTP3_H3_NO_ERROR; 114613498266Sopenharmony_ci SSL_get_stream_read_error_code(x->s->ssl, &app_error_code); 114713498266Sopenharmony_ci CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] h3_quic_recv -> RESET, " 114813498266Sopenharmony_ci "rv=%d, app_err=%" PRIu64, 114913498266Sopenharmony_ci x->s->id, rv, app_error_code); 115013498266Sopenharmony_ci if(app_error_code != NGHTTP3_H3_NO_ERROR) { 115113498266Sopenharmony_ci x->s->reset = TRUE; 115213498266Sopenharmony_ci } 115313498266Sopenharmony_ci x->s->recvd_eos = TRUE; 115413498266Sopenharmony_ci return 0; 115513498266Sopenharmony_ci } 115613498266Sopenharmony_ci else { 115713498266Sopenharmony_ci *err = cf_osslq_ssl_err(x->cf, x->data, detail, CURLE_RECV_ERROR); 115813498266Sopenharmony_ci return -1; 115913498266Sopenharmony_ci } 116013498266Sopenharmony_ci } 116113498266Sopenharmony_ci else { 116213498266Sopenharmony_ci /* CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] h3_quic_recv -> %zu bytes", 116313498266Sopenharmony_ci x->s->id, nread); */ 116413498266Sopenharmony_ci } 116513498266Sopenharmony_ci return (ssize_t)nread; 116613498266Sopenharmony_ci} 116713498266Sopenharmony_ci 116813498266Sopenharmony_cistatic CURLcode cf_osslq_stream_recv(struct cf_osslq_stream *s, 116913498266Sopenharmony_ci struct Curl_cfilter *cf, 117013498266Sopenharmony_ci struct Curl_easy *data) 117113498266Sopenharmony_ci{ 117213498266Sopenharmony_ci struct cf_osslq_ctx *ctx = cf->ctx; 117313498266Sopenharmony_ci CURLcode result = CURLE_OK; 117413498266Sopenharmony_ci ssize_t nread; 117513498266Sopenharmony_ci struct h3_quic_recv_ctx x; 117613498266Sopenharmony_ci int rv, eagain = FALSE; 117713498266Sopenharmony_ci size_t total_recv_len = 0; 117813498266Sopenharmony_ci 117913498266Sopenharmony_ci DEBUGASSERT(s); 118013498266Sopenharmony_ci if(s->closed) 118113498266Sopenharmony_ci return CURLE_OK; 118213498266Sopenharmony_ci 118313498266Sopenharmony_ci x.cf = cf; 118413498266Sopenharmony_ci x.data = data; 118513498266Sopenharmony_ci x.s = s; 118613498266Sopenharmony_ci while(s->ssl && !s->closed && !eagain && 118713498266Sopenharmony_ci (total_recv_len < H3_STREAM_CHUNK_SIZE)) { 118813498266Sopenharmony_ci if(Curl_bufq_is_empty(&s->recvbuf) && !s->recvd_eos) { 118913498266Sopenharmony_ci while(!eagain && !s->recvd_eos && !Curl_bufq_is_full(&s->recvbuf)) { 119013498266Sopenharmony_ci nread = Curl_bufq_sipn(&s->recvbuf, 0, h3_quic_recv, &x, &result); 119113498266Sopenharmony_ci if(nread < 0) { 119213498266Sopenharmony_ci if(result != CURLE_AGAIN) 119313498266Sopenharmony_ci goto out; 119413498266Sopenharmony_ci result = CURLE_OK; 119513498266Sopenharmony_ci eagain = TRUE; 119613498266Sopenharmony_ci } 119713498266Sopenharmony_ci } 119813498266Sopenharmony_ci } 119913498266Sopenharmony_ci 120013498266Sopenharmony_ci /* Forward what we have to nghttp3 */ 120113498266Sopenharmony_ci if(!Curl_bufq_is_empty(&s->recvbuf)) { 120213498266Sopenharmony_ci const unsigned char *buf; 120313498266Sopenharmony_ci size_t blen; 120413498266Sopenharmony_ci 120513498266Sopenharmony_ci while(Curl_bufq_peek(&s->recvbuf, &buf, &blen)) { 120613498266Sopenharmony_ci nread = nghttp3_conn_read_stream(ctx->h3.conn, s->id, 120713498266Sopenharmony_ci buf, blen, 0); 120813498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] forward %zu bytes " 120913498266Sopenharmony_ci "to nghttp3 -> %zd", s->id, blen, nread); 121013498266Sopenharmony_ci if(nread < 0) { 121113498266Sopenharmony_ci failf(data, "nghttp3_conn_read_stream(len=%zu) error: %s", 121213498266Sopenharmony_ci blen, nghttp3_strerror((int)nread)); 121313498266Sopenharmony_ci result = CURLE_RECV_ERROR; 121413498266Sopenharmony_ci goto out; 121513498266Sopenharmony_ci } 121613498266Sopenharmony_ci /* success, `nread` is the flow for QUIC to count as "consumed", 121713498266Sopenharmony_ci * not sure how that will work with OpenSSL. Anyways, without error, 121813498266Sopenharmony_ci * all data that we passed is not owned by nghttp3. */ 121913498266Sopenharmony_ci Curl_bufq_skip(&s->recvbuf, blen); 122013498266Sopenharmony_ci total_recv_len += blen; 122113498266Sopenharmony_ci } 122213498266Sopenharmony_ci } 122313498266Sopenharmony_ci 122413498266Sopenharmony_ci /* When we forwarded everything, handle RESET/EOS */ 122513498266Sopenharmony_ci if(Curl_bufq_is_empty(&s->recvbuf) && !s->closed) { 122613498266Sopenharmony_ci result = CURLE_OK; 122713498266Sopenharmony_ci if(s->reset) { 122813498266Sopenharmony_ci uint64_t app_error; 122913498266Sopenharmony_ci if(!SSL_get_stream_read_error_code(s->ssl, &app_error)) { 123013498266Sopenharmony_ci failf(data, "SSL_get_stream_read_error_code returned error"); 123113498266Sopenharmony_ci result = CURLE_RECV_ERROR; 123213498266Sopenharmony_ci goto out; 123313498266Sopenharmony_ci } 123413498266Sopenharmony_ci rv = nghttp3_conn_close_stream(ctx->h3.conn, s->id, app_error); 123513498266Sopenharmony_ci s->closed = TRUE; 123613498266Sopenharmony_ci if(rv < 0 && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { 123713498266Sopenharmony_ci failf(data, "nghttp3_conn_close_stream returned error: %s", 123813498266Sopenharmony_ci nghttp3_strerror(rv)); 123913498266Sopenharmony_ci result = CURLE_RECV_ERROR; 124013498266Sopenharmony_ci goto out; 124113498266Sopenharmony_ci } 124213498266Sopenharmony_ci } 124313498266Sopenharmony_ci else if(s->recvd_eos) { 124413498266Sopenharmony_ci rv = nghttp3_conn_close_stream(ctx->h3.conn, s->id, 124513498266Sopenharmony_ci NGHTTP3_H3_NO_ERROR); 124613498266Sopenharmony_ci s->closed = TRUE; 124713498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] close nghttp3 stream -> %d", 124813498266Sopenharmony_ci s->id, rv); 124913498266Sopenharmony_ci if(rv < 0 && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { 125013498266Sopenharmony_ci failf(data, "nghttp3_conn_close_stream returned error: %s", 125113498266Sopenharmony_ci nghttp3_strerror(rv)); 125213498266Sopenharmony_ci result = CURLE_RECV_ERROR; 125313498266Sopenharmony_ci goto out; 125413498266Sopenharmony_ci } 125513498266Sopenharmony_ci } 125613498266Sopenharmony_ci } 125713498266Sopenharmony_ci } 125813498266Sopenharmony_ciout: 125913498266Sopenharmony_ci if(result) 126013498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_osslq_stream_recv -> %d", 126113498266Sopenharmony_ci s->id, result); 126213498266Sopenharmony_ci return result; 126313498266Sopenharmony_ci} 126413498266Sopenharmony_ci 126513498266Sopenharmony_cistatic CURLcode cf_progress_ingress(struct Curl_cfilter *cf, 126613498266Sopenharmony_ci struct Curl_easy *data) 126713498266Sopenharmony_ci{ 126813498266Sopenharmony_ci struct cf_osslq_ctx *ctx = cf->ctx; 126913498266Sopenharmony_ci CURLcode result = CURLE_OK; 127013498266Sopenharmony_ci 127113498266Sopenharmony_ci if(!ctx->tls.ssl) 127213498266Sopenharmony_ci goto out; 127313498266Sopenharmony_ci 127413498266Sopenharmony_ci ERR_clear_error(); 127513498266Sopenharmony_ci 127613498266Sopenharmony_ci /* 1. Check for new incoming streams */ 127713498266Sopenharmony_ci while(1) { 127813498266Sopenharmony_ci SSL *snew = SSL_accept_stream(ctx->tls.ssl, SSL_ACCEPT_STREAM_NO_BLOCK); 127913498266Sopenharmony_ci if(!snew) 128013498266Sopenharmony_ci break; 128113498266Sopenharmony_ci 128213498266Sopenharmony_ci (void)cf_osslq_h3conn_add_stream(&ctx->h3, snew, cf, data); 128313498266Sopenharmony_ci } 128413498266Sopenharmony_ci 128513498266Sopenharmony_ci if(!SSL_handle_events(ctx->tls.ssl)) { 128613498266Sopenharmony_ci int detail = SSL_get_error(ctx->tls.ssl, 0); 128713498266Sopenharmony_ci result = cf_osslq_ssl_err(cf, data, detail, CURLE_RECV_ERROR); 128813498266Sopenharmony_ci } 128913498266Sopenharmony_ci 129013498266Sopenharmony_ci if(ctx->h3.conn) { 129113498266Sopenharmony_ci size_t i; 129213498266Sopenharmony_ci for(i = 0; i < ctx->h3.remote_ctrl_n; ++i) { 129313498266Sopenharmony_ci result = cf_osslq_stream_recv(&ctx->h3.remote_ctrl[i], cf, data); 129413498266Sopenharmony_ci if(result) 129513498266Sopenharmony_ci goto out; 129613498266Sopenharmony_ci } 129713498266Sopenharmony_ci } 129813498266Sopenharmony_ci 129913498266Sopenharmony_ci if(ctx->h3.conn) { 130013498266Sopenharmony_ci struct Curl_easy *sdata; 130113498266Sopenharmony_ci struct h3_stream_ctx *stream; 130213498266Sopenharmony_ci /* PULL all open streams */ 130313498266Sopenharmony_ci DEBUGASSERT(data->multi); 130413498266Sopenharmony_ci for(sdata = data->multi->easyp; sdata; sdata = sdata->next) { 130513498266Sopenharmony_ci if(sdata->conn == data->conn && CURL_WANT_RECV(sdata)) { 130613498266Sopenharmony_ci stream = H3_STREAM_CTX(sdata); 130713498266Sopenharmony_ci if(stream && !stream->closed && 130813498266Sopenharmony_ci !Curl_bufq_is_full(&stream->recvbuf)) { 130913498266Sopenharmony_ci result = cf_osslq_stream_recv(&stream->s, cf, sdata); 131013498266Sopenharmony_ci if(result) 131113498266Sopenharmony_ci goto out; 131213498266Sopenharmony_ci } 131313498266Sopenharmony_ci } 131413498266Sopenharmony_ci } 131513498266Sopenharmony_ci } 131613498266Sopenharmony_ci 131713498266Sopenharmony_ciout: 131813498266Sopenharmony_ci CURL_TRC_CF(data, cf, "progress_ingress -> %d", result); 131913498266Sopenharmony_ci return result; 132013498266Sopenharmony_ci} 132113498266Sopenharmony_ci 132213498266Sopenharmony_ci/* Iterate over all streams and check if blocked can be unblocked */ 132313498266Sopenharmony_cistatic CURLcode cf_osslq_check_and_unblock(struct Curl_cfilter *cf, 132413498266Sopenharmony_ci struct Curl_easy *data) 132513498266Sopenharmony_ci{ 132613498266Sopenharmony_ci struct cf_osslq_ctx *ctx = cf->ctx; 132713498266Sopenharmony_ci struct Curl_easy *sdata; 132813498266Sopenharmony_ci struct h3_stream_ctx *stream; 132913498266Sopenharmony_ci 133013498266Sopenharmony_ci if(ctx->h3.conn) { 133113498266Sopenharmony_ci for(sdata = data->multi->easyp; sdata; sdata = sdata->next) { 133213498266Sopenharmony_ci if(sdata->conn == data->conn) { 133313498266Sopenharmony_ci stream = H3_STREAM_CTX(sdata); 133413498266Sopenharmony_ci if(stream && stream->s.ssl && stream->s.send_blocked && 133513498266Sopenharmony_ci !SSL_want_write(stream->s.ssl)) { 133613498266Sopenharmony_ci nghttp3_conn_unblock_stream(ctx->h3.conn, stream->s.id); 133713498266Sopenharmony_ci stream->s.send_blocked = FALSE; 133813498266Sopenharmony_ci h3_drain_stream(cf, sdata); 133913498266Sopenharmony_ci CURL_TRC_CF(sdata, cf, "unblocked"); 134013498266Sopenharmony_ci } 134113498266Sopenharmony_ci } 134213498266Sopenharmony_ci } 134313498266Sopenharmony_ci } 134413498266Sopenharmony_ci return CURLE_OK; 134513498266Sopenharmony_ci} 134613498266Sopenharmony_ci 134713498266Sopenharmony_cistatic CURLcode h3_send_streams(struct Curl_cfilter *cf, 134813498266Sopenharmony_ci struct Curl_easy *data) 134913498266Sopenharmony_ci{ 135013498266Sopenharmony_ci struct cf_osslq_ctx *ctx = cf->ctx; 135113498266Sopenharmony_ci CURLcode result = CURLE_OK; 135213498266Sopenharmony_ci 135313498266Sopenharmony_ci if(!ctx->tls.ssl || !ctx->h3.conn) 135413498266Sopenharmony_ci goto out; 135513498266Sopenharmony_ci 135613498266Sopenharmony_ci for(;;) { 135713498266Sopenharmony_ci struct cf_osslq_stream *s = NULL; 135813498266Sopenharmony_ci nghttp3_vec vec[16]; 135913498266Sopenharmony_ci nghttp3_ssize n, i; 136013498266Sopenharmony_ci int64_t stream_id; 136113498266Sopenharmony_ci size_t written; 136213498266Sopenharmony_ci int eos, ok, rv; 136313498266Sopenharmony_ci size_t total_len, acked_len = 0; 136413498266Sopenharmony_ci bool blocked = FALSE; 136513498266Sopenharmony_ci 136613498266Sopenharmony_ci n = nghttp3_conn_writev_stream(ctx->h3.conn, &stream_id, &eos, 136713498266Sopenharmony_ci vec, ARRAYSIZE(vec)); 136813498266Sopenharmony_ci if(n < 0) { 136913498266Sopenharmony_ci failf(data, "nghttp3_conn_writev_stream returned error: %s", 137013498266Sopenharmony_ci nghttp3_strerror((int)n)); 137113498266Sopenharmony_ci result = CURLE_SEND_ERROR; 137213498266Sopenharmony_ci goto out; 137313498266Sopenharmony_ci } 137413498266Sopenharmony_ci if(stream_id < 0) { 137513498266Sopenharmony_ci result = CURLE_OK; 137613498266Sopenharmony_ci goto out; 137713498266Sopenharmony_ci } 137813498266Sopenharmony_ci 137913498266Sopenharmony_ci /* Get the stream for this data */ 138013498266Sopenharmony_ci s = cf_osslq_get_qstream(cf, data, stream_id); 138113498266Sopenharmony_ci if(!s) { 138213498266Sopenharmony_ci failf(data, "nghttp3_conn_writev_stream gave unknown stream %" PRId64, 138313498266Sopenharmony_ci stream_id); 138413498266Sopenharmony_ci result = CURLE_SEND_ERROR; 138513498266Sopenharmony_ci goto out; 138613498266Sopenharmony_ci } 138713498266Sopenharmony_ci /* Now write the data to the stream's SSL*, it may not all fit! */ 138813498266Sopenharmony_ci DEBUGASSERT(s->id == stream_id); 138913498266Sopenharmony_ci for(i = 0, total_len = 0; i < n; ++i) { 139013498266Sopenharmony_ci total_len += vec[i].len; 139113498266Sopenharmony_ci } 139213498266Sopenharmony_ci for(i = 0; (i < n) && !blocked; ++i) { 139313498266Sopenharmony_ci /* Without stream->s.ssl, we closed that already, so 139413498266Sopenharmony_ci * pretend the write did succeed. */ 139513498266Sopenharmony_ci written = vec[i].len; 139613498266Sopenharmony_ci ok = !s->ssl || SSL_write_ex(s->ssl, vec[i].base, vec[i].len, 139713498266Sopenharmony_ci &written); 139813498266Sopenharmony_ci if(ok) { 139913498266Sopenharmony_ci /* As OpenSSL buffers the data, we count this as acknowledged 140013498266Sopenharmony_ci * from nghttp3's point of view */ 140113498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%"PRId64"] send %zu bytes to QUIC ok", 140213498266Sopenharmony_ci s->id, vec[i].len); 140313498266Sopenharmony_ci acked_len += vec[i].len; 140413498266Sopenharmony_ci } 140513498266Sopenharmony_ci else { 140613498266Sopenharmony_ci int detail = SSL_get_error(s->ssl, 0); 140713498266Sopenharmony_ci switch(detail) { 140813498266Sopenharmony_ci case SSL_ERROR_WANT_WRITE: 140913498266Sopenharmony_ci case SSL_ERROR_WANT_READ: 141013498266Sopenharmony_ci /* QUIC blocked us from writing more */ 141113498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%"PRId64"] send %zu bytes to QUIC blocked", 141213498266Sopenharmony_ci s->id, vec[i].len); 141313498266Sopenharmony_ci written = 0; 141413498266Sopenharmony_ci nghttp3_conn_block_stream(ctx->h3.conn, s->id); 141513498266Sopenharmony_ci s->send_blocked = blocked = TRUE; 141613498266Sopenharmony_ci break; 141713498266Sopenharmony_ci default: 141813498266Sopenharmony_ci failf(data, "[%"PRId64"] send %zu bytes to QUIC, SSL error %d", 141913498266Sopenharmony_ci s->id, vec[i].len, detail); 142013498266Sopenharmony_ci result = cf_osslq_ssl_err(cf, data, detail, CURLE_SEND_ERROR); 142113498266Sopenharmony_ci goto out; 142213498266Sopenharmony_ci } 142313498266Sopenharmony_ci } 142413498266Sopenharmony_ci } 142513498266Sopenharmony_ci 142613498266Sopenharmony_ci if(acked_len > 0 || (eos && !s->send_blocked)) { 142713498266Sopenharmony_ci /* Since QUIC buffers the data written internally, we can tell 142813498266Sopenharmony_ci * nghttp3 that it can move forward on it */ 142913498266Sopenharmony_ci rv = nghttp3_conn_add_write_offset(ctx->h3.conn, s->id, acked_len); 143013498266Sopenharmony_ci if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { 143113498266Sopenharmony_ci failf(data, "nghttp3_conn_add_write_offset returned error: %s\n", 143213498266Sopenharmony_ci nghttp3_strerror(rv)); 143313498266Sopenharmony_ci result = CURLE_SEND_ERROR; 143413498266Sopenharmony_ci goto out; 143513498266Sopenharmony_ci } 143613498266Sopenharmony_ci rv = nghttp3_conn_add_ack_offset(ctx->h3.conn, s->id, acked_len); 143713498266Sopenharmony_ci if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { 143813498266Sopenharmony_ci failf(data, "nghttp3_conn_add_ack_offset returned error: %s\n", 143913498266Sopenharmony_ci nghttp3_strerror(rv)); 144013498266Sopenharmony_ci result = CURLE_SEND_ERROR; 144113498266Sopenharmony_ci goto out; 144213498266Sopenharmony_ci } 144313498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] forwarded %zu/%zu h3 bytes " 144413498266Sopenharmony_ci "to QUIC, eos=%d", s->id, acked_len, total_len, eos); 144513498266Sopenharmony_ci } 144613498266Sopenharmony_ci 144713498266Sopenharmony_ci if(eos && !s->send_blocked) { 144813498266Sopenharmony_ci /* wrote everything and H3 indicates end of stream */ 144913498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] closing QUIC stream", s->id); 145013498266Sopenharmony_ci SSL_stream_conclude(s->ssl, 0); 145113498266Sopenharmony_ci } 145213498266Sopenharmony_ci } 145313498266Sopenharmony_ci 145413498266Sopenharmony_ciout: 145513498266Sopenharmony_ci CURL_TRC_CF(data, cf, "h3_send_streams -> %d", result); 145613498266Sopenharmony_ci return result; 145713498266Sopenharmony_ci} 145813498266Sopenharmony_ci 145913498266Sopenharmony_cistatic CURLcode cf_progress_egress(struct Curl_cfilter *cf, 146013498266Sopenharmony_ci struct Curl_easy *data) 146113498266Sopenharmony_ci{ 146213498266Sopenharmony_ci struct cf_osslq_ctx *ctx = cf->ctx; 146313498266Sopenharmony_ci CURLcode result = CURLE_OK; 146413498266Sopenharmony_ci 146513498266Sopenharmony_ci if(!ctx->tls.ssl) 146613498266Sopenharmony_ci goto out; 146713498266Sopenharmony_ci 146813498266Sopenharmony_ci ERR_clear_error(); 146913498266Sopenharmony_ci result = h3_send_streams(cf, data); 147013498266Sopenharmony_ci if(result) 147113498266Sopenharmony_ci goto out; 147213498266Sopenharmony_ci 147313498266Sopenharmony_ci if(!SSL_handle_events(ctx->tls.ssl)) { 147413498266Sopenharmony_ci int detail = SSL_get_error(ctx->tls.ssl, 0); 147513498266Sopenharmony_ci result = cf_osslq_ssl_err(cf, data, detail, CURLE_SEND_ERROR); 147613498266Sopenharmony_ci } 147713498266Sopenharmony_ci 147813498266Sopenharmony_ci result = cf_osslq_check_and_unblock(cf, data); 147913498266Sopenharmony_ci 148013498266Sopenharmony_ciout: 148113498266Sopenharmony_ci CURL_TRC_CF(data, cf, "progress_egress -> %d", result); 148213498266Sopenharmony_ci return result; 148313498266Sopenharmony_ci} 148413498266Sopenharmony_ci 148513498266Sopenharmony_cistatic CURLcode check_and_set_expiry(struct Curl_cfilter *cf, 148613498266Sopenharmony_ci struct Curl_easy *data) 148713498266Sopenharmony_ci{ 148813498266Sopenharmony_ci struct cf_osslq_ctx *ctx = cf->ctx; 148913498266Sopenharmony_ci CURLcode result = CURLE_OK; 149013498266Sopenharmony_ci struct timeval tv; 149113498266Sopenharmony_ci timediff_t timeoutms; 149213498266Sopenharmony_ci int is_infinite = TRUE; 149313498266Sopenharmony_ci 149413498266Sopenharmony_ci if(ctx->tls.ssl && 149513498266Sopenharmony_ci SSL_get_event_timeout(ctx->tls.ssl, &tv, &is_infinite) && 149613498266Sopenharmony_ci !is_infinite) { 149713498266Sopenharmony_ci timeoutms = curlx_tvtoms(&tv); 149813498266Sopenharmony_ci /* QUIC want to be called again latest at the returned timeout */ 149913498266Sopenharmony_ci if(timeoutms <= 0) { 150013498266Sopenharmony_ci result = cf_progress_ingress(cf, data); 150113498266Sopenharmony_ci if(result) 150213498266Sopenharmony_ci goto out; 150313498266Sopenharmony_ci result = cf_progress_egress(cf, data); 150413498266Sopenharmony_ci if(result) 150513498266Sopenharmony_ci goto out; 150613498266Sopenharmony_ci if(SSL_get_event_timeout(ctx->tls.ssl, &tv, &is_infinite)) { 150713498266Sopenharmony_ci timeoutms = curlx_tvtoms(&tv); 150813498266Sopenharmony_ci } 150913498266Sopenharmony_ci } 151013498266Sopenharmony_ci if(!is_infinite) { 151113498266Sopenharmony_ci Curl_expire(data, timeoutms, EXPIRE_QUIC); 151213498266Sopenharmony_ci CURL_TRC_CF(data, cf, "QUIC expiry in %ldms", (long)timeoutms); 151313498266Sopenharmony_ci } 151413498266Sopenharmony_ci } 151513498266Sopenharmony_ciout: 151613498266Sopenharmony_ci return result; 151713498266Sopenharmony_ci} 151813498266Sopenharmony_ci 151913498266Sopenharmony_cistatic CURLcode cf_osslq_connect(struct Curl_cfilter *cf, 152013498266Sopenharmony_ci struct Curl_easy *data, 152113498266Sopenharmony_ci bool blocking, bool *done) 152213498266Sopenharmony_ci{ 152313498266Sopenharmony_ci struct cf_osslq_ctx *ctx = cf->ctx; 152413498266Sopenharmony_ci CURLcode result = CURLE_OK; 152513498266Sopenharmony_ci struct cf_call_data save; 152613498266Sopenharmony_ci struct curltime now; 152713498266Sopenharmony_ci int err; 152813498266Sopenharmony_ci 152913498266Sopenharmony_ci if(cf->connected) { 153013498266Sopenharmony_ci *done = TRUE; 153113498266Sopenharmony_ci return CURLE_OK; 153213498266Sopenharmony_ci } 153313498266Sopenharmony_ci 153413498266Sopenharmony_ci /* Connect the UDP filter first */ 153513498266Sopenharmony_ci if(!cf->next->connected) { 153613498266Sopenharmony_ci result = Curl_conn_cf_connect(cf->next, data, blocking, done); 153713498266Sopenharmony_ci if(result || !*done) 153813498266Sopenharmony_ci return result; 153913498266Sopenharmony_ci } 154013498266Sopenharmony_ci 154113498266Sopenharmony_ci *done = FALSE; 154213498266Sopenharmony_ci now = Curl_now(); 154313498266Sopenharmony_ci CF_DATA_SAVE(save, cf, data); 154413498266Sopenharmony_ci 154513498266Sopenharmony_ci if(ctx->reconnect_at.tv_sec && Curl_timediff(now, ctx->reconnect_at) < 0) { 154613498266Sopenharmony_ci /* Not time yet to attempt the next connect */ 154713498266Sopenharmony_ci CURL_TRC_CF(data, cf, "waiting for reconnect time"); 154813498266Sopenharmony_ci goto out; 154913498266Sopenharmony_ci } 155013498266Sopenharmony_ci 155113498266Sopenharmony_ci if(!ctx->tls.ssl) { 155213498266Sopenharmony_ci ctx->started_at = now; 155313498266Sopenharmony_ci result = cf_osslq_ctx_start(cf, data); 155413498266Sopenharmony_ci if(result) 155513498266Sopenharmony_ci goto out; 155613498266Sopenharmony_ci } 155713498266Sopenharmony_ci 155813498266Sopenharmony_ci if(!ctx->got_first_byte) { 155913498266Sopenharmony_ci int readable = SOCKET_READABLE(ctx->q.sockfd, 0); 156013498266Sopenharmony_ci if(readable > 0 && (readable & CURL_CSELECT_IN)) { 156113498266Sopenharmony_ci ctx->got_first_byte = TRUE; 156213498266Sopenharmony_ci ctx->first_byte_at = Curl_now(); 156313498266Sopenharmony_ci } 156413498266Sopenharmony_ci } 156513498266Sopenharmony_ci 156613498266Sopenharmony_ci ERR_clear_error(); 156713498266Sopenharmony_ci err = SSL_do_handshake(ctx->tls.ssl); 156813498266Sopenharmony_ci 156913498266Sopenharmony_ci if(err == 1) { 157013498266Sopenharmony_ci /* connected */ 157113498266Sopenharmony_ci ctx->handshake_at = now; 157213498266Sopenharmony_ci CURL_TRC_CF(data, cf, "handshake complete after %dms", 157313498266Sopenharmony_ci (int)Curl_timediff(now, ctx->started_at)); 157413498266Sopenharmony_ci result = cf_osslq_verify_peer(cf, data); 157513498266Sopenharmony_ci if(!result) { 157613498266Sopenharmony_ci CURL_TRC_CF(data, cf, "peer verified"); 157713498266Sopenharmony_ci cf->connected = TRUE; 157813498266Sopenharmony_ci cf->conn->alpn = CURL_HTTP_VERSION_3; 157913498266Sopenharmony_ci *done = TRUE; 158013498266Sopenharmony_ci connkeep(cf->conn, "HTTP/3 default"); 158113498266Sopenharmony_ci } 158213498266Sopenharmony_ci } 158313498266Sopenharmony_ci else { 158413498266Sopenharmony_ci int detail = SSL_get_error(ctx->tls.ssl, err); 158513498266Sopenharmony_ci switch(detail) { 158613498266Sopenharmony_ci case SSL_ERROR_WANT_READ: 158713498266Sopenharmony_ci CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_RECV"); 158813498266Sopenharmony_ci result = Curl_vquic_tls_before_recv(&ctx->tls, cf, data); 158913498266Sopenharmony_ci goto out; 159013498266Sopenharmony_ci case SSL_ERROR_WANT_WRITE: 159113498266Sopenharmony_ci CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_SEND"); 159213498266Sopenharmony_ci result = CURLE_OK; 159313498266Sopenharmony_ci goto out; 159413498266Sopenharmony_ci#ifdef SSL_ERROR_WANT_ASYNC 159513498266Sopenharmony_ci case SSL_ERROR_WANT_ASYNC: 159613498266Sopenharmony_ci CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_ASYNC"); 159713498266Sopenharmony_ci result = CURLE_OK; 159813498266Sopenharmony_ci goto out; 159913498266Sopenharmony_ci#endif 160013498266Sopenharmony_ci#ifdef SSL_ERROR_WANT_RETRY_VERIFY 160113498266Sopenharmony_ci case SSL_ERROR_WANT_RETRY_VERIFY: 160213498266Sopenharmony_ci result = CURLE_OK; 160313498266Sopenharmony_ci goto out; 160413498266Sopenharmony_ci#endif 160513498266Sopenharmony_ci default: 160613498266Sopenharmony_ci result = cf_osslq_ssl_err(cf, data, detail, CURLE_COULDNT_CONNECT); 160713498266Sopenharmony_ci goto out; 160813498266Sopenharmony_ci } 160913498266Sopenharmony_ci } 161013498266Sopenharmony_ci 161113498266Sopenharmony_ciout: 161213498266Sopenharmony_ci if(result == CURLE_RECV_ERROR && ctx->tls.ssl && ctx->protocol_shutdown) { 161313498266Sopenharmony_ci /* When a QUIC server instance is shutting down, it may send us a 161413498266Sopenharmony_ci * CONNECTION_CLOSE right away. Our connection then enters the DRAINING 161513498266Sopenharmony_ci * state. The CONNECT may work in the near future again. Indicate 161613498266Sopenharmony_ci * that as a "weird" reply. */ 161713498266Sopenharmony_ci result = CURLE_WEIRD_SERVER_REPLY; 161813498266Sopenharmony_ci } 161913498266Sopenharmony_ci 162013498266Sopenharmony_ci#ifndef CURL_DISABLE_VERBOSE_STRINGS 162113498266Sopenharmony_ci if(result) { 162213498266Sopenharmony_ci const char *r_ip = NULL; 162313498266Sopenharmony_ci int r_port = 0; 162413498266Sopenharmony_ci 162513498266Sopenharmony_ci Curl_cf_socket_peek(cf->next, data, NULL, NULL, 162613498266Sopenharmony_ci &r_ip, &r_port, NULL, NULL); 162713498266Sopenharmony_ci infof(data, "QUIC connect to %s port %u failed: %s", 162813498266Sopenharmony_ci r_ip, r_port, curl_easy_strerror(result)); 162913498266Sopenharmony_ci } 163013498266Sopenharmony_ci#endif 163113498266Sopenharmony_ci if(!result) 163213498266Sopenharmony_ci result = check_and_set_expiry(cf, data); 163313498266Sopenharmony_ci if(result || *done) 163413498266Sopenharmony_ci CURL_TRC_CF(data, cf, "connect -> %d, done=%d", result, *done); 163513498266Sopenharmony_ci CF_DATA_RESTORE(cf, save); 163613498266Sopenharmony_ci return result; 163713498266Sopenharmony_ci} 163813498266Sopenharmony_ci 163913498266Sopenharmony_cistatic ssize_t h3_stream_open(struct Curl_cfilter *cf, 164013498266Sopenharmony_ci struct Curl_easy *data, 164113498266Sopenharmony_ci const void *buf, size_t len, 164213498266Sopenharmony_ci CURLcode *err) 164313498266Sopenharmony_ci{ 164413498266Sopenharmony_ci struct cf_osslq_ctx *ctx = cf->ctx; 164513498266Sopenharmony_ci struct h3_stream_ctx *stream = NULL; 164613498266Sopenharmony_ci struct dynhds h2_headers; 164713498266Sopenharmony_ci size_t nheader; 164813498266Sopenharmony_ci nghttp3_nv *nva = NULL; 164913498266Sopenharmony_ci int rc = 0; 165013498266Sopenharmony_ci unsigned int i; 165113498266Sopenharmony_ci ssize_t nwritten = -1; 165213498266Sopenharmony_ci nghttp3_data_reader reader; 165313498266Sopenharmony_ci nghttp3_data_reader *preader = NULL; 165413498266Sopenharmony_ci 165513498266Sopenharmony_ci Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); 165613498266Sopenharmony_ci 165713498266Sopenharmony_ci *err = h3_data_setup(cf, data); 165813498266Sopenharmony_ci if(*err) 165913498266Sopenharmony_ci goto out; 166013498266Sopenharmony_ci stream = H3_STREAM_CTX(data); 166113498266Sopenharmony_ci DEBUGASSERT(stream); 166213498266Sopenharmony_ci if(!stream) { 166313498266Sopenharmony_ci *err = CURLE_FAILED_INIT; 166413498266Sopenharmony_ci goto out; 166513498266Sopenharmony_ci } 166613498266Sopenharmony_ci 166713498266Sopenharmony_ci nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err); 166813498266Sopenharmony_ci if(nwritten < 0) 166913498266Sopenharmony_ci goto out; 167013498266Sopenharmony_ci if(!stream->h1.done) { 167113498266Sopenharmony_ci /* need more data */ 167213498266Sopenharmony_ci goto out; 167313498266Sopenharmony_ci } 167413498266Sopenharmony_ci DEBUGASSERT(stream->h1.req); 167513498266Sopenharmony_ci 167613498266Sopenharmony_ci *err = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data); 167713498266Sopenharmony_ci if(*err) { 167813498266Sopenharmony_ci nwritten = -1; 167913498266Sopenharmony_ci goto out; 168013498266Sopenharmony_ci } 168113498266Sopenharmony_ci /* no longer needed */ 168213498266Sopenharmony_ci Curl_h1_req_parse_free(&stream->h1); 168313498266Sopenharmony_ci 168413498266Sopenharmony_ci nheader = Curl_dynhds_count(&h2_headers); 168513498266Sopenharmony_ci nva = malloc(sizeof(nghttp3_nv) * nheader); 168613498266Sopenharmony_ci if(!nva) { 168713498266Sopenharmony_ci *err = CURLE_OUT_OF_MEMORY; 168813498266Sopenharmony_ci nwritten = -1; 168913498266Sopenharmony_ci goto out; 169013498266Sopenharmony_ci } 169113498266Sopenharmony_ci 169213498266Sopenharmony_ci for(i = 0; i < nheader; ++i) { 169313498266Sopenharmony_ci struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i); 169413498266Sopenharmony_ci nva[i].name = (unsigned char *)e->name; 169513498266Sopenharmony_ci nva[i].namelen = e->namelen; 169613498266Sopenharmony_ci nva[i].value = (unsigned char *)e->value; 169713498266Sopenharmony_ci nva[i].valuelen = e->valuelen; 169813498266Sopenharmony_ci nva[i].flags = NGHTTP3_NV_FLAG_NONE; 169913498266Sopenharmony_ci } 170013498266Sopenharmony_ci 170113498266Sopenharmony_ci DEBUGASSERT(stream->s.id == -1); 170213498266Sopenharmony_ci *err = cf_osslq_stream_open(&stream->s, ctx->tls.ssl, 0, 170313498266Sopenharmony_ci &ctx->stream_bufcp, data); 170413498266Sopenharmony_ci if(*err) { 170513498266Sopenharmony_ci failf(data, "can't get bidi streams"); 170613498266Sopenharmony_ci *err = CURLE_SEND_ERROR; 170713498266Sopenharmony_ci goto out; 170813498266Sopenharmony_ci } 170913498266Sopenharmony_ci 171013498266Sopenharmony_ci switch(data->state.httpreq) { 171113498266Sopenharmony_ci case HTTPREQ_POST: 171213498266Sopenharmony_ci case HTTPREQ_POST_FORM: 171313498266Sopenharmony_ci case HTTPREQ_POST_MIME: 171413498266Sopenharmony_ci case HTTPREQ_PUT: 171513498266Sopenharmony_ci /* known request body size or -1 */ 171613498266Sopenharmony_ci if(data->state.infilesize != -1) 171713498266Sopenharmony_ci stream->upload_left = data->state.infilesize; 171813498266Sopenharmony_ci else 171913498266Sopenharmony_ci /* data sending without specifying the data amount up front */ 172013498266Sopenharmony_ci stream->upload_left = -1; /* unknown */ 172113498266Sopenharmony_ci break; 172213498266Sopenharmony_ci default: 172313498266Sopenharmony_ci /* there is not request body */ 172413498266Sopenharmony_ci stream->upload_left = 0; /* no request body */ 172513498266Sopenharmony_ci break; 172613498266Sopenharmony_ci } 172713498266Sopenharmony_ci 172813498266Sopenharmony_ci stream->send_closed = (stream->upload_left == 0); 172913498266Sopenharmony_ci if(!stream->send_closed) { 173013498266Sopenharmony_ci reader.read_data = cb_h3_read_req_body; 173113498266Sopenharmony_ci preader = &reader; 173213498266Sopenharmony_ci } 173313498266Sopenharmony_ci 173413498266Sopenharmony_ci rc = nghttp3_conn_submit_request(ctx->h3.conn, stream->s.id, 173513498266Sopenharmony_ci nva, nheader, preader, data); 173613498266Sopenharmony_ci if(rc) { 173713498266Sopenharmony_ci switch(rc) { 173813498266Sopenharmony_ci case NGHTTP3_ERR_CONN_CLOSING: 173913498266Sopenharmony_ci CURL_TRC_CF(data, cf, "h3sid[%"PRId64"] failed to send, " 174013498266Sopenharmony_ci "connection is closing", stream->s.id); 174113498266Sopenharmony_ci break; 174213498266Sopenharmony_ci default: 174313498266Sopenharmony_ci CURL_TRC_CF(data, cf, "h3sid[%"PRId64"] failed to send -> %d (%s)", 174413498266Sopenharmony_ci stream->s.id, rc, nghttp3_strerror(rc)); 174513498266Sopenharmony_ci break; 174613498266Sopenharmony_ci } 174713498266Sopenharmony_ci *err = CURLE_SEND_ERROR; 174813498266Sopenharmony_ci nwritten = -1; 174913498266Sopenharmony_ci goto out; 175013498266Sopenharmony_ci } 175113498266Sopenharmony_ci 175213498266Sopenharmony_ci if(Curl_trc_is_verbose(data)) { 175313498266Sopenharmony_ci infof(data, "[HTTP/3] [%" PRId64 "] OPENED stream for %s", 175413498266Sopenharmony_ci stream->s.id, data->state.url); 175513498266Sopenharmony_ci for(i = 0; i < nheader; ++i) { 175613498266Sopenharmony_ci infof(data, "[HTTP/3] [%" PRId64 "] [%.*s: %.*s]", stream->s.id, 175713498266Sopenharmony_ci (int)nva[i].namelen, nva[i].name, 175813498266Sopenharmony_ci (int)nva[i].valuelen, nva[i].value); 175913498266Sopenharmony_ci } 176013498266Sopenharmony_ci } 176113498266Sopenharmony_ci 176213498266Sopenharmony_ciout: 176313498266Sopenharmony_ci free(nva); 176413498266Sopenharmony_ci Curl_dynhds_free(&h2_headers); 176513498266Sopenharmony_ci return nwritten; 176613498266Sopenharmony_ci} 176713498266Sopenharmony_ci 176813498266Sopenharmony_cistatic ssize_t cf_osslq_send(struct Curl_cfilter *cf, struct Curl_easy *data, 176913498266Sopenharmony_ci const void *buf, size_t len, CURLcode *err) 177013498266Sopenharmony_ci{ 177113498266Sopenharmony_ci struct cf_osslq_ctx *ctx = cf->ctx; 177213498266Sopenharmony_ci struct h3_stream_ctx *stream = H3_STREAM_CTX(data); 177313498266Sopenharmony_ci struct cf_call_data save; 177413498266Sopenharmony_ci ssize_t nwritten; 177513498266Sopenharmony_ci CURLcode result; 177613498266Sopenharmony_ci 177713498266Sopenharmony_ci CF_DATA_SAVE(save, cf, data); 177813498266Sopenharmony_ci DEBUGASSERT(cf->connected); 177913498266Sopenharmony_ci DEBUGASSERT(ctx->tls.ssl); 178013498266Sopenharmony_ci DEBUGASSERT(ctx->h3.conn); 178113498266Sopenharmony_ci *err = CURLE_OK; 178213498266Sopenharmony_ci 178313498266Sopenharmony_ci result = cf_progress_ingress(cf, data); 178413498266Sopenharmony_ci if(result) { 178513498266Sopenharmony_ci *err = result; 178613498266Sopenharmony_ci nwritten = -1; 178713498266Sopenharmony_ci goto out; 178813498266Sopenharmony_ci } 178913498266Sopenharmony_ci 179013498266Sopenharmony_ci result = cf_progress_egress(cf, data); 179113498266Sopenharmony_ci if(result) { 179213498266Sopenharmony_ci *err = result; 179313498266Sopenharmony_ci nwritten = -1; 179413498266Sopenharmony_ci goto out; 179513498266Sopenharmony_ci } 179613498266Sopenharmony_ci 179713498266Sopenharmony_ci if(!stream || stream->s.id < 0) { 179813498266Sopenharmony_ci nwritten = h3_stream_open(cf, data, buf, len, err); 179913498266Sopenharmony_ci if(nwritten < 0) { 180013498266Sopenharmony_ci CURL_TRC_CF(data, cf, "failed to open stream -> %d", *err); 180113498266Sopenharmony_ci goto out; 180213498266Sopenharmony_ci } 180313498266Sopenharmony_ci stream = H3_STREAM_CTX(data); 180413498266Sopenharmony_ci } 180513498266Sopenharmony_ci else if(stream->upload_blocked_len) { 180613498266Sopenharmony_ci /* the data in `buf` has already been submitted or added to the 180713498266Sopenharmony_ci * buffers, but have been EAGAINed on the last invocation. */ 180813498266Sopenharmony_ci DEBUGASSERT(len >= stream->upload_blocked_len); 180913498266Sopenharmony_ci if(len < stream->upload_blocked_len) { 181013498266Sopenharmony_ci /* Did we get called again with a smaller `len`? This should not 181113498266Sopenharmony_ci * happen. We are not prepared to handle that. */ 181213498266Sopenharmony_ci failf(data, "HTTP/3 send again with decreased length"); 181313498266Sopenharmony_ci *err = CURLE_HTTP3; 181413498266Sopenharmony_ci nwritten = -1; 181513498266Sopenharmony_ci goto out; 181613498266Sopenharmony_ci } 181713498266Sopenharmony_ci nwritten = (ssize_t)stream->upload_blocked_len; 181813498266Sopenharmony_ci stream->upload_blocked_len = 0; 181913498266Sopenharmony_ci } 182013498266Sopenharmony_ci else if(stream->closed) { 182113498266Sopenharmony_ci if(stream->resp_hds_complete) { 182213498266Sopenharmony_ci /* Server decided to close the stream after having sent us a final 182313498266Sopenharmony_ci * response. This is valid if it is not interested in the request 182413498266Sopenharmony_ci * body. This happens on 30x or 40x responses. 182513498266Sopenharmony_ci * We silently discard the data sent, since this is not a transport 182613498266Sopenharmony_ci * error situation. */ 182713498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] discarding data" 182813498266Sopenharmony_ci "on closed stream with response", stream->s.id); 182913498266Sopenharmony_ci *err = CURLE_OK; 183013498266Sopenharmony_ci nwritten = (ssize_t)len; 183113498266Sopenharmony_ci goto out; 183213498266Sopenharmony_ci } 183313498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) " 183413498266Sopenharmony_ci "-> stream closed", stream->s.id, len); 183513498266Sopenharmony_ci *err = CURLE_HTTP3; 183613498266Sopenharmony_ci nwritten = -1; 183713498266Sopenharmony_ci goto out; 183813498266Sopenharmony_ci } 183913498266Sopenharmony_ci else { 184013498266Sopenharmony_ci nwritten = Curl_bufq_write(&stream->sendbuf, buf, len, err); 184113498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send, add to " 184213498266Sopenharmony_ci "sendbuf(len=%zu) -> %zd, %d", 184313498266Sopenharmony_ci stream->s.id, len, nwritten, *err); 184413498266Sopenharmony_ci if(nwritten < 0) { 184513498266Sopenharmony_ci goto out; 184613498266Sopenharmony_ci } 184713498266Sopenharmony_ci 184813498266Sopenharmony_ci (void)nghttp3_conn_resume_stream(ctx->h3.conn, stream->s.id); 184913498266Sopenharmony_ci } 185013498266Sopenharmony_ci 185113498266Sopenharmony_ci result = cf_progress_egress(cf, data); 185213498266Sopenharmony_ci if(result) { 185313498266Sopenharmony_ci *err = result; 185413498266Sopenharmony_ci nwritten = -1; 185513498266Sopenharmony_ci } 185613498266Sopenharmony_ci 185713498266Sopenharmony_ci if(stream && nwritten > 0 && stream->sendbuf_len_in_flight) { 185813498266Sopenharmony_ci /* We have unacknowledged DATA and cannot report success to our 185913498266Sopenharmony_ci * caller. Instead we EAGAIN and remember how much we have already 186013498266Sopenharmony_ci * "written" into our various internal connection buffers. */ 186113498266Sopenharmony_ci stream->upload_blocked_len = nwritten; 186213498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu), " 186313498266Sopenharmony_ci "%zu bytes in flight -> EGAIN", stream->s.id, len, 186413498266Sopenharmony_ci stream->sendbuf_len_in_flight); 186513498266Sopenharmony_ci *err = CURLE_AGAIN; 186613498266Sopenharmony_ci nwritten = -1; 186713498266Sopenharmony_ci } 186813498266Sopenharmony_ci 186913498266Sopenharmony_ciout: 187013498266Sopenharmony_ci result = check_and_set_expiry(cf, data); 187113498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu) -> %zd, %d", 187213498266Sopenharmony_ci stream? stream->s.id : -1, len, nwritten, *err); 187313498266Sopenharmony_ci CF_DATA_RESTORE(cf, save); 187413498266Sopenharmony_ci return nwritten; 187513498266Sopenharmony_ci} 187613498266Sopenharmony_ci 187713498266Sopenharmony_cistatic ssize_t recv_closed_stream(struct Curl_cfilter *cf, 187813498266Sopenharmony_ci struct Curl_easy *data, 187913498266Sopenharmony_ci struct h3_stream_ctx *stream, 188013498266Sopenharmony_ci CURLcode *err) 188113498266Sopenharmony_ci{ 188213498266Sopenharmony_ci ssize_t nread = -1; 188313498266Sopenharmony_ci 188413498266Sopenharmony_ci (void)cf; 188513498266Sopenharmony_ci if(stream->reset) { 188613498266Sopenharmony_ci failf(data, 188713498266Sopenharmony_ci "HTTP/3 stream %" PRId64 " reset by server", stream->s.id); 188813498266Sopenharmony_ci *err = stream->resp_hds_complete? CURLE_PARTIAL_FILE : CURLE_HTTP3; 188913498266Sopenharmony_ci goto out; 189013498266Sopenharmony_ci } 189113498266Sopenharmony_ci else if(!stream->resp_hds_complete) { 189213498266Sopenharmony_ci failf(data, 189313498266Sopenharmony_ci "HTTP/3 stream %" PRId64 " was closed cleanly, but before getting" 189413498266Sopenharmony_ci " all response header fields, treated as error", 189513498266Sopenharmony_ci stream->s.id); 189613498266Sopenharmony_ci *err = CURLE_HTTP3; 189713498266Sopenharmony_ci goto out; 189813498266Sopenharmony_ci } 189913498266Sopenharmony_ci *err = CURLE_OK; 190013498266Sopenharmony_ci nread = 0; 190113498266Sopenharmony_ci 190213498266Sopenharmony_ciout: 190313498266Sopenharmony_ci return nread; 190413498266Sopenharmony_ci} 190513498266Sopenharmony_ci 190613498266Sopenharmony_cistatic ssize_t cf_osslq_recv(struct Curl_cfilter *cf, struct Curl_easy *data, 190713498266Sopenharmony_ci char *buf, size_t len, CURLcode *err) 190813498266Sopenharmony_ci{ 190913498266Sopenharmony_ci struct cf_osslq_ctx *ctx = cf->ctx; 191013498266Sopenharmony_ci struct h3_stream_ctx *stream = H3_STREAM_CTX(data); 191113498266Sopenharmony_ci ssize_t nread = -1; 191213498266Sopenharmony_ci struct cf_call_data save; 191313498266Sopenharmony_ci CURLcode result; 191413498266Sopenharmony_ci 191513498266Sopenharmony_ci (void)ctx; 191613498266Sopenharmony_ci CF_DATA_SAVE(save, cf, data); 191713498266Sopenharmony_ci DEBUGASSERT(cf->connected); 191813498266Sopenharmony_ci DEBUGASSERT(ctx); 191913498266Sopenharmony_ci DEBUGASSERT(ctx->tls.ssl); 192013498266Sopenharmony_ci DEBUGASSERT(ctx->h3.conn); 192113498266Sopenharmony_ci *err = CURLE_OK; 192213498266Sopenharmony_ci 192313498266Sopenharmony_ci if(!stream) { 192413498266Sopenharmony_ci *err = CURLE_RECV_ERROR; 192513498266Sopenharmony_ci goto out; 192613498266Sopenharmony_ci } 192713498266Sopenharmony_ci 192813498266Sopenharmony_ci if(!Curl_bufq_is_empty(&stream->recvbuf)) { 192913498266Sopenharmony_ci nread = Curl_bufq_read(&stream->recvbuf, 193013498266Sopenharmony_ci (unsigned char *)buf, len, err); 193113498266Sopenharmony_ci if(nread < 0) { 193213498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) " 193313498266Sopenharmony_ci "-> %zd, %d", stream->s.id, len, nread, *err); 193413498266Sopenharmony_ci goto out; 193513498266Sopenharmony_ci } 193613498266Sopenharmony_ci } 193713498266Sopenharmony_ci 193813498266Sopenharmony_ci result = cf_progress_ingress(cf, data); 193913498266Sopenharmony_ci if(result) { 194013498266Sopenharmony_ci *err = result; 194113498266Sopenharmony_ci nread = -1; 194213498266Sopenharmony_ci goto out; 194313498266Sopenharmony_ci } 194413498266Sopenharmony_ci 194513498266Sopenharmony_ci /* recvbuf had nothing before, maybe after progressing ingress? */ 194613498266Sopenharmony_ci if(nread < 0 && !Curl_bufq_is_empty(&stream->recvbuf)) { 194713498266Sopenharmony_ci nread = Curl_bufq_read(&stream->recvbuf, 194813498266Sopenharmony_ci (unsigned char *)buf, len, err); 194913498266Sopenharmony_ci if(nread < 0) { 195013498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) " 195113498266Sopenharmony_ci "-> %zd, %d", stream->s.id, len, nread, *err); 195213498266Sopenharmony_ci goto out; 195313498266Sopenharmony_ci } 195413498266Sopenharmony_ci } 195513498266Sopenharmony_ci 195613498266Sopenharmony_ci if(nread > 0) { 195713498266Sopenharmony_ci h3_drain_stream(cf, data); 195813498266Sopenharmony_ci } 195913498266Sopenharmony_ci else { 196013498266Sopenharmony_ci if(stream->closed) { 196113498266Sopenharmony_ci nread = recv_closed_stream(cf, data, stream, err); 196213498266Sopenharmony_ci goto out; 196313498266Sopenharmony_ci } 196413498266Sopenharmony_ci *err = CURLE_AGAIN; 196513498266Sopenharmony_ci nread = -1; 196613498266Sopenharmony_ci } 196713498266Sopenharmony_ci 196813498266Sopenharmony_ciout: 196913498266Sopenharmony_ci if(cf_progress_egress(cf, data)) { 197013498266Sopenharmony_ci *err = CURLE_SEND_ERROR; 197113498266Sopenharmony_ci nread = -1; 197213498266Sopenharmony_ci } 197313498266Sopenharmony_ci else { 197413498266Sopenharmony_ci CURLcode result2 = check_and_set_expiry(cf, data); 197513498266Sopenharmony_ci if(result2) { 197613498266Sopenharmony_ci *err = result2; 197713498266Sopenharmony_ci nread = -1; 197813498266Sopenharmony_ci } 197913498266Sopenharmony_ci } 198013498266Sopenharmony_ci CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv(len=%zu) -> %zd, %d", 198113498266Sopenharmony_ci stream? stream->s.id : -1, len, nread, *err); 198213498266Sopenharmony_ci CF_DATA_RESTORE(cf, save); 198313498266Sopenharmony_ci return nread; 198413498266Sopenharmony_ci} 198513498266Sopenharmony_ci 198613498266Sopenharmony_ci/* 198713498266Sopenharmony_ci * Called from transfer.c:data_pending to know if we should keep looping 198813498266Sopenharmony_ci * to receive more data from the connection. 198913498266Sopenharmony_ci */ 199013498266Sopenharmony_cistatic bool cf_osslq_data_pending(struct Curl_cfilter *cf, 199113498266Sopenharmony_ci const struct Curl_easy *data) 199213498266Sopenharmony_ci{ 199313498266Sopenharmony_ci const struct h3_stream_ctx *stream = H3_STREAM_CTX(data); 199413498266Sopenharmony_ci (void)cf; 199513498266Sopenharmony_ci return stream && !Curl_bufq_is_empty(&stream->recvbuf); 199613498266Sopenharmony_ci} 199713498266Sopenharmony_ci 199813498266Sopenharmony_cistatic CURLcode cf_osslq_data_event(struct Curl_cfilter *cf, 199913498266Sopenharmony_ci struct Curl_easy *data, 200013498266Sopenharmony_ci int event, int arg1, void *arg2) 200113498266Sopenharmony_ci{ 200213498266Sopenharmony_ci struct cf_osslq_ctx *ctx = cf->ctx; 200313498266Sopenharmony_ci CURLcode result = CURLE_OK; 200413498266Sopenharmony_ci struct cf_call_data save; 200513498266Sopenharmony_ci 200613498266Sopenharmony_ci CF_DATA_SAVE(save, cf, data); 200713498266Sopenharmony_ci (void)arg1; 200813498266Sopenharmony_ci (void)arg2; 200913498266Sopenharmony_ci switch(event) { 201013498266Sopenharmony_ci case CF_CTRL_DATA_SETUP: 201113498266Sopenharmony_ci break; 201213498266Sopenharmony_ci case CF_CTRL_DATA_PAUSE: 201313498266Sopenharmony_ci result = h3_data_pause(cf, data, (arg1 != 0)); 201413498266Sopenharmony_ci break; 201513498266Sopenharmony_ci case CF_CTRL_DATA_DETACH: 201613498266Sopenharmony_ci h3_data_done(cf, data); 201713498266Sopenharmony_ci break; 201813498266Sopenharmony_ci case CF_CTRL_DATA_DONE: 201913498266Sopenharmony_ci h3_data_done(cf, data); 202013498266Sopenharmony_ci break; 202113498266Sopenharmony_ci case CF_CTRL_DATA_DONE_SEND: { 202213498266Sopenharmony_ci struct h3_stream_ctx *stream = H3_STREAM_CTX(data); 202313498266Sopenharmony_ci if(stream && !stream->send_closed) { 202413498266Sopenharmony_ci stream->send_closed = TRUE; 202513498266Sopenharmony_ci stream->upload_left = Curl_bufq_len(&stream->sendbuf); 202613498266Sopenharmony_ci (void)nghttp3_conn_resume_stream(ctx->h3.conn, stream->s.id); 202713498266Sopenharmony_ci } 202813498266Sopenharmony_ci break; 202913498266Sopenharmony_ci } 203013498266Sopenharmony_ci case CF_CTRL_DATA_IDLE: { 203113498266Sopenharmony_ci struct h3_stream_ctx *stream = H3_STREAM_CTX(data); 203213498266Sopenharmony_ci CURL_TRC_CF(data, cf, "data idle"); 203313498266Sopenharmony_ci if(stream && !stream->closed) { 203413498266Sopenharmony_ci result = check_and_set_expiry(cf, data); 203513498266Sopenharmony_ci } 203613498266Sopenharmony_ci break; 203713498266Sopenharmony_ci } 203813498266Sopenharmony_ci default: 203913498266Sopenharmony_ci break; 204013498266Sopenharmony_ci } 204113498266Sopenharmony_ci CF_DATA_RESTORE(cf, save); 204213498266Sopenharmony_ci return result; 204313498266Sopenharmony_ci} 204413498266Sopenharmony_ci 204513498266Sopenharmony_cistatic bool cf_osslq_conn_is_alive(struct Curl_cfilter *cf, 204613498266Sopenharmony_ci struct Curl_easy *data, 204713498266Sopenharmony_ci bool *input_pending) 204813498266Sopenharmony_ci{ 204913498266Sopenharmony_ci struct cf_osslq_ctx *ctx = cf->ctx; 205013498266Sopenharmony_ci bool alive = FALSE; 205113498266Sopenharmony_ci struct cf_call_data save; 205213498266Sopenharmony_ci 205313498266Sopenharmony_ci CF_DATA_SAVE(save, cf, data); 205413498266Sopenharmony_ci *input_pending = FALSE; 205513498266Sopenharmony_ci if(!ctx->tls.ssl) 205613498266Sopenharmony_ci goto out; 205713498266Sopenharmony_ci 205813498266Sopenharmony_ci /* TODO: how to check negotiated connection idle time? */ 205913498266Sopenharmony_ci 206013498266Sopenharmony_ci if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending)) 206113498266Sopenharmony_ci goto out; 206213498266Sopenharmony_ci 206313498266Sopenharmony_ci alive = TRUE; 206413498266Sopenharmony_ci if(*input_pending) { 206513498266Sopenharmony_ci CURLcode result; 206613498266Sopenharmony_ci /* This happens before we've sent off a request and the connection is 206713498266Sopenharmony_ci not in use by any other transfer, there shouldn't be any data here, 206813498266Sopenharmony_ci only "protocol frames" */ 206913498266Sopenharmony_ci *input_pending = FALSE; 207013498266Sopenharmony_ci result = cf_progress_ingress(cf, data); 207113498266Sopenharmony_ci CURL_TRC_CF(data, cf, "is_alive, progress ingress -> %d", result); 207213498266Sopenharmony_ci alive = result? FALSE : TRUE; 207313498266Sopenharmony_ci } 207413498266Sopenharmony_ci 207513498266Sopenharmony_ciout: 207613498266Sopenharmony_ci CF_DATA_RESTORE(cf, save); 207713498266Sopenharmony_ci return alive; 207813498266Sopenharmony_ci} 207913498266Sopenharmony_ci 208013498266Sopenharmony_cistatic void cf_osslq_adjust_pollset(struct Curl_cfilter *cf, 208113498266Sopenharmony_ci struct Curl_easy *data, 208213498266Sopenharmony_ci struct easy_pollset *ps) 208313498266Sopenharmony_ci{ 208413498266Sopenharmony_ci struct cf_osslq_ctx *ctx = cf->ctx; 208513498266Sopenharmony_ci 208613498266Sopenharmony_ci if(!ctx->tls.ssl) { 208713498266Sopenharmony_ci /* NOP */ 208813498266Sopenharmony_ci } 208913498266Sopenharmony_ci else if(!cf->connected) { 209013498266Sopenharmony_ci /* during handshake, transfer has not started yet. we always 209113498266Sopenharmony_ci * add our socket for polling if SSL wants to send/recv */ 209213498266Sopenharmony_ci Curl_pollset_set(data, ps, ctx->q.sockfd, 209313498266Sopenharmony_ci SSL_net_read_desired(ctx->tls.ssl), 209413498266Sopenharmony_ci SSL_net_write_desired(ctx->tls.ssl)); 209513498266Sopenharmony_ci } 209613498266Sopenharmony_ci else { 209713498266Sopenharmony_ci /* once connected, we only modify the socket if it is present. 209813498266Sopenharmony_ci * this avoids adding it for paused transfers. */ 209913498266Sopenharmony_ci bool want_recv, want_send; 210013498266Sopenharmony_ci Curl_pollset_check(data, ps, ctx->q.sockfd, &want_recv, &want_send); 210113498266Sopenharmony_ci if(want_recv || want_send) { 210213498266Sopenharmony_ci Curl_pollset_set(data, ps, ctx->q.sockfd, 210313498266Sopenharmony_ci SSL_net_read_desired(ctx->tls.ssl), 210413498266Sopenharmony_ci SSL_net_write_desired(ctx->tls.ssl)); 210513498266Sopenharmony_ci } 210613498266Sopenharmony_ci } 210713498266Sopenharmony_ci} 210813498266Sopenharmony_ci 210913498266Sopenharmony_cistatic CURLcode cf_osslq_query(struct Curl_cfilter *cf, 211013498266Sopenharmony_ci struct Curl_easy *data, 211113498266Sopenharmony_ci int query, int *pres1, void *pres2) 211213498266Sopenharmony_ci{ 211313498266Sopenharmony_ci struct cf_osslq_ctx *ctx = cf->ctx; 211413498266Sopenharmony_ci struct cf_call_data save; 211513498266Sopenharmony_ci 211613498266Sopenharmony_ci switch(query) { 211713498266Sopenharmony_ci case CF_QUERY_MAX_CONCURRENT: { 211813498266Sopenharmony_ci /* TODO: how to get this? */ 211913498266Sopenharmony_ci CF_DATA_SAVE(save, cf, data); 212013498266Sopenharmony_ci *pres1 = 100; 212113498266Sopenharmony_ci CURL_TRC_CF(data, cf, "query max_conncurrent -> %d", *pres1); 212213498266Sopenharmony_ci CF_DATA_RESTORE(cf, save); 212313498266Sopenharmony_ci return CURLE_OK; 212413498266Sopenharmony_ci } 212513498266Sopenharmony_ci case CF_QUERY_CONNECT_REPLY_MS: 212613498266Sopenharmony_ci if(ctx->got_first_byte) { 212713498266Sopenharmony_ci timediff_t ms = Curl_timediff(ctx->first_byte_at, ctx->started_at); 212813498266Sopenharmony_ci *pres1 = (ms < INT_MAX)? (int)ms : INT_MAX; 212913498266Sopenharmony_ci } 213013498266Sopenharmony_ci else 213113498266Sopenharmony_ci *pres1 = -1; 213213498266Sopenharmony_ci return CURLE_OK; 213313498266Sopenharmony_ci case CF_QUERY_TIMER_CONNECT: { 213413498266Sopenharmony_ci struct curltime *when = pres2; 213513498266Sopenharmony_ci if(ctx->got_first_byte) 213613498266Sopenharmony_ci *when = ctx->first_byte_at; 213713498266Sopenharmony_ci return CURLE_OK; 213813498266Sopenharmony_ci } 213913498266Sopenharmony_ci case CF_QUERY_TIMER_APPCONNECT: { 214013498266Sopenharmony_ci struct curltime *when = pres2; 214113498266Sopenharmony_ci if(cf->connected) 214213498266Sopenharmony_ci *when = ctx->handshake_at; 214313498266Sopenharmony_ci return CURLE_OK; 214413498266Sopenharmony_ci } 214513498266Sopenharmony_ci default: 214613498266Sopenharmony_ci break; 214713498266Sopenharmony_ci } 214813498266Sopenharmony_ci return cf->next? 214913498266Sopenharmony_ci cf->next->cft->query(cf->next, data, query, pres1, pres2) : 215013498266Sopenharmony_ci CURLE_UNKNOWN_OPTION; 215113498266Sopenharmony_ci} 215213498266Sopenharmony_ci 215313498266Sopenharmony_cistruct Curl_cftype Curl_cft_http3 = { 215413498266Sopenharmony_ci "HTTP/3", 215513498266Sopenharmony_ci CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX, 215613498266Sopenharmony_ci 0, 215713498266Sopenharmony_ci cf_osslq_destroy, 215813498266Sopenharmony_ci cf_osslq_connect, 215913498266Sopenharmony_ci cf_osslq_close, 216013498266Sopenharmony_ci Curl_cf_def_get_host, 216113498266Sopenharmony_ci cf_osslq_adjust_pollset, 216213498266Sopenharmony_ci cf_osslq_data_pending, 216313498266Sopenharmony_ci cf_osslq_send, 216413498266Sopenharmony_ci cf_osslq_recv, 216513498266Sopenharmony_ci cf_osslq_data_event, 216613498266Sopenharmony_ci cf_osslq_conn_is_alive, 216713498266Sopenharmony_ci Curl_cf_def_conn_keep_alive, 216813498266Sopenharmony_ci cf_osslq_query, 216913498266Sopenharmony_ci}; 217013498266Sopenharmony_ci 217113498266Sopenharmony_ciCURLcode Curl_cf_osslq_create(struct Curl_cfilter **pcf, 217213498266Sopenharmony_ci struct Curl_easy *data, 217313498266Sopenharmony_ci struct connectdata *conn, 217413498266Sopenharmony_ci const struct Curl_addrinfo *ai) 217513498266Sopenharmony_ci{ 217613498266Sopenharmony_ci struct cf_osslq_ctx *ctx = NULL; 217713498266Sopenharmony_ci struct Curl_cfilter *cf = NULL, *udp_cf = NULL; 217813498266Sopenharmony_ci CURLcode result; 217913498266Sopenharmony_ci 218013498266Sopenharmony_ci (void)data; 218113498266Sopenharmony_ci ctx = calloc(1, sizeof(*ctx)); 218213498266Sopenharmony_ci if(!ctx) { 218313498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 218413498266Sopenharmony_ci goto out; 218513498266Sopenharmony_ci } 218613498266Sopenharmony_ci cf_osslq_ctx_clear(ctx); 218713498266Sopenharmony_ci 218813498266Sopenharmony_ci result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); 218913498266Sopenharmony_ci if(result) 219013498266Sopenharmony_ci goto out; 219113498266Sopenharmony_ci 219213498266Sopenharmony_ci result = Curl_cf_udp_create(&udp_cf, data, conn, ai, TRNSPRT_QUIC); 219313498266Sopenharmony_ci if(result) 219413498266Sopenharmony_ci goto out; 219513498266Sopenharmony_ci 219613498266Sopenharmony_ci cf->conn = conn; 219713498266Sopenharmony_ci udp_cf->conn = cf->conn; 219813498266Sopenharmony_ci udp_cf->sockindex = cf->sockindex; 219913498266Sopenharmony_ci cf->next = udp_cf; 220013498266Sopenharmony_ci 220113498266Sopenharmony_ciout: 220213498266Sopenharmony_ci *pcf = (!result)? cf : NULL; 220313498266Sopenharmony_ci if(result) { 220413498266Sopenharmony_ci if(udp_cf) 220513498266Sopenharmony_ci Curl_conn_cf_discard_sub(cf, udp_cf, data, TRUE); 220613498266Sopenharmony_ci Curl_safefree(cf); 220713498266Sopenharmony_ci Curl_safefree(ctx); 220813498266Sopenharmony_ci } 220913498266Sopenharmony_ci return result; 221013498266Sopenharmony_ci} 221113498266Sopenharmony_ci 221213498266Sopenharmony_cibool Curl_conn_is_osslq(const struct Curl_easy *data, 221313498266Sopenharmony_ci const struct connectdata *conn, 221413498266Sopenharmony_ci int sockindex) 221513498266Sopenharmony_ci{ 221613498266Sopenharmony_ci struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL; 221713498266Sopenharmony_ci 221813498266Sopenharmony_ci (void)data; 221913498266Sopenharmony_ci for(; cf; cf = cf->next) { 222013498266Sopenharmony_ci if(cf->cft == &Curl_cft_http3) 222113498266Sopenharmony_ci return TRUE; 222213498266Sopenharmony_ci if(cf->cft->flags & CF_TYPE_IP_CONNECT) 222313498266Sopenharmony_ci return FALSE; 222413498266Sopenharmony_ci } 222513498266Sopenharmony_ci return FALSE; 222613498266Sopenharmony_ci} 222713498266Sopenharmony_ci 222813498266Sopenharmony_ci/* 222913498266Sopenharmony_ci * Store ngtcp2 version info in this buffer. 223013498266Sopenharmony_ci */ 223113498266Sopenharmony_civoid Curl_osslq_ver(char *p, size_t len) 223213498266Sopenharmony_ci{ 223313498266Sopenharmony_ci const nghttp3_info *ht3 = nghttp3_version(0); 223413498266Sopenharmony_ci (void)msnprintf(p, len, "nghttp3/%s", ht3->version_str); 223513498266Sopenharmony_ci} 223613498266Sopenharmony_ci 223713498266Sopenharmony_ci#endif /* USE_OPENSSL_QUIC && USE_NGHTTP3 */ 2238