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

@ -125,6 +125,7 @@ LIB_VQUIC_CFILES = \
vquic/capsule.c \
vquic/cf-capsule.c \
vquic/cf-ngtcp2.c \
vquic/cf-ngtcp2-cmn.c \
vquic/cf-ngtcp2-proxy.c \
vquic/cf-quiche.c \
vquic/vquic.c \
@ -134,6 +135,7 @@ LIB_VQUIC_HFILES = \
vquic/capsule.h \
vquic/cf-capsule.h \
vquic/cf-ngtcp2.h \
vquic/cf-ngtcp2-cmn.h \
vquic/cf-ngtcp2-proxy.h \
vquic/cf-quiche.h \
vquic/vquic.h \

View file

@ -62,7 +62,7 @@
struct transport_provider {
cf_ip_connect_create *cf_create;
uint8_t transport;
bool tunnel_proxy;
bool tunnel;
};
static
@ -88,12 +88,12 @@ struct transport_provider transport_providers[] = {
};
static cf_ip_connect_create *get_cf_create(uint8_t transport,
bool tunnel_proxy)
bool tunnel)
{
size_t i;
for(i = 0; i < CURL_ARRAYSIZE(transport_providers); ++i) {
if((transport == transport_providers[i].transport) &&
(tunnel_proxy == transport_providers[i].tunnel_proxy))
(tunnel == transport_providers[i].tunnel))
return transport_providers[i].cf_create;
}
return NULL;
@ -155,14 +155,17 @@ static bool cf_ai_iter_has_more(struct cf_ai_iter *iter,
struct cf_ip_attempt {
struct cf_ip_attempt *next;
struct Curl_peer *origin;
struct Curl_peer *peer;
struct Curl_peer *tunnel_peer;
struct Curl_sockaddr_ex addr;
struct Curl_cfilter *cf; /* current sub-cfilter connecting */
cf_ip_connect_create *cf_create;
struct curltime started; /* start of current attempt */
CURLcode result;
int ai_family;
uint8_t transport_in;
uint8_t transport_out;
uint8_t transport_peer;
uint8_t tunnel_transport;
int error;
BIT(connected); /* cf has connected */
BIT(shutdown); /* cf has shutdown */
@ -176,17 +179,23 @@ static void cf_ip_attempt_free(struct cf_ip_attempt *a,
if(a) {
if(a->cf)
Curl_conn_cf_discard_chain(&a->cf, data);
Curl_peer_unlink(&a->origin);
Curl_peer_unlink(&a->peer);
Curl_peer_unlink(&a->tunnel_peer);
curlx_free(a);
}
}
static CURLcode cf_ip_attempt_new(struct cf_ip_attempt **pa,
struct Curl_cfilter *cf,
struct Curl_easy *data,
struct Curl_cfilter *cf,
struct Curl_peer *origin,
struct Curl_peer *peer,
uint8_t transport_peer,
struct Curl_sockaddr_ex *addr,
int ai_family,
uint8_t transport_in,
uint8_t transport_out,
struct Curl_peer *tunnel_peer,
uint8_t tunnel_transport,
cf_ip_connect_create *cf_create)
{
struct Curl_cfilter *wcf;
@ -198,16 +207,20 @@ static CURLcode cf_ip_attempt_new(struct cf_ip_attempt **pa,
if(!a)
return CURLE_OUT_OF_MEMORY;
Curl_peer_link(&a->origin, origin);
Curl_peer_link(&a->peer, peer);
a->transport_peer = transport_peer;
Curl_peer_link(&a->tunnel_peer, tunnel_peer);
a->tunnel_transport = tunnel_transport;
a->addr = *addr;
a->ai_family = ai_family;
a->transport_in = transport_in;
a->transport_out = transport_out;
a->result = CURLE_OK;
a->cf_create = cf_create;
*pa = a;
result = a->cf_create(&a->cf, data, cf->conn, &a->addr,
a->transport_in, a->transport_out);
result = a->cf_create(&a->cf, data, a->origin, a->peer, a->transport_peer,
cf->conn, &a->addr, a->tunnel_peer,
a->tunnel_transport);
if(result)
goto out;
@ -256,14 +269,17 @@ struct cf_ip_ballers {
#ifdef USE_IPV6
struct cf_ai_iter ipv6_iter;
#endif
struct Curl_peer *origin;
struct Curl_peer *peer;
struct Curl_peer *tunnel_peer;
cf_ip_connect_create *cf_create; /* for creating cf */
struct curltime started;
struct curltime last_attempt_started;
timediff_t attempt_delay_ms;
int last_attempt_ai_family;
uint32_t max_concurrent;
uint8_t transport_in;
uint8_t transport_out;
uint8_t transport_peer;
uint8_t tunnel_transport;
};
static CURLcode cf_ip_attempt_restart(struct cf_ip_attempt *a,
@ -281,8 +297,9 @@ static CURLcode cf_ip_attempt_restart(struct cf_ip_attempt *a,
a->inconclusive = FALSE;
a->cf = NULL;
result = a->cf_create(&a->cf, data, cf->conn, &a->addr, a->transport_in,
a->transport_out);
result = a->cf_create(&a->cf, data, a->origin, a->peer, a->transport_peer,
cf->conn, &a->addr,
a->tunnel_peer, a->tunnel_transport);
if(!result) {
bool dummy;
/* the new filter might have sub-filters */
@ -295,11 +312,9 @@ static CURLcode cf_ip_attempt_restart(struct cf_ip_attempt *a,
return result;
}
static void cf_ip_ballers_clear(struct Curl_cfilter *cf,
struct Curl_easy *data,
static void cf_ip_ballers_clear(struct Curl_easy *data,
struct cf_ip_ballers *bs)
{
(void)cf;
while(bs->running) {
struct cf_ip_attempt *a = bs->running;
bs->running = a->next;
@ -307,37 +322,36 @@ static void cf_ip_ballers_clear(struct Curl_cfilter *cf,
}
cf_ip_attempt_free(bs->winner, data);
bs->winner = NULL;
Curl_peer_unlink(&bs->origin);
Curl_peer_unlink(&bs->peer);
Curl_peer_unlink(&bs->tunnel_peer);
}
static CURLcode cf_ip_ballers_init(struct cf_ip_ballers *bs,
struct Curl_cfilter *cf,
cf_ip_connect_create *cf_create,
uint8_t transport_in,
uint8_t transport_out,
struct Curl_easy *data,
struct Curl_peer *origin,
struct Curl_peer *peer,
uint8_t transport_peer,
struct Curl_peer *tunnel_peer,
uint8_t tunnel_transport,
timediff_t attempt_delay_ms,
uint32_t max_concurrent)
{
memset(bs, 0, sizeof(*bs));
bs->cf_create = cf_create;
bs->transport_in = transport_in;
bs->transport_out = transport_out;
bs->cf_create = get_cf_create(transport_peer, !!tunnel_peer);
if(!bs->cf_create) {
failf(data, "unsupported transport type %u%s",
transport_peer, tunnel_peer ? "to proxy" : "");
return CURLE_UNSUPPORTED_PROTOCOL;
}
Curl_peer_link(&bs->origin, origin);
Curl_peer_link(&bs->peer, peer);
bs->transport_peer = transport_peer;
Curl_peer_link(&bs->tunnel_peer, tunnel_peer);
bs->tunnel_transport = tunnel_transport;
bs->attempt_delay_ms = attempt_delay_ms;
bs->max_concurrent = max_concurrent;
bs->last_attempt_ai_family = AF_INET; /* so AF_INET6 is next */
if(transport_in == TRNSPRT_UNIX) {
#ifdef USE_UNIX_SOCKETS
cf_ai_iter_init(&bs->addr_iter, cf, AF_UNIX);
#else
return CURLE_UNSUPPORTED_PROTOCOL;
#endif
}
else { /* TCP/UDP/QUIC */
#ifdef USE_IPV6
cf_ai_iter_init(&bs->ipv6_iter, cf, AF_INET6);
#endif
cf_ai_iter_init(&bs->addr_iter, cf, AF_INET);
}
return CURLE_OK;
}
@ -473,12 +487,13 @@ evaluate:
if(bs->max_concurrent)
cf_ip_ballers_prune(bs, cf, data, bs->max_concurrent - 1);
result = Curl_socket_addr_from_ai(&addr, ai, bs->transport_out);
result = Curl_socket_addr_from_ai(&addr, ai, bs->transport_peer);
if(result)
goto out;
result = cf_ip_attempt_new(&a, cf, data, &addr, ai_family,
bs->transport_in, bs->transport_out,
result = cf_ip_attempt_new(&a, data, cf, bs->origin, bs->peer,
bs->transport_peer, &addr, ai_family,
bs->tunnel_peer, bs->tunnel_transport,
bs->cf_create);
CURL_TRC_CF(data, cf, "starting %s attempt for ipv%s -> %d",
bs->running ? "next" : "first",
@ -668,13 +683,10 @@ typedef enum {
} cf_connect_state;
struct cf_ip_happy_ctx {
struct Curl_peer *peer;
cf_ip_connect_create *cf_create;
cf_connect_state state;
struct cf_ip_ballers ballers;
struct curltime started;
uint8_t transport_in;
uint8_t transport_out;
BIT(dns_resolved);
};
@ -750,29 +762,39 @@ static CURLcode cf_ip_happy_init(struct Curl_cfilter *cf,
return CURLE_OPERATION_TIMEDOUT;
}
if(ctx->ballers.transport_peer == TRNSPRT_UNIX) {
#ifdef USE_UNIX_SOCKETS
cf_ai_iter_init(&ctx->ballers.addr_iter, cf, AF_UNIX);
#else
return CURLE_UNSUPPORTED_PROTOCOL;
#endif
}
else { /* TCP/UDP/QUIC */
#ifdef USE_IPV6
cf_ai_iter_init(&ctx->ballers.ipv6_iter, cf, AF_INET6);
#endif
cf_ai_iter_init(&ctx->ballers.addr_iter, cf, AF_INET);
}
CURL_TRC_CF(data, cf, "init ip ballers for transport %u",
ctx->transport_out);
ctx->ballers.transport_peer);
ctx->started = *Curl_pgrs_now(data);
return cf_ip_ballers_init(&ctx->ballers, cf, ctx->cf_create,
ctx->transport_in, ctx->transport_out,
data->set.happy_eyeballs_timeout,
IP_HE_MAX_CONCURRENT_ATTEMPTS);
return CURLE_OK;
}
static void cf_ip_happy_ctx_clear(struct Curl_cfilter *cf,
static void cf_ip_happy_ctx_clear(struct cf_ip_happy_ctx *ctx,
struct Curl_easy *data)
{
struct cf_ip_happy_ctx *ctx = cf->ctx;
DEBUGASSERT(ctx);
DEBUGASSERT(data);
cf_ip_ballers_clear(cf, data, &ctx->ballers);
if(ctx)
cf_ip_ballers_clear(data, &ctx->ballers);
}
static void cf_ip_happy_ctx_destroy(struct cf_ip_happy_ctx *ctx)
static void cf_ip_happy_ctx_destroy(struct cf_ip_happy_ctx *ctx,
struct Curl_easy *data)
{
if(ctx) {
Curl_peer_unlink(&ctx->peer);
cf_ip_happy_ctx_clear(ctx, data);
curlx_free(ctx);
}
}
@ -860,7 +882,7 @@ static CURLcode cf_ip_happy_connect(struct Curl_cfilter *cf,
cf->connected = TRUE;
cf->next = ctx->ballers.winner->cf;
ctx->ballers.winner->cf = NULL;
cf_ip_happy_ctx_clear(cf, data);
cf_ip_happy_ctx_clear(ctx, data);
Curl_expire_done(data, EXPIRE_HAPPY_EYEBALLS);
/* whatever errors where reported by ballers, clear our errorbuf */
Curl_reset_fail(data);
@ -943,8 +965,8 @@ static void cf_ip_happy_destroy(struct Curl_cfilter *cf,
CURL_TRC_CF(data, cf, "destroy");
if(ctx) {
cf_ip_happy_ctx_clear(cf, data);
cf_ip_happy_ctx_destroy(ctx);
cf_ip_happy_ctx_clear(ctx, data);
cf_ip_happy_ctx_destroy(ctx, data);
}
}
@ -977,11 +999,12 @@ struct Curl_cftype Curl_cft_ip_happy = {
*/
static CURLcode cf_ip_happy_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct Curl_peer *origin,
struct Curl_peer *peer,
uint8_t transport_peer,
struct connectdata *conn,
cf_ip_connect_create *cf_create,
uint8_t transport_in,
uint8_t transport_out)
struct Curl_peer *tunnel_peer,
uint8_t tunnel_transport)
{
struct cf_ip_happy_ctx *ctx = NULL;
CURLcode result;
@ -994,42 +1017,39 @@ static CURLcode cf_ip_happy_create(struct Curl_cfilter **pcf,
result = CURLE_OUT_OF_MEMORY;
goto out;
}
ctx->transport_in = transport_in;
ctx->transport_out = transport_out;
ctx->cf_create = cf_create;
Curl_peer_link(&ctx->peer, peer);
result = cf_ip_ballers_init(&ctx->ballers, data,
origin, peer, transport_peer,
tunnel_peer, tunnel_transport,
data->set.happy_eyeballs_timeout,
IP_HE_MAX_CONCURRENT_ATTEMPTS);
if(result)
goto out;
result = Curl_cf_create(pcf, &Curl_cft_ip_happy, ctx);
out:
if(result) {
curlx_safefree(*pcf);
cf_ip_happy_ctx_destroy(ctx);
cf_ip_happy_ctx_destroy(ctx, data);
}
return result;
}
CURLcode cf_ip_happy_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data,
struct Curl_peer *origin,
struct Curl_peer *peer,
uint8_t transport_in,
uint8_t transport_out,
bool tunnel_proxy)
uint8_t transport_peer,
struct Curl_peer *tunnel_peer,
uint8_t tunnel_transport)
{
cf_ip_connect_create *cf_create;
struct Curl_cfilter *cf;
CURLcode result;
/* Need to be first */
DEBUGASSERT(cf_at);
cf_create = get_cf_create(transport_out, tunnel_proxy);
if(!cf_create) {
CURL_TRC_CF(data, cf_at, "unsupported transport type %u%s",
transport_out, tunnel_proxy ? "to proxy" : "");
return CURLE_UNSUPPORTED_PROTOCOL;
}
result = cf_ip_happy_create(&cf, data, peer, cf_at->conn, cf_create,
transport_in, transport_out);
result = cf_ip_happy_create(&cf, data, origin, peer, transport_peer,
cf_at->conn, tunnel_peer, tunnel_transport);
if(result)
return result;

View file

@ -33,11 +33,15 @@ struct Curl_peer;
struct Curl_sockaddr_ex;
/**
* Create a cfilter for making an "ip" connection to the
* given address, using parameters from `conn`. The "ip" connection
* can be a TCP socket, a UDP socket or even a QUIC connection.
*
* It MUST use only the supplied `ai` for its connection attempt.
* Create a cfilter for making an "ip" connect to a peer.
* `pcf`: the filter created on success
* `data`: the transfer initiating the connect
* `peer`: the peer to connect to
* `transport_peer': the transport used for the peer connect
* `conn`: the connection that gets connected
* `addr`: the socket address to connect to
* `tunnel_peer`: NULL or the peer to tunnel through
* `tunnel_transport`: the transport that goes through the tunnel
*
* Such a filter may be used in "happy eyeball" scenarios, and its
* `connect` implementation needs to support non-blocking. Once connected,
@ -45,26 +49,21 @@ struct Curl_sockaddr_ex;
*/
typedef CURLcode cf_ip_connect_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct Curl_peer *origin,
struct Curl_peer *peer,
uint8_t transport_peer,
struct connectdata *conn,
struct Curl_sockaddr_ex *addr,
uint8_t transport_in,
uint8_t transport_out);
struct Curl_peer *tunnel_peer,
uint8_t tunnel_transport);
CURLcode cf_ip_happy_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data,
struct Curl_peer *origin,
struct Curl_peer *peer,
uint8_t transport_in,
uint8_t transport_out,
bool tunnel_proxy);
#if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3) && \
defined(USE_PROXY_HTTP3)
/* For H3 proxy: create happy eyeballs that races IPv4/IPv6 using raw UDP
sockets with TRNSPRT_QUIC transport so the socket is connected to the
proxy peer. H3-PROXY manages its own ngtcp2 QUIC stack on top. */
CURLcode cf_ip_happy_quic_udp_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data);
#endif /* !CURL_DISABLE_HTTP && USE_HTTP3 && USE_PROXY_HTTP3 */
uint8_t transport_peer,
struct Curl_peer *tunnel_peer,
uint8_t tunnel_transport);
extern struct Curl_cftype Curl_cft_ip_happy;

View file

@ -906,7 +906,7 @@ static CURLcode socket_connect_result(struct Curl_easy *data,
}
struct cf_socket_ctx {
uint8_t transport;
struct Curl_peer *peer;
struct Curl_sockaddr_ex addr; /* address to connect to */
curl_socket_t sock; /* current attempt socket */
struct ip_quadruple ip; /* The IP quadruple 2x(addr+port) */
@ -924,6 +924,7 @@ struct cf_socket_ctx {
int rblock_percent; /* percent of reads doing EAGAIN */
size_t recv_max; /* max enforced read size */
#endif
uint8_t transport;
BIT(got_first_byte); /* if first byte was received */
BIT(listening); /* socket is listening */
BIT(accepted); /* socket was accepted, not connected */
@ -932,10 +933,12 @@ struct cf_socket_ctx {
};
static CURLcode cf_socket_ctx_init(struct cf_socket_ctx *ctx,
struct Curl_peer *peer,
struct Curl_sockaddr_ex *addr,
uint8_t transport)
{
memset(ctx, 0, sizeof(*ctx));
Curl_peer_link(&ctx->peer, peer);
ctx->sock = CURL_SOCKET_BAD;
ctx->transport = transport;
ctx->addr = *addr;
@ -972,6 +975,14 @@ static CURLcode cf_socket_ctx_init(struct cf_socket_ctx *ctx,
return CURLE_OK;
}
static void cf_socket_ctx_free(struct cf_socket_ctx *ctx)
{
if(ctx) {
Curl_peer_unlink(&ctx->peer);
curlx_free(ctx);
}
}
static CURLcode cf_socket_shutdown(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool *done)
@ -1006,7 +1017,7 @@ static void cf_socket_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD;
socket_close(data, cf->conn, !ctx->accepted, ctx->sock);
}
curlx_free(ctx);
cf_socket_ctx_free(ctx);
}
}
@ -1754,19 +1765,24 @@ struct Curl_cftype Curl_cft_tcp = {
CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct Curl_peer *origin,
struct Curl_peer *peer,
uint8_t transport_peer,
struct connectdata *conn,
struct Curl_sockaddr_ex *addr,
uint8_t transport_in,
uint8_t transport_out)
struct Curl_peer *tunnel_peer,
uint8_t tunnel_transport)
{
struct cf_socket_ctx *ctx = NULL;
struct Curl_cfilter *cf = NULL;
CURLcode result;
(void)data;
(void)origin;
(void)conn;
(void)transport_in;
DEBUGASSERT(transport_out == TRNSPRT_TCP);
(void)tunnel_peer;
(void)tunnel_transport;
DEBUGASSERT(transport_peer == TRNSPRT_TCP);
if(!addr) {
result = CURLE_BAD_FUNCTION_ARGUMENT;
goto out;
@ -1778,7 +1794,7 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf,
goto out;
}
result = cf_socket_ctx_init(ctx, addr, transport_out);
result = cf_socket_ctx_init(ctx, peer, addr, transport_peer);
if(result)
goto out;
@ -1788,7 +1804,7 @@ out:
*pcf = (!result) ? cf : NULL;
if(result) {
curlx_safefree(cf);
curlx_safefree(ctx);
cf_socket_ctx_free(ctx);
}
return result;
@ -1921,26 +1937,31 @@ struct Curl_cftype Curl_cft_udp = {
CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct Curl_peer *origin,
struct Curl_peer *peer,
uint8_t transport_peer,
struct connectdata *conn,
struct Curl_sockaddr_ex *addr,
uint8_t transport_in,
uint8_t transport_out)
struct Curl_peer *tunnel_peer,
uint8_t tunnel_transport)
{
struct cf_socket_ctx *ctx = NULL;
struct Curl_cfilter *cf = NULL;
CURLcode result;
(void)data;
(void)origin;
(void)conn;
(void)transport_in;
DEBUGASSERT(transport_out == TRNSPRT_UDP || transport_out == TRNSPRT_QUIC);
(void)tunnel_peer;
(void)tunnel_transport;
DEBUGASSERT(transport_peer == TRNSPRT_UDP || transport_peer == TRNSPRT_QUIC);
ctx = curlx_calloc(1, sizeof(*ctx));
if(!ctx) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
result = cf_socket_ctx_init(ctx, addr, transport_out);
result = cf_socket_ctx_init(ctx, peer, addr, transport_peer);
if(result)
goto out;
@ -1950,7 +1971,7 @@ out:
*pcf = (!result) ? cf : NULL;
if(result) {
curlx_safefree(cf);
curlx_safefree(ctx);
cf_socket_ctx_free(ctx);
}
return result;
@ -1975,27 +1996,32 @@ struct Curl_cftype Curl_cft_unix = {
};
CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
struct Curl_sockaddr_ex *addr,
uint8_t transport_in,
uint8_t transport_out)
struct Curl_easy *data,
struct Curl_peer *origin,
struct Curl_peer *peer,
uint8_t transport_peer,
struct connectdata *conn,
struct Curl_sockaddr_ex *addr,
struct Curl_peer *tunnel_peer,
uint8_t tunnel_transport)
{
struct cf_socket_ctx *ctx = NULL;
struct Curl_cfilter *cf = NULL;
CURLcode result;
(void)data;
(void)origin;
(void)conn;
(void)transport_in;
DEBUGASSERT(transport_out == TRNSPRT_UNIX);
(void)tunnel_peer;
(void)tunnel_transport;
DEBUGASSERT(transport_peer == TRNSPRT_UNIX);
ctx = curlx_calloc(1, sizeof(*ctx));
if(!ctx) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
result = cf_socket_ctx_init(ctx, addr, transport_out);
result = cf_socket_ctx_init(ctx, peer, addr, transport_peer);
if(result)
goto out;
@ -2005,7 +2031,7 @@ out:
*pcf = (!result) ? cf : NULL;
if(result) {
curlx_safefree(cf);
curlx_safefree(ctx);
cf_socket_ctx_free(ctx);
}
return result;

View file

@ -94,10 +94,13 @@ int Curl_socket_close(struct Curl_easy *data, struct connectdata *conn,
*/
CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct Curl_peer *origin,
struct Curl_peer *peer,
uint8_t transport_peer,
struct connectdata *conn,
struct Curl_sockaddr_ex *addr,
uint8_t transport_in,
uint8_t transport_out);
struct Curl_peer *tunnel_peer,
uint8_t tunnel_transport);
/**
* Creates a cfilter that opens a UDP socket to the given address
@ -108,10 +111,13 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf,
*/
CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct Curl_peer *origin,
struct Curl_peer *peer,
uint8_t transport_peer,
struct connectdata *conn,
struct Curl_sockaddr_ex *addr,
uint8_t transport_in,
uint8_t transport_out);
struct Curl_peer *tunnel_peer,
uint8_t tunnel_transport);
/**
* Creates a cfilter that opens a UNIX socket to the given address
@ -122,10 +128,13 @@ CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf,
*/
CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct Curl_peer *origin,
struct Curl_peer *peer,
uint8_t transport_peer,
struct connectdata *conn,
struct Curl_sockaddr_ex *addr,
uint8_t transport_in,
uint8_t transport_out);
struct Curl_peer *tunnel_peer,
uint8_t tunnel_transport);
/**
* Creates a cfilter that keeps a listening socket.

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)
{

View file

@ -126,12 +126,21 @@ CURLcode Curl_conn_setup(struct Curl_easy *data,
/* Set conn to allow multiplexing. */
void Curl_conn_set_multiplex(struct connectdata *conn);
/* Get the origin peer at sockindex. */
struct Curl_peer *Curl_conn_get_origin(struct connectdata *conn,
int sockindex);
/* Get the peer the connection actually connects to at sockindex.
* Often the same as "origin", but can be redirected via "connect-to"
* or "alt-svc". May tunnel through proxies. */
struct Curl_peer *Curl_conn_get_destination(struct connectdata *conn,
int sockindex);
/* Get the origin curl connects its socket to.
* Can be origin or the first proxy. */
struct Curl_peer *Curl_conn_get_first_origin(struct connectdata *conn,
int sockindex);
/* Get the peer curl connects its socket to.
* Can be origin, "connect-to" or the first proxy. */
struct Curl_peer *Curl_conn_get_first_peer(struct connectdata *conn,

View file

@ -1390,10 +1390,13 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
ftp_state(data, ftpc, FTP_STOP);
}
else {
/* successfully set up the listen socket filter. SSL needed? */
/* successfully set up the listen socket filter. SSL needed?
* Use the control connections origin for cert verification. */
if(conn->bits.ftp_use_data_ssl && data->set.ftp_use_port &&
!Curl_conn_is_ssl(conn, SECONDARYSOCKET)) {
result = Curl_ssl_cfilter_add(data, conn, SECONDARYSOCKET);
result = Curl_ssl_cfilter_add(
data, Curl_conn_get_origin(conn, FIRSTSOCKET),
conn, SECONDARYSOCKET);
}
conn->bits.do_more = FALSE;
Curl_pgrsTime(data, TIMER_STARTACCEPT);
@ -3196,7 +3199,8 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data,
/* this was BLOCKING, keep it so for now */
bool done;
if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
result = Curl_ssl_cfilter_add(
data, Curl_conn_get_origin(conn, FIRSTSOCKET), conn, FIRSTSOCKET);
if(result) {
/* we failed and bail out */
return CURLE_USE_SSL_FAILED;

View file

@ -172,10 +172,11 @@ static CURLcode dynhds_add_custom(struct Curl_easy *data,
}
struct cf_proxy_ctx {
struct Curl_peer *dest; /* tunnel destination */
struct Curl_peer *peer; /* proxy */
struct Curl_peer *tunnel_peer; /* tunnel destination */
uint8_t proxytype;
uint8_t tunnel_transport;
BIT(sub_filter_installed);
BIT(udp_tunnel);
};
static int proxy_http_ver_major(proxy_http_ver ver)
@ -556,9 +557,8 @@ static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf,
{
struct cf_proxy_ctx *ctx = cf->ctx;
CURLcode result;
const char *tunnel_type; /* Determine tunnel type once and reuse */
tunnel_type = ctx->udp_tunnel ? "CONNECT-UDP" : "CONNECT";
bool udp_tunnel = TRNSPRT_IS_DGRAM(ctx->tunnel_transport);
const char *tunnel_type = udp_tunnel ? "CONNECT-UDP" : "CONNECT";
if(cf->connected) {
*done = TRUE;
@ -606,8 +606,8 @@ connect_sub:
if(!strcmp(alpn, "http/1.0")) {
CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.0");
result = Curl_cf_h1_proxy_insert_after(cf, data, ctx->dest, 10,
(bool)ctx->udp_tunnel);
result = Curl_cf_h1_proxy_insert_after(cf, data, ctx->tunnel_peer, 10,
udp_tunnel);
if(result)
goto out;
}
@ -615,16 +615,16 @@ connect_sub:
int httpversion = (ctx->proxytype == CURLPROXY_HTTP_1_0) ? 10 : 11;
CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.%d",
httpversion % 10);
result = Curl_cf_h1_proxy_insert_after(cf, data, ctx->dest, httpversion,
(bool)ctx->udp_tunnel);
result = Curl_cf_h1_proxy_insert_after(cf, data, ctx->tunnel_peer,
httpversion, udp_tunnel);
if(result)
goto out;
}
#ifdef USE_NGHTTP2
else if(!strcmp(alpn, "h2")) {
CURL_TRC_CF(data, cf, "installing subfilter for HTTP/2");
result = Curl_cf_h2_proxy_insert_after(cf, data, ctx->dest,
(bool)ctx->udp_tunnel);
result = Curl_cf_h2_proxy_insert_after(cf, data, ctx->tunnel_peer,
udp_tunnel);
if(result)
goto out;
}
@ -633,8 +633,9 @@ connect_sub:
defined(USE_NGTCP2) && defined(USE_OPENSSL)
else if(!strcmp(alpn, "h3")) {
CURL_TRC_CF(data, cf, "installing subfilter for HTTP/3");
result = Curl_cf_h3_proxy_insert_after(cf, data, ctx->dest,
(bool)ctx->udp_tunnel);
result = Curl_cf_h3_proxy_insert_after(cf, data, ctx->peer, ctx->peer,
ctx->tunnel_peer,
ctx->tunnel_transport);
if(result)
goto out;
}
@ -673,8 +674,8 @@ static CURLcode cf_http_proxy_query(struct Curl_cfilter *cf,
struct cf_proxy_ctx *ctx = cf->ctx;
switch(query) {
case CF_QUERY_HOST_PORT:
*pres1 = (int)ctx->dest->port;
*((const char **)pres2) = ctx->dest->hostname;
*pres1 = (int)ctx->tunnel_peer->port;
*((const char **)pres2) = ctx->tunnel_peer->hostname;
return CURLE_OK;
case CF_QUERY_ALPN_NEGOTIATED: {
const char **palpn = pres2;
@ -693,7 +694,8 @@ static CURLcode cf_http_proxy_query(struct Curl_cfilter *cf,
static void cf_https_proxy_ctx_free(struct cf_proxy_ctx *ctx)
{
if(ctx) {
Curl_peer_unlink(&ctx->dest);
Curl_peer_unlink(&ctx->peer);
Curl_peer_unlink(&ctx->tunnel_peer);
curlx_free(ctx);
}
}
@ -727,8 +729,9 @@ struct Curl_cftype Curl_cft_http_proxy = {
CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data,
struct Curl_peer *dest,
uint8_t transport,
struct Curl_peer *peer,
struct Curl_peer *tunnel_peer,
uint8_t tunnel_transport,
uint8_t proxytype)
{
struct Curl_cfilter *cf;
@ -736,7 +739,7 @@ CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at,
CURLcode result;
(void)data;
if(!dest)
if(!peer || !tunnel_peer)
return CURLE_FAILED_INIT;
ctx = curlx_calloc(1, sizeof(*ctx));
@ -744,9 +747,10 @@ CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at,
result = CURLE_OUT_OF_MEMORY;
goto out;
}
Curl_peer_link(&ctx->dest, dest);
Curl_peer_link(&ctx->peer, peer);
Curl_peer_link(&ctx->tunnel_peer, tunnel_peer);
ctx->proxytype = proxytype;
ctx->udp_tunnel = (transport == TRNSPRT_QUIC);
ctx->tunnel_transport = tunnel_transport;
result = Curl_cf_create(&cf, &Curl_cft_http_proxy, ctx);
if(result)

View file

@ -68,8 +68,9 @@ CURLcode Curl_http_proxy_inspect_tunnel_response(
CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data,
struct Curl_peer *dest,
uint8_t transport,
struct Curl_peer *peer,
struct Curl_peer *tunnel_peer,
uint8_t tunnel_transport,
uint8_t proxytype);
extern struct Curl_cftype Curl_cft_http_proxy;

View file

@ -555,7 +555,8 @@ static CURLcode imap_perform_upgrade_tls(struct Curl_easy *data,
bool ssldone = FALSE;
if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
result = Curl_ssl_cfilter_add(
data, Curl_conn_get_origin(conn, FIRSTSOCKET), conn, FIRSTSOCKET);
if(result)
goto out;
/* Change the connection handler */

View file

@ -4074,11 +4074,12 @@ CURLcode Curl_multi_xfer_sockbuf_borrow(struct Curl_easy *data,
size_t blen, char **pbuf)
{
DEBUGASSERT(data);
DEBUGASSERT(data->multi);
*pbuf = NULL;
if(!data->multi) {
failf(data, "transfer has no multi handle");
return CURLE_FAILED_INIT;
/* When a SHARE gets destroyed and has a connection pool, we get
* call with share->admin which does not have a multi handle. */
*pbuf = curlx_malloc(blen);
return *pbuf ? CURLE_OK : CURLE_OUT_OF_MEMORY;
}
if(data->multi->xfer_sockbuf_borrowed) {
failf(data, "attempt to borrow xfer_sockbuf when already borrowed");
@ -4107,11 +4108,16 @@ CURLcode Curl_multi_xfer_sockbuf_borrow(struct Curl_easy *data,
void Curl_multi_xfer_sockbuf_release(struct Curl_easy *data, char *buf)
{
(void)buf;
DEBUGASSERT(data);
DEBUGASSERT(data->multi);
DEBUGASSERT(!buf || data->multi->xfer_sockbuf == buf);
data->multi->xfer_sockbuf_borrowed = FALSE;
if(!data->multi) {
/* When a SHARE gets destroyed and has a connection pool, we get
* call with share->admin which does not have a multi handle. */
curlx_free(buf);
}
else {
DEBUGASSERT(!buf || data->multi->xfer_sockbuf == buf);
data->multi->xfer_sockbuf_borrowed = FALSE;
}
}
static void multi_xfer_bufs_free(struct Curl_multi *multi)

View file

@ -900,7 +900,8 @@ static CURLcode oldap_connecting(struct Curl_easy *data, bool *done)
result = oldap_perform_bind(data, OLDAP_BIND);
break;
}
result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
result = Curl_ssl_cfilter_add(
data, Curl_conn_get_origin(conn, FIRSTSOCKET), conn, FIRSTSOCKET);
if(result)
break;
FALLTHROUGH();

View file

@ -485,7 +485,8 @@ static CURLcode pop3_perform_upgrade_tls(struct Curl_easy *data,
return CURLE_FAILED_INIT;
if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
result = Curl_ssl_cfilter_add(
data, Curl_conn_get_origin(conn, FIRSTSOCKET), conn, FIRSTSOCKET);
if(result)
goto out;
/* Change the connection handler */

View file

@ -689,7 +689,8 @@ static CURLcode smtp_perform_upgrade_tls(struct Curl_easy *data,
DEBUGASSERT(smtpc->state == SMTP_UPGRADETLS);
if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
result = Curl_ssl_cfilter_add(
data, Curl_conn_get_origin(conn, FIRSTSOCKET), conn, FIRSTSOCKET);
if(result)
goto out;
/* Change the connection handler and SMTP state */

View file

@ -253,6 +253,8 @@ struct hostname {
#define TRNSPRT_QUIC 5
#define TRNSPRT_UNIX 6
#define TRNSPRT_IS_DGRAM(x) (((x) == TRNSPRT_UDP) || ((x) == TRNSPRT_QUIC))
struct ip_quadruple {
char remote_ip[MAX_IPADR_LEN];
char local_ip[MAX_IPADR_LEN];

1965
lib/vquic/cf-ngtcp2-cmn.c Normal file

File diff suppressed because it is too large Load diff

239
lib/vquic/cf-ngtcp2-cmn.h Normal file
View file

@ -0,0 +1,239 @@
#ifndef HEADER_CURL_VQUIC_CF_NGTCP2_CMN_H
#define HEADER_CURL_VQUIC_CF_NGTCP2_CMN_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#if !defined(CURL_DISABLE_HTTP) && defined(USE_NGTCP2) && defined(USE_NGHTTP3)
#include <ngtcp2/ngtcp2.h>
#include <ngtcp2/ngtcp2_crypto.h>
#ifdef USE_OPENSSL
#include <openssl/err.h>
#if defined(OPENSSL_IS_AWSLC) || defined(OPENSSL_IS_BORINGSSL)
#include <ngtcp2/ngtcp2_crypto_boringssl.h>
#elif defined(OPENSSL_QUIC_API2)
#include <ngtcp2/ngtcp2_crypto_ossl.h>
#else
#include <ngtcp2/ngtcp2_crypto_quictls.h>
#endif
#include "vtls/openssl.h"
#elif defined(USE_GNUTLS)
#include <ngtcp2/ngtcp2_crypto_gnutls.h>
#include "vtls/gtls.h"
#elif defined(USE_WOLFSSL)
#include <wolfssl/options.h>
#include <wolfssl/ssl.h>
#include <wolfssl/quic.h>
#include <ngtcp2/ngtcp2_crypto_wolfssl.h>
#include "vtls/wolfssl.h"
#endif
#ifdef HAVE_NETINET_UDP_H
#include <netinet/udp.h>
#endif
#include <nghttp3/nghttp3.h>
#include "http1.h"
#include "uint-hash.h"
#include "vtls/vtls.h"
#include "vquic/vquic_int.h"
#include "vquic/vquic-tls.h"
struct Curl_cfilter;
struct Curl_easy;
struct cf_ngtcp2_ctx;
struct cf_quic_ctx;
#define QUIC_MAX_STREAMS (256 * 1024)
#define QUIC_HANDSHAKE_TIMEOUT (10 * NGTCP2_SECONDS)
#define QUIC_TUNNEL_INBUF_SIZE (64 * 1024)
/* We announce a small window size in transport param to the server,
* and grow that immediately to max when no rate limit is in place.
* We need to start small as we are not able to decrease it. */
#define H3_STREAM_WINDOW_SIZE_INITIAL (32 * 1024)
#define H3_STREAM_WINDOW_SIZE_MAX (10 * 1024 * 1024)
#define H3_CONN_WINDOW_SIZE_MAX (100 * H3_STREAM_WINDOW_SIZE_MAX)
#define H3_STREAM_CHUNK_SIZE (64 * 1024)
#if H3_STREAM_CHUNK_SIZE < NGTCP2_MAX_UDP_PAYLOAD_SIZE
#error H3_STREAM_CHUNK_SIZE smaller than NGTCP2_MAX_UDP_PAYLOAD_SIZE
#endif
/* The pool keeps spares around and half of a full stream window
* seems good. More does not seem to improve performance.
* The benefit of the pool is that stream buffers do not keep
* spares. Memory consumption goes down when streams run empty,
* have a large upload done, etc. */
#define H3_STREAM_POOL_SPARES 2
/* The max amount of un-acked upload data we keep around per stream */
#define H3_STREAM_SEND_BUFFER_MAX (10 * 1024 * 1024)
#define H3_STREAM_SEND_CHUNKS \
(H3_STREAM_SEND_BUFFER_MAX / H3_STREAM_CHUNK_SIZE)
#define QUIC_TUNNEL_INGRESS_PKT_LIMIT 1000
void Curl_ngtcp2_ver(char *p, size_t len);
typedef CURLcode cf_ngtcp2_init_h3_conn(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct cf_ngtcp2_ctx *ctx);
struct cf_ngtcp2_ctx {
struct cf_quic_ctx q;
struct ssl_peer ssl_peer;
struct curl_tls_ctx tls;
#ifdef OPENSSL_QUIC_API2
ngtcp2_crypto_ossl_ctx *ossl_ctx;
#endif
ngtcp2_path connected_path;
ngtcp2_conn *qconn;
ngtcp2_cid dcid;
ngtcp2_cid scid;
uint32_t version;
ngtcp2_settings settings;
ngtcp2_transport_params transport_params;
ngtcp2_ccerr last_error;
ngtcp2_crypto_conn_ref conn_ref;
struct cf_call_data call_data;
cf_ngtcp2_init_h3_conn *init_h3_conn_cb;
nghttp3_conn *h3conn;
nghttp3_settings h3settings;
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 dynbuf scratch; /* temp buffer for header construction */
struct uint_hash streams; /* hash data->mid to h3_stream_ctx */
uint64_t used_bidi_streams; /* bidi streams we have opened */
uint64_t max_bidi_streams; /* max bidi streams we can open */
size_t earlydata_max; /* max amount of early data supported by
server on session reuse */
size_t earlydata_skip; /* sending bytes to skip when earlydata
is accepted by peer */
CURLcode tls_vrfy_result; /* result of TLS peer verification */
int qlogfd;
unsigned char *tunnel_inbuf; /* ingress buffer for tunneled packets */
size_t tunnel_inbuf_len;
BIT(initialized);
BIT(tls_handshake_complete); /* TLS handshake is done */
BIT(use_earlydata); /* Using 0RTT data */
BIT(earlydata_accepted); /* 0RTT was accepted by server */
BIT(shutdown_started); /* queued shutdown packets */
};
/* How to access `call_data` from a cf_ngtcp2 filter */
#undef CF_CTX_CALL_DATA
#define CF_CTX_CALL_DATA(cf) ((struct cf_ngtcp2_ctx *)(cf)->ctx)->call_data
CURLcode Curl_cf_ngtcp2_ctx_init(struct cf_ngtcp2_ctx *ctx,
struct Curl_peer *origin,
struct Curl_peer *peer,
struct ssl_primary_config *sslc,
cf_ngtcp2_init_h3_conn *init_h3_conn_cb);
void Curl_cf_ngtcp2_ctx_cleanup(struct cf_ngtcp2_ctx *ctx);
void Curl_cf_ngtcp2_cmn_err_set(struct Curl_cfilter *cf,
struct Curl_easy *data, int code);
/**
* All about the H3 internals of a stream
*/
struct h3_stream_ctx {
int64_t id; /* HTTP/3 stream identifier */
struct bufq sendbuf; /* h3 request body */
struct h1_req_parser h1; /* h1 request parsing */
size_t sendbuf_len_in_flight; /* sendbuf amount "in flight" */
uint64_t error3; /* HTTP/3 stream error code */
curl_off_t upload_left; /* number of request bytes left to upload */
curl_off_t rx_total; /* total number of bytes received */
uint64_t rx_offset; /* current receive offset */
uint64_t rx_offset_max; /* allowed receive offset */
uint64_t window_size_max; /* max flow control window set for stream */
int status_code; /* HTTP status code */
CURLcode xfer_result; /* result from xfer_resp_write(_hd) */
BIT(resp_hds_complete); /* we have a complete, final response */
BIT(closed); /* TRUE on stream close */
BIT(reset); /* TRUE on stream reset */
BIT(send_closed); /* stream is local closed */
BIT(quic_flow_blocked); /* stream is blocked by QUIC flow control */
};
void Curl_cf_ngtcp2_h3_stream_ctx_free(struct h3_stream_ctx *stream);
void Curl_cf_ngtcp2_h3_err_set(struct Curl_cfilter *cf,
struct Curl_easy *data, int code);
CURLcode Curl_cf_ngtcp2_h3_init_ctrls(struct cf_ngtcp2_ctx *ctx,
struct Curl_easy *data);
CURLcode Curl_cf_ngtcp2_cmn_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool *done);
CURLcode Curl_cf_ngtcp2_cmn_shutdown(struct Curl_cfilter *cf,
struct Curl_easy *data, bool *done);
void Curl_cf_ngtcp2_cmn_conn_close(struct Curl_cfilter *cf,
struct Curl_easy *data);
struct cf_ngtcp2_io_ctx {
struct Curl_cfilter *cf;
struct Curl_easy *data;
ngtcp2_tstamp ts;
ngtcp2_path_storage ps;
};
void Curl_cf_ngtcp2_io_ctx_init(struct cf_ngtcp2_io_ctx *io_ctx,
struct Curl_cfilter *cf,
struct Curl_easy *data);
void Curl_cf_ngtcp2_io_ctx_update_time(struct Curl_easy *data,
struct cf_ngtcp2_io_ctx *pktx,
struct Curl_cfilter *cf);
CURLcode Curl_cf_ngtcp2_progress_egress(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct cf_ngtcp2_io_ctx *pktx);
CURLcode Curl_cf_ngtcp2_progress_ingress(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct cf_ngtcp2_io_ctx *pktx);
CURLcode Curl_cf_ngtcp2_cmn_set_expiry(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct cf_ngtcp2_io_ctx *pktx);
CURLcode Curl_cf_ngtcp2_h3_stream_setup(struct Curl_cfilter *cf,
struct Curl_easy *data);
void Curl_cf_ngtcp2_h3_stream_close(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct h3_stream_ctx *stream);
void Curl_cf_ngtcp2_h3_stream_done(struct Curl_cfilter *cf,
struct Curl_easy *data);
bool Curl_cf_ngtcp2_cmn_conn_is_alive(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool *input_pending);
#endif /* !CURL_DISABLE_HTTP && USE_NGTCP2 && USE_NGHTTP3 */
#endif /* HEADER_CURL_VQUIC_CF_NGTCP2_CMN_H */

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
#ifndef HEADER_CURL_H3_PROXY_H
#define HEADER_CURL_H3_PROXY_H
#ifndef HEADER_CURL_VQUIC_CF_NGTCP2_PROXY_H
#define HEADER_CURL_VQUIC_CF_NGTCP2_PROXY_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
@ -32,16 +32,21 @@
CURLcode Curl_cf_ngtcp2_proxy_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data,
struct Curl_peer *dest,
bool udp_tunnel);
struct Curl_peer *origin,
struct Curl_peer *peer,
struct Curl_peer *tunnel_peer,
uint8_t tunnel_transport);
CURLcode Curl_cf_ngtcp2_proxy_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct Curl_peer *origin,
struct Curl_peer *peer,
uint8_t transport_peer,
struct connectdata *conn,
struct Curl_sockaddr_ex *addr,
uint8_t transport_in,
uint8_t transport_out);
struct Curl_peer *tunnel_peer,
uint8_t tunnel_transport);
#endif
#endif /* HEADER_CURL_H3_PROXY_H */
#endif /* HEADER_CURL_VQUIC_CF_NGTCP2_PROXY_H */

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
#ifndef HEADER_CURL_VQUIC_CURL_NGTCP2_H
#define HEADER_CURL_VQUIC_CURL_NGTCP2_H
#ifndef HEADER_CURL_VQUIC_CF_NGTCP2_H
#define HEADER_CURL_VQUIC_CF_NGTCP2_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
@ -48,14 +48,16 @@ struct Curl_cfilter;
#include "urldata.h"
void Curl_ngtcp2_ver(char *p, size_t len);
CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct Curl_peer *origin,
struct Curl_peer *peer,
struct connectdata *conn,
struct Curl_sockaddr_ex *addr);
CURLcode Curl_cf_ngtcp2_insert_after(struct Curl_cfilter *cf_at);
CURLcode Curl_cf_ngtcp2_insert_after(struct Curl_cfilter *cf_at,
struct Curl_peer *origin,
struct Curl_peer *peer);
#endif
#endif /* HEADER_CURL_VQUIC_CURL_NGTCP2_H */
#endif /* HEADER_CURL_VQUIC_CF_NGTCP2_H */

View file

@ -75,7 +75,7 @@ void Curl_quiche_ver(char *p, size_t len)
struct cf_quiche_ctx {
struct cf_quic_ctx q;
struct ssl_peer peer;
struct ssl_peer ssl_peer;
struct curl_tls_ctx tls;
quiche_conn *qconn;
quiche_config *cfg;
@ -106,7 +106,10 @@ static void quiche_debug_log(const char *line, void *argp)
static void h3_stream_hash_free(unsigned int id, void *stream);
static void cf_quiche_ctx_init(struct cf_quiche_ctx *ctx)
static CURLcode cf_quiche_ctx_init(struct cf_quiche_ctx *ctx,
struct Curl_peer *origin,
struct Curl_peer *peer,
struct ssl_primary_config *sslc)
{
DEBUGASSERT(!ctx->initialized);
#ifdef DEBUG_QUICHE
@ -121,6 +124,7 @@ static void cf_quiche_ctx_init(struct cf_quiche_ctx *ctx)
BUFQ_OPT_SOFT_LIMIT);
ctx->data_recvd = 0;
ctx->initialized = TRUE;
return Curl_vquic_tls_peer_init(origin, peer, sslc, &ctx->ssl_peer);
}
static void cf_quiche_ctx_free(struct cf_quiche_ctx *ctx)
@ -129,7 +133,7 @@ static void cf_quiche_ctx_free(struct cf_quiche_ctx *ctx)
/* quiche freed it */
ctx->tls.ossl.ssl = NULL;
Curl_vquic_tls_cleanup(&ctx->tls);
Curl_ssl_peer_cleanup(&ctx->peer);
Curl_ssl_peer_cleanup(&ctx->ssl_peer);
vquic_ctx_free(&ctx->q);
Curl_uint32_hash_destroy(&ctx->streams);
curlx_dyn_free(&ctx->h1hdr);
@ -156,7 +160,7 @@ static void cf_quiche_ctx_close(struct cf_quiche_ctx *ctx)
quiche_config_free(ctx->cfg);
ctx->cfg = NULL;
}
Curl_ssl_peer_cleanup(&ctx->peer);
Curl_ssl_peer_cleanup(&ctx->ssl_peer);
}
static CURLcode cf_flush_egress(struct Curl_cfilter *cf,
@ -1291,7 +1295,7 @@ static CURLcode cf_quiche_ctx_open(struct Curl_cfilter *cf,
sizeof(QUICHE_H3_APPLICATION_PROTOCOL)
- 1);
result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer,
result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->ssl_peer,
&ALPN_SPEC_H3, NULL, NULL, cf, NULL);
if(result)
return result;
@ -1357,7 +1361,7 @@ static CURLcode cf_quiche_verify_peer(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_quiche_ctx *ctx = cf->ctx;
return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer);
return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->ssl_peer);
}
static CURLcode cf_quiche_connect(struct Curl_cfilter *cf,
@ -1629,6 +1633,8 @@ struct Curl_cftype Curl_cft_http3 = {
CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct Curl_peer *origin,
struct Curl_peer *peer,
struct connectdata *conn,
struct Curl_sockaddr_ex *addr)
{
@ -1641,15 +1647,15 @@ CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf,
result = CURLE_OUT_OF_MEMORY;
goto out;
}
cf_quiche_ctx_init(ctx);
result = Curl_cf_create(&cf, &Curl_cft_http3, ctx);
result = cf_quiche_ctx_init(ctx, origin, peer, &conn->ssl_config);
if(!result)
result = Curl_cf_create(&cf, &Curl_cft_http3, ctx);
if(result)
goto out;
cf->conn = conn;
result = Curl_cf_udp_create(&cf->next, data, conn, addr,
TRNSPRT_QUIC, TRNSPRT_QUIC);
result = Curl_cf_udp_create(&cf->next, data, origin, peer, TRNSPRT_QUIC,
conn, addr, NULL, TRNSPRT_QUIC);
if(result)
goto out;
cf->next->conn = cf->conn;
@ -1667,4 +1673,34 @@ out:
return result;
}
CURLcode Curl_cf_quiche_insert_after(struct Curl_cfilter *cf_at,
struct Curl_peer *origin,
struct Curl_peer *peer)
{
struct cf_quiche_ctx *ctx = NULL;
struct Curl_cfilter *cf = NULL;
CURLcode result;
ctx = curlx_calloc(1, sizeof(*ctx));
if(!ctx) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
result = cf_quiche_ctx_init(ctx, origin, peer, &cf_at->conn->ssl_config);
if(!result)
result = Curl_cf_create(&cf, &Curl_cft_http3, ctx);
if(result)
goto out;
Curl_conn_cf_insert_after(cf_at, cf);
out:
if(result) {
curlx_safefree(cf);
if(ctx)
cf_quiche_ctx_free(ctx);
}
return result;
}
#endif

View file

@ -1,5 +1,5 @@
#ifndef HEADER_CURL_VQUIC_CURL_QUICHE_H
#define HEADER_CURL_VQUIC_CURL_QUICHE_H
#ifndef HEADER_CURL_VQUIC_CF_QUICHE_H
#define HEADER_CURL_VQUIC_CF_QUICHE_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
@ -37,9 +37,14 @@ void Curl_quiche_ver(char *p, size_t len);
CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct Curl_peer *origin,
struct Curl_peer *peer,
struct connectdata *conn,
struct Curl_sockaddr_ex *addr);
CURLcode Curl_cf_quiche_insert_after(struct Curl_cfilter *cf_at,
struct Curl_peer *origin,
struct Curl_peer *peer);
#endif
#endif /* HEADER_CURL_VQUIC_CURL_QUICHE_H */
#endif /* HEADER_CURL_VQUIC_CF_QUICHE_H */

View file

@ -49,17 +49,12 @@
#include "vtls/vtls_scache.h"
#include "vquic/vquic-tls.h"
CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx,
struct Curl_cfilter *cf,
struct Curl_easy *data,
struct ssl_peer *peer,
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)
CURLcode Curl_vquic_tls_peer_init(struct Curl_peer *origin,
struct Curl_peer *peer,
struct ssl_primary_config *sslc,
struct ssl_peer *ssl_peer)
{
char tls_id[80];
CURLcode result;
#ifdef USE_OPENSSL
Curl_ossl_version(tls_id, sizeof(tls_id));
@ -71,24 +66,31 @@ CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx,
#error "no TLS lib in used, should not happen"
return CURLE_FAILED_INIT;
#endif
(void)session_reuse_cb;
if(peer->dest)
Curl_ssl_peer_cleanup(peer);
result = Curl_ssl_peer_init(peer, cf, tls_id, TRNSPRT_QUIC);
if(result)
return result;
if(ssl_peer->origin || ssl_peer->peer)
Curl_ssl_peer_cleanup(ssl_peer);
return Curl_ssl_peer_init(ssl_peer, origin, peer, sslc,
tls_id, TRNSPRT_QUIC);
}
CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx,
struct Curl_cfilter *cf,
struct Curl_easy *data,
struct ssl_peer *ssl_peer,
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)
{
#ifdef USE_OPENSSL
(void)result;
return Curl_ossl_ctx_init(&ctx->ossl, cf, data, peer, alpns,
return Curl_ossl_ctx_init(&ctx->ossl, cf, data, ssl_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, alpns,
return Curl_gtls_ctx_init(&ctx->gtls, cf, data, ssl_peer, alpns,
cb_setup, cb_user_data, ssl_user_data,
session_reuse_cb);
#elif defined(USE_WOLFSSL)
return Curl_wssl_ctx_init(&ctx->wssl, cf, data, peer, alpns,
return Curl_wssl_ctx_init(&ctx->wssl, cf, data, ssl_peer, alpns,
cb_setup, cb_user_data,
ssl_user_data, session_reuse_cb);
#else
@ -180,7 +182,7 @@ CURLcode Curl_vquic_tls_verify_peer(struct curl_tls_ctx *ctx,
NULL) == WOLFSSL_FAILURE))
result = CURLE_PEER_FAILED_VERIFICATION;
else if(!peer->sni &&
(wolfSSL_X509_check_ip_asc(cert, peer->dest->hostname,
(wolfSSL_X509_check_ip_asc(cert, peer->origin->hostname,
0) == WOLFSSL_FAILURE))
result = CURLE_PEER_FAILED_VERIFICATION;
wolfSSL_X509_free(cert);

View file

@ -66,13 +66,18 @@ typedef CURLcode Curl_vquic_session_reuse_cb(struct Curl_cfilter *cf,
struct Curl_ssl_session *scs,
bool *do_early_data);
CURLcode Curl_vquic_tls_peer_init(struct Curl_peer *origin,
struct Curl_peer *peer,
struct ssl_primary_config *sslc,
struct ssl_peer *ssl_peer);
/**
* Initialize the QUIC TLS instances based of the SSL configurations
* for the connection filter, transfer and peer.
* @param ctx the TLS context to initialize
* @param cf the connection filter involved
* @param data the transfer involved
* @param peer the peer to be connected to
* @param ssl_peer the SSL peer to be connected to
* @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
@ -82,7 +87,7 @@ typedef CURLcode Curl_vquic_session_reuse_cb(struct Curl_cfilter *cf,
CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx,
struct Curl_cfilter *cf,
struct Curl_easy *data,
struct ssl_peer *peer,
struct ssl_peer *ssl_peer,
const struct alpn_spec *alpns,
Curl_vquic_tls_ctx_setup *cb_setup,
void *cb_user_data,

View file

@ -42,6 +42,7 @@
#include "curlx/fopen.h"
#include "cfilters.h"
#include "vquic/cf-ngtcp2.h"
#include "vquic/cf-ngtcp2-cmn.h"
#include "vquic/cf-ngtcp2-proxy.h"
#include "vquic/cf-quiche.h"
#include "multiif.h"
@ -760,35 +761,49 @@ CURLcode Curl_qlogdir(struct Curl_easy *data,
return CURLE_OK;
}
CURLcode Curl_cf_quic_insert_after(struct Curl_cfilter *cf_at)
CURLcode Curl_cf_quic_insert_after(struct Curl_cfilter *cf_at,
struct Curl_peer *origin,
struct Curl_peer *peer)
{
#if defined(USE_NGTCP2) && defined(USE_NGHTTP3)
return Curl_cf_ngtcp2_insert_after(cf_at);
return Curl_cf_ngtcp2_insert_after(cf_at, origin, peer);
#elif defined(USE_QUICHE)
return Curl_cf_quiche_insert_after(cf_at, origin, peer);
#else
(void)cf_at;
(void)origin;
(void)peer;
return CURLE_NOT_BUILT_IN;
#endif
}
CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct Curl_peer *origin,
struct Curl_peer *peer,
uint8_t transport_peer,
struct connectdata *conn,
struct Curl_sockaddr_ex *addr,
uint8_t transport_in,
uint8_t transport_out)
struct Curl_peer *tunnel_peer,
uint8_t tunnel_transport)
{
(void)transport_in;
(void)transport_out;
DEBUGASSERT(transport_out == TRNSPRT_QUIC);
(void)transport_peer;
(void)tunnel_transport;
(void)tunnel_peer;
DEBUGASSERT(transport_peer == TRNSPRT_QUIC);
#if defined(USE_NGTCP2) && defined(USE_NGHTTP3)
return Curl_cf_ngtcp2_create(pcf, data, conn, addr);
return Curl_cf_ngtcp2_create(pcf, data, origin, peer, conn, addr);
#elif defined(USE_QUICHE)
return Curl_cf_quiche_create(pcf, data, conn, addr);
return Curl_cf_quiche_create(pcf, data, origin, peer, conn, addr);
#else
*pcf = NULL;
(void)data;
(void)origin;
(void)peer;
(void)conn;
(void)addr;
(void)tunnel_peer;
(void)tunnel_transport;
return CURLE_NOT_BUILT_IN;
#endif
}
@ -797,35 +812,49 @@ CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf,
CURLcode Curl_cf_h3_proxy_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data,
struct Curl_peer *dest,
bool udp_tunnel)
struct Curl_peer *origin,
struct Curl_peer *peer,
struct Curl_peer *tunnel_peer,
uint8_t tunnel_transport)
{
#if defined(USE_NGTCP2) && defined(USE_NGHTTP3)
return Curl_cf_ngtcp2_proxy_insert_after(cf_at, data, dest, udp_tunnel);
return Curl_cf_ngtcp2_proxy_insert_after(cf_at, data, origin, peer,
tunnel_peer, tunnel_transport);
#else
(void)cf_at;
(void)data;
(void)origin;
(void)peer;
(void)tunnel_peer;
(void)tunnel_transport;
return CURLE_NOT_BUILT_IN;
#endif
}
CURLcode Curl_cf_h3_proxy_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct Curl_peer *origin,
struct Curl_peer *peer,
uint8_t transport_peer,
struct connectdata *conn,
struct Curl_sockaddr_ex *addr,
uint8_t transport_in,
uint8_t transport_out)
struct Curl_peer *tunnel_peer,
uint8_t tunnel_transport)
{
(void)transport_in;
(void)transport_out;
DEBUGASSERT(transport_out == TRNSPRT_QUIC);
DEBUGASSERT(transport_peer == TRNSPRT_QUIC);
#if defined(USE_NGTCP2) && defined(USE_NGHTTP3)
return Curl_cf_ngtcp2_proxy_create(pcf, data, conn, addr,
transport_in, transport_out);
return Curl_cf_ngtcp2_proxy_create(pcf, data, origin, peer, transport_peer,
conn, addr,
tunnel_peer, tunnel_transport);
#else
*pcf = NULL;
(void)data;
(void)conn;
(void)addr;
(void)peer;
(void)transport_peer;
(void)tunnel_peer;
(void)tunnel_transport;
return CURLE_NOT_BUILT_IN;
#endif
}

View file

@ -39,14 +39,19 @@ CURLcode Curl_qlogdir(struct Curl_easy *data,
size_t scidlen,
int *qlogfdp);
CURLcode Curl_cf_quic_insert_after(struct Curl_cfilter *cf_at);
CURLcode Curl_cf_quic_insert_after(struct Curl_cfilter *cf_at,
struct Curl_peer *origin,
struct Curl_peer *peer);
CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct Curl_peer *origin,
struct Curl_peer *peer,
uint8_t transport_peer,
struct connectdata *conn,
struct Curl_sockaddr_ex *addr,
uint8_t transport_in,
uint8_t transport_out);
struct Curl_peer *tunnel_peer,
uint8_t tunnel_transport);
extern struct Curl_cftype Curl_cft_http3;
@ -54,15 +59,20 @@ extern struct Curl_cftype Curl_cft_http3;
CURLcode Curl_cf_h3_proxy_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data,
struct Curl_peer *dest,
bool udp_tunnel);
struct Curl_peer *origin,
struct Curl_peer *peer,
struct Curl_peer *tunnel_peer,
uint8_t tunnel_transport);
CURLcode Curl_cf_h3_proxy_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct Curl_peer *origin,
struct Curl_peer *peer,
uint8_t transport_peer,
struct connectdata *conn,
struct Curl_sockaddr_ex *addr,
uint8_t transport_in,
uint8_t transport_out);
struct Curl_peer *tunnel_peer,
uint8_t tunnel_transport);
extern struct Curl_cftype Curl_cft_h3_proxy;

View file

@ -102,7 +102,7 @@ CURLcode Curl_vtls_apple_verify(struct Curl_cfilter *cf,
if(conn_config->verifyhost) {
host_str = CFStringCreateWithCString(NULL,
peer->sni ? peer->sni : peer->dest->hostname, kCFStringEncodingUTF8);
peer->sni ? peer->sni : peer->origin->hostname, kCFStringEncodingUTF8);
if(!host_str) {
result = CURLE_OUT_OF_MEMORY;
goto out;

View file

@ -1366,11 +1366,11 @@ static void gtls_msg_verify_result(struct Curl_easy *data,
if(needs_verified) {
failf(data, "SSL: certificate subject name (%s) does not match "
"target hostname '%s'", certname,
peer->dest->user_hostname);
peer->origin->user_hostname);
}
else
infof(data, " common name: %s (does not match '%s')",
certname, peer->dest->user_hostname);
certname, peer->origin->user_hostname);
}
else
infof(data, " common name: %s (matched)", certname);
@ -1848,7 +1848,7 @@ CURLcode Curl_gtls_verifyserver(struct Curl_cfilter *cf,
IP addresses) */
rc = (int)gnutls_x509_crt_check_hostname(x509_cert,
peer->sni ? peer->sni :
peer->dest->hostname);
peer->origin->hostname);
result = (!rc && config->verifyhost) ?
CURLE_PEER_FAILED_VERIFICATION : CURLE_OK;
gtls_msg_verify_result(data, peer, x509_cert, rc, config->verifyhost);

View file

@ -798,7 +798,7 @@ static CURLcode mbed_configure_ssl(struct Curl_cfilter *cf,
char errorbuf[128];
infof(data, "mbedTLS: Connecting to %s:%d",
connssl->peer.dest->hostname, connssl->peer.dest->port);
connssl->peer.origin->hostname, connssl->peer.origin->port);
mbedtls_ssl_config_init(&backend->config);
ret = mbedtls_ssl_config_defaults(&backend->config,
@ -940,7 +940,7 @@ static CURLcode mbed_configure_ssl(struct Curl_cfilter *cf,
if(mbedtls_ssl_set_hostname(&backend->ssl, connssl->peer.sni ?
connssl->peer.sni :
connssl->peer.dest->hostname)) {
connssl->peer.origin->hostname)) {
/* mbedtls_ssl_set_hostname() sets the name to use in CN/SAN checks and
the name to set in the SNI extension. Thus even if curl connects to
a host specified as an IP address, this function must be used. */

View file

@ -2042,19 +2042,19 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data,
CURLcode result = CURLE_OK;
bool dNSName = FALSE; /* if a dNSName field exists in the cert */
bool iPAddress = FALSE; /* if an iPAddress field exists in the cert */
size_t hostlen = strlen(peer->dest->hostname);
size_t hostlen = strlen(peer->origin->hostname);
(void)conn;
switch(peer->type) {
case CURL_SSL_PEER_IPV4:
if(!curlx_inet_pton(AF_INET, peer->dest->hostname, &addr))
if(!curlx_inet_pton(AF_INET, peer->origin->hostname, &addr))
return CURLE_PEER_FAILED_VERIFICATION;
target = GEN_IPADD;
addrlen = sizeof(struct in_addr);
break;
#ifdef USE_IPV6
case CURL_SSL_PEER_IPV6:
if(!curlx_inet_pton(AF_INET6, peer->dest->hostname, &addr))
if(!curlx_inet_pton(AF_INET6, peer->origin->hostname, &addr))
return CURLE_PEER_FAILED_VERIFICATION;
target = GEN_IPADD;
addrlen = sizeof(struct in6_addr);
@ -2116,10 +2116,10 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data,
/* if this is not true, there was an embedded zero in the name
string and we cannot match it. */
Curl_cert_hostcheck(altptr, altlen,
peer->dest->hostname, hostlen)) {
peer->origin->hostname, hostlen)) {
matched = TRUE;
infof(data, " subjectAltName: \"%s\" matches cert's \"%.*s\"",
peer->dest->user_hostname, (int)altlen, altptr);
peer->origin->user_hostname, (int)altlen, altptr);
}
break;
@ -2129,7 +2129,7 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data,
if((altlen == addrlen) && !memcmp(altptr, &addr, altlen)) {
matched = TRUE;
infof(data, " subjectAltName: \"%s\" matches cert's IP address!",
peer->dest->user_hostname);
peer->origin->user_hostname);
}
break;
}
@ -2146,9 +2146,9 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data,
(peer->type == CURL_SSL_PEER_IPV4) ?
"ipv4 address" : "ipv6 address";
infof(data, " subjectAltName does not match %s %s", tname,
peer->dest->user_hostname);
peer->origin->user_hostname);
failf(data, "SSL: no alternative certificate subject name matches "
"target %s '%s'", tname, peer->dest->user_hostname);
"target %s '%s'", tname, peer->origin->user_hostname);
result = CURLE_PEER_FAILED_VERIFICATION;
}
else {
@ -2208,9 +2208,9 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data,
result = CURLE_PEER_FAILED_VERIFICATION;
}
else if(!Curl_cert_hostcheck((const char *)cn, cnlen,
peer->dest->hostname, hostlen)) {
peer->origin->hostname, hostlen)) {
failf(data, "SSL: certificate subject name '%s' does not match "
"target hostname '%s'", cn, peer->dest->user_hostname);
"target hostname '%s'", cn, peer->origin->user_hostname);
result = CURLE_PEER_FAILED_VERIFICATION;
}
else {
@ -3534,9 +3534,9 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx,
#else
if(trying_ech_now && outername) {
infof(data, "ECH: inner: '%s', outer: '%s'",
peer->dest->hostname ? peer->dest->hostname : "NULL", outername);
peer->origin->hostname ? peer->origin->hostname : "NULL", outername);
result = SSL_ech_set1_server_names(octx->ssl,
peer->dest->hostname, outername,
peer->origin->hostname, outername,
0 /* do send outer */);
if(result != 1) {
infof(data, "ECH: rv failed to set server name(s) %d [ERROR]", result);
@ -4010,19 +4010,12 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
{
struct ssl_connect_data *connssl = cf->ctx;
struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
char tls_id[80];
BIO *bio;
CURLcode result;
DEBUGASSERT(ssl_connect_1 == connssl->connecting_state);
DEBUGASSERT(octx);
if(!connssl->peer.dest) {
Curl_ossl_version(tls_id, sizeof(tls_id));
result = Curl_ssl_peer_init(&connssl->peer, cf, tls_id, TRNSPRT_TCP);
if(result)
return result;
}
DEBUGASSERT(connssl->peer.origin);
result = Curl_ossl_ctx_init(octx, cf, data, &connssl->peer,
connssl->alpn, NULL, NULL,
@ -4277,7 +4270,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
curlx_strerror(sockerr, extramsg, sizeof(extramsg));
failf(data, OSSL_PACKAGE " SSL_connect: %s in connection to %s:%d ",
extramsg[0] ? extramsg : SSL_ERROR_to_str(detail),
connssl->peer.dest->hostname, connssl->peer.dest->port);
connssl->peer.origin->hostname, connssl->peer.origin->port);
}
return result;
@ -4324,7 +4317,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
struct ssl_primary_config *conn_config =
Curl_ssl_cf_get_primary_config(cf);
if(!conn_config->verifypeer && !conn_config->verifyhost &&
inner && !strcmp(inner, connssl->peer.dest->hostname)) {
inner && !strcmp(inner, connssl->peer.origin->hostname)) {
VERBOSE(status = "bad name (tolerated without peer verification)");
rv = SSL_ECH_STATUS_SUCCESS;
}

View file

@ -1102,7 +1102,7 @@ static CURLcode cr_init_backend(struct Curl_cfilter *cf,
DEBUGASSERT(!rconn);
rr = rustls_client_connection_new(backend->config,
connssl->peer.dest->hostname,
connssl->peer.origin->hostname,
&rconn);
if(rr != RUSTLS_RESULT_OK) {
rustls_failf(data, rr, "rustls_client_connection_new");

View file

@ -849,7 +849,7 @@ static CURLcode schannel_connect_step1(struct Curl_cfilter *cf,
DEBUGASSERT(backend);
DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %d (step 1/3)",
connssl->peer.dest->hostname, connssl->peer.dest->port));
connssl->peer.origin->hostname, connssl->peer.origin->port));
#ifdef HAS_ALPN_SCHANNEL
backend->use_alpn = connssl->alpn && s_win_has_alpn;
@ -902,7 +902,7 @@ static CURLcode schannel_connect_step1(struct Curl_cfilter *cf,
/* A hostname associated with the credential is needed by
InitializeSecurityContext for SNI and other reasons. */
snihost = connssl->peer.sni ?
connssl->peer.sni : connssl->peer.dest->hostname;
connssl->peer.sni : connssl->peer.origin->hostname;
backend->cred->sni_hostname = curlx_convert_UTF8_to_tchar(snihost);
if(!backend->cred->sni_hostname)
return CURLE_OUT_OF_MEMORY;
@ -1245,7 +1245,7 @@ static CURLcode schannel_connect_step2(struct Curl_cfilter *cf,
connssl->io_need = CURL_SSL_IO_NEED_NONE;
DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %d (step 2/3)",
connssl->peer.dest->hostname, connssl->peer.dest->port));
connssl->peer.origin->hostname, connssl->peer.origin->port));
if(!backend->cred || !backend->ctxt)
return CURLE_SSL_CONNECT_ERROR;
@ -1597,7 +1597,7 @@ static CURLcode schannel_connect_step3(struct Curl_cfilter *cf,
DEBUGASSERT(backend);
DEBUGF(infof(data, "schannel: SSL/TLS connection with %s port %d (step 3/3)",
connssl->peer.dest->hostname, connssl->peer.dest->port));
connssl->peer.origin->hostname, connssl->peer.origin->port));
if(!backend->cred)
return CURLE_SSL_CONNECT_ERROR;
@ -2435,7 +2435,7 @@ static CURLcode schannel_shutdown(struct Curl_cfilter *cf,
*done = FALSE;
if(backend->ctxt) {
infof(data, "schannel: shutting down SSL/TLS connection with %s port %d",
connssl->peer.dest->hostname, connssl->peer.dest->port);
connssl->peer.origin->hostname, connssl->peer.origin->port);
}
if(!backend->ctxt || cf->shutdown) {

View file

@ -480,7 +480,7 @@ CURLcode Curl_verify_host(struct Curl_cfilter *cf, struct Curl_easy *data)
SECURITY_STATUS sspi_status;
TCHAR *cert_hostname_buff = NULL;
size_t cert_hostname_buff_index = 0;
const char *conn_hostname = connssl->peer.dest->hostname;
const char *conn_hostname = connssl->peer.origin->hostname;
size_t hostlen = strlen(conn_hostname);
DWORD len = 0;
DWORD actual_len = 0;

View file

@ -869,7 +869,8 @@ CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name,
void Curl_ssl_peer_cleanup(struct ssl_peer *peer)
{
Curl_peer_unlink(&peer->dest);
Curl_peer_unlink(&peer->origin);
Curl_peer_unlink(&peer->peer);
curlx_safefree(peer->sni);
curlx_safefree(peer->scache_key);
peer->transport = TRNSPRT_NONE;
@ -908,62 +909,49 @@ static ssl_peer_type get_peer_type(const char *hostname)
return CURL_SSL_PEER_DNS;
}
CURLcode Curl_ssl_peer_init(struct ssl_peer *peer,
struct Curl_cfilter *cf,
CURLcode Curl_ssl_peer_init(struct ssl_peer *ssl_peer,
struct Curl_peer *origin,
struct Curl_peer *peer,
struct ssl_primary_config *sslc,
const char *tls_id,
uint8_t transport)
{
struct Curl_peer *dest = NULL;
CURLcode result = CURLE_OUT_OF_MEMORY;
/* We expect a clean struct, e.g. called only ONCE */
DEBUGASSERT(peer);
DEBUGASSERT(!peer->dest);
DEBUGASSERT(!peer->sni);
/* We need the hostname for SNI negotiation. Once handshaked, this remains
* the SNI hostname for the TLS connection. When the connection is reused,
* the settings in cf->conn might change. We keep a copy of the hostname we
* use for SNI.
*/
peer->transport = transport;
#ifndef CURL_DISABLE_PROXY
if(Curl_ssl_cf_is_proxy(cf)) {
dest = cf->conn->http_proxy.peer;
}
else
#endif
{
dest = cf->conn->origin;
if(!ssl_peer || !origin) {
DEBUGASSERT(0);
return CURLE_FAILED_INIT;
}
DEBUGASSERT(!ssl_peer->origin);
DEBUGASSERT(!ssl_peer->peer);
DEBUGASSERT(!ssl_peer->sni);
ssl_peer->transport = transport;
/* hostname MUST exist and not be empty */
if(!dest) {
result = CURLE_FAILED_INIT;
goto out;
}
Curl_peer_link(&peer->dest, dest);
peer->type = get_peer_type(dest->hostname);
if(peer->type == CURL_SSL_PEER_DNS) {
Curl_peer_link(&ssl_peer->origin, origin);
Curl_peer_link(&ssl_peer->peer, peer);
ssl_peer->type = get_peer_type(origin->hostname);
if(ssl_peer->type == CURL_SSL_PEER_DNS) {
/* not an IP address, normalize according to RCC 6066 ch. 3,
* max len of SNI is 2^16-1, no trailing dot */
size_t len = strlen(dest->hostname);
if(len && (dest->hostname[len - 1] == '.'))
size_t len = strlen(origin->hostname);
if(len && (origin->hostname[len - 1] == '.'))
len--;
if(len < USHRT_MAX) {
peer->sni = curlx_calloc(1, len + 1);
if(!peer->sni)
ssl_peer->sni = curlx_calloc(1, len + 1);
if(!ssl_peer->sni)
goto out;
Curl_strntolower(peer->sni, dest->hostname, len);
peer->sni[len] = 0;
Curl_strntolower(ssl_peer->sni, origin->hostname, len);
ssl_peer->sni[len] = 0;
}
}
result = Curl_ssl_peer_key_make(cf, peer, tls_id, &peer->scache_key);
result = Curl_ssl_peer_key_make(ssl_peer, sslc, tls_id,
&ssl_peer->scache_key);
out:
if(result)
Curl_ssl_peer_cleanup(peer);
Curl_ssl_peer_cleanup(ssl_peer);
return result;
}
@ -991,7 +979,7 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf,
return CURLE_OK;
}
if(!cf->next) {
if(!cf->next || !connssl->peer.origin) {
*done = FALSE;
return CURLE_FAILED_INIT;
}
@ -1016,14 +1004,6 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf,
connssl->prefs_checked = TRUE;
}
if(!connssl->peer.dest) {
char tls_id[80];
connssl->ssl_impl->version(tls_id, sizeof(tls_id) - 1);
result = Curl_ssl_peer_init(&connssl->peer, cf, tls_id, TRNSPRT_TCP);
if(result)
goto out;
}
result = connssl->ssl_impl->do_connect(cf, data, done);
if(!result && *done) {
@ -1406,28 +1386,53 @@ out:
return result;
}
static CURLcode cf_ssl_peer_init(struct Curl_cfilter *cf,
struct Curl_peer *origin,
struct Curl_peer *peer,
struct ssl_primary_config *sslc)
{
struct ssl_connect_data *connssl = cf->ctx;
char tls_id[80];
connssl->ssl_impl->version(tls_id, sizeof(tls_id) - 1);
return Curl_ssl_peer_init(&connssl->peer, origin, peer, sslc,
tls_id, TRNSPRT_TCP);
}
CURLcode Curl_ssl_cfilter_add(struct Curl_easy *data,
struct Curl_peer *origin,
struct connectdata *conn,
int sockindex)
{
struct Curl_cfilter *cf;
struct Curl_peer *peer = (sockindex == SECONDARYSOCKET) ?
conn->via_peer2 : conn->via_peer;
CURLcode result;
result = cf_ssl_create(&cf, data, conn);
if(!result)
result = cf_ssl_peer_init(cf, origin, peer, &conn->ssl_config);
if(!result)
Curl_conn_cf_add(data, conn, sockindex, cf);
else if(cf)
Curl_conn_cf_discard_chain(&cf, data);
return result;
}
CURLcode Curl_cf_ssl_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data)
struct Curl_easy *data,
struct Curl_peer *origin,
struct Curl_peer *peer)
{
struct Curl_cfilter *cf;
CURLcode result;
result = cf_ssl_create(&cf, data, cf_at->conn);
if(!result)
result = cf_ssl_peer_init(cf, origin, peer, &cf_at->conn->ssl_config);
if(!result)
Curl_conn_cf_insert_after(cf_at, cf);
else if(cf)
Curl_conn_cf_discard_chain(&cf, data);
return result;
}
@ -1467,14 +1472,19 @@ out:
}
CURLcode Curl_cf_ssl_proxy_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data)
struct Curl_easy *data,
struct Curl_peer *peer)
{
struct Curl_cfilter *cf;
CURLcode result;
result = cf_ssl_proxy_create(&cf, data, cf_at->conn);
if(!result)
result = cf_ssl_peer_init(cf, peer, NULL, &cf_at->conn->proxy_ssl_config);
if(!result)
Curl_conn_cf_insert_after(cf_at, cf);
else if(cf)
Curl_conn_cf_discard_chain(&cf, data);
return result;
}

View file

@ -89,7 +89,8 @@ typedef enum {
} ssl_peer_type;
struct ssl_peer {
struct Curl_peer *dest;
struct Curl_peer *origin; /* the authority we talk to */
struct Curl_peer *peer; /* the machine we are connected to */
char *sni; /* SNI version of hostname or NULL if not usable */
char *scache_key; /* for lookups in session cache */
ssl_peer_type type; /* type of the peer information */
@ -106,8 +107,10 @@ curl_sslbackend Curl_ssl_backend(void);
/**
* Init SSL peer information for filter. Can be called repeatedly.
*/
CURLcode Curl_ssl_peer_init(struct ssl_peer *peer,
struct Curl_cfilter *cf,
CURLcode Curl_ssl_peer_init(struct ssl_peer *ssl_peer,
struct Curl_peer *origin,
struct Curl_peer *peer,
struct ssl_primary_config *sslc,
const char *tls_id,
uint8_t transport);
/**
@ -174,18 +177,22 @@ CURLcode Curl_ssl_get_channel_binding(struct Curl_easy *data, int sockindex,
#define SSL_SHUTDOWN_TIMEOUT 10000 /* ms */
CURLcode Curl_ssl_cfilter_add(struct Curl_easy *data,
struct Curl_peer *origin,
struct connectdata *conn,
int sockindex);
CURLcode Curl_cf_ssl_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data);
struct Curl_easy *data,
struct Curl_peer *origin,
struct Curl_peer *peer);
CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data,
int sockindex, bool send_shutdown);
#ifndef CURL_DISABLE_PROXY
CURLcode Curl_cf_ssl_proxy_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data);
struct Curl_easy *data,
struct Curl_peer *peer);
#endif /* !CURL_DISABLE_PROXY */
/**
@ -225,7 +232,7 @@ extern struct Curl_cftype Curl_cft_ssl_proxy;
#define Curl_ssl_random(x, y, z) ((void)(x), CURLE_NOT_BUILT_IN)
#define Curl_ssl_cert_status_request() FALSE
#define Curl_ssl_supports(a, b) FALSE
#define Curl_ssl_cfilter_add(a, b, c) CURLE_NOT_BUILT_IN
#define Curl_ssl_cfilter_add(a, b, c, d) CURLE_NOT_BUILT_IN
#define Curl_ssl_cfilter_remove(a, b, c) CURLE_OK
#define Curl_ssl_cf_get_config(a, b) NULL
#define Curl_ssl_cf_get_primary_config(a) NULL

View file

@ -177,11 +177,59 @@ static bool cf_ssl_peer_key_is_global(const char *peer_key)
(peer_key[len - 2] == ':');
}
CURLcode Curl_ssl_peer_key_build(struct ssl_primary_config *ssl,
const struct ssl_peer *peer,
const struct Curl_peer *via_peer,
const char *tls_id,
char **ppeer_key)
static CURLcode ssl_peer_key_add_transport(struct dynbuf *buf,
uint8_t transport)
{
switch(transport) {
case TRNSPRT_TCP:
return CURLE_OK;
case TRNSPRT_UDP:
return curlx_dyn_add(buf, ":UDP");
case TRNSPRT_QUIC:
return curlx_dyn_add(buf, ":QUIC");
case TRNSPRT_UNIX:
return curlx_dyn_add(buf, ":UNIX");
default:
return curlx_dyn_addf(buf, ":TRNSPRT-%d", transport);
}
}
static CURLcode ssl_peer_key_add_vrfy(struct dynbuf *buf,
struct ssl_primary_config *ssl,
const struct ssl_peer *peer)
{
CURLcode result;
if(!ssl->verifypeer) {
result = curlx_dyn_add(buf, ":NO-VRFY-PEER");
if(result)
return result;
}
if(!ssl->verifyhost) {
result = curlx_dyn_add(buf, ":NO-VRFY-HOST");
if(result)
return result;
}
if(ssl->verifystatus) {
result = curlx_dyn_add(buf, ":VRFY-STATUS");
if(result)
return result;
}
if((!ssl->verifypeer || !ssl->verifyhost) &&
peer->peer && !Curl_peer_equal(peer->origin, peer->peer)) {
result = curlx_dyn_addf(buf, ":CHOST-%s:CPORT-%u",
peer->peer->hostname,
peer->peer->port);
if(result)
return result;
}
return CURLE_OK;
}
static CURLcode ssl_peer_key_build(struct ssl_primary_config *ssl,
const struct ssl_peer *peer,
const char *tls_id,
char **ppeer_key)
{
struct dynbuf buf;
size_t key_len;
@ -192,54 +240,15 @@ CURLcode Curl_ssl_peer_key_build(struct ssl_primary_config *ssl,
curlx_dyn_init(&buf, 10 * 1024);
result = curlx_dyn_addf(&buf, "%s:%d",
peer->dest->hostname, peer->dest->port);
peer->origin->hostname, peer->origin->port);
if(result)
goto out;
switch(peer->transport) {
case TRNSPRT_TCP:
break;
case TRNSPRT_UDP:
result = curlx_dyn_add(&buf, ":UDP");
break;
case TRNSPRT_QUIC:
result = curlx_dyn_add(&buf, ":QUIC");
break;
case TRNSPRT_UNIX:
result = curlx_dyn_add(&buf, ":UNIX");
break;
default:
result = curlx_dyn_addf(&buf, ":TRNSPRT-%d", peer->transport);
break;
}
result = ssl_peer_key_add_transport(&buf, peer->transport);
if(result)
goto out;
result = ssl_peer_key_add_vrfy(&buf, ssl, peer);
if(result)
goto out;
if(!ssl->verifypeer) {
result = curlx_dyn_add(&buf, ":NO-VRFY-PEER");
if(result)
goto out;
}
if(!ssl->verifyhost) {
result = curlx_dyn_add(&buf, ":NO-VRFY-HOST");
if(result)
goto out;
}
if(ssl->verifystatus) {
result = curlx_dyn_add(&buf, ":VRFY-STATUS");
if(result)
goto out;
}
if(!ssl->verifypeer || !ssl->verifyhost) {
if(via_peer) {
result = curlx_dyn_addf(&buf, ":CHOST-%s:CPORT-%u",
via_peer->hostname,
via_peer->port);
if(result)
goto out;
}
}
if(ssl->version || ssl->version_max) {
result = curlx_dyn_addf(&buf, ":TLSVER-%d-%u", ssl->version,
(ssl->version_max >> 16));
@ -342,14 +351,12 @@ out:
return result;
}
CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf,
const struct ssl_peer *peer,
CURLcode Curl_ssl_peer_key_make(const struct ssl_peer *peer,
struct ssl_primary_config *sslc,
const char *tls_id,
char **ppeer_key)
{
struct ssl_primary_config *ssl = Curl_ssl_cf_get_primary_config(cf);
return Curl_ssl_peer_key_build(ssl, peer, cf->conn->via_peer, tls_id,
ppeer_key);
return ssl_peer_key_build(sslc, peer, tls_id, ppeer_key);
}
struct Curl_ssl_scache {

View file

@ -54,34 +54,18 @@ void Curl_ssl_scache_destroy(struct Curl_ssl_scache *scache);
* connection to the peer.
* If the filter is a TLS proxy filter, it uses the proxy relevant
* information.
* @param cf the connection filter wanting to use it
* @param peer the peer the filter wants to talk to
* @param sslc the relevant ssl configuration
* @param tls_id identifier of TLS implementation for sessions. Should
* include full version if session data from other versions
* is to be avoided.
* @param ppeer_key on successful return, the key generated
*/
CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf,
const struct ssl_peer *peer,
CURLcode Curl_ssl_peer_key_make(const struct ssl_peer *peer,
struct ssl_primary_config *sslc,
const char *tls_id,
char **ppeer_key);
/**
* Like Curl_ssl_peer_key_make() but takes the primary config and peer
* descriptors directly, without requiring a Curl_cfilter. Exposed for
* unit testing.
* @param ssl the primary SSL config to key on
* @param peer the peer the filter wants to talk to
* @param via_peer the connecting-through peer, or NULL
* @param tls_id identifier of TLS implementation for sessions
* @param ppeer_key on successful return, the key generated
*/
CURLcode Curl_ssl_peer_key_build(struct ssl_primary_config *ssl,
const struct ssl_peer *peer,
const struct Curl_peer *via_peer,
const char *tls_id,
char **ppeer_key);
/* Return if there is a session cache shall be used.
* An SSL session might not be configured or not available for
* "connect-only" transfers.

View file

@ -1763,9 +1763,9 @@ static CURLcode wssl_handshake(struct Curl_cfilter *cf, struct Curl_easy *data)
failf(data, "unable to get peer certificate");
return CURLE_PEER_FAILED_VERIFICATION;
}
ret = wolfSSL_X509_check_ip_asc(cert, connssl->peer.dest->hostname, 0);
ret = wolfSSL_X509_check_ip_asc(cert, connssl->peer.origin->hostname, 0);
CURL_TRC_CF(data, cf, "check peer certificate for IP match on %s -> %d",
connssl->peer.dest->hostname, ret);
connssl->peer.origin->hostname, ret);
if(ret != WOLFSSL_SUCCESS)
detail = DOMAIN_NAME_MISMATCH;
wolfSSL_X509_free(cert);
@ -1788,7 +1788,7 @@ static CURLcode wssl_handshake(struct Curl_cfilter *cf, struct Curl_easy *data)
* This enables the override of both mismatching SubjectAltNames
* as also mismatching CN fields */
failf(data, " subject alt name(s) or common name do not match \"%s\"",
connssl->peer.dest->hostname);
connssl->peer.origin->hostname);
return CURLE_PEER_FAILED_VERIFICATION;
}
else if(ASN_NO_SIGNER_E == detail) {

View file

@ -214,3 +214,16 @@ class TestEyeballs:
r.check_exit_code(0)
r.check_response(count=1, http_status=200)
assert r.stats[0]['http_version'] == '2'
# h3 download using --connect-to IPv6 address
@pytest.mark.skipif(condition=not Env.have_h3(), reason="missing HTTP/3 support")
@pytest.mark.skipif(condition=not Env.curl_has_feature('IPv6'), reason="no IPv6")
def test_06_25_h3_connect_to(self, env: Env, httpd, nghttpx):
curl = CurlClient(env=env, force_resolv=False)
urln = f'https://{env.authority_for(env.domain1, "h3")}/data.json'
r = curl.http_download(urls=[urln], extra_args=[
'--http3-only', '--connect-to',
f'{env.authority_for(env.domain1, "h3")}:[::1]:{env.https_port}'
])
r.check_response(count=1, http_status=200)
assert r.stats[0]['http_version'] == '3'

View file

@ -113,8 +113,7 @@ static int test_idx;
struct cf_test_ctx {
int idx;
int ai_family;
uint8_t transport_in;
uint8_t transport_out;
uint8_t transport_peer;
char id[16];
struct curltime started;
timediff_t fail_delay_ms;
@ -166,10 +165,13 @@ static CURLcode cf_test_adjust_pollset(struct Curl_cfilter *cf,
static CURLcode cf_test_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct Curl_peer *origin,
struct Curl_peer *peer,
uint8_t transport_peer,
struct connectdata *conn,
struct Curl_sockaddr_ex *addr,
uint8_t transport_in,
uint8_t transport_out)
struct Curl_peer *tunnel_peer,
uint8_t transport_above)
{
static const struct Curl_cftype cft_test = {
"TEST",
@ -194,7 +196,11 @@ static CURLcode cf_test_create(struct Curl_cfilter **pcf,
CURLcode result;
(void)data;
(void)origin;
(void)peer;
(void)conn;
(void)tunnel_peer;
(void)transport_above;
ctx = curlx_calloc(1, sizeof(*ctx));
if(!ctx) {
result = CURLE_OUT_OF_MEMORY;
@ -202,8 +208,7 @@ static CURLcode cf_test_create(struct Curl_cfilter **pcf,
}
ctx->idx = test_idx++;
ctx->ai_family = addr->family;
ctx->transport_in = transport_in;
ctx->transport_out = transport_out;
ctx->transport_peer = transport_peer;
ctx->started = curlx_now();
current_tr->ongoing++;
if(current_tr->ongoing > current_tr->max_concurrent)

View file

@ -43,7 +43,7 @@ static CURLcode test_unit3304(const char *arg)
UNITTEST_BEGIN_SIMPLE
#ifdef USE_SSL
struct Curl_peer dest;
struct Curl_peer origin;
struct ssl_peer peer;
struct ssl_primary_config ssl;
char *key1 = NULL;
@ -60,12 +60,12 @@ static CURLcode test_unit3304(const char *arg)
static char lc_ctype[] = "pem";
static char lc_ktype[] = "pem";
memset(&dest, 0, sizeof(dest));
dest.hostname = base_hostname;
dest.port = 443;
memset(&origin, 0, sizeof(origin));
origin.hostname = base_hostname;
origin.port = 443;
memset(&peer, 0, sizeof(peer));
peer.dest = &dest;
peer.origin = &origin;
peer.transport = TRNSPRT_TCP;
memset(&ssl, 0, sizeof(ssl));
@ -78,9 +78,9 @@ static CURLcode test_unit3304(const char *arg)
ssl.key_type = base_ktype;
/* Baseline: same config produces same key. */
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1),
fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key1),
"peer key build failed");
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2),
fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key2),
"peer key build failed");
fail_unless(key1 && key2 && !strcmp(key1, key2),
"identical config should produce identical peer key");
@ -89,10 +89,10 @@ static CURLcode test_unit3304(const char *arg)
/* key_passwd is NOT in the peer key: lookup uses timing-safe comparison
* via cf_ssl_scache_match_auth(), same as SRP credentials. */
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1),
fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key1),
"peer key build failed");
ssl.key_passwd = NULL;
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2),
fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key2),
"peer key build failed");
fail_unless(key1 && key2 && !strcmp(key1, key2),
"key_passwd must not affect the peer key");
@ -101,10 +101,10 @@ static CURLcode test_unit3304(const char *arg)
ssl.key_passwd = base_passwd;
/* Different key path must produce a different peer key. */
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1),
fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key1),
"peer key build failed");
ssl.key = alt_key;
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2),
fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key2),
"peer key build failed");
fail_unless(key1 && key2 && strcmp(key1, key2),
"different key must produce different peer key");
@ -113,10 +113,10 @@ static CURLcode test_unit3304(const char *arg)
ssl.key = base_key;
/* Different key_type must produce a different peer key. */
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1),
fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key1),
"peer key build failed");
ssl.key_type = alt_ktype;
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2),
fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key2),
"peer key build failed");
fail_unless(key1 && key2 && strcmp(key1, key2),
"different key_type must produce different peer key");
@ -125,10 +125,10 @@ static CURLcode test_unit3304(const char *arg)
ssl.key_type = base_ktype;
/* Different cert_type must produce a different peer key. */
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1),
fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key1),
"peer key build failed");
ssl.cert_type = alt_ctype;
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2),
fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key2),
"peer key build failed");
fail_unless(key1 && key2 && strcmp(key1, key2),
"different cert_type must produce different peer key");
@ -138,10 +138,10 @@ static CURLcode test_unit3304(const char *arg)
/* cert_type is case-insensitive: "PEM" and "pem" must produce the
* same peer key, consistent with the conn-reuse comparison. */
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1),
fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key1),
"peer key build failed");
ssl.cert_type = lc_ctype;
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2),
fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key2),
"peer key build failed");
fail_unless(key1 && key2 && !strcmp(key1, key2),
"cert_type case must not affect peer key");
@ -151,10 +151,10 @@ static CURLcode test_unit3304(const char *arg)
/* key_type is case-insensitive: "PEM" and "pem" must produce the
* same peer key. */
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key1),
fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key1),
"peer key build failed");
ssl.key_type = lc_ktype;
fail_unless(!Curl_ssl_peer_key_build(&ssl, &peer, NULL, "test", &key2),
fail_unless(!Curl_ssl_peer_key_make(&peer, &ssl, "test", &key2),
"peer key build failed");
fail_unless(key1 && key2 && !strcmp(key1, key2),
"key_type case must not affect peer key");