ngtcp2: share common functionality

Share common functions/structs between ngtcp2 HTTP/3 and the proxy
version.

Fix bugs in proxy implementation when it comes to stream and pollset
handling and transfer lifetimes.

Curl_multi_xfer_sockbuf_borrow: work without multi

When a connection gets shutdown by a share, the easy handle used is
share->admin and it does not have a multi handle. In that case let
Curl_multi_xfer_sockbuf_borrow() allocate a buffer to be freed on
release.

This happens when a TLS filter sends its last notify through a HTTP/3
proxy tunnel.

Closes #21871
This commit is contained in:
Stefan Eissing 2026-06-05 12:55:50 +02:00 committed by Daniel Stenberg
parent 4fcf9c8f59
commit f924489b25
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
43 changed files with 3254 additions and 4970 deletions

View file

@ -413,7 +413,8 @@ static CURLcode cf_setup_add_http_proxy(struct Curl_cfilter *cf,
#ifdef USE_SSL
if(IS_HTTPS_PROXY(cf->conn->http_proxy.proxytype) &&
!Curl_conn_is_ssl(cf->conn, cf->sockindex)) {
result = Curl_cf_ssl_proxy_insert_after(cf, data);
result = Curl_cf_ssl_proxy_insert_after(
cf, data, cf->conn->http_proxy.peer);
if(result) {
CURL_TRC_CF(data, cf, "adding SSL filter for HTTP proxy failed -> %d",
result);
@ -424,10 +425,12 @@ static CURLcode cf_setup_add_http_proxy(struct Curl_cfilter *cf,
#endif /* USE_SSL */
if(cf->conn->bits.tunnel_proxy) {
struct Curl_peer *dest; /* where HTTP should tunnel to */
dest = Curl_conn_get_destination(cf->conn, cf->sockindex);
struct Curl_peer *peer = cf->conn->http_proxy.peer;
struct Curl_peer *tunnel_peer; /* where HTTP should tunnel to */
tunnel_peer = Curl_conn_get_destination(cf->conn, cf->sockindex);
result = Curl_cf_http_proxy_insert_after(
cf, data, dest, ctx->transport, cf->conn->http_proxy.proxytype);
cf, data, peer, tunnel_peer,
ctx->transport, cf->conn->http_proxy.proxytype);
if(result) {
CURL_TRC_CF(data, cf, "adding HTTP proxy tunnel filter failed -> %d",
result);
@ -449,41 +452,47 @@ static CURLcode cf_setup_add_ip_happy(struct Curl_cfilter *cf,
CURLcode result = CURLE_OK;
if(ctx->state < CF_SETUP_CNNCT_EYEBALLS) {
/* What is the fist hop we directly connect to and what transport
* do we use for it? Only on the first hop we can do Happy Eyeballs. */
/* What is the first hop we directly connect to and what transport
* do we use for it? Only on the first hop we can do Happy Eyeballs.
* first_origin and first_peer differ on --connect-to. */
struct Curl_peer *first_origin =
Curl_conn_get_first_origin(cf->conn, cf->sockindex);
struct Curl_peer *first_peer =
Curl_conn_get_first_peer(cf->conn, cf->sockindex);
struct Curl_peer *tunnel_peer = NULL;
uint8_t first_transport = ctx->transport;
bool tunnel_proxy = FALSE;
if(!first_peer)
return CURLE_FAILED_INIT;
#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
if(cf->conn->bits.httpproxy && cf->conn->bits.tunnel_proxy) {
first_transport =
Curl_http_proxy_transport(cf->conn->http_proxy.proxytype);
tunnel_peer = Curl_conn_get_destination(cf->conn, cf->sockindex);
if((first_transport == TRNSPRT_QUIC) && (cf->conn->bits.socksproxy)) {
failf(data, "HTTP/3 proxy not possible via SOCKS");
return CURLE_UNSUPPORTED_PROTOCOL;
}
tunnel_proxy = TRUE;
}
#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */
result = cf_ip_happy_insert_after(cf, data, first_peer,
ctx->transport, first_transport,
tunnel_proxy);
result = cf_ip_happy_insert_after(cf, data, first_origin, first_peer,
first_transport,
tunnel_peer, ctx->transport);
if(result) {
CURL_TRC_CF(data, cf, "adding happy eyeballs failed -> %d", result);
return result;
}
if(tunnel_proxy && (first_transport == TRNSPRT_QUIC)) {
if(tunnel_peer && (first_transport == TRNSPRT_QUIC)) {
CURL_TRC_CF(data, cf, "happy eyeballing to HTTP/3 proxy %s:%u",
first_peer->hostname, first_peer->port);
ctx->state = CF_SETUP_CNNCT_HTTP_PROXY;
}
else {
CURL_TRC_CF(data, cf, "happy eyeballing to %s %s:%u",
tunnel_proxy ? "proxy" : "origin",
tunnel_peer ? "proxy" : "origin",
first_peer->hostname, first_peer->port);
ctx->state = CF_SETUP_CNNCT_EYEBALLS;
}
@ -501,17 +510,22 @@ static CURLcode cf_setup_add_origin_filters(struct Curl_cfilter *cf,
if(ctx->state < CF_SETUP_CNNCT_SSL) {
#if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3) && \
!defined(CURL_DISABLE_PROXY)
/* Wanting QUIC with a HTTP tunneling filter, we now need to add
* the QUIC filter on top. Without tunneling, this has already
* happened in the Happy Eyeball filter. */
if(ctx->transport == TRNSPRT_QUIC && cf->conn->bits.httpproxy &&
cf->conn->bits.tunnel_proxy) {
struct Curl_peer *origin = Curl_conn_get_origin(cf->conn, cf->sockindex);
struct Curl_peer *peer =
Curl_conn_get_destination(cf->conn, cf->sockindex);
result = Curl_cf_capsule_insert_after(cf, data);
if(result) {
CURL_TRC_CF(data, cf, "adding capsule filter failed -> %d", result);
return result;
}
result = Curl_cf_quic_insert_after(cf);
result = Curl_cf_quic_insert_after(cf, origin, peer);
if(result) {
CURL_TRC_CF(data, cf, "adding QUIC filter failed -> %d", result);
return result;
@ -525,7 +539,13 @@ static CURLcode cf_setup_add_origin_filters(struct Curl_cfilter *cf,
(ctx->ssl_mode != CURL_CF_SSL_DISABLE &&
cf->conn->scheme->flags & PROTOPT_SSL)) && /* we want SSL */
!Curl_conn_is_ssl(cf->conn, cf->sockindex)) { /* it is missing */
result = Curl_cf_ssl_insert_after(cf, data);
/* Another FTP quirk: when adding SSL verification, to a DATA
* connection, always verify against the control's origin */
struct Curl_peer *origin = Curl_conn_get_origin(cf->conn, FIRSTSOCKET);
struct Curl_peer *peer =
Curl_conn_get_destination(cf->conn, cf->sockindex);
result = Curl_cf_ssl_insert_after(cf, data, origin, peer);
if(result) {
CURL_TRC_CF(data, cf, "adding SSL filter for origin failed -> %d",
result);
@ -777,6 +797,13 @@ void Curl_conn_set_multiplex(struct connectdata *conn)
}
}
struct Curl_peer *Curl_conn_get_origin(struct connectdata *conn,
int sockindex)
{
return (sockindex == SECONDARYSOCKET) ?
conn->origin2 : conn->origin;
}
struct Curl_peer *Curl_conn_get_destination(struct connectdata *conn,
int sockindex)
{
@ -789,6 +816,18 @@ struct Curl_peer *Curl_conn_get_destination(struct connectdata *conn,
(conn->via_peer ? conn->via_peer : conn->origin);
}
struct Curl_peer *Curl_conn_get_first_origin(struct connectdata *conn,
int sockindex)
{
#ifndef CURL_DISABLE_PROXY
if(conn->socks_proxy.peer)
return conn->socks_proxy.peer;
if(conn->http_proxy.peer)
return conn->http_proxy.peer;
#endif
return (sockindex == SECONDARYSOCKET) ? conn->origin2 : conn->origin;
}
struct Curl_peer *Curl_conn_get_first_peer(struct connectdata *conn,
int sockindex)
{