diff options
Diffstat (limited to 'libs/libcurl/src/vquic')
| -rw-r--r-- | libs/libcurl/src/vquic/curl_msh3.c | 30 | ||||
| -rw-r--r-- | libs/libcurl/src/vquic/curl_ngtcp2.c | 156 | ||||
| -rw-r--r-- | libs/libcurl/src/vquic/curl_osslq.c | 220 | ||||
| -rw-r--r-- | libs/libcurl/src/vquic/curl_quiche.c | 221 | ||||
| -rw-r--r-- | libs/libcurl/src/vquic/vquic-tls.c | 197 | ||||
| -rw-r--r-- | libs/libcurl/src/vquic/vquic-tls.h | 10 | ||||
| -rw-r--r-- | libs/libcurl/src/vquic/vquic.c | 44 |
7 files changed, 411 insertions, 467 deletions
diff --git a/libs/libcurl/src/vquic/curl_msh3.c b/libs/libcurl/src/vquic/curl_msh3.c index ae9e79a662..c3f1375016 100644 --- a/libs/libcurl/src/vquic/curl_msh3.c +++ b/libs/libcurl/src/vquic/curl_msh3.c @@ -28,6 +28,7 @@ #include "urldata.h"
#include "hash.h"
+#include "hash_offt.h"
#include "timeval.h"
#include "multiif.h"
#include "sendf.h"
@@ -119,7 +120,7 @@ struct cf_msh3_ctx { struct cf_call_data call_data;
struct curltime connect_started; /* time the current attempt started */
struct curltime handshake_at; /* time connect handshake finished */
- struct Curl_hash streams; /* hash `data->mid` to `stream_ctx` */
+ struct Curl_hash_offt streams; /* hash `data->mid` to `stream_ctx` */
/* Flags written by msh3/msquic thread */
bool handshake_complete;
bool handshake_succeeded;
@@ -130,7 +131,7 @@ struct cf_msh3_ctx { BIT(active);
};
-static void h3_stream_hash_free(void *stream);
+static void h3_stream_hash_free(curl_off_t id, void *stream);
static CURLcode cf_msh3_ctx_init(struct cf_msh3_ctx *ctx,
const struct Curl_addrinfo *ai)
@@ -154,7 +155,7 @@ static CURLcode cf_msh3_ctx_init(struct cf_msh3_ctx *ctx, static void cf_msh3_ctx_free(struct cf_msh3_ctx *ctx)
{
if(ctx && ctx->initialized) {
- Curl_hash_destroy(&ctx->streams);
+ Curl_hash_offt_destroy(&ctx->streams);
}
free(ctx);
}
@@ -196,8 +197,9 @@ static void h3_stream_ctx_free(struct stream_ctx *stream) free(stream);
}
-static void h3_stream_hash_free(void *stream)
+static void h3_stream_hash_free(curl_off_t id, void *stream)
{
+ (void)id;
DEBUGASSERT(stream);
h3_stream_ctx_free((struct stream_ctx *)stream);
}
@@ -398,7 +400,7 @@ static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request, msh3_lock_acquire(&stream->recv_lock);
if((hd->NameLength == 7) &&
- !strncmp(HTTP_PSEUDO_STATUS, (char *)hd->Name, 7)) {
+ !strncmp(HTTP_PSEUDO_STATUS, (const char *)hd->Name, 7)) {
char line[14]; /* status line is always 13 characters long */
size_t ncopy;
@@ -752,12 +754,13 @@ static bool cf_msh3_data_pending(struct Curl_cfilter *cf, (void)cf;
if(stream && stream->req) {
msh3_lock_acquire(&stream->recv_lock);
- CURL_TRC_CF((struct Curl_easy *)data, cf, "data pending = %zu",
+ CURL_TRC_CF((struct Curl_easy *)CURL_UNCONST(data), cf,
+ "data pending = %zu",
Curl_bufq_len(&stream->recvbuf));
pending = !Curl_bufq_is_empty(&stream->recvbuf);
msh3_lock_release(&stream->recv_lock);
if(pending)
- h3_drain_stream(cf, (struct Curl_easy *)data);
+ h3_drain_stream(cf, (struct Curl_easy *)CURL_UNCONST(data));
}
CF_DATA_RESTORE(cf, save);
@@ -838,16 +841,10 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, MSH3_SET_PORT(&addr, (uint16_t)cf->conn->remote_port);
if(verify && (conn_config->CAfile || conn_config->CApath)) {
- /* Need a way to provide trust anchors to MSH3 */
-#ifdef DEBUGBUILD
- /* we need this for our test cases to run */
- CURL_TRC_CF(data, cf, "non-standard CA not supported, "
- "switching off verifypeer in DEBUG mode");
- verify = 0;
-#else
+ /* Note there's currently no way to provide trust anchors to MSH3 and
+ that causes tests to fail. */
CURL_TRC_CF(data, cf, "non-standard CA not supported, "
"attempting with built-in verification");
-#endif
}
CURL_TRC_CF(data, cf, "connecting to %s:%d (verify=%d)",
@@ -883,13 +880,12 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, static CURLcode cf_msh3_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
- bool blocking, bool *done)
+ bool *done)
{
struct cf_msh3_ctx *ctx = cf->ctx;
struct cf_call_data save;
CURLcode result = CURLE_OK;
- (void)blocking;
if(cf->connected) {
*done = TRUE;
return CURLE_OK;
diff --git a/libs/libcurl/src/vquic/curl_ngtcp2.c b/libs/libcurl/src/vquic/curl_ngtcp2.c index 8f54f159ee..7b3f0f9b7e 100644 --- a/libs/libcurl/src/vquic/curl_ngtcp2.c +++ b/libs/libcurl/src/vquic/curl_ngtcp2.c @@ -45,7 +45,7 @@ #endif
#include "urldata.h"
-#include "hash.h"
+#include "hash_offt.h"
#include "sendf.h"
#include "strdup.h"
#include "rand.h"
@@ -133,7 +133,7 @@ struct cf_ngtcp2_ctx { struct curltime handshake_at; /* time connect handshake finished */
struct bufc_pool stream_bufcp; /* chunk pool for streams */
struct dynbuf scratch; /* temp buffer for header construction */
- struct Curl_hash streams; /* hash `data->mid` to `h3_stream_ctx` */
+ struct Curl_hash_offt streams; /* hash `data->mid` to `h3_stream_ctx` */
size_t max_stream_window; /* max flow window for one stream */
uint64_t max_idle_ms; /* max idle time for QUIC connection */
uint64_t used_bidi_streams; /* bidi streams we have opened */
@@ -156,7 +156,7 @@ struct cf_ngtcp2_ctx { #define CF_CTX_CALL_DATA(cf) \
((struct cf_ngtcp2_ctx *)(cf)->ctx)->call_data
-static void h3_stream_hash_free(void *stream);
+static void h3_stream_hash_free(curl_off_t id, void *stream);
static void cf_ngtcp2_ctx_init(struct cf_ngtcp2_ctx *ctx)
{
@@ -179,8 +179,7 @@ static void cf_ngtcp2_ctx_free(struct cf_ngtcp2_ctx *ctx) vquic_ctx_free(&ctx->q);
Curl_bufcp_free(&ctx->stream_bufcp);
Curl_dyn_free(&ctx->scratch);
- Curl_hash_clean(&ctx->streams);
- Curl_hash_destroy(&ctx->streams);
+ Curl_hash_offt_destroy(&ctx->streams);
Curl_ssl_peer_cleanup(&ctx->peer);
}
free(ctx);
@@ -225,8 +224,9 @@ static void h3_stream_ctx_free(struct h3_stream_ctx *stream) free(stream);
}
-static void h3_stream_hash_free(void *stream)
+static void h3_stream_hash_free(curl_off_t id, void *stream)
{
+ (void)id;
DEBUGASSERT(stream);
h3_stream_ctx_free((struct h3_stream_ctx *)stream);
}
@@ -297,38 +297,6 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) }
}
-static struct Curl_easy *get_stream_easy(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- int64_t stream_id,
- struct h3_stream_ctx **pstream)
-{
- struct cf_ngtcp2_ctx *ctx = cf->ctx;
- struct h3_stream_ctx *stream;
-
- (void)cf;
- stream = H3_STREAM_CTX(ctx, data);
- if(stream && stream->id == stream_id) {
- *pstream = stream;
- return data;
- }
- else {
- struct Curl_llist_node *e;
- DEBUGASSERT(data->multi);
- for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) {
- struct Curl_easy *sdata = Curl_node_elem(e);
- if(sdata->conn != data->conn)
- continue;
- stream = H3_STREAM_CTX(ctx, sdata);
- if(stream && stream->id == stream_id) {
- *pstream = stream;
- return sdata;
- }
- }
- }
- *pstream = NULL;
- return NULL;
-}
-
static void h3_drain_stream(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
@@ -481,17 +449,32 @@ static int cf_ngtcp2_handshake_completed(ngtcp2_conn *tconn, void *user_data) * the handshake time when we really did connect */
if(ctx->use_earlydata)
Curl_pgrsTimeWas(data, TIMER_APPCONNECT, ctx->handshake_at);
-#ifdef USE_GNUTLS
if(ctx->use_earlydata) {
+#if defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA)
+ ctx->earlydata_accepted =
+ (SSL_get_early_data_status(ctx->tls.ossl.ssl) !=
+ SSL_EARLY_DATA_REJECTED);
+#endif
+#ifdef USE_GNUTLS
int flags = gnutls_session_get_flags(ctx->tls.gtls.session);
ctx->earlydata_accepted = !!(flags & GNUTLS_SFLAGS_EARLY_DATA);
+#endif
+#ifdef USE_WOLFSSL
+#ifdef WOLFSSL_EARLY_DATA
+ ctx->earlydata_accepted =
+ (wolfSSL_get_early_data_status(ctx->tls.wssl.ssl) !=
+ WOLFSSL_EARLY_DATA_REJECTED);
+#else
+ DEBUGASSERT(0); /* should not come here if ED is disabled. */
+ ctx->earlydata_accepted = FALSE;
+#endif /* WOLFSSL_EARLY_DATA */
+#endif
CURL_TRC_CF(data, cf, "server did%s accept %zu bytes of early data",
ctx->earlydata_accepted ? "" : " not", ctx->earlydata_skip);
Curl_pgrsEarlyData(data, ctx->earlydata_accepted ?
(curl_off_t)ctx->earlydata_skip :
-(curl_off_t)ctx->earlydata_skip);
}
-#endif
return 0;
}
@@ -695,28 +678,26 @@ static int cb_extend_max_local_streams_bidi(ngtcp2_conn *tconn, return 0;
}
-static int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t sid,
+static int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t stream_id,
uint64_t max_data, void *user_data,
void *stream_user_data)
{
struct Curl_cfilter *cf = user_data;
struct cf_ngtcp2_ctx *ctx = cf->ctx;
- curl_int64_t stream_id = (curl_int64_t)sid;
- struct Curl_easy *data = CF_DATA_CURRENT(cf);
- struct Curl_easy *s_data;
+ struct Curl_easy *s_data = stream_user_data;
struct h3_stream_ctx *stream;
int rv;
(void)tconn;
(void)max_data;
- (void)stream_user_data;
rv = nghttp3_conn_unblock_stream(ctx->h3conn, stream_id);
if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
return NGTCP2_ERR_CALLBACK_FAILURE;
}
- s_data = get_stream_easy(cf, data, stream_id, &stream);
- if(s_data && stream && stream->quic_flow_blocked) {
- CURL_TRC_CF(s_data, cf, "[%" FMT_PRId64 "] unblock quic flow", stream_id);
+ stream = H3_STREAM_CTX(ctx, s_data);
+ if(stream && stream->quic_flow_blocked) {
+ CURL_TRC_CF(s_data, cf, "[%" FMT_PRId64 "] unblock quic flow",
+ (curl_int64_t)stream_id);
stream->quic_flow_blocked = FALSE;
h3_drain_stream(cf, s_data);
}
@@ -996,7 +977,7 @@ static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id, if(!stream)
return NGHTTP3_ERR_CALLBACK_FAILURE;
- h3_xfer_write_resp(cf, data, stream, (char *)buf, blen, FALSE);
+ h3_xfer_write_resp(cf, data, stream, (const char *)buf, blen, FALSE);
if(blen) {
CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] ACK %zu bytes of DATA",
stream->id, blen);
@@ -1400,7 +1381,7 @@ cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id, while(nvecs < veccnt &&
Curl_bufq_peek_at(&stream->sendbuf,
stream->sendbuf_len_in_flight,
- (const unsigned char **)&vec[nvecs].base,
+ CURL_UNCONST(&vec[nvecs].base),
&vec[nvecs].len)) {
stream->sendbuf_len_in_flight += vec[nvecs].len;
nwritten += vec[nvecs].len;
@@ -1759,7 +1740,7 @@ static ssize_t read_pkt_to_send(void *userp, if(ctx->h3conn && ngtcp2_conn_get_max_data_left(ctx->qconn)) {
veccnt = nghttp3_conn_writev_stream(ctx->h3conn, &stream_id, &fin, vec,
- sizeof(vec) / sizeof(vec[0]));
+ CURL_ARRAYSIZE(vec));
if(veccnt < 0) {
failf(x->data, "nghttp3_conn_writev_stream returned error: %s",
nghttp3_strerror((int)veccnt));
@@ -2173,8 +2154,24 @@ static int quic_ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid) ctx = cf ? cf->ctx : NULL;
data = cf ? CF_DATA_CURRENT(cf) : NULL;
if(cf && data && ctx) {
+ unsigned char *quic_tp = NULL;
+ size_t quic_tp_len = 0;
+#ifdef HAVE_OPENSSL_EARLYDATA
+ ngtcp2_ssize tplen;
+ uint8_t tpbuf[256];
+
+ tplen = ngtcp2_conn_encode_0rtt_transport_params(ctx->qconn, tpbuf,
+ sizeof(tpbuf));
+ if(tplen < 0)
+ CURL_TRC_CF(data, cf, "error encoding 0RTT transport data: %s",
+ ngtcp2_strerror((int)tplen));
+ else {
+ quic_tp = (unsigned char *)tpbuf;
+ quic_tp_len = (size_t)tplen;
+ }
+#endif
Curl_ossl_add_session(cf, data, ctx->peer.scache_key, ssl_sessionid,
- SSL_version(ssl), "h3");
+ SSL_version(ssl), "h3", quic_tp, quic_tp_len);
return 1;
}
return 0;
@@ -2257,8 +2254,23 @@ static int wssl_quic_new_session_cb(WOLFSSL *ssl, WOLFSSL_SESSION *session) struct Curl_easy *data = CF_DATA_CURRENT(cf);
DEBUGASSERT(data);
if(data && ctx) {
+ ngtcp2_ssize tplen;
+ uint8_t tpbuf[256];
+ unsigned char *quic_tp = NULL;
+ size_t quic_tp_len = 0;
+
+ tplen = ngtcp2_conn_encode_0rtt_transport_params(ctx->qconn, tpbuf,
+ sizeof(tpbuf));
+ if(tplen < 0)
+ CURL_TRC_CF(data, cf, "error encoding 0RTT transport data: %s",
+ ngtcp2_strerror((int)tplen));
+ else {
+ quic_tp = (unsigned char *)tpbuf;
+ quic_tp_len = (size_t)tplen;
+ }
(void)Curl_wssl_cache_session(cf, data, ctx->peer.scache_key,
- session, wolfSSL_version(ssl), "h3");
+ session, wolfSSL_version(ssl),
+ "h3", quic_tp, quic_tp_len);
}
}
return 0;
@@ -2308,13 +2320,13 @@ static CURLcode cf_ngtcp2_tls_ctx_setup(struct Curl_cfilter *cf, }
#elif defined(USE_WOLFSSL)
- if(ngtcp2_crypto_wolfssl_configure_client_context(ctx->wssl.ctx) != 0) {
+ if(ngtcp2_crypto_wolfssl_configure_client_context(ctx->wssl.ssl_ctx) != 0) {
failf(data, "ngtcp2_crypto_wolfssl_configure_client_context failed");
return CURLE_FAILED_INIT;
}
if(ssl_config->primary.cache_session) {
/* Register to get notified when a new session is received */
- wolfSSL_CTX_sess_set_new_cb(ctx->wssl.ctx, wssl_quic_new_session_cb);
+ wolfSSL_CTX_sess_set_new_cb(ctx->wssl.ssl_ctx, wssl_quic_new_session_cb);
}
#endif
return CURLE_OK;
@@ -2322,6 +2334,7 @@ static CURLcode cf_ngtcp2_tls_ctx_setup(struct Curl_cfilter *cf, static CURLcode cf_ngtcp2_on_session_reuse(struct Curl_cfilter *cf,
struct Curl_easy *data,
+ struct alpn_spec *alpns,
struct Curl_ssl_session *scs,
bool *do_early_data)
{
@@ -2329,13 +2342,26 @@ static CURLcode cf_ngtcp2_on_session_reuse(struct Curl_cfilter *cf, CURLcode result = CURLE_OK;
*do_early_data = FALSE;
+#if defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA)
+ ctx->earlydata_max = scs->earlydata_max;
+#endif
#ifdef USE_GNUTLS
ctx->earlydata_max =
gnutls_record_get_max_early_data_size(ctx->tls.gtls.session);
+#endif
+#ifdef USE_WOLFSSL
+#ifdef WOLFSSL_EARLY_DATA
+ ctx->earlydata_max = scs->earlydata_max;
+#else
+ ctx->earlydata_max = 0;
+#endif /* WOLFSSL_EARLY_DATA */
+#endif
+#if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || \
+ (defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA))
if((!ctx->earlydata_max)) {
CURL_TRC_CF(data, cf, "SSL session does not allow earlydata");
}
- else if(strcmp("h3", scs->alpn)) {
+ else if(!Curl_alpn_contains_proto(alpns, scs->alpn)) {
CURL_TRC_CF(data, cf, "SSL session from different ALPN, no early data");
}
else if(!scs->quic_tp || !scs->quic_tp_len) {
@@ -2344,7 +2370,7 @@ static CURLcode cf_ngtcp2_on_session_reuse(struct Curl_cfilter *cf, else {
int rv;
rv = ngtcp2_conn_decode_and_set_0rtt_transport_params(
- ctx->qconn, (uint8_t *)scs->quic_tp, scs->quic_tp_len);
+ ctx->qconn, (const uint8_t *)scs->quic_tp, scs->quic_tp_len);
if(rv)
CURL_TRC_CF(data, cf, "no early data, failed to set 0RTT transport "
"parameters: %s", ngtcp2_strerror(rv));
@@ -2359,10 +2385,11 @@ static CURLcode cf_ngtcp2_on_session_reuse(struct Curl_cfilter *cf, }
}
}
-#else /* USE_GNUTLS */
+#else /* not supported in the TLS backend */
(void)data;
(void)ctx;
(void)scs;
+ (void)alpns;
#endif
return result;
}
@@ -2380,6 +2407,9 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, CURLcode result;
const struct Curl_sockaddr_ex *sockaddr = NULL;
int qfd;
+static const struct alpn_spec ALPN_SPEC_H3 = {
+ { "h3", "h3-29" }, 2
+};
DEBUGASSERT(ctx->initialized);
ctx->dcid.datalen = NGTCP2_MAX_CIDLEN;
@@ -2423,9 +2453,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, if(rc)
return CURLE_QUIC_CONNECT_ERROR;
-#define H3_ALPN "\x2h3\x5h3-29"
- result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer,
- H3_ALPN, sizeof(H3_ALPN) - 1,
+ result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer, &ALPN_SPEC_H3,
cf_ngtcp2_tls_ctx_setup, &ctx->tls,
&ctx->conn_ref,
cf_ngtcp2_on_session_reuse);
@@ -2438,7 +2466,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, #elif defined(USE_GNUTLS)
ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.gtls.session);
#elif defined(USE_WOLFSSL)
- ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.wssl.handle);
+ ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.wssl.ssl);
#else
#error "ngtcp2 TLS backend not defined"
#endif
@@ -2453,7 +2481,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf, static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
- bool blocking, bool *done)
+ bool *done)
{
struct cf_ngtcp2_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
@@ -2468,7 +2496,7 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf, /* Connect the UDP filter first */
if(!cf->next->connected) {
- result = Curl_conn_cf_connect(cf->next, data, blocking, done);
+ result = Curl_conn_cf_connect(cf->next, data, done);
if(result || !*done)
return result;
}
diff --git a/libs/libcurl/src/vquic/curl_osslq.c b/libs/libcurl/src/vquic/curl_osslq.c index 47dbd9afa2..7dbd0f13f3 100644 --- a/libs/libcurl/src/vquic/curl_osslq.c +++ b/libs/libcurl/src/vquic/curl_osslq.c @@ -171,7 +171,7 @@ static CURLcode make_bio_addr(BIO_ADDR **pbio_addr, switch(addr->family) {
case AF_INET: {
struct sockaddr_in * const sin =
- (struct sockaddr_in * const)(void *)&addr->curl_sa_addr;
+ (struct sockaddr_in * const)CURL_UNCONST(&addr->curl_sa_addr);
if(!BIO_ADDR_rawmake(ba, AF_INET, &sin->sin_addr,
sizeof(sin->sin_addr), sin->sin_port)) {
goto out;
@@ -182,7 +182,7 @@ static CURLcode make_bio_addr(BIO_ADDR **pbio_addr, #ifdef USE_IPV6
case AF_INET6: {
struct sockaddr_in6 * const sin =
- (struct sockaddr_in6 * const)(void *)&addr->curl_sa_addr;
+ (struct sockaddr_in6 * const)CURL_UNCONST(&addr->curl_sa_addr);
if(!BIO_ADDR_rawmake(ba, AF_INET6, &sin->sin6_addr,
sizeof(sin->sin6_addr), sin->sin6_port)) {
}
@@ -285,12 +285,12 @@ struct cf_osslq_ctx { struct curltime handshake_at; /* time connect handshake finished */
struct curltime first_byte_at; /* when first byte was recvd */
struct bufc_pool stream_bufcp; /* chunk pool for streams */
- struct Curl_hash streams; /* hash `data->mid` to `h3_stream_ctx` */
+ struct Curl_hash_offt streams; /* hash `data->mid` to `h3_stream_ctx` */
size_t max_stream_window; /* max flow window for one stream */
uint64_t max_idle_ms; /* max idle time for QUIC connection */
SSL_POLL_ITEM *poll_items; /* Array for polling on writable state */
struct Curl_easy **curl_items; /* Array of easy objs */
- size_t item_count; /* count of elements in poll/curl_items */
+ size_t items_max; /* max elements in poll/curl_items */
BIT(initialized);
BIT(got_first_byte); /* if first byte was received */
BIT(x509_store_setup); /* if x509 store has been set up */
@@ -299,7 +299,7 @@ struct cf_osslq_ctx { BIT(need_send); /* QUIC connection needs to send */
};
-static void h3_stream_hash_free(void *stream);
+static void h3_stream_hash_free(curl_off_t id, void *stream);
static void cf_osslq_ctx_init(struct cf_osslq_ctx *ctx)
{
@@ -309,7 +309,7 @@ static void cf_osslq_ctx_init(struct cf_osslq_ctx *ctx) Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free);
ctx->poll_items = NULL;
ctx->curl_items = NULL;
- ctx->item_count = 0;
+ ctx->items_max = 0;
ctx->initialized = TRUE;
}
@@ -317,8 +317,7 @@ static void cf_osslq_ctx_free(struct cf_osslq_ctx *ctx) {
if(ctx && ctx->initialized) {
Curl_bufcp_free(&ctx->stream_bufcp);
- Curl_hash_clean(&ctx->streams);
- Curl_hash_destroy(&ctx->streams);
+ Curl_hash_offt_destroy(&ctx->streams);
Curl_ssl_peer_cleanup(&ctx->peer);
free(ctx->poll_items);
free(ctx->curl_items);
@@ -603,8 +602,9 @@ static void h3_stream_ctx_free(struct h3_stream_ctx *stream) free(stream);
}
-static void h3_stream_hash_free(void *stream)
+static void h3_stream_hash_free(curl_off_t id, void *stream)
{
+ (void)id;
DEBUGASSERT(stream);
h3_stream_ctx_free((struct h3_stream_ctx *)stream);
}
@@ -666,6 +666,24 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) }
}
+struct cf_ossq_find_ctx {
+ curl_int64_t stream_id;
+ struct h3_stream_ctx *stream;
+};
+
+static bool cf_osslq_find_stream(curl_off_t mid, void *val, void *user_data)
+{
+ struct h3_stream_ctx *stream = val;
+ struct cf_ossq_find_ctx *fctx = user_data;
+
+ (void)mid;
+ if(stream && stream->s.id == fctx->stream_id) {
+ fctx->stream = stream;
+ return FALSE; /* stop iterating */
+ }
+ return TRUE;
+}
+
static struct cf_osslq_stream *cf_osslq_get_qstream(struct Curl_cfilter *cf,
struct Curl_easy *data,
int64_t stream_id)
@@ -686,17 +704,12 @@ static struct cf_osslq_stream *cf_osslq_get_qstream(struct Curl_cfilter *cf, return &ctx->h3.s_qpack_dec;
}
else {
- struct Curl_llist_node *e;
- DEBUGASSERT(data->multi);
- for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) {
- struct Curl_easy *sdata = Curl_node_elem(e);
- if(sdata->conn != data->conn)
- continue;
- stream = H3_STREAM_CTX(ctx, sdata);
- if(stream && stream->s.id == stream_id) {
- return &stream->s;
- }
- }
+ struct cf_ossq_find_ctx fctx;
+ fctx.stream_id = stream_id;
+ fctx.stream = NULL;
+ Curl_hash_offt_visit(&ctx->streams, cf_osslq_find_stream, &fctx);
+ if(fctx.stream)
+ return &fctx.stream->s;
}
return NULL;
}
@@ -1012,7 +1025,7 @@ cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id, while(nvecs < veccnt &&
Curl_bufq_peek_at(&stream->sendbuf,
stream->sendbuf_len_in_flight,
- (const unsigned char **)&vec[nvecs].base,
+ CURL_UNCONST(&vec[nvecs].base),
&vec[nvecs].len)) {
stream->sendbuf_len_in_flight += vec[nvecs].len;
nwritten += vec[nvecs].len;
@@ -1163,13 +1176,15 @@ static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf, const struct Curl_sockaddr_ex *peer_addr = NULL;
BIO *bio = NULL;
BIO_ADDR *baddr = NULL;
+static const struct alpn_spec ALPN_SPEC_H3 = {
+ { "h3" }, 1
+};
DEBUGASSERT(ctx->initialized);
#define H3_ALPN "\x2h3"
result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer,
- H3_ALPN, sizeof(H3_ALPN) - 1,
- NULL, NULL, NULL, NULL);
+ &ALPN_SPEC_H3, NULL, NULL, NULL, NULL);
if(result)
goto out;
@@ -1399,6 +1414,29 @@ out: return result;
}
+struct cf_ossq_recv_ctx {
+ struct Curl_cfilter *cf;
+ struct Curl_multi *multi;
+ CURLcode result;
+};
+
+static bool cf_osslq_iter_recv(curl_off_t mid, void *val, void *user_data)
+{
+ struct h3_stream_ctx *stream = val;
+ struct cf_ossq_recv_ctx *rctx = user_data;
+
+ (void)mid;
+ if(stream && !stream->closed && !Curl_bufq_is_full(&stream->recvbuf)) {
+ struct Curl_easy *sdata = Curl_multi_get_handle(rctx->multi, mid);
+ if(sdata) {
+ rctx->result = cf_osslq_stream_recv(&stream->s, rctx->cf, sdata);
+ if(rctx->result)
+ return FALSE; /* abort iteration */
+ }
+ }
+ return TRUE;
+}
+
static CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
@@ -1435,22 +1473,14 @@ static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, }
if(ctx->h3.conn) {
- struct Curl_llist_node *e;
- struct h3_stream_ctx *stream;
- /* PULL all open streams */
+ struct cf_ossq_recv_ctx rctx;
+
DEBUGASSERT(data->multi);
- for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) {
- struct Curl_easy *sdata = Curl_node_elem(e);
- if(sdata->conn == data->conn && CURL_WANT_RECV(sdata)) {
- stream = H3_STREAM_CTX(ctx, sdata);
- if(stream && !stream->closed &&
- !Curl_bufq_is_full(&stream->recvbuf)) {
- result = cf_osslq_stream_recv(&stream->s, cf, sdata);
- if(result)
- goto out;
- }
- }
- }
+ rctx.cf = cf;
+ rctx.multi = data->multi;
+ rctx.result = CURLE_OK;
+ Curl_hash_offt_visit(&ctx->streams, cf_osslq_iter_recv, &rctx);
+ result = rctx.result;
}
out:
@@ -1458,13 +1488,43 @@ out: return result;
}
+struct cf_ossq_fill_ctx {
+ struct cf_osslq_ctx *ctx;
+ struct Curl_multi *multi;
+ size_t n;
+};
+
+static bool cf_osslq_collect_block_send(curl_off_t mid, void *val,
+ void *user_data)
+{
+ struct h3_stream_ctx *stream = val;
+ struct cf_ossq_fill_ctx *fctx = user_data;
+ struct cf_osslq_ctx *ctx = fctx->ctx;
+
+ if(fctx->n >= ctx->items_max) /* should not happen, prevent mayhem */
+ return FALSE;
+
+ if(stream && stream->s.ssl && stream->s.send_blocked) {
+ struct Curl_easy *sdata = Curl_multi_get_handle(fctx->multi, mid);
+ fprintf(stderr, "[OSSLQ] stream %" FMT_PRId64 " sdata=%p\n",
+ stream->s.id, (void *)sdata);
+ if(sdata) {
+ ctx->poll_items[fctx->n].desc = SSL_as_poll_descriptor(stream->s.ssl);
+ ctx->poll_items[fctx->n].events = SSL_POLL_EVENT_W;
+ ctx->curl_items[fctx->n] = sdata;
+ fctx->n++;
+ }
+ }
+ return TRUE;
+}
+
/* Iterate over all streams and check if blocked can be unblocked */
static CURLcode cf_osslq_check_and_unblock(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_osslq_ctx *ctx = cf->ctx;
struct h3_stream_ctx *stream;
- size_t poll_count = 0;
+ size_t poll_count;
size_t result_count = 0;
size_t idx_count = 0;
CURLcode res = CURLE_OK;
@@ -1472,66 +1532,58 @@ static CURLcode cf_osslq_check_and_unblock(struct Curl_cfilter *cf, void *tmpptr;
if(ctx->h3.conn) {
- struct Curl_llist_node *e;
-
- res = CURLE_OUT_OF_MEMORY;
+ struct cf_ossq_fill_ctx fill_ctx;
- if(ctx->item_count < Curl_llist_count(&data->multi->process)) {
- ctx->item_count = 0;
- tmpptr = realloc(ctx->poll_items,
- Curl_llist_count(&data->multi->process) *
- sizeof(SSL_POLL_ITEM));
+ if(ctx->items_max < Curl_hash_offt_count(&ctx->streams)) {
+ size_t nmax = Curl_hash_offt_count(&ctx->streams);
+ ctx->items_max = 0;
+ tmpptr = realloc(ctx->poll_items, nmax * sizeof(SSL_POLL_ITEM));
if(!tmpptr) {
free(ctx->poll_items);
ctx->poll_items = NULL;
+ res = CURLE_OUT_OF_MEMORY;
goto out;
}
ctx->poll_items = tmpptr;
- tmpptr = realloc(ctx->curl_items,
- Curl_llist_count(&data->multi->process) *
- sizeof(struct Curl_easy *));
+ tmpptr = realloc(ctx->curl_items, nmax * sizeof(struct Curl_easy *));
if(!tmpptr) {
free(ctx->curl_items);
ctx->curl_items = NULL;
+ res = CURLE_OUT_OF_MEMORY;
goto out;
}
ctx->curl_items = tmpptr;
-
- ctx->item_count = Curl_llist_count(&data->multi->process);
- }
-
- for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) {
- struct Curl_easy *sdata = Curl_node_elem(e);
- if(sdata->conn == data->conn) {
- stream = H3_STREAM_CTX(ctx, sdata);
- if(stream && stream->s.ssl && stream->s.send_blocked) {
- ctx->poll_items[poll_count].desc =
- SSL_as_poll_descriptor(stream->s.ssl);
- ctx->poll_items[poll_count].events = SSL_POLL_EVENT_W;
- ctx->curl_items[poll_count] = sdata;
- poll_count++;
- }
- }
+ ctx->items_max = nmax;
}
- memset(&timeout, 0, sizeof(struct timeval));
- res = CURLE_UNRECOVERABLE_POLL;
- if(!SSL_poll(ctx->poll_items, poll_count, sizeof(SSL_POLL_ITEM), &timeout,
- 0, &result_count))
- goto out;
+ fill_ctx.ctx = ctx;
+ fill_ctx.multi = data->multi;
+ fill_ctx.n = 0;
+ Curl_hash_offt_visit(&ctx->streams, cf_osslq_collect_block_send,
+ &fill_ctx);
+ poll_count = fill_ctx.n;
+ if(poll_count) {
+ CURL_TRC_CF(data, cf, "polling %zu blocked streams", poll_count);
+
+ memset(&timeout, 0, sizeof(struct timeval));
+ res = CURLE_UNRECOVERABLE_POLL;
+ if(!SSL_poll(ctx->poll_items, poll_count, sizeof(SSL_POLL_ITEM),
+ &timeout, 0, &result_count))
+ goto out;
- res = CURLE_OK;
-
- for(idx_count = 0; idx_count < poll_count && result_count > 0;
- idx_count++) {
- if(ctx->poll_items[idx_count].revents & SSL_POLL_EVENT_W) {
- stream = H3_STREAM_CTX(ctx, ctx->curl_items[idx_count]);
- nghttp3_conn_unblock_stream(ctx->h3.conn, stream->s.id);
- stream->s.send_blocked = FALSE;
- h3_drain_stream(cf, ctx->curl_items[idx_count]);
- CURL_TRC_CF(ctx->curl_items[idx_count], cf, "unblocked");
- result_count--;
+ res = CURLE_OK;
+
+ for(idx_count = 0; idx_count < poll_count && result_count > 0;
+ idx_count++) {
+ if(ctx->poll_items[idx_count].revents & SSL_POLL_EVENT_W) {
+ stream = H3_STREAM_CTX(ctx, ctx->curl_items[idx_count]);
+ nghttp3_conn_unblock_stream(ctx->h3.conn, stream->s.id);
+ stream->s.send_blocked = FALSE;
+ h3_drain_stream(cf, ctx->curl_items[idx_count]);
+ CURL_TRC_CF(ctx->curl_items[idx_count], cf, "unblocked");
+ result_count--;
+ }
}
}
}
@@ -1718,7 +1770,7 @@ out: static CURLcode cf_osslq_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
- bool blocking, bool *done)
+ bool *done)
{
struct cf_osslq_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
@@ -1733,7 +1785,7 @@ static CURLcode cf_osslq_connect(struct Curl_cfilter *cf, /* Connect the UDP filter first */
if(!cf->next->connected) {
- result = Curl_conn_cf_connect(cf->next, data, blocking, done);
+ result = Curl_conn_cf_connect(cf->next, data, done);
if(result || !*done)
return result;
}
diff --git a/libs/libcurl/src/vquic/curl_quiche.c b/libs/libcurl/src/vquic/curl_quiche.c index d4824958de..5d565e31c1 100644 --- a/libs/libcurl/src/vquic/curl_quiche.c +++ b/libs/libcurl/src/vquic/curl_quiche.c @@ -29,7 +29,7 @@ #include <openssl/err.h>
#include <openssl/ssl.h>
#include "bufq.h"
-#include "hash.h"
+#include "hash_offt.h"
#include "urldata.h"
#include "cfilters.h"
#include "cf-socket.h"
@@ -97,7 +97,7 @@ struct cf_quiche_ctx { struct curltime started_at; /* time the current attempt started */
struct curltime handshake_at; /* time connect handshake finished */
struct bufc_pool stream_bufcp; /* chunk pool for streams */
- struct Curl_hash streams; /* hash `data->mid` to `stream_ctx` */
+ struct Curl_hash_offt streams; /* hash `data->mid` to `stream_ctx` */
curl_off_t data_recvd;
BIT(initialized);
BIT(goaway); /* got GOAWAY from server */
@@ -115,7 +115,7 @@ static void quiche_debug_log(const char *line, void *argp) }
#endif
-static void h3_stream_hash_free(void *stream);
+static void h3_stream_hash_free(curl_off_t id, void *stream);
static void cf_quiche_ctx_init(struct cf_quiche_ctx *ctx)
{
@@ -142,8 +142,7 @@ static void cf_quiche_ctx_free(struct cf_quiche_ctx *ctx) Curl_ssl_peer_cleanup(&ctx->peer);
vquic_ctx_free(&ctx->q);
Curl_bufcp_free(&ctx->stream_bufcp);
- Curl_hash_clean(&ctx->streams);
- Curl_hash_destroy(&ctx->streams);
+ Curl_hash_offt_destroy(&ctx->streams);
}
free(ctx);
}
@@ -190,30 +189,73 @@ static void h3_stream_ctx_free(struct stream_ctx *stream) free(stream);
}
-static void h3_stream_hash_free(void *stream)
+static void h3_stream_hash_free(curl_off_t id, void *stream)
{
+ (void)id;
DEBUGASSERT(stream);
h3_stream_ctx_free((struct stream_ctx *)stream);
}
-static void check_resumes(struct Curl_cfilter *cf,
- struct Curl_easy *data)
+typedef bool cf_quiche_svisit(struct Curl_cfilter *cf,
+ struct Curl_easy *sdata,
+ struct stream_ctx *stream,
+ void *user_data);
+
+struct cf_quiche_visit_ctx {
+ struct Curl_cfilter *cf;
+ struct Curl_multi *multi;
+ cf_quiche_svisit *cb;
+ void *user_data;
+};
+
+static bool cf_quiche_stream_do(curl_off_t mid, void *val, void *user_data)
+{
+ struct cf_quiche_visit_ctx *vctx = user_data;
+ struct stream_ctx *stream = val;
+ struct Curl_easy *sdata = Curl_multi_get_handle(vctx->multi, mid);
+ if(sdata)
+ return vctx->cb(vctx->cf, sdata, stream, vctx->user_data);
+ return TRUE;
+}
+
+static void cf_quiche_for_all_streams(struct Curl_cfilter *cf,
+ struct Curl_multi *multi,
+ cf_quiche_svisit *do_cb,
+ void *user_data)
{
struct cf_quiche_ctx *ctx = cf->ctx;
- struct Curl_llist_node *e;
+ struct cf_quiche_visit_ctx vctx;
+ vctx.cf = cf;
+ vctx.multi = multi;
+ vctx.cb = do_cb;
+ vctx.user_data = user_data;
+ Curl_hash_offt_visit(&ctx->streams, cf_quiche_stream_do, &vctx);
+}
- DEBUGASSERT(data->multi);
- for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) {
- struct Curl_easy *sdata = Curl_node_elem(e);
- if(sdata->conn == data->conn) {
- struct stream_ctx *stream = H3_STREAM_CTX(ctx, sdata);
- if(stream && stream->quic_flow_blocked) {
- stream->quic_flow_blocked = FALSE;
- Curl_expire(data, 0, EXPIRE_RUN_NOW);
- CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] unblock", stream->id);
- }
- }
+static bool cf_quiche_do_resume(struct Curl_cfilter *cf,
+ struct Curl_easy *sdata,
+ struct stream_ctx *stream,
+ void *user_data)
+{
+ (void)user_data;
+ if(stream->quic_flow_blocked) {
+ stream->quic_flow_blocked = FALSE;
+ Curl_expire(sdata, 0, EXPIRE_RUN_NOW);
+ CURL_TRC_CF(sdata, cf, "[%"FMT_PRIu64"] unblock", stream->id);
}
+ return TRUE;
+}
+
+static bool cf_quiche_do_expire(struct Curl_cfilter *cf,
+ struct Curl_easy *sdata,
+ struct stream_ctx *stream,
+ void *user_data)
+{
+ (void)stream;
+ (void)user_data;
+ CURL_TRC_CF(sdata, cf, "conn closed, expire transfer");
+ Curl_expire(sdata, 0, EXPIRE_RUN_NOW);
+ return TRUE;
}
static CURLcode h3_data_setup(struct Curl_cfilter *cf,
@@ -285,52 +327,12 @@ static void h3_drain_stream(struct Curl_cfilter *cf, }
}
-static struct Curl_easy *get_stream_easy(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- curl_uint64_t stream_id,
- struct stream_ctx **pstream)
-{
- struct cf_quiche_ctx *ctx = cf->ctx;
- struct stream_ctx *stream;
-
- (void)cf;
- stream = H3_STREAM_CTX(ctx, data);
- if(stream && stream->id == stream_id) {
- *pstream = stream;
- return data;
- }
- else {
- struct Curl_llist_node *e;
- DEBUGASSERT(data->multi);
- for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) {
- struct Curl_easy *sdata = Curl_node_elem(e);
- if(sdata->conn != data->conn)
- continue;
- stream = H3_STREAM_CTX(ctx, sdata);
- if(stream && stream->id == stream_id) {
- *pstream = stream;
- return sdata;
- }
- }
- }
- *pstream = NULL;
- return NULL;
-}
-
static void cf_quiche_expire_conn_closed(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
- struct Curl_llist_node *e;
-
DEBUGASSERT(data->multi);
CURL_TRC_CF(data, cf, "conn closed, expire all transfers");
- for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) {
- struct Curl_easy *sdata = Curl_node_elem(e);
- if(sdata == data || sdata->conn != data->conn)
- continue;
- CURL_TRC_CF(sdata, cf, "conn closed, expire transfer");
- Curl_expire(sdata, 0, EXPIRE_RUN_NOW);
- }
+ cf_quiche_for_all_streams(cf, data->multi, cf_quiche_do_expire, NULL);
}
/*
@@ -557,14 +559,48 @@ static CURLcode h3_process_event(struct Curl_cfilter *cf, return result;
}
+static CURLcode cf_quiche_ev_process(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct stream_ctx *stream,
+ quiche_h3_event *ev)
+{
+ CURLcode result = h3_process_event(cf, data, stream, ev);
+ h3_drain_stream(cf, data);
+ if(result)
+ CURL_TRC_CF(data, cf, "error processing event %s "
+ "for [%"FMT_PRIu64"] -> %d", cf_ev_name(ev),
+ stream->id, result);
+ return result;
+}
+
+struct cf_quich_disp_ctx {
+ curl_uint64_t stream_id;
+ struct Curl_cfilter *cf;
+ struct Curl_multi *multi;
+ quiche_h3_event *ev;
+ CURLcode result;
+};
+
+static bool cf_quiche_disp_event(curl_off_t mid, void *val, void *user_data)
+{
+ struct cf_quich_disp_ctx *dctx = user_data;
+ struct stream_ctx *stream = val;
+
+ if(stream->id == dctx->stream_id) {
+ struct Curl_easy *sdata = Curl_multi_get_handle(dctx->multi, mid);
+ if(sdata)
+ dctx->result = cf_quiche_ev_process(dctx->cf, sdata, stream, dctx->ev);
+ return FALSE; /* stop iterating */
+ }
+ return TRUE;
+}
+
static CURLcode cf_poll_events(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_quiche_ctx *ctx = cf->ctx;
struct stream_ctx *stream = NULL;
- struct Curl_easy *sdata;
quiche_h3_event *ev;
- CURLcode result;
/* Take in the events and distribute them to the transfers. */
while(ctx->h3c) {
@@ -576,28 +612,27 @@ static CURLcode cf_poll_events(struct Curl_cfilter *cf, CURL_TRC_CF(data, cf, "error poll: %"FMT_PRId64, stream3_id);
return CURLE_HTTP3;
}
-
- sdata = get_stream_easy(cf, data, stream3_id, &stream);
- if(!sdata || !stream) {
- CURL_TRC_CF(data, cf, "discard event %s for unknown [%"FMT_PRId64"]",
- cf_ev_name(ev), stream3_id);
- }
else {
- result = h3_process_event(cf, sdata, stream, ev);
- h3_drain_stream(cf, sdata);
- if(result) {
- CURL_TRC_CF(data, cf, "error processing event %s "
- "for [%"FMT_PRIu64"] -> %d", cf_ev_name(ev),
- stream3_id, result);
- if(data == sdata) {
- /* Only report this error to the caller if it is about the
- * transfer we were called with. Otherwise we fail a transfer
- * due to a problem in another one. */
- quiche_h3_event_free(ev);
+ struct cf_quich_disp_ctx dctx;
+ dctx.stream_id = (curl_uint64_t)stream3_id;
+ dctx.cf = cf;
+ dctx.multi = data->multi;
+ dctx.ev = ev;
+ dctx.result = CURLE_OK;
+ stream = H3_STREAM_CTX(ctx, data);
+ if(stream && stream->id == dctx.stream_id) {
+ /* event for calling transfer */
+ CURLcode result = cf_quiche_ev_process(cf, data, stream, ev);
+ quiche_h3_event_free(ev);
+ if(result)
return result;
- }
}
- quiche_h3_event_free(ev);
+ else {
+ /* another transfer, do not return errors, as they are not for
+ * the calling transfer */
+ Curl_hash_offt_visit(&ctx->streams, cf_quiche_disp_event, &dctx);
+ quiche_h3_event_free(ev);
+ }
}
}
return CURLE_OK;
@@ -627,7 +662,8 @@ static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen, recv_info.from = (struct sockaddr *)remote_addr;
recv_info.from_len = remote_addrlen;
- nread = quiche_conn_recv(ctx->qconn, (unsigned char *)pkt, pktlen,
+ nread = quiche_conn_recv(ctx->qconn,
+ (unsigned char *)CURL_UNCONST(pkt), pktlen,
&recv_info);
if(nread < 0) {
if(QUICHE_ERR_DONE == nread) {
@@ -686,7 +722,8 @@ static CURLcode cf_process_ingress(struct Curl_cfilter *cf, if(rctx.pkts > 0) {
/* quiche digested ingress packets. It might have opened flow control
* windows again. */
- check_resumes(cf, data);
+ DEBUGASSERT(data->multi);
+ cf_quiche_for_all_streams(cf, data->multi, cf_quiche_do_resume, NULL);
}
return cf_poll_events(cf, data);
}
@@ -924,7 +961,7 @@ static ssize_t cf_quiche_send_body(struct Curl_cfilter *cf, ssize_t nwritten;
nwritten = quiche_h3_send_body(ctx->h3c, ctx->qconn, stream->id,
- (uint8_t *)buf, len, eos);
+ (uint8_t *)CURL_UNCONST(buf), len, eos);
if(nwritten == QUICHE_H3_ERR_DONE || (nwritten == 0 && len > 0)) {
/* Blocked on flow control and should HOLD sending. But when do we open
* again? */
@@ -1267,6 +1304,9 @@ static CURLcode cf_quiche_ctx_open(struct Curl_cfilter *cf, int rv;
CURLcode result;
const struct Curl_sockaddr_ex *sockaddr;
+static const struct alpn_spec ALPN_SPEC_H3 = {
+ { "h3" }, 1
+};
DEBUGASSERT(ctx->q.sockfd != CURL_SOCKET_BAD);
DEBUGASSERT(ctx->initialized);
@@ -1298,15 +1338,12 @@ static CURLcode cf_quiche_ctx_open(struct Curl_cfilter *cf, 10 * QUIC_MAX_STREAMS * H3_STREAM_WINDOW_SIZE);
quiche_config_set_max_stream_window(ctx->cfg, 10 * H3_STREAM_WINDOW_SIZE);
quiche_config_set_application_protos(ctx->cfg,
- (uint8_t *)
- QUICHE_H3_APPLICATION_PROTOCOL,
+ (uint8_t *)CURL_UNCONST(QUICHE_H3_APPLICATION_PROTOCOL),
sizeof(QUICHE_H3_APPLICATION_PROTOCOL)
- 1);
result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer,
- QUICHE_H3_APPLICATION_PROTOCOL,
- sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1,
- NULL, NULL, cf, NULL);
+ &ALPN_SPEC_H3, NULL, NULL, cf, NULL);
if(result)
return result;
@@ -1378,7 +1415,7 @@ static CURLcode cf_quiche_verify_peer(struct Curl_cfilter *cf, static CURLcode cf_quiche_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
- bool blocking, bool *done)
+ bool *done)
{
struct cf_quiche_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
@@ -1390,7 +1427,7 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf, /* Connect the UDP filter first */
if(!cf->next->connected) {
- result = Curl_conn_cf_connect(cf->next, data, blocking, done);
+ result = Curl_conn_cf_connect(cf->next, data, done);
if(result || !*done)
return result;
}
diff --git a/libs/libcurl/src/vquic/vquic-tls.c b/libs/libcurl/src/vquic/vquic-tls.c index d92df69cea..feae0f594a 100644 --- a/libs/libcurl/src/vquic/vquic-tls.c +++ b/libs/libcurl/src/vquic/vquic-tls.c @@ -58,178 +58,11 @@ #include "curl_memory.h"
#include "memdebug.h"
-#if defined(USE_WOLFSSL)
-
-#define QUIC_CIPHERS \
- "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \
- "POLY1305_SHA256:TLS_AES_128_CCM_SHA256"
-#define QUIC_GROUPS "P-256:P-384:P-521"
-
-#if defined(HAVE_SECRET_CALLBACK)
-static void keylog_callback(const WOLFSSL *ssl, const char *line)
-{
- (void)ssl;
- Curl_tls_keylog_write_line(line);
-}
-#endif
-
-static CURLcode wssl_init_ctx(struct curl_tls_ctx *ctx,
- struct Curl_cfilter *cf,
- struct Curl_easy *data,
- Curl_vquic_tls_ctx_setup *cb_setup,
- void *cb_user_data)
-{
- struct ssl_primary_config *conn_config;
- CURLcode result = CURLE_FAILED_INIT;
-
- conn_config = Curl_ssl_cf_get_primary_config(cf);
- if(!conn_config) {
- result = CURLE_FAILED_INIT;
- goto out;
- }
-
- ctx->wssl.ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method());
- if(!ctx->wssl.ctx) {
- result = CURLE_OUT_OF_MEMORY;
- goto out;
- }
-
- if(cb_setup) {
- result = cb_setup(cf, data, cb_user_data);
- if(result)
- goto out;
- }
-
- wolfSSL_CTX_set_default_verify_paths(ctx->wssl.ctx);
-
- if(wolfSSL_CTX_set_cipher_list(ctx->wssl.ctx, conn_config->cipher_list13 ?
- conn_config->cipher_list13 :
- QUIC_CIPHERS) != 1) {
- char error_buffer[256];
- ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer));
- failf(data, "wolfSSL failed to set ciphers: %s", error_buffer);
- result = CURLE_BAD_FUNCTION_ARGUMENT;
- goto out;
- }
-
- if(wolfSSL_CTX_set1_groups_list(ctx->wssl.ctx, conn_config->curves ?
- conn_config->curves :
- (char *)QUIC_GROUPS) != 1) {
- failf(data, "wolfSSL failed to set curves");
- result = CURLE_BAD_FUNCTION_ARGUMENT;
- goto out;
- }
-
- /* Open the file if a TLS or QUIC backend has not done this before. */
- Curl_tls_keylog_open();
- if(Curl_tls_keylog_enabled()) {
-#if defined(HAVE_SECRET_CALLBACK)
- wolfSSL_CTX_set_keylog_callback(ctx->wssl.ctx, keylog_callback);
-#else
- failf(data, "wolfSSL was built without keylog callback");
- result = CURLE_NOT_BUILT_IN;
- goto out;
-#endif
- }
-
- if(conn_config->verifypeer) {
- const char * const ssl_cafile = conn_config->CAfile;
- const char * const ssl_capath = conn_config->CApath;
-
- wolfSSL_CTX_set_verify(ctx->wssl.ctx, SSL_VERIFY_PEER, NULL);
- if(ssl_cafile || ssl_capath) {
- /* tell wolfSSL where to find CA certificates that are used to verify
- the server's certificate. */
- int rc =
- wolfSSL_CTX_load_verify_locations_ex(ctx->wssl.ctx, ssl_cafile,
- ssl_capath,
- WOLFSSL_LOAD_FLAG_IGNORE_ERR);
- if(SSL_SUCCESS != rc) {
- /* Fail if we insist on successfully verifying the server. */
- failf(data, "error setting certificate verify locations:"
- " CAfile: %s CApath: %s",
- ssl_cafile ? ssl_cafile : "none",
- ssl_capath ? ssl_capath : "none");
- result = CURLE_SSL_CACERT_BADFILE;
- goto out;
- }
- infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
- infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
- }
-#ifdef CURL_CA_FALLBACK
- else {
- /* verifying the peer without any CA certificates will not work so
- use wolfSSL's built-in default as fallback */
- wolfSSL_CTX_set_default_verify_paths(ctx->wssl.ctx);
- }
-#endif
- }
- else {
- wolfSSL_CTX_set_verify(ctx->wssl.ctx, SSL_VERIFY_NONE, NULL);
- }
-
- /* give application a chance to interfere with SSL set up. */
- if(data->set.ssl.fsslctx) {
- Curl_set_in_callback(data, TRUE);
- result = (*data->set.ssl.fsslctx)(data, ctx->wssl.ctx,
- data->set.ssl.fsslctxp);
- Curl_set_in_callback(data, FALSE);
- if(result) {
- failf(data, "error signaled by ssl ctx callback");
- goto out;
- }
- }
- result = CURLE_OK;
-
-out:
- if(result && ctx->wssl.ctx) {
- SSL_CTX_free(ctx->wssl.ctx);
- ctx->wssl.ctx = NULL;
- }
- return result;
-}
-
-/** SSL callbacks ***/
-
-static CURLcode wssl_init_ssl(struct curl_tls_ctx *ctx,
- struct Curl_cfilter *cf,
- struct Curl_easy *data,
- struct ssl_peer *peer,
- const char *alpn, size_t alpn_len,
- void *user_data)
-{
- struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
-
- DEBUGASSERT(!ctx->wssl.handle);
- DEBUGASSERT(ctx->wssl.ctx);
- ctx->wssl.handle = wolfSSL_new(ctx->wssl.ctx);
-
- wolfSSL_set_app_data(ctx->wssl.handle, user_data);
- wolfSSL_set_connect_state(ctx->wssl.handle);
- wolfSSL_set_quic_use_legacy_codepoint(ctx->wssl.handle, 0);
-
- if(alpn)
- wolfSSL_set_alpn_protos(ctx->wssl.handle, (const unsigned char *)alpn,
- (unsigned int)alpn_len);
-
- if(peer->sni) {
- wolfSSL_UseSNI(ctx->wssl.handle, WOLFSSL_SNI_HOST_NAME,
- peer->sni, (unsigned short)strlen(peer->sni));
- }
-
- if(ssl_config->primary.cache_session) {
- (void)Curl_wssl_setup_session(cf, data, &ctx->wssl, peer->scache_key);
- }
-
- return CURLE_OK;
-}
-#endif /* defined(USE_WOLFSSL) */
-
CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx,
struct Curl_cfilter *cf,
struct Curl_easy *data,
struct ssl_peer *peer,
- const char *alpn, size_t alpn_len,
+ const struct alpn_spec *alpns,
Curl_vquic_tls_ctx_setup *cb_setup,
void *cb_user_data, void *ssl_user_data,
Curl_vquic_session_reuse_cb *session_reuse_cb)
@@ -254,21 +87,17 @@ CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx, #ifdef USE_OPENSSL
(void)result;
- return Curl_ossl_ctx_init(&ctx->ossl, cf, data, peer,
- (const unsigned char *)alpn, alpn_len,
- cb_setup, cb_user_data, NULL, ssl_user_data);
+ return Curl_ossl_ctx_init(&ctx->ossl, cf, data, peer, alpns,
+ cb_setup, cb_user_data, NULL, ssl_user_data,
+ session_reuse_cb);
#elif defined(USE_GNUTLS)
- return Curl_gtls_ctx_init(&ctx->gtls, cf, data, peer,
- (const unsigned char *)alpn, alpn_len,
+ return Curl_gtls_ctx_init(&ctx->gtls, cf, data, peer, alpns,
cb_setup, cb_user_data, ssl_user_data,
session_reuse_cb);
#elif defined(USE_WOLFSSL)
- result = wssl_init_ctx(ctx, cf, data, cb_setup, cb_user_data);
- if(result)
- return result;
-
- (void)session_reuse_cb;
- return wssl_init_ssl(ctx, cf, data, peer, alpn, alpn_len, ssl_user_data);
+ return Curl_wssl_ctx_init(&ctx->wssl, cf, data, peer, alpns,
+ cb_setup, cb_user_data,
+ ssl_user_data, session_reuse_cb);
#else
#error "no TLS lib in used, should not happen"
return CURLE_FAILED_INIT;
@@ -287,10 +116,10 @@ void Curl_vquic_tls_cleanup(struct curl_tls_ctx *ctx) gnutls_deinit(ctx->gtls.session);
Curl_gtls_shared_creds_free(&ctx->gtls.shared_creds);
#elif defined(USE_WOLFSSL)
- if(ctx->wssl.handle)
- wolfSSL_free(ctx->wssl.handle);
- if(ctx->wssl.ctx)
- wolfSSL_CTX_free(ctx->wssl.ctx);
+ if(ctx->wssl.ssl)
+ wolfSSL_free(ctx->wssl.ssl);
+ if(ctx->wssl.ssl_ctx)
+ wolfSSL_CTX_free(ctx->wssl.ssl_ctx);
#endif
memset(ctx, 0, sizeof(*ctx));
}
@@ -351,7 +180,7 @@ CURLcode Curl_vquic_tls_verify_peer(struct curl_tls_ctx *ctx, (void)data;
if(conn_config->verifyhost) {
if(peer->sni) {
- WOLFSSL_X509* cert = wolfSSL_get_peer_certificate(ctx->wssl.handle);
+ WOLFSSL_X509* cert = wolfSSL_get_peer_certificate(ctx->wssl.ssl);
if(wolfSSL_X509_check_host(cert, peer->sni, strlen(peer->sni), 0, NULL)
== WOLFSSL_FAILURE) {
result = CURLE_PEER_FAILED_VERIFICATION;
diff --git a/libs/libcurl/src/vquic/vquic-tls.h b/libs/libcurl/src/vquic/vquic-tls.h index 8b250f2245..7bc24819d8 100644 --- a/libs/libcurl/src/vquic/vquic-tls.h +++ b/libs/libcurl/src/vquic/vquic-tls.h @@ -27,6 +27,7 @@ #include "curl_setup.h"
#include "bufq.h"
#include "vtls/vtls.h"
+#include "vtls/vtls_int.h"
#include "vtls/openssl.h"
#if defined(USE_HTTP3) && \
@@ -43,7 +44,7 @@ struct curl_tls_ctx { #elif defined(USE_GNUTLS)
struct gtls_ctx gtls;
#elif defined(USE_WOLFSSL)
- struct wolfssl_ctx wssl;
+ struct wssl_ctx wssl;
#endif
};
@@ -60,6 +61,7 @@ typedef CURLcode Curl_vquic_tls_ctx_setup(struct Curl_cfilter *cf, typedef CURLcode Curl_vquic_session_reuse_cb(struct Curl_cfilter *cf,
struct Curl_easy *data,
+ struct alpn_spec *alpns,
struct Curl_ssl_session *scs,
bool *do_early_data);
@@ -70,9 +72,7 @@ typedef CURLcode Curl_vquic_session_reuse_cb(struct Curl_cfilter *cf, * @param cf the connection filter involved
* @param data the transfer involved
* @param peer the peer that will be connected to
- * @param alpn the ALPN string in protocol format ((len+bytes+)+),
- * may be NULL
- * @param alpn_len the overall number of bytes in `alpn`
+ * @param alpns the ALPN specifications to negotiate, may be NULL
* @param cb_setup optional callback for early TLS config
* @param cb_user_data user_data param for callback
* @param ssl_user_data optional pointer to set in TLS application context
@@ -82,7 +82,7 @@ CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx, struct Curl_cfilter *cf,
struct Curl_easy *data,
struct ssl_peer *peer,
- const char *alpn, size_t alpn_len,
+ const struct alpn_spec *alpns,
Curl_vquic_tls_ctx_setup *cb_setup,
void *cb_user_data,
void *ssl_user_data,
diff --git a/libs/libcurl/src/vquic/vquic.c b/libs/libcurl/src/vquic/vquic.c index 44f29963d1..f24c9dc799 100644 --- a/libs/libcurl/src/vquic/vquic.c +++ b/libs/libcurl/src/vquic/vquic.c @@ -44,6 +44,7 @@ #include "vquic.h"
#include "vquic_int.h"
#include "strerror.h"
+#include "strparse.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -81,10 +82,10 @@ CURLcode vquic_ctx_init(struct cf_quic_ctx *qctx) #endif
#ifdef DEBUGBUILD
{
- char *p = getenv("CURL_DBG_QUIC_WBLOCK");
+ const char *p = getenv("CURL_DBG_QUIC_WBLOCK");
if(p) {
- long l = strtol(p, NULL, 10);
- if(l >= 0 && l <= 100)
+ curl_off_t l;
+ if(!Curl_str_number(&p, &l, 100))
qctx->wblock_percent = (int)l;
}
}
@@ -126,7 +127,7 @@ static CURLcode do_sendmsg(struct Curl_cfilter *cf, #endif
*psent = 0;
- msg_iov.iov_base = (uint8_t *)pkt;
+ msg_iov.iov_base = (uint8_t *)CURL_UNCONST(pkt);
msg_iov.iov_len = pktlen;
msg.msg_iov = &msg_iov;
msg.msg_iovlen = 1;
@@ -147,17 +148,18 @@ static CURLcode do_sendmsg(struct Curl_cfilter *cf, #endif
- while((sent = sendmsg(qctx->sockfd, &msg, 0)) == -1 && SOCKERRNO == EINTR)
+ while((sent = sendmsg(qctx->sockfd, &msg, 0)) == -1 &&
+ SOCKERRNO == SOCKEINTR)
;
if(sent == -1) {
switch(SOCKERRNO) {
case EAGAIN:
-#if EAGAIN != EWOULDBLOCK
- case EWOULDBLOCK:
+#if EAGAIN != SOCKEWOULDBLOCK
+ case SOCKEWOULDBLOCK:
#endif
return CURLE_AGAIN;
- case EMSGSIZE:
+ case SOCKEMSGSIZE:
/* UDP datagram is too large; caused by PMTUD. Just let it be lost. */
break;
case EIO:
@@ -185,16 +187,16 @@ static CURLcode do_sendmsg(struct Curl_cfilter *cf, while((sent = send(qctx->sockfd,
(const char *)pkt, (SEND_TYPE_ARG3)pktlen, 0)) == -1 &&
- SOCKERRNO == EINTR)
+ SOCKERRNO == SOCKEINTR)
;
if(sent == -1) {
- if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
+ if(SOCKERRNO == EAGAIN || SOCKERRNO == SOCKEWOULDBLOCK) {
return CURLE_AGAIN;
}
else {
failf(data, "send() returned %zd (errno %d)", sent, SOCKERRNO);
- if(SOCKERRNO != EMSGSIZE) {
+ if(SOCKERRNO != SOCKEMSGSIZE) {
return CURLE_SEND_ERROR;
}
/* UDP datagram is too large; caused by PMTUD. Just let it be
@@ -377,7 +379,7 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, total_nread = 0;
while(pkts < max_pkts) {
- n = (int)CURLMIN(MMSG_NUM, max_pkts);
+ n = (int)CURLMIN(CURLMIN(MMSG_NUM, IOV_MAX), max_pkts);
memset(&mmsg, 0, sizeof(mmsg));
for(i = 0; i < n; ++i) {
msg_iov[i].iov_base = bufs[i];
@@ -391,14 +393,14 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, }
while((mcount = recvmmsg(qctx->sockfd, mmsg, n, 0, NULL)) == -1 &&
- SOCKERRNO == EINTR)
+ SOCKERRNO == SOCKEINTR)
;
if(mcount == -1) {
- if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
+ if(SOCKERRNO == EAGAIN || SOCKERRNO == SOCKEWOULDBLOCK) {
CURL_TRC_CF(data, cf, "ingress, recvmmsg -> EAGAIN");
goto out;
}
- if(!cf->connected && SOCKERRNO == ECONNREFUSED) {
+ if(!cf->connected && SOCKERRNO == SOCKECONNREFUSED) {
struct ip_quadruple ip;
Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip);
failf(data, "QUIC: connection to %s port %u refused",
@@ -483,13 +485,13 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf, msg.msg_namelen = sizeof(remote_addr);
msg.msg_controllen = sizeof(msg_ctrl);
while((nread = recvmsg(qctx->sockfd, &msg, 0)) == -1 &&
- SOCKERRNO == EINTR)
+ SOCKERRNO == SOCKEINTR)
;
if(nread == -1) {
- if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
+ if(SOCKERRNO == EAGAIN || SOCKERRNO == SOCKEWOULDBLOCK) {
goto out;
}
- if(!cf->connected && SOCKERRNO == ECONNREFUSED) {
+ if(!cf->connected && SOCKERRNO == SOCKECONNREFUSED) {
struct ip_quadruple ip;
Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip);
failf(data, "QUIC: connection to %s port %u refused",
@@ -557,14 +559,14 @@ static CURLcode recvfrom_packets(struct Curl_cfilter *cf, while((nread = recvfrom(qctx->sockfd, (char *)buf, bufsize, 0,
(struct sockaddr *)&remote_addr,
&remote_addrlen)) == -1 &&
- SOCKERRNO == EINTR)
+ SOCKERRNO == SOCKEINTR)
;
if(nread == -1) {
- if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
+ if(SOCKERRNO == EAGAIN || SOCKERRNO == SOCKEWOULDBLOCK) {
CURL_TRC_CF(data, cf, "ingress, recvfrom -> EAGAIN");
goto out;
}
- if(!cf->connected && SOCKERRNO == ECONNREFUSED) {
+ if(!cf->connected && SOCKERRNO == SOCKECONNREFUSED) {
struct ip_quadruple ip;
Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip);
failf(data, "QUIC: connection to %s port %u refused",
|
