From: Stefan Eissing Date: Mon, 1 Jun 2026 12:23:30 +0000 (+0200) Subject: cf-setup: improve readability X-Git-Tag: rc-8_21_0-2~63 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=032b15c4342e8eddb51fbe089a9d8ba3ee2070fb;p=thirdparty%2Fcurl.git cf-setup: improve readability Restructure the code in cf-setup connect to make it better readable what is happening for establishing the connection's filter chain. Closes #21827 --- diff --git a/lib/cf-h3-proxy.c b/lib/cf-h3-proxy.c index a785615388..4fdaef47d0 100644 --- a/lib/cf-h3-proxy.c +++ b/lib/cf-h3-proxy.c @@ -3449,17 +3449,6 @@ CURLcode Curl_cf_h3_proxy_create(struct Curl_cfilter **pcf, 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) { diff --git a/lib/connect.c b/lib/connect.c index 0ed7b22a5b..9d74e35bc7 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -55,6 +55,7 @@ #include "cfilters.h" #include "connect.h" #include "cf-dns.h" +#include "cf-capsule.h" #include "cf-haproxy.h" #include "cf-https-connect.h" #include "cf-ip-happy.h" @@ -342,174 +343,256 @@ struct cf_setup_ctx { uint8_t transport; }; -static CURLcode cf_setup_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *done) +#ifndef CURL_DISABLE_PROXY + +static CURLcode cf_setup_add_haproxy(struct Curl_cfilter *cf, + struct Curl_easy *data) { 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; - return CURLE_OK; - } - /* connect current sub-chain */ -connect_sub_chain: - VERBOSE(Curl_conn_trc_filters(data, cf->sockindex, "cf_setup_connect")); - - if(cf->next && !cf->next->connected) { - result = Curl_conn_cf_connect(cf->next, data, done); - if(result || !*done) - return result; - } - - if(ctx->state < CF_SETUP_CNNCT_EYEBALLS) { - /* 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"); + if(ctx->state < CF_SETUP_CNNCT_HAPROXY) { + if(data->set.haproxyprotocol) { + if(ctx->transport == TRNSPRT_QUIC) { + failf(data, "haproxy protocol does not support QUIC"); return CURLE_UNSUPPORTED_PROTOCOL; } + result = Curl_cf_haproxy_insert_after(cf, data); + if(result) { + CURL_TRC_CF(data, cf, "adding HAPROXY filter failed -> %d", result); + return result; + } + CURL_TRC_CF(data, cf, "added HAPROXY filter"); } -#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 = (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; + ctx->state = CF_SETUP_CNNCT_HAPROXY; } + return result; +} - /* sub-chain connected, do we need to add more? */ -#ifndef CURL_DISABLE_PROXY +static CURLcode cf_setup_add_socks(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_setup_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; if(ctx->state < CF_SETUP_CNNCT_SOCKS && cf->conn->bits.socksproxy) { - struct Curl_peer *dest; /* where SOCKS should tunnel to */ + /* Add a SOCKS proxy to go through `first_peer` to `second_peer`*/ + struct Curl_peer *second_peer; if(cf->conn->bits.httpproxy) - dest = cf->conn->http_proxy.peer; + second_peer = cf->conn->http_proxy.peer; else - dest = Curl_conn_get_destination(cf->conn, cf->sockindex); - if(!dest) + second_peer = Curl_conn_get_destination(cf->conn, cf->sockindex); + if(!second_peer) return CURLE_FAILED_INIT; result = Curl_cf_socks_proxy_insert_after( - cf, data, dest, cf->conn->ip_version, + cf, data, second_peer, cf->conn->ip_version, cf->conn->socks_proxy.proxytype, cf->conn->socks_proxy.creds); - if(result) { - /* 'dest' might be freed now so it can't be dereferenced */ - CURL_TRC_CF(data, cf, "added SOCKS filter failed -> %d", result); + CURL_TRC_CF(data, cf, "adding SOCKS filter failed -> %d", result); return result; } - CURL_TRC_CF(data, cf, "added SOCKS filter to %s:%u -> %d", - dest->hostname, dest->port, result); + + CURL_TRC_CF(data, cf, "added SOCKS filter to %s:%u", + second_peer->hostname, second_peer->port); ctx->state = CF_SETUP_CNNCT_SOCKS; - if(!cf->next || !cf->next->connected) - goto connect_sub_chain; } + return result; +} + +#ifndef CURL_DISABLE_HTTP +static CURLcode cf_setup_add_http_proxy(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_setup_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; if(ctx->state < CF_SETUP_CNNCT_HTTP_PROXY && cf->conn->bits.httpproxy) { #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) + if(result) { + CURL_TRC_CF(data, cf, "adding SSL filter for HTTP proxy failed -> %d", + result); return result; + } + CURL_TRC_CF(data, cf, "added SSL filter for HTTP proxy"); } #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) + if(result) { + CURL_TRC_CF(data, cf, "adding HTTP proxy tunnel filter failed -> %d", + result); return result; + } + CURL_TRC_CF(data, cf, "added HTTP proxy tunnel filter"); } -#endif /* !CURL_DISABLE_HTTP */ ctx->state = CF_SETUP_CNNCT_HTTP_PROXY; - if(!cf->next || !cf->next->connected) - goto connect_sub_chain; } -#endif /* !CURL_DISABLE_PROXY */ + return result; +} +#endif /* !CURL_DISABLE_HTTP */ +#endif /* CURL_DISABLE_PROXY */ - if(ctx->state < CF_SETUP_CNNCT_HAPROXY) { -#ifndef CURL_DISABLE_PROXY - if(data->set.haproxyprotocol) { - if(ctx->transport == TRNSPRT_QUIC) { - failf(data, "haproxy protocol not support QUIC"); +static CURLcode cf_setup_add_ip_happy(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_setup_ctx *ctx = cf->ctx; + 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. */ + struct Curl_peer *first_peer = + Curl_conn_get_first_peer(cf->conn, cf->sockindex); + uint8_t first_transport = ctx->transport; + bool tunnel_proxy = FALSE; + +#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); + if((first_transport == TRNSPRT_QUIC) && (cf->conn->bits.socksproxy)) { + failf(data, "HTTP/3 proxy not possible via SOCKS"); return CURLE_UNSUPPORTED_PROTOCOL; } - result = Curl_cf_haproxy_insert_after(cf, data); - if(result) - return result; + 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); + if(result) { + CURL_TRC_CF(data, cf, "adding happy eyeballs failed -> %d", result); + return result; + } + + if(tunnel_proxy && (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", + first_peer->hostname, first_peer->port); + ctx->state = CF_SETUP_CNNCT_EYEBALLS; } -#endif /* !CURL_DISABLE_PROXY */ - ctx->state = CF_SETUP_CNNCT_HAPROXY; - if(!cf->next || !cf->next->connected) - goto connect_sub_chain; } + return result; +} - /* Adding Curl_cf_quic_insert_after() because now we - need the next filter to be QUIC/HTTP/3 (which has SSL) */ +static CURLcode cf_setup_add_origin_filters(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + struct cf_setup_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; + + (void)data; /* not used in all builds */ + if(ctx->state < CF_SETUP_CNNCT_SSL) { #if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3) && \ - defined(USE_PROXY_HTTP3) - if(ctx->transport == TRNSPRT_QUIC && cf->conn->bits.httpproxy && - cf->conn->bits.tunnel_proxy && - (data->state.http_neg.wanted == CURL_HTTP_V3x)) { - if(ctx->state < CF_SETUP_CNNCT_SSL) { + !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) { + 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); - if(result) + if(result) { + CURL_TRC_CF(data, cf, "adding QUIC filter failed -> %d", result); return result; - ctx->state = CF_SETUP_CNNCT_SSL; + } + CURL_TRC_CF(data, cf, "added QUIC filter for origin"); } - if(!cf->next || !cf->next->connected) - goto connect_sub_chain; - } - else -#endif /* !CURL_DISABLE_HTTP && USE_HTTP3 && USE_PROXY_HTTP3 */ - { - if(ctx->state < CF_SETUP_CNNCT_SSL) { + else +#endif /* !CURL_DISABLE_HTTP && USE_HTTP3 && CURL_DISABLE_PROXY */ #ifdef USE_SSL - if((ctx->ssl_mode == CURL_CF_SSL_ENABLE || - (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); - if(result) - return result; + if((ctx->ssl_mode == CURL_CF_SSL_ENABLE || + (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); + if(result) { + CURL_TRC_CF(data, cf, "adding SSL filter for origin failed -> %d", + result); + return result; } -#endif /* USE_SSL */ - ctx->state = CF_SETUP_CNNCT_SSL; - if(!cf->next || !cf->next->connected) - goto connect_sub_chain; + CURL_TRC_CF(data, cf, "added SSL filter for origin"); } +#endif /* USE_SSL */ + ctx->state = CF_SETUP_CNNCT_SSL; } + return result; +} + +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; + + if(cf->connected) { + *done = TRUE; + return CURLE_OK; + } + + /* connect current sub-chain */ +connect_sub_chain: + VERBOSE(Curl_conn_trc_filters(data, cf->sockindex, "cf_setup_connect")); + + if(cf->next && !cf->next->connected) { + result = Curl_conn_cf_connect(cf->next, data, done); + if(result || !*done) + return result; + } + + result = cf_setup_add_ip_happy(cf, data); + if(result) + return result; + if(!cf->next || !cf->next->connected) + goto connect_sub_chain; + +#ifndef CURL_DISABLE_PROXY + result = cf_setup_add_socks(cf, data); + if(result) + return result; + if(!cf->next || !cf->next->connected) + goto connect_sub_chain; + +#ifndef CURL_DISABLE_HTTP + result = cf_setup_add_http_proxy(cf, data); + if(result) + return result; + if(!cf->next || !cf->next->connected) + goto connect_sub_chain; +#endif /* !CURL_DISABLE_HTTP */ + + result = cf_setup_add_haproxy(cf, data); + if(result) + return result; + if(!cf->next || !cf->next->connected) + goto connect_sub_chain; +#endif /* !CURL_DISABLE_PROXY */ + + result = cf_setup_add_origin_filters(cf, data); + if(result) + return result; + if(!cf->next || !cf->next->connected) + goto connect_sub_chain; ctx->state = CF_SETUP_DONE; cf->connected = TRUE; diff --git a/lib/http_proxy.c b/lib/http_proxy.c index 39cfb12446..9f1ed7963c 100644 --- a/lib/http_proxy.c +++ b/lib/http_proxy.c @@ -654,22 +654,8 @@ connect_sub: } else { /* subchain connected and we had already installed the protocol filter. - * This means the protocol tunnel is established, we are done. - */ + * This means the protocol tunnel is established, we are done. */ DEBUGASSERT(ctx->sub_filter_installed); - if(ctx->udp_tunnel) { -#ifdef USE_PROXY_HTTP3 - /* Insert capsule filter between us and the protocol sub-filter. - * This handles encap/decap of UDP datagrams in capsule format. */ - result = Curl_cf_capsule_insert_after(cf, data); - if(result) - goto out; - CURL_TRC_CF(data, cf, "installed capsule filter for UDP tunnel"); -#else - result = CURLE_NOT_BUILT_IN; - goto out; -#endif /* USE_PROXY_HTTP3 */ - } result = CURLE_OK; } diff --git a/tests/http/test_60_h3_proxy.py b/tests/http/test_60_h3_proxy.py index 7ebba8a2b1..39c6d265f6 100644 --- a/tests/http/test_60_h3_proxy.py +++ b/tests/http/test_60_h3_proxy.py @@ -195,12 +195,12 @@ class TestH3Proxy: @pytest.mark.parametrize( ["alpn_proto", "proxy_proto", "exp_err"], [ - #pytest.param( - # "http/1.1", - # "h3", - # "could not connect to server", - # id="fail_h1_over_h3_proxytunnel", - #), + pytest.param( + "http/1.1", + "h3", + "could not connect to server", + id="fail_h1_over_h3_proxytunnel", + ), pytest.param( "h2", "h3", @@ -208,12 +208,12 @@ class TestH3Proxy: marks=MARK_NEEDS_NGHTTP2, id="fail_h2_over_h3_proxytunnel", ), - #pytest.param( - # "h3", - # "h3", - # "could not connect to server", - # id="fail_h3_over_h3_proxytunnel", - #), + pytest.param( + "h3", + "h3", + "could not connect to server", + id="fail_h3_over_h3_proxytunnel", + ), #pytest.param( # "h3", # "h2", @@ -235,11 +235,13 @@ class TestH3Proxy: httpd, nghttpx, nghttpx_fwd, + h2o_proxy, alpn_proto, proxy_proto, exp_err, ): - _require_available(httpd=httpd, nghttpx=nghttpx, nghttpx_fwd=nghttpx_fwd) + _require_available(httpd=httpd, nghttpx=nghttpx, nghttpx_fwd=nghttpx_fwd, + h2o_proxy=h2o_proxy) curl = CurlClient(env=env) url = f"https://localhost:{env.https_port}/data.json"