h3-proxy: fixes around H3 proxy

code:
- less exception handling in existing code
- true ip happy eyeballing
- enable certificate verification
- cf-h2-proxy: abort connection when server closed connection

tests:
- remove all --insecure and --proxy-insecure args
- make session reuse test_60_12 a working one
- resolve port conflicts between h2o and nghttpx
- use proxy args better
- make test_60_06 run shorter
- kill h2o at the end of tests, normal stop takes too long

Ref: 59213f8248 #21789
Follow-up to e78b1b3ecc #21153

Closes #21798
This commit is contained in:
Stefan Eissing 2026-05-27 16:50:18 +02:00 committed by Viktor Szakats
parent 59213f8248
commit e4139a73c8
No known key found for this signature in database
25 changed files with 442 additions and 365 deletions

View file

@ -224,29 +224,47 @@ struct Curl_cftype Curl_cft_capsule = {
Curl_cf_def_query,
};
CURLcode Curl_cf_capsule_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data)
CURLcode Curl_cf_capsule_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn)
{
struct Curl_cfilter *cf;
struct Curl_cfilter *cf = NULL;
struct cf_capsule_ctx *ctx;
CURLcode result;
(void)data;
(void)conn;
*pcf = NULL;
ctx = curlx_calloc(1, sizeof(*ctx));
if(!ctx)
return CURLE_OUT_OF_MEMORY;
if(!ctx) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
Curl_bufq_init2(&ctx->recvbuf, CAPSULE_CHUNK_SIZE, CAPSULE_RECV_CHUNKS,
BUFQ_OPT_SOFT_LIMIT);
result = Curl_cf_create(&cf, &Curl_cft_capsule, ctx);
if(result) {
out:
*pcf = (!result) ? cf : NULL;
if(result && ctx) {
Curl_bufq_free(&ctx->recvbuf);
curlx_free(ctx);
return result;
}
Curl_conn_cf_insert_after(cf_at, cf);
return CURLE_OK;
return result;
}
CURLcode Curl_cf_capsule_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data)
{
struct Curl_cfilter *cf;
CURLcode result;
result = Curl_cf_capsule_create(&cf, data, cf_at->conn);
if(!result)
Curl_conn_cf_insert_after(cf_at, cf);
return result;
}
#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */

View file

@ -27,6 +27,10 @@
#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
CURLcode Curl_cf_capsule_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn);
/* Insert a capsule protocol filter after `cf_at` in the filter chain.
* The capsule filter encapsulates/decapsulates UDP datagrams using
* the HTTP Datagram capsule format (RFC 9297). */

View file

@ -381,6 +381,7 @@ static CURLcode proxy_h2_progress_ingress(struct Curl_cfilter *cf,
break;
}
else if(nread == 0) {
CURL_TRC_CF(data, cf, "server closed connection");
ctx->conn_closed = TRUE;
break;
}
@ -832,6 +833,11 @@ static CURLcode H2_CONNECT(struct Curl_cfilter *cf,
DEBUGASSERT(ts);
DEBUGASSERT(ts->authority);
if(ctx->conn_closed) {
failf(data, "proxy closed connection");
return CURLE_COULDNT_CONNECT;
}
do {
switch(ts->state) {
case H2_TUNNEL_INIT:

View file

@ -53,6 +53,7 @@
#include "sendf.h"
#include "multiif.h"
#include "cfilters.h"
#include "cf-capsule.h"
#include "cf-socket.h"
#include "connect.h"
#include "progress.h"
@ -3057,6 +3058,10 @@ static CURLcode cf_h3_proxy_quic_connect(struct Curl_cfilter *cf,
}
*done = FALSE;
if(!proxy_ctx->dest) {
Curl_peer_link(&proxy_ctx->dest,
Curl_conn_get_destination(cf->conn, cf->sockindex));
}
if(!proxy_ctx->ngtcp2_ctx) {
result = cf_h3_proxy_ctx_init(cf, data);
@ -3414,6 +3419,63 @@ struct Curl_cftype Curl_cft_h3_proxy = {
cf_h3_proxy_query,
};
CURLcode Curl_cf_h3_proxy_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_cfilter *cf = NULL;
struct cf_h3_proxy_ctx *ctx;
CURLcode result = CURLE_OUT_OF_MEMORY;
if((transport_out != TRNSPRT_QUIC) || (!conn->http_proxy.peer))
return CURLE_FAILED_INIT;
ctx = curlx_calloc(1, sizeof(*ctx));
if(!ctx) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
ctx->udp_tunnel = (transport_in == TRNSPRT_QUIC);
result = Curl_cf_create(&cf, &Curl_cft_h3_proxy, ctx);
if(result)
goto out;
cf->conn = conn;
result = Curl_cf_udp_create(&cf->next, data, conn, addr,
TRNSPRT_QUIC, TRNSPRT_QUIC);
if(result)
goto out;
cf->next->conn = cf->conn;
cf->next->sockindex = cf->sockindex;
if(ctx->udp_tunnel) {
struct Curl_cfilter *cf_caps = NULL;
result = Curl_cf_capsule_create(&cf_caps, data, conn);
if(result)
goto out;
cf_caps->conn = conn;
cf_caps->sockindex = cf->sockindex;
cf_caps->next = cf;
cf = cf_caps;
}
out:
*pcf = (!result) ? cf : NULL;
if(result) {
if(cf)
Curl_conn_cf_discard_chain(&cf, data);
else if(ctx)
cf_h3_proxy_ctx_free(ctx);
}
else
CURL_TRC_CF(data, cf, "created, udp_tunnel=%d", ctx->udp_tunnel);
return result;
}
CURLcode Curl_cf_h3_proxy_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data,
struct Curl_peer *dest,

View file

@ -35,6 +35,13 @@ CURLcode Curl_cf_h3_proxy_insert_after(struct Curl_cfilter *cf_at,
struct Curl_peer *dest,
bool udp_tunnel);
CURLcode Curl_cf_h3_proxy_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);
extern struct Curl_cftype Curl_cft_h3_proxy;
#endif

View file

@ -51,6 +51,7 @@
#include "cfilters.h"
#include "cf-dns.h"
#include "cf-ip-happy.h"
#include "cf-h3-proxy.h"
#include "curl_addrinfo.h"
#include "curl_trc.h"
#include "multiif.h"
@ -60,8 +61,9 @@
struct transport_provider {
uint8_t transport;
cf_ip_connect_create *cf_create;
uint8_t transport;
bool tunnel_proxy;
};
static
@ -69,23 +71,30 @@ static
const
#endif
struct transport_provider transport_providers[] = {
{ TRNSPRT_TCP, Curl_cf_tcp_create },
{ Curl_cf_tcp_create, TRNSPRT_TCP, FALSE },
{ Curl_cf_tcp_create, TRNSPRT_TCP, TRUE },
#if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3)
{ TRNSPRT_QUIC, Curl_cf_quic_create },
{ Curl_cf_quic_create, TRNSPRT_QUIC, FALSE },
#endif
#if !defined(CURL_DISABLE_HTTP) && defined(USE_PROXY_HTTP3)
{ Curl_cf_h3_proxy_create, TRNSPRT_QUIC, TRUE },
#endif
#ifndef CURL_DISABLE_TFTP
{ TRNSPRT_UDP, Curl_cf_udp_create },
{ Curl_cf_udp_create, TRNSPRT_UDP, FALSE },
#endif
#ifdef USE_UNIX_SOCKETS
{ TRNSPRT_UNIX, Curl_cf_unix_create },
{ Curl_cf_unix_create, TRNSPRT_UNIX, FALSE },
{ Curl_cf_unix_create, TRNSPRT_UNIX, TRUE },
#endif
};
static cf_ip_connect_create *get_cf_create(uint8_t transport)
static cf_ip_connect_create *get_cf_create(uint8_t transport,
bool tunnel_proxy)
{
size_t i;
for(i = 0; i < CURL_ARRAYSIZE(transport_providers); ++i) {
if(transport == transport_providers[i].transport)
if((transport == transport_providers[i].transport) &&
(tunnel_proxy == transport_providers[i].tunnel_proxy))
return transport_providers[i].cf_create;
}
return NULL;
@ -102,7 +111,6 @@ UNITTEST void debug_set_transport_provider(
for(i = 0; i < CURL_ARRAYSIZE(transport_providers); ++i) {
if(transport == transport_providers[i].transport) {
transport_providers[i].cf_create = cf_create;
return;
}
}
}
@ -154,7 +162,8 @@ struct cf_ip_attempt {
struct curltime started; /* start of current attempt */
CURLcode result;
int ai_family;
uint8_t transport;
uint8_t transport_in;
uint8_t transport_out;
int error;
BIT(connected); /* cf has connected */
BIT(shutdown); /* cf has shutdown */
@ -177,7 +186,8 @@ static CURLcode cf_ip_attempt_new(struct cf_ip_attempt **pa,
struct Curl_easy *data,
struct Curl_sockaddr_ex *addr,
int ai_family,
uint8_t transport,
uint8_t transport_in,
uint8_t transport_out,
cf_ip_connect_create *cf_create)
{
struct Curl_cfilter *wcf;
@ -191,12 +201,14 @@ static CURLcode cf_ip_attempt_new(struct cf_ip_attempt **pa,
a->addr = *addr;
a->ai_family = ai_family;
a->transport = transport;
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);
result = a->cf_create(&a->cf, data, cf->conn, &a->addr,
a->transport_in, a->transport_out);
if(result)
goto out;
@ -251,7 +263,8 @@ struct cf_ip_ballers {
timediff_t attempt_delay_ms;
int last_attempt_ai_family;
uint32_t max_concurrent;
uint8_t transport;
uint8_t transport_in;
uint8_t transport_out;
};
static CURLcode cf_ip_attempt_restart(struct cf_ip_attempt *a,
@ -269,7 +282,8 @@ 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);
result = a->cf_create(&a->cf, data, cf->conn, &a->addr, a->transport_in,
a->transport_out);
if(!result) {
bool dummy;
/* the new filter might have sub-filters */
@ -299,18 +313,20 @@ static void cf_ip_ballers_clear(struct Curl_cfilter *cf,
static CURLcode cf_ip_ballers_init(struct cf_ip_ballers *bs,
struct Curl_cfilter *cf,
cf_ip_connect_create *cf_create,
uint8_t transport,
uint8_t transport_in,
uint8_t transport_out,
timediff_t attempt_delay_ms,
uint32_t max_concurrent)
{
memset(bs, 0, sizeof(*bs));
bs->cf_create = cf_create;
bs->transport = transport;
bs->transport_in = transport_in;
bs->transport_out = transport_out;
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 == TRNSPRT_UNIX) {
if(transport_in == TRNSPRT_UNIX) {
#ifdef USE_UNIX_SOCKETS
cf_ai_iter_init(&bs->addr_iter, cf, AF_UNIX);
#else
@ -458,12 +474,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);
result = Curl_socket_addr_from_ai(&addr, ai, bs->transport_out);
if(result)
goto out;
result = cf_ip_attempt_new(&a, cf, data, &addr, ai_family,
bs->transport, bs->cf_create);
bs->transport_in, bs->transport_out,
bs->cf_create);
CURL_TRC_CF(data, cf, "starting %s attempt for ipv%s -> %d",
bs->running ? "next" : "first",
(ai_family == AF_INET) ? "4" : "6", result);
@ -652,11 +669,13 @@ typedef enum {
} cf_connect_state;
struct cf_ip_happy_ctx {
uint8_t transport;
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);
};
@ -732,10 +751,11 @@ static CURLcode cf_ip_happy_init(struct Curl_cfilter *cf,
return CURLE_OPERATION_TIMEDOUT;
}
CURL_TRC_CF(data, cf, "init ip ballers for transport %u", ctx->transport);
CURL_TRC_CF(data, cf, "init ip ballers for transport %u",
ctx->transport_out);
ctx->started = *Curl_pgrs_now(data);
return cf_ip_ballers_init(&ctx->ballers, cf,
ctx->cf_create, ctx->transport,
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);
}
@ -752,8 +772,10 @@ static void cf_ip_happy_ctx_clear(struct Curl_cfilter *cf,
static void cf_ip_happy_ctx_destroy(struct cf_ip_happy_ctx *ctx)
{
if(ctx)
if(ctx) {
Curl_peer_unlink(&ctx->peer);
curlx_free(ctx);
}
}
static CURLcode cf_ip_happy_shutdown(struct Curl_cfilter *cf,
@ -973,9 +995,11 @@ struct Curl_cftype Curl_cft_ip_happy = {
*/
static CURLcode cf_ip_happy_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct Curl_peer *peer,
struct connectdata *conn,
cf_ip_connect_create *cf_create,
uint8_t transport)
uint8_t transport_in,
uint8_t transport_out)
{
struct cf_ip_happy_ctx *ctx = NULL;
CURLcode result;
@ -988,8 +1012,10 @@ static CURLcode cf_ip_happy_create(struct Curl_cfilter **pcf,
result = CURLE_OUT_OF_MEMORY;
goto out;
}
ctx->transport = transport;
ctx->transport_in = transport_in;
ctx->transport_out = transport_out;
ctx->cf_create = cf_create;
Curl_peer_link(&ctx->peer, peer);
result = Curl_cf_create(pcf, &Curl_cft_ip_happy, ctx);
@ -1003,7 +1029,10 @@ out:
CURLcode cf_ip_happy_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data,
uint8_t transport)
struct Curl_peer *peer,
uint8_t transport_in,
uint8_t transport_out,
bool tunnel_proxy)
{
cf_ip_connect_create *cf_create;
struct Curl_cfilter *cf;
@ -1011,40 +1040,17 @@ CURLcode cf_ip_happy_insert_after(struct Curl_cfilter *cf_at,
/* Need to be first */
DEBUGASSERT(cf_at);
cf_create = get_cf_create(transport);
cf_create = get_cf_create(transport_out, tunnel_proxy);
if(!cf_create) {
CURL_TRC_CF(data, cf_at, "unsupported transport type %u", transport);
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, cf_at->conn, cf_create, transport);
result = cf_ip_happy_create(&cf, data, peer, cf_at->conn, cf_create,
transport_in, transport_out);
if(result)
return result;
Curl_conn_cf_insert_after(cf_at, cf);
return CURLE_OK;
}
#if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3) && \
defined(USE_PROXY_HTTP3)
CURLcode cf_ip_happy_quic_udp_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data)
{
/* For H3 proxy: create happy eyeballs that races IPv4/IPv6 using raw
UDP sockets with TRNSPRT_QUIC transport. Using TRNSPRT_QUIC causes
cf_udp_connect() to call cf_udp_setup_quic() which connects the
socket to the peer address, making send() work without an explicit
destination. We use Curl_cf_udp_create (not Curl_cf_quic_create)
because H3-PROXY manages its own ngtcp2 QUIC stack on top. */
struct Curl_cfilter *cf;
CURLcode result;
DEBUGASSERT(cf_at);
result = cf_ip_happy_create(&cf, data, cf_at->conn,
Curl_cf_udp_create, TRNSPRT_QUIC);
if(result)
return result;
Curl_conn_cf_insert_after(cf_at, cf);
return CURLE_OK;
}
#endif /* !CURL_DISABLE_HTTP && USE_HTTP3 && USE_PROXY_HTTP3 */

View file

@ -29,6 +29,7 @@ struct connectdata;
struct Curl_addrinfo;
struct Curl_cfilter;
struct Curl_easy;
struct Curl_peer;
struct Curl_sockaddr_ex;
/**
@ -46,11 +47,15 @@ typedef CURLcode cf_ip_connect_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
struct Curl_sockaddr_ex *addr,
uint8_t transport);
uint8_t transport_in,
uint8_t transport_out);
CURLcode cf_ip_happy_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data,
uint8_t transport);
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)

View file

@ -1768,7 +1768,8 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
struct Curl_sockaddr_ex *addr,
uint8_t transport)
uint8_t transport_in,
uint8_t transport_out)
{
struct cf_socket_ctx *ctx = NULL;
struct Curl_cfilter *cf = NULL;
@ -1776,7 +1777,8 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf,
(void)data;
(void)conn;
DEBUGASSERT(transport == TRNSPRT_TCP);
(void)transport_in;
DEBUGASSERT(transport_out == TRNSPRT_TCP);
if(!addr) {
result = CURLE_BAD_FUNCTION_ARGUMENT;
goto out;
@ -1788,7 +1790,7 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf,
goto out;
}
result = cf_socket_ctx_init(ctx, addr, transport);
result = cf_socket_ctx_init(ctx, addr, transport_out);
if(result)
goto out;
@ -1934,7 +1936,8 @@ CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
struct Curl_sockaddr_ex *addr,
uint8_t transport)
uint8_t transport_in,
uint8_t transport_out)
{
struct cf_socket_ctx *ctx = NULL;
struct Curl_cfilter *cf = NULL;
@ -1942,14 +1945,15 @@ CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf,
(void)data;
(void)conn;
DEBUGASSERT(transport == TRNSPRT_UDP || transport == TRNSPRT_QUIC);
(void)transport_in;
DEBUGASSERT(transport_out == TRNSPRT_UDP || transport_out == 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);
result = cf_socket_ctx_init(ctx, addr, transport_out);
if(result)
goto out;
@ -1988,7 +1992,8 @@ CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
struct Curl_sockaddr_ex *addr,
uint8_t transport)
uint8_t transport_in,
uint8_t transport_out)
{
struct cf_socket_ctx *ctx = NULL;
struct Curl_cfilter *cf = NULL;
@ -1996,14 +2001,15 @@ CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf,
(void)data;
(void)conn;
DEBUGASSERT(transport == TRNSPRT_UNIX);
(void)transport_in;
DEBUGASSERT(transport_out == 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);
result = cf_socket_ctx_init(ctx, addr, transport_out);
if(result)
goto out;

View file

@ -96,7 +96,8 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
struct Curl_sockaddr_ex *addr,
uint8_t transport);
uint8_t transport_in,
uint8_t transport_out);
/**
* Creates a cfilter that opens a UDP socket to the given address
@ -109,7 +110,8 @@ CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
struct Curl_sockaddr_ex *addr,
uint8_t transport);
uint8_t transport_in,
uint8_t transport_out);
/**
* Creates a cfilter that opens a UNIX socket to the given address
@ -122,7 +124,8 @@ CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
struct Curl_sockaddr_ex *addr,
uint8_t transport);
uint8_t transport_in,
uint8_t transport_out);
/**
* Creates a cfilter that keeps a listening socket.

View file

@ -690,7 +690,9 @@ bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex)
static bool cf_is_ssl(struct Curl_cfilter *cf)
{
for(; cf; cf = cf->next) {
if(cf->cft->flags & CF_TYPE_SSL)
/* A tunneling proxy does not offer end2end encryption, even if
* it does SSL itself (e.g. QUIC H3 proxy) */
if((cf->cft->flags & CF_TYPE_SSL) && !(cf->cft->flags & CF_TYPE_PROXY))
return TRUE;
if(cf->cft->flags & CF_TYPE_IP_CONNECT)
return FALSE;

View file

@ -342,72 +342,14 @@ struct cf_setup_ctx {
uint8_t transport;
};
#ifndef CURL_DISABLE_PROXY
static CURLcode cf_setup_add_http_proxy(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct cf_setup_ctx *ctx)
{
CURLcode result = CURLE_OK;
#ifndef USE_SSL
(void)cf;
(void)data;
(void)ctx;
#else
/* Skipping the Curl_conn_is_ssl check because SSL is a part of QUIC
For CURLPROXY_HTTPS and CURLPROXY_HTTPS2:
Curl_cft_setup --> Curl_cft_ssl --> Curl_cft_http_proxy --> ...
For CURLPROXY_HTTPS3:
Curl_cft_setup --> Curl_cft_http3 --> Curl_cft_http_proxy --> ... */
if(ctx->transport == TRNSPRT_QUIC && cf->conn->bits.httpproxy) {
if(!IS_QUIC_PROXY(cf->conn->http_proxy.proxytype)) {
result = Curl_cf_ssl_proxy_insert_after(cf, data);
if(result)
return result;
}
}
else {
if(IS_HTTPS_PROXY(cf->conn->http_proxy.proxytype) &&
!Curl_conn_is_ssl(cf->conn, cf->sockindex) &&
!IS_QUIC_PROXY(cf->conn->http_proxy.proxytype)) {
result = Curl_cf_ssl_proxy_insert_after(cf, data);
if(result)
return result;
}
}
#endif /* USE_SSL */
#ifndef CURL_DISABLE_HTTP
if(cf->conn->bits.tunnel_proxy) {
struct Curl_peer *dest; /* where HTTP should tunnel to */
bool udp_tun = false;
dest = Curl_conn_get_destination(cf->conn, cf->sockindex);
/* Use CONNECT-UDP only for explicit HTTP/3-only target tunnels.
Do not derive this from proxy transport (for example HTTPS3 proxy). */
if(data->state.http_neg.wanted == CURL_HTTP_V3x) {
#ifdef USE_PROXY_HTTP3
udp_tun = TRUE;
#else
failf(data, "HTTP/3 proxy tunnel support not built-in");
return CURLE_NOT_BUILT_IN;
#endif /* USE_PROXY_HTTP3 */
}
result = Curl_cf_http_proxy_insert_after(cf, data, dest,
cf->conn->http_proxy.proxytype,
udp_tun);
if(result)
return result;
}
#endif /* !CURL_DISABLE_HTTP */
return result;
}
#endif /* !CURL_DISABLE_PROXY */
static CURLcode cf_setup_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool *done)
{
struct cf_setup_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
struct Curl_peer *first_peer =
Curl_conn_get_first_peer(cf->conn, cf->sockindex);
if(cf->connected) {
*done = TRUE;
@ -425,38 +367,38 @@ connect_sub_chain:
}
if(ctx->state < CF_SETUP_CNNCT_EYEBALLS) {
#ifndef CURL_DISABLE_PROXY
#if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3) && \
defined(USE_PROXY_HTTP3)
if(IS_QUIC_PROXY(cf->conn->http_proxy.proxytype) &&
cf->conn->bits.tunnel_proxy) {
/* For HTTPS3 proxy tunnels, H3-PROXY manages the QUIC connection
on top of the UDP socket. Let happy eyeballs race IPv4/IPv6 using
QUIC-transport UDP sockets so the socket is connected to the
proxy peer and H3-PROXY can send directly via send().
Filter chains:
H1/H2 target (CONNECT over QUIC):
SETUP --> HTTP/1.1 or HTTP/2 --> SSL --> HTTP-PROXY -->
H3-PROXY --> HAPPY-EYEBALLS --> UDP
H3 target (MASQUE CONNECT-UDP over QUIC):
SETUP --> HTTP/3 --> CAPSULE --> HTTP-PROXY -->
H3-PROXY --> HAPPY-EYEBALLS --> UDP */
result = cf_ip_happy_quic_udp_insert_after(cf, data);
/* What type of thing we do connect to first?
* - without a proxy, `ctx->transport` defines it
* - with non-tunneling proxy, `ctx->transport` also applies, but
* for QUIC we need the cf-h3-proxy, not the standard vquic one
* - with tunneling proxy, transport is defined by the proxytype
* chosen and `ctx->transport` is tunneled through it.
*/
uint8_t transport_out = ctx->transport;
bool tunnel_proxy = FALSE;
#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
CURL_TRC_CF(data, cf, "happy eyeballing, httpproxy=%d, type=%d, "
"transport=%d",
cf->conn->bits.httpproxy, cf->conn->http_proxy.proxytype,
ctx->transport);
if(cf->conn->bits.httpproxy && cf->conn->bits.tunnel_proxy) {
transport_out =
Curl_http_proxy_transport(cf->conn->http_proxy.proxytype);
tunnel_proxy = TRUE;
if((transport_out == TRNSPRT_QUIC) && (cf->conn->bits.socksproxy)) {
failf(data, "HTTP/3 proxy not possible via SOCKS");
return CURLE_UNSUPPORTED_PROTOCOL;
}
}
/* When tunneling QUIC through an HTTP proxy (CONNECT-UDP),
the underlying conn to the proxy is TCP. */
else
#endif /* !CURL_DISABLE_HTTP && USE_HTTP3 && USE_PROXY_HTTP3 */
if(ctx->transport == TRNSPRT_QUIC && cf->conn->bits.httpproxy &&
!IS_QUIC_PROXY(cf->conn->http_proxy.proxytype))
result = cf_ip_happy_insert_after(cf, data, TRNSPRT_TCP);
else
#endif /* !CURL_DISABLE_PROXY */
result = cf_ip_happy_insert_after(cf, data, ctx->transport);
#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */
result = cf_ip_happy_insert_after(cf, data, first_peer,
ctx->transport, transport_out,
tunnel_proxy);
if(result)
return result;
ctx->state = CF_SETUP_CNNCT_EYEBALLS;
ctx->state = (tunnel_proxy && (transport_out == TRNSPRT_QUIC)) ?
CF_SETUP_CNNCT_HTTP_PROXY : CF_SETUP_CNNCT_EYEBALLS;
if(!cf->next || !cf->next->connected)
goto connect_sub_chain;
}
@ -491,9 +433,25 @@ connect_sub_chain:
}
if(ctx->state < CF_SETUP_CNNCT_HTTP_PROXY && cf->conn->bits.httpproxy) {
result = cf_setup_add_http_proxy(cf, data, ctx);
if(result)
return result;
#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);
if(result)
return result;
}
#endif /* USE_SSL */
#ifndef CURL_DISABLE_HTTP
if(cf->conn->bits.tunnel_proxy) {
struct Curl_peer *dest; /* where HTTP should tunnel to */
dest = 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);
if(result)
return result;
}
#endif /* !CURL_DISABLE_HTTP */
ctx->state = CF_SETUP_CNNCT_HTTP_PROXY;
if(!cf->next || !cf->next->connected)
goto connect_sub_chain;
@ -503,9 +461,8 @@ connect_sub_chain:
if(ctx->state < CF_SETUP_CNNCT_HAPROXY) {
#ifndef CURL_DISABLE_PROXY
if(data->set.haproxyprotocol) {
if(Curl_conn_is_ssl(cf->conn, cf->sockindex)) {
failf(data, "haproxy protocol not supported with SSL "
"encryption in place (QUIC?)");
if(ctx->transport == TRNSPRT_QUIC) {
failf(data, "haproxy protocol not support QUIC");
return CURLE_UNSUPPORTED_PROTOCOL;
}
result = Curl_cf_haproxy_insert_after(cf, data);

View file

@ -753,8 +753,8 @@ 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 proxytype,
bool udp_tunnel)
uint8_t transport,
uint8_t proxytype)
{
struct Curl_cfilter *cf;
struct cf_proxy_ctx *ctx = NULL;
@ -771,7 +771,7 @@ CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at,
}
Curl_peer_link(&ctx->dest, dest);
ctx->proxytype = proxytype;
ctx->udp_tunnel = udp_tunnel;
ctx->udp_tunnel = (transport == TRNSPRT_QUIC);
result = Curl_cf_create(&cf, &Curl_cft_http_proxy, ctx);
if(result)
@ -784,4 +784,14 @@ out:
return result;
}
uint8_t Curl_http_proxy_transport(uint8_t proxytype)
{
switch(proxytype) {
case CURLPROXY_HTTPS3:
return TRNSPRT_QUIC;
default:
return TRNSPRT_TCP;
}
}
#endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_PROXY */

View file

@ -69,8 +69,8 @@ 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 proxytype,
bool udp_tunnel);
uint8_t transport,
uint8_t proxytype);
extern struct Curl_cftype Curl_cft_http_proxy;
@ -83,4 +83,6 @@ extern struct Curl_cftype Curl_cft_http_proxy;
#define IS_QUIC_PROXY(t) ((t) == CURLPROXY_HTTPS3)
uint8_t Curl_http_proxy_transport(uint8_t proxytype);
#endif /* HEADER_CURL_HTTP_PROXY_H */

View file

@ -1317,12 +1317,7 @@ static struct connectdata *allocate_conn(struct Curl_easy *data)
#endif
conn->ip_version = data->set.ipver;
conn->bits.connect_only = (bool)data->set.connect_only;
#ifndef CURL_DISABLE_PROXY
if(conn->http_proxy.proxytype == CURLPROXY_HTTPS3)
conn->transport_wanted = TRNSPRT_QUIC;
else
#endif
conn->transport_wanted = TRNSPRT_TCP; /* most of them are TCP streams */
conn->transport_wanted = TRNSPRT_TCP; /* most of them are TCP streams */
/* Store the local bind parameters that will be used for this connection */
if(data->set.str[STRING_DEVICE]) {

View file

@ -3120,7 +3120,8 @@ CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf,
goto out;
cf->conn = conn;
result = Curl_cf_udp_create(&cf->next, data, conn, addr, TRNSPRT_QUIC);
result = Curl_cf_udp_create(&cf->next, data, conn, addr,
TRNSPRT_QUIC, TRNSPRT_QUIC);
if(result)
goto out;
cf->next->conn = cf->conn;

View file

@ -1659,7 +1659,8 @@ CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf,
goto out;
cf->conn = conn;
result = Curl_cf_udp_create(&cf->next, data, conn, addr, TRNSPRT_QUIC);
result = Curl_cf_udp_create(&cf->next, data, conn, addr,
TRNSPRT_QUIC, TRNSPRT_QUIC);
if(result)
goto out;
cf->next->conn = cf->conn;

View file

@ -769,10 +769,12 @@ CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
struct Curl_sockaddr_ex *addr,
uint8_t transport)
uint8_t transport_in,
uint8_t transport_out)
{
(void)transport;
DEBUGASSERT(transport == TRNSPRT_QUIC);
(void)transport_in;
(void)transport_out;
DEBUGASSERT(transport_out == TRNSPRT_QUIC);
#if defined(USE_NGTCP2) && defined(USE_NGHTTP3)
return Curl_cf_ngtcp2_create(pcf, data, conn, addr);
#elif defined(USE_QUICHE)

View file

@ -45,7 +45,8 @@ CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
struct Curl_sockaddr_ex *addr,
uint8_t transport);
uint8_t transport_in,
uint8_t transport_out);
extern struct Curl_cftype Curl_cft_http3;