| @@ -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; |