cf-setup: own source file
No binary (build failed)
—
(log yok)
| @@ -165,6 +165,7 @@ LIB_CFILES = \ | |
| cf-haproxy.c \ | |
| cf-https-connect.c \ | |
| cf-ip-happy.c \ | |
| + | cf-setup.c \ |
| cf-socket.c \ | |
| cfilters.c \ | |
| conncache.c \ | |
| @@ -297,6 +298,7 @@ LIB_HFILES = \ | |
| cf-haproxy.h \ | |
| cf-https-connect.h \ | |
| cf-ip-happy.h \ | |
| + | cf-setup.h \ |
| cf-socket.h \ | |
| cfilters.h \ | |
| conncache.h \ | |
| @@ -29,6 +29,7 @@ | |
| #include "curl_trc.h" | |
| #include "cfilters.h" | |
| #include "cf-dns.h" | |
| + | #include "cf-setup.h" |
| #include "connect.h" | |
| #include "hostip.h" | |
| #include "httpsrr.h" | |
| @@ -0,0 +1,476 @@ | |
| + | /*************************************************************************** |
| + | * _ _ ____ _ |
| + | * Project ___| | | | _ \| | |
| + | * / __| | | | |_) | | |
| + | * | (__| |_| | _ <| |___ |
| + | * \___|\___/|_| \_\_____| |
| + | * |
| + | * Copyright (C) Daniel Stenberg, <[email protected]>, 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" |
| + | |
| + | #include "urldata.h" |
| + | #include "cfilters.h" |
| + | #include "cf-haproxy.h" |
| + | #include "cf-ip-happy.h" |
| + | #include "cf-setup.h" |
| + | #include "curl_trc.h" |
| + | #include "connect.h" |
| + | #include "http_proxy.h" |
| + | #include "socks.h" |
| + | #include "vquic/cf-capsule.h" |
| + | #include "vquic/vquic.h" |
| + | #include "vtls/vtls.h" |
| + | |
| + | |
| + | typedef enum { |
| + | CF_SETUP_INIT, |
| + | CF_SETUP_CNNCT_EYEBALLS, |
| + | CF_SETUP_CNNCT_SOCKS, |
| + | CF_SETUP_CNNCT_HTTP_PROXY, |
| + | CF_SETUP_CNNCT_HAPROXY, |
| + | CF_SETUP_CNNCT_SSL, |
| + | CF_SETUP_DONE |
| + | } cf_setup_state; |
| + | |
| + | struct cf_setup_ctx { |
| + | cf_setup_state state; |
| + | int ssl_mode; |
| + | uint8_t transport; |
| + | uint8_t retry_count; |
| + | }; |
| + | |
| + | #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; |
| + | |
| + | 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", |
| + | (int)result); |
| + | return result; |
| + | } |
| + | CURL_TRC_CF(data, cf, "added HAPROXY filter"); |
| + | } |
| + | ctx->state = CF_SETUP_CNNCT_HAPROXY; |
| + | } |
| + | return result; |
| + | } |
| + | |
| + | 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->socks_proxy.peer) { |
| + | /* Add a SOCKS proxy to go through `first_peer` to `second_peer`*/ |
| + | struct Curl_peer *second_peer; |
| + | |
| + | if(cf->conn->http_proxy.peer) |
| + | second_peer = cf->conn->http_proxy.peer; |
| + | else |
| + | 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, second_peer, cf->conn->ip_version, |
| + | cf->conn->socks_proxy.proxytype, |
| + | cf->conn->socks_proxy.creds); |
| + | if(result) { |
| + | CURL_TRC_CF(data, cf, "adding SOCKS filter failed -> %d", (int)result); |
| + | return result; |
| + | } |
| + | |
| + | CURL_TRC_CF(data, cf, "added SOCKS filter to %s:%u", |
| + | second_peer->hostname, second_peer->port); |
| + | ctx->state = CF_SETUP_CNNCT_SOCKS; |
| + | } |
| + | 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->http_proxy.peer && !cf->conn->bits.origin_is_proxy) { |
| + | struct Curl_peer *peer = cf->conn->http_proxy.peer; |
| + | struct Curl_peer *tunnel_peer = |
| + | Curl_conn_get_destination(cf->conn, cf->sockindex); |
| + | |
| + | #ifdef USE_SSL |
| + | if(CURL_PROXY_IS_HTTPS(cf->conn->http_proxy.proxytype) && |
| + | !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { |
| + | 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", |
| + | (int)result); |
| + | return result; |
| + | } |
| + | CURL_TRC_CF(data, cf, "added SSL filter for HTTP proxy"); |
| + | } |
| + | #endif /* USE_SSL */ |
| + | |
| + | result = Curl_cf_http_proxy_insert_after( |
| + | 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", |
| + | (int)result); |
| + | return result; |
| + | } |
| + | CURL_TRC_CF(data, cf, "added HTTP proxy tunnel filter"); |
| + | ctx->state = CF_SETUP_CNNCT_HTTP_PROXY; |
| + | } |
| + | return result; |
| + | } |
| + | #endif /* !CURL_DISABLE_HTTP */ |
| + | #endif /* CURL_DISABLE_PROXY */ |
| + | |
| + | /* Get the origin curl connects its socket to. |
| + | * Can be origin or the first proxy. */ |
| + | static struct Curl_peer *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; |
| + | } |
| + | |
| + | 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 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 = |
| + | 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; |
| + | |
| + | if(!first_peer) |
| + | return CURLE_FAILED_INIT; |
| + | |
| + | #if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) |
| + | if(cf->conn->http_proxy.peer && !cf->conn->bits.origin_is_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->socks_proxy.peer) { |
| + | failf(data, "HTTP/3 proxy not possible via SOCKS"); |
| + | return CURLE_UNSUPPORTED_PROTOCOL; |
| + | } |
| + | } |
| + | #endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ |
| + | |
| + | 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", (int)result); |
| + | return result; |
| + | } |
| + | |
| + | 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_peer ? "proxy" : "origin", |
| + | first_peer->hostname, first_peer->port); |
| + | ctx->state = CF_SETUP_CNNCT_EYEBALLS; |
| + | } |
| + | } |
| + | return result; |
| + | } |
| + | |
| + | 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(CURL_DISABLE_PROXY) |
| + | |
| + | /* Wanting QUIC with an 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->http_proxy.peer && !cf->conn->bits.origin_is_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", |
| + | (int)result); |
| + | return result; |
| + | } |
| + | result = Curl_cf_quic_insert_after(cf, origin, peer); |
| + | if(result) { |
| + | CURL_TRC_CF(data, cf, "adding QUIC filter failed -> %d", (int)result); |
| + | return result; |
| + | } |
| + | CURL_TRC_CF(data, cf, "added QUIC filter for origin"); |
| + | } |
| + | 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 */ |
| + | |
| + | #ifndef CURL_DISABLE_PROXY |
| + | if(cf->conn->bits.origin_is_proxy) { |
| + | result = Curl_cf_ssl_proxy_insert_after(cf, data, cf->conn->origin); |
| + | } |
| + | else |
| + | #endif |
| + | { |
| + | /* 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", |
| + | (int)result); |
| + | return result; |
| + | } |
| + | 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_steps(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; |
| + | *done = TRUE; |
| + | return CURLE_OK; |
| + | } |
| + | |
| + | static CURLcode cf_setup_connect(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data, |
| + | bool *done) |
| + | { |
| + | struct cf_setup_ctx *ctx = cf->ctx; |
| + | CURLcode result; |
| + | |
| + | /* In some situations, a server/proxy may close the connection and |
| + | * we need to connect again (HTTP/1.x proxy auth, for example). |
| + | * We used to close the filters and reuse them for another attempt, |
| + | * however that complicates filter code and it is simpler to tear them |
| + | * all down and start over. */ |
| + | retry: |
| + | result = cf_setup_connect_steps(cf, data, done); |
| + | |
| + | if(result == CURLE_AGAIN) { |
| + | ++ctx->retry_count; |
| + | if(ctx->retry_count > 5) /* arbitrary limit, better just timeout? */ |
| + | return CURLE_COULDNT_CONNECT; |
| + | |
| + | CURL_TRC_CF(data, cf, "retrying connect, %d. time", ctx->retry_count); |
| + | Curl_conn_cf_discard_chain(&cf->next, data); |
| + | ctx->state = CF_SETUP_INIT; |
| + | goto retry; |
| + | } |
| + | return result; |
| + | } |
| + | |
| + | static void cf_setup_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) |
| + | { |
| + | struct cf_setup_ctx *ctx = cf->ctx; |
| + | |
| + | CURL_TRC_CF(data, cf, "destroy"); |
| + | curlx_safefree(ctx); |
| + | } |
| + | |
| + | struct Curl_cftype Curl_cft_setup = { |
| + | "SETUP", |
| + | CF_TYPE_SETUP, |
| + | CURL_LOG_LVL_NONE, |
| + | cf_setup_destroy, |
| + | cf_setup_connect, |
| + | Curl_cf_def_shutdown, |
| + | Curl_cf_def_adjust_pollset, |
| + | Curl_cf_def_data_pending, |
| + | Curl_cf_def_send, |
| + | Curl_cf_def_recv, |
| + | Curl_cf_def_cntrl, |
| + | Curl_cf_def_conn_is_alive, |
| + | Curl_cf_def_conn_keep_alive, |
| + | Curl_cf_def_query, |
| + | }; |
| + | |
| + | static CURLcode cf_setup_create(struct Curl_cfilter **pcf, |
| + | struct Curl_easy *data, |
| + | uint8_t transport, |
| + | int ssl_mode) |
| + | { |
| + | struct Curl_cfilter *cf = NULL; |
| + | struct cf_setup_ctx *ctx; |
| + | CURLcode result = CURLE_OK; |
| + | |
| + | (void)data; |
| + | ctx = curlx_calloc(1, sizeof(*ctx)); |
| + | if(!ctx) { |
| + | result = CURLE_OUT_OF_MEMORY; |
| + | goto out; |
| + | } |
| + | ctx->state = CF_SETUP_INIT; |
| + | ctx->ssl_mode = ssl_mode; |
| + | ctx->transport = transport; |
| + | |
| + | result = Curl_cf_create(&cf, &Curl_cft_setup, ctx); |
| + | if(result) |
| + | goto out; |
| + | ctx = NULL; |
| + | |
| + | out: |
| + | *pcf = result ? NULL : cf; |
| + | if(ctx) { |
| + | curlx_free(ctx); |
| + | } |
| + | return result; |
| + | } |
| + | |
| + | CURLcode Curl_cf_setup_add(struct Curl_easy *data, |
| + | struct connectdata *conn, |
| + | int sockindex, |
| + | uint8_t transport, |
| + | int ssl_mode) |
| + | { |
| + | struct Curl_cfilter *cf; |
| + | CURLcode result = CURLE_OK; |
| + | |
| + | DEBUGASSERT(data); |
| + | result = cf_setup_create(&cf, data, transport, ssl_mode); |
| + | if(result) |
| + | goto out; |
| + | Curl_conn_cf_add(data, conn, sockindex, cf); |
| + | out: |
| + | return result; |
| + | } |
| + | |
| + | CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at, |
| + | struct Curl_easy *data, |
| + | uint8_t transport, |
| + | int ssl_mode) |
| + | { |
| + | struct Curl_cfilter *cf; |
| + | CURLcode result; |
| + | |
| + | DEBUGASSERT(data); |
| + | result = cf_setup_create(&cf, data, transport, ssl_mode); |
| + | if(result) |
| + | goto out; |
| + | Curl_conn_cf_insert_after(cf_at, cf); |
| + | out: |
| + | return result; |
| + | } |
| @@ -0,0 +1,46 @@ | |
| + | #ifndef HEADER_CURL_CF_SETUP_H |
| + | #define HEADER_CURL_CF_SETUP_H |
| + | /*************************************************************************** |
| + | * _ _ ____ _ |
| + | * Project ___| | | | _ \| | |
| + | * / __| | | | |_) | | |
| + | * | (__| |_| | _ <| |___ |
| + | * \___|\___/|_| \_\_____| |
| + | * |
| + | * Copyright (C) Daniel Stenberg, <[email protected]>, 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" |
| + | |
| + | struct Curl_dns_entry; |
| + | struct ip_quadruple; |
| + | struct Curl_peer; |
| + | struct Curl_str; |
| + | |
| + | CURLcode Curl_cf_setup_add(struct Curl_easy *data, |
| + | struct connectdata *conn, |
| + | int sockindex, |
| + | uint8_t transport, |
| + | int ssl_mode); |
| + | |
| + | CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at, |
| + | struct Curl_easy *data, |
| + | uint8_t transport, |
| + | int ssl_mode); |
| + | |
| + | extern struct Curl_cftype Curl_cft_setup; |
| + | |
| + | #endif /* HEADER_CURL_CF_SETUP_H */ |
| @@ -423,29 +423,6 @@ CURLcode Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data, | |
| return CURLE_RECV_ERROR; | |
| } | |
| - | #ifdef CURLVERBOSE |
| - | static CURLcode cf_verboseconnect(struct Curl_easy *data, |
| - | struct Curl_cfilter *cf) |
| - | { |
| - | if(Curl_trc_is_verbose(data)) { |
| - | struct ip_quadruple ipquad; |
| - | bool is_ipv6; |
| - | CURLcode result; |
| - | |
| - | result = Curl_conn_cf_get_ip_info(cf, data, &is_ipv6, &ipquad); |
| - | if(result) |
| - | return result; |
| - | |
| - | infof(data, "Established %sconnection to %s (%s port %u) from %s port %u ", |
| - | (cf->sockindex == SECONDARYSOCKET) ? "2nd " : "", |
| - | CURL_CONN_HOST_DISPNAME(data->conn), |
| - | ipquad.remote_ip, ipquad.remote_port, |
| - | ipquad.local_ip, ipquad.local_port); |
| - | } |
| - | return CURLE_OK; |
| - | } |
| - | #endif |
| - | |
| static CURLcode cf_cntrl_all(struct connectdata *conn, | |
| struct Curl_easy *data, | |
| bool ignore_result, | |
| @@ -463,36 +440,14 @@ static CURLcode cf_cntrl_all(struct connectdata *conn, | |
| return result; | |
| } | |
| - | static void cf_cntrl_update_info(struct Curl_easy *data, |
| - | struct connectdata *conn) |
| + | void Curl_conn_cntrl_update_info(struct Curl_easy *data, |
| + | struct connectdata *conn) |
| { | |
| cf_cntrl_all(conn, data, TRUE, CF_CTRL_CONN_INFO_UPDATE, 0, NULL); | |
| } | |
| - | /** |
| - | * Update connection statistics |
| - | */ |
| - | static void conn_report_connect_stats(struct Curl_cfilter *cf, |
| - | struct Curl_easy *data) |
| - | { |
| - | if(cf) { |
| - | struct curltime connected; |
| - | struct curltime appconnected; |
| - | |
| - | memset(&connected, 0, sizeof(connected)); |
| - | cf->cft->query(cf, data, CF_QUERY_TIMER_CONNECT, NULL, &connected); |
| - | if(connected.tv_sec || connected.tv_usec) |
| - | Curl_pgrsTimeWas(data, TIMER_CONNECT, connected); |
| - | |
| - | memset(&appconnected, 0, sizeof(appconnected)); |
| - | cf->cft->query(cf, data, CF_QUERY_TIMER_APPCONNECT, NULL, &appconnected); |
| - | if(appconnected.tv_sec || appconnected.tv_usec) |
| - | Curl_pgrsTimeWas(data, TIMER_APPCONNECT, appconnected); |
| - | } |
| - | } |
| - | |
| - | static void conn_remove_setup_filters(struct Curl_easy *data, |
| - | int sockindex) |
| + | void Curl_conn_remove_setup_filters(struct Curl_easy *data, |
| + | int sockindex) |
| { | |
| struct Curl_cfilter **anchor = &data->conn->cfilter[sockindex]; | |
| while(*anchor) { | |
| @@ -509,127 +464,6 @@ static void conn_remove_setup_filters(struct Curl_easy *data, | |
| } | |
| } | |
| - | CURLcode Curl_conn_connect(struct Curl_easy *data, |
| - | int sockindex, |
| - | bool blocking, |
| - | bool *done) |
| - | { |
| - | #define CF_CONN_NUM_POLLS_ON_STACK 5 |
| - | struct pollfd a_few_on_stack[CF_CONN_NUM_POLLS_ON_STACK]; |
| - | struct easy_pollset ps; |
| - | struct curl_pollfds cpfds; |
| - | struct Curl_cfilter *cf; |
| - | CURLcode result = CURLE_OK; |
| - | |
| - | DEBUGASSERT(data); |
| - | DEBUGASSERT(data->conn); |
| - | if(!CONN_SOCK_IDX_VALID(sockindex)) |
| - | return CURLE_BAD_FUNCTION_ARGUMENT; |
| - | |
| - | if(data->conn->scheme->flags & PROTOPT_NONETWORK) { |
| - | *done = TRUE; |
| - | return CURLE_OK; |
| - | } |
| - | |
| - | cf = data->conn->cfilter[sockindex]; |
| - | if(!cf) { |
| - | *done = FALSE; |
| - | return CURLE_FAILED_INIT; |
| - | } |
| - | |
| - | *done = (bool)cf->connected; |
| - | if(*done) |
| - | return CURLE_OK; |
| - | |
| - | Curl_pollset_init(&ps); |
| - | Curl_pollfds_init(&cpfds, a_few_on_stack, CF_CONN_NUM_POLLS_ON_STACK); |
| - | while(!*done) { |
| - | if(Curl_conn_needs_flush(data, sockindex)) { |
| - | DEBUGF(infof(data, "Curl_conn_connect(index=%d), flush", sockindex)); |
| - | result = Curl_conn_flush(data, sockindex); |
| - | if(result && (result != CURLE_AGAIN)) |
| - | return result; |
| - | } |
| - | |
| - | result = cf->cft->do_connect(cf, data, done); |
| - | CURL_TRC_CF(data, cf, "Curl_conn_connect(block=%d) -> %d, done=%d", |
| - | blocking, (int)result, *done); |
| - | if(!result && *done) { |
| - | /* A final sanity check on connection security */ |
| - | if((data->state.origin->scheme->flags & PROTOPT_SSL) && |
| - | (sockindex == FIRSTSOCKET) && |
| - | !Curl_conn_is_ssl(data->conn, FIRSTSOCKET)) { |
| - | DEBUGASSERT(0); |
| - | failf(data, "transfer requires SSL, but not connected via SSL"); |
| - | return CURLE_FAILED_INIT; |
| - | } |
| - | /* Now that the complete filter chain is connected, let all filters |
| - | * persist information at the connection. E.g. cf-socket sets the |
| - | * socket and ip related information. */ |
| - | cf_cntrl_update_info(data, data->conn); |
| - | conn_report_connect_stats(cf, data); |
| - | data->conn->keepalive = *Curl_pgrs_now(data); |
| - | VERBOSE(result = cf_verboseconnect(data, cf)); |
| - | VERBOSE(Curl_conn_trc_filters(data, sockindex, "connected")); |
| - | conn_remove_setup_filters(data, sockindex); |
| - | VERBOSE(Curl_conn_trc_filters(data, sockindex, "reduced to")); |
| - | goto out; |
| - | } |
| - | else if(result) { |
| - | CURL_TRC_CF(data, cf, "Curl_conn_connect(), filter returned %d", |
| - | (int)result); |
| - | VERBOSE(Curl_conn_trc_filters(data, sockindex, "failed to connect")); |
| - | conn_report_connect_stats(cf, data); |
| - | goto out; |
| - | } |
| - | |
| - | if(!blocking) |
| - | goto out; |
| - | else { |
| - | /* check allowed time left */ |
| - | const timediff_t timeout_ms = Curl_timeleft_ms(data); |
| - | curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); |
| - | int rc; |
| - | |
| - | if(timeout_ms < 0) { |
| - | /* no need to continue if time already is up */ |
| - | failf(data, "connect timeout"); |
| - | result = CURLE_OPERATION_TIMEDOUT; |
| - | goto out; |
| - | } |
| - | |
| - | CURL_TRC_CF(data, cf, "Curl_conn_connect(block=1), do poll"); |
| - | Curl_pollset_reset(&ps); |
| - | Curl_pollfds_reset(&cpfds); |
| - | /* In general, we want to send after connect, wait on that. */ |
| - | if(sockfd != CURL_SOCKET_BAD) |
| - | result = Curl_pollset_set_out_only(data, &ps, sockfd); |
| - | if(!result) |
| - | result = Curl_conn_adjust_pollset(data, data->conn, &ps); |
| - | if(result) |
| - | goto out; |
| - | result = Curl_pollfds_add_ps(&cpfds, &ps); |
| - | if(result) |
| - | goto out; |
| - | |
| - | rc = Curl_poll(cpfds.pfds, cpfds.n, |
| - | CURLMIN(timeout_ms, (cpfds.n ? 1000 : 10))); |
| - | CURL_TRC_CF(data, cf, "Curl_conn_connect(block=1), Curl_poll() -> %d", |
| - | rc); |
| - | if(rc < 0) { |
| - | result = CURLE_COULDNT_CONNECT; |
| - | goto out; |
| - | } |
| - | /* continue iterating */ |
| - | } |
| - | } |
| - | |
| - | out: |
| - | Curl_pollset_cleanup(&ps); |
| - | Curl_pollfds_cleanup(&cpfds); |
| - | return result; |
| - | } |
| - | |
| bool Curl_conn_is_setup(struct connectdata *conn, int sockindex) | |
| { | |
| if(!CONN_SOCK_IDX_VALID(sockindex)) | |
| @@ -358,16 +358,6 @@ bool Curl_conn_cf_wants_httpsrr(struct Curl_cfilter *cf, | |
| #define CURL_CF_SSL_DISABLE 0 | |
| #define CURL_CF_SSL_ENABLE 1 | |
| - | /** |
| - | * Bring the filter chain at `sockindex` for connection `data->conn` into |
| - | * connected state. Which will set `*done` to TRUE. |
| - | * This can be called on an already connected chain with no side effects. |
| - | * When not `blocking`, calls may return without error and `*done != TRUE`, |
| - | * while the individual filters negotiated the connection. |
| - | */ |
| - | CURLcode Curl_conn_connect(struct Curl_easy *data, int sockindex, |
| - | bool blocking, bool *done); |
| - | |
| /** | |
| * Check if a filter chain at `sockindex` for connection `conn` exists. | |
| */ | |
| @@ -431,6 +421,12 @@ unsigned char Curl_conn_get_transport(struct Curl_easy *data, | |
| const char *Curl_conn_get_alpn_negotiated(struct Curl_easy *data, | |
| struct connectdata *conn); | |
| + | void Curl_conn_cntrl_update_info(struct Curl_easy *data, |
| + | struct connectdata *conn); |
| + | |
| + | void Curl_conn_remove_setup_filters(struct Curl_easy *data, |
| + | int sockindex); |
| + | |
| /** | |
| * Shutdown the connection at `sockindex` non-blocking, using timeout | |
| * from `data->set.shutdowntimeout`, default DEFAULT_SHUTDOWN_TIMEOUT_MS. | |
| @@ -23,49 +23,20 @@ | |
| ***************************************************************************/ | |
| #include "curl_setup.h" | |
| - | #ifdef HAVE_NETINET_IN_H |
| - | #include <netinet/in.h> /* <netinet/tcp.h> may need it */ |
| - | #endif |
| - | #ifdef HAVE_LINUX_TCP_H |
| - | #include <linux/tcp.h> |
| - | #elif defined(HAVE_NETINET_TCP_H) |
| - | #include <netinet/tcp.h> |
| - | #endif |
| - | #ifdef HAVE_SYS_IOCTL_H |
| - | #include <sys/ioctl.h> |
| - | #endif |
| - | #ifdef HAVE_NETDB_H |
| - | #include <netdb.h> |
| - | #endif |
| - | #ifdef HAVE_ARPA_INET_H |
| - | #include <arpa/inet.h> |
| - | #endif |
| - | |
| - | #ifdef __VMS |
| - | #include <in.h> |
| - | #include <inet.h> |
| - | #endif |
| - | |
| #include "urldata.h" | |
| #include "curl_trc.h" | |
| #include "strerror.h" | |
| #include "cfilters.h" | |
| #include "connect.h" | |
| #include "cf-dns.h" | |
| - | #include "cf-haproxy.h" |
| #include "cf-https-connect.h" | |
| - | #include "cf-ip-happy.h" |
| - | #include "cf-socket.h" |
| + | #include "cf-setup.h" |
| #include "multiif.h" | |
| - | #include "curlx/strparse.h" |
| - | #include "vtls/vtls.h" /* for vtls cfilters */ |
| - | #include "vquic/vquic.h" /* for QUIC cfilters */ |
| - | #include "vquic/cf-capsule.h" |
| #include "progress.h" | |
| #include "conncache.h" | |
| #include "multihandle.h" | |
| - | #include "http_proxy.h" |
| - | #include "socks.h" |
| + | #include "select.h" |
| + | #include "curlx/strparse.h" |
| #if !defined(CURL_DISABLE_ALTSVC) || defined(USE_HTTPSRR) | |
| @@ -270,487 +241,216 @@ void Curl_conncontrol(struct connectdata *conn, | |
| } | |
| } | |
| - | typedef enum { |
| - | CF_SETUP_INIT, |
| - | CF_SETUP_CNNCT_EYEBALLS, |
| - | CF_SETUP_CNNCT_SOCKS, |
| - | CF_SETUP_CNNCT_HTTP_PROXY, |
| - | CF_SETUP_CNNCT_HAPROXY, |
| - | CF_SETUP_CNNCT_SSL, |
| - | CF_SETUP_DONE |
| - | } cf_setup_state; |
| - | |
| - | struct cf_setup_ctx { |
| - | cf_setup_state state; |
| - | int ssl_mode; |
| - | uint8_t transport; |
| - | uint8_t retry_count; |
| - | }; |
| - | |
| - | #ifndef CURL_DISABLE_PROXY |
| - | |
| - | static CURLcode cf_setup_add_haproxy(struct Curl_cfilter *cf, |
| - | struct Curl_easy *data) |
| + | CURLcode Curl_conn_setup(struct Curl_easy *data, |
| + | struct connectdata *conn, |
| + | int sockindex, |
| + | int ssl_mode) |
| { | |
| - | struct cf_setup_ctx *ctx = cf->ctx; |
| CURLcode result = CURLE_OK; | |
| + | struct Curl_peer *peer = Curl_conn_get_first_peer(conn, sockindex); |
| + | uint8_t dns_queries; |
| - | 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", |
| - | (int)result); |
| - | return result; |
| - | } |
| - | CURL_TRC_CF(data, cf, "added HAPROXY filter"); |
| - | } |
| - | ctx->state = CF_SETUP_CNNCT_HAPROXY; |
| - | } |
| - | return result; |
| - | } |
| - | |
| - | 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->socks_proxy.peer) { |
| - | /* Add a SOCKS proxy to go through `first_peer` to `second_peer`*/ |
| - | struct Curl_peer *second_peer; |
| - | |
| - | if(cf->conn->http_proxy.peer) |
| - | second_peer = cf->conn->http_proxy.peer; |
| - | else |
| - | 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, second_peer, cf->conn->ip_version, |
| - | cf->conn->socks_proxy.proxytype, |
| - | cf->conn->socks_proxy.creds); |
| - | if(result) { |
| - | CURL_TRC_CF(data, cf, "adding SOCKS filter failed -> %d", (int)result); |
| - | return result; |
| - | } |
| + | DEBUGASSERT(data); |
| + | DEBUGASSERT(conn->scheme); |
| + | DEBUGASSERT(!conn->cfilter[sockindex]); |
| - | CURL_TRC_CF(data, cf, "added SOCKS filter to %s:%u", |
| - | second_peer->hostname, second_peer->port); |
| - | ctx->state = CF_SETUP_CNNCT_SOCKS; |
| - | } |
| - | return result; |
| - | } |
| + | if(!peer) |
| + | return CURLE_FAILED_INIT; |
| #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->http_proxy.peer && !cf->conn->bits.origin_is_proxy) { |
| - | struct Curl_peer *peer = cf->conn->http_proxy.peer; |
| - | struct Curl_peer *tunnel_peer = |
| - | Curl_conn_get_destination(cf->conn, cf->sockindex); |
| - | |
| - | #ifdef USE_SSL |
| - | if(CURL_PROXY_IS_HTTPS(cf->conn->http_proxy.proxytype) && |
| - | !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { |
| - | 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", |
| - | (int)result); |
| - | return result; |
| - | } |
| - | CURL_TRC_CF(data, cf, "added SSL filter for HTTP proxy"); |
| - | } |
| - | #endif /* USE_SSL */ |
| - | |
| - | result = Curl_cf_http_proxy_insert_after( |
| - | 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", |
| - | (int)result); |
| - | return result; |
| - | } |
| - | CURL_TRC_CF(data, cf, "added HTTP proxy tunnel filter"); |
| - | ctx->state = CF_SETUP_CNNCT_HTTP_PROXY; |
| + | if(!conn->cfilter[sockindex] && |
| + | conn->scheme->protocol == CURLPROTO_HTTPS) { |
| + | DEBUGASSERT(ssl_mode != CURL_CF_SSL_DISABLE); |
| + | result = Curl_cf_https_setup(data, conn, sockindex); |
| + | if(result) |
| + | goto out; |
| } | |
| - | return result; |
| - | } |
| #endif /* !CURL_DISABLE_HTTP */ | |
| - | #endif /* CURL_DISABLE_PROXY */ |
| - | /* Get the origin curl connects its socket to. |
| - | * Can be origin or the first proxy. */ |
| - | static struct Curl_peer *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; |
| - | } |
| - | |
| - | 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 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 = |
| - | 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; |
| - | |
| - | if(!first_peer) |
| - | return CURLE_FAILED_INIT; |
| - | |
| - | #if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP) |
| - | if(cf->conn->http_proxy.peer && !cf->conn->bits.origin_is_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->socks_proxy.peer) { |
| - | failf(data, "HTTP/3 proxy not possible via SOCKS"); |
| - | return CURLE_UNSUPPORTED_PROTOCOL; |
| - | } |
| - | } |
| - | #endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ |
| - | |
| - | 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", (int)result); |
| - | return result; |
| - | } |
| - | |
| - | 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_peer ? "proxy" : "origin", |
| - | first_peer->hostname, first_peer->port); |
| - | ctx->state = CF_SETUP_CNNCT_EYEBALLS; |
| - | } |
| + | /* Still no cfilter set, apply default. */ |
| + | if(!conn->cfilter[sockindex]) { |
| + | result = Curl_cf_setup_add(data, conn, sockindex, |
| + | conn->transport_wanted, ssl_mode); |
| + | if(result) |
| + | goto out; |
| } | |
| - | return result; |
| - | } |
| - | |
| - | 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(CURL_DISABLE_PROXY) |
| - | |
| - | /* Wanting QUIC with an 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->http_proxy.peer && !cf->conn->bits.origin_is_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", |
| - | (int)result); |
| - | return result; |
| - | } |
| - | result = Curl_cf_quic_insert_after(cf, origin, peer); |
| - | if(result) { |
| - | CURL_TRC_CF(data, cf, "adding QUIC filter failed -> %d", (int)result); |
| - | return result; |
| - | } |
| - | CURL_TRC_CF(data, cf, "added QUIC filter for origin"); |
| - | } |
| - | 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 && |
| - | data->state.origin->scheme->flags & PROTOPT_SSL)) && /* we want SSL */ |
| - | !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { /* it is missing */ |
| - | #ifndef CURL_DISABLE_PROXY |
| - | if(cf->conn->bits.origin_is_proxy) { |
| - | result = Curl_cf_ssl_proxy_insert_after(cf, data, cf->conn->origin); |
| - | } |
| - | else |
| + | dns_queries = Curl_resolv_dns_queries(data, conn->ip_version); |
| + | #ifdef USE_HTTPSRR |
| + | if(sockindex == FIRSTSOCKET) |
| + | dns_queries |= CURL_DNSQ_HTTPS; |
| #endif | |
| - | { |
| - | /* 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", |
| - | (int)result); |
| - | return result; |
| - | } |
| - | CURL_TRC_CF(data, cf, "added SSL filter for origin"); |
| - | } |
| - | #endif /* USE_SSL */ |
| - | ctx->state = CF_SETUP_CNNCT_SSL; |
| - | } |
| + | result = Curl_cf_dns_add(data, conn, sockindex, peer, dns_queries, |
| + | conn->transport_wanted); |
| + | DEBUGASSERT(conn->cfilter[sockindex]); |
| + | out: |
| return result; | |
| } | |
| - | static CURLcode cf_setup_connect_steps(struct Curl_cfilter *cf, |
| - | struct Curl_easy *data, |
| - | bool *done) |
| + | #ifdef CURLVERBOSE |
| + | static CURLcode conn_connect_trace(struct Curl_easy *data, |
| + | struct Curl_cfilter *cf) |
| { | |
| - | struct cf_setup_ctx *ctx = cf->ctx; |
| - | CURLcode result = CURLE_OK; |
| + | if(Curl_trc_is_verbose(data)) { |
| + | struct ip_quadruple ipquad; |
| + | bool is_ipv6; |
| + | CURLcode result; |
| - | 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) |
| + | result = Curl_conn_cf_get_ip_info(cf, data, &is_ipv6, &ipquad); |
| + | if(result) |
| 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; |
| - | *done = TRUE; |
| - | return CURLE_OK; |
| - | } |
| - | |
| - | static CURLcode cf_setup_connect(struct Curl_cfilter *cf, |
| - | struct Curl_easy *data, |
| - | bool *done) |
| - | { |
| - | struct cf_setup_ctx *ctx = cf->ctx; |
| - | CURLcode result; |
| - | |
| - | /* In some situations, a server/proxy may close the connection and |
| - | * we need to connect again (HTTP/1.x proxy auth, for example). |
| - | * We used to close the filters and reuse them for another attempt, |
| - | * however that complicates filter code and it is simpler to tear them |
| - | * all down and start over. */ |
| - | retry: |
| - | result = cf_setup_connect_steps(cf, data, done); |
| - | |
| - | if(result == CURLE_AGAIN) { |
| - | ++ctx->retry_count; |
| - | if(ctx->retry_count > 5) /* arbitrary limit, better just timeout? */ |
| - | return CURLE_COULDNT_CONNECT; |
| - | |
| - | CURL_TRC_CF(data, cf, "retrying connect, %d. time", ctx->retry_count); |
| - | Curl_conn_cf_discard_chain(&cf->next, data); |
| - | ctx->state = CF_SETUP_INIT; |
| - | goto retry; |
| + | infof(data, "Established %sconnection to %s (%s port %u) from %s port %u ", |
| + | (cf->sockindex == SECONDARYSOCKET) ? "2nd " : "", |
| + | CURL_CONN_HOST_DISPNAME(data->conn), |
| + | ipquad.remote_ip, ipquad.remote_port, |
| + | ipquad.local_ip, ipquad.local_port); |
| } | |
| - | return result; |
| - | } |
| - | |
| - | static void cf_setup_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) |
| - | { |
| - | struct cf_setup_ctx *ctx = cf->ctx; |
| - | |
| - | CURL_TRC_CF(data, cf, "destroy"); |
| - | curlx_safefree(ctx); |
| + | return CURLE_OK; |
| } | |
| + | #endif |
| - | struct Curl_cftype Curl_cft_setup = { |
| - | "SETUP", |
| - | CF_TYPE_SETUP, |
| - | CURL_LOG_LVL_NONE, |
| - | cf_setup_destroy, |
| - | cf_setup_connect, |
| - | Curl_cf_def_shutdown, |
| - | Curl_cf_def_adjust_pollset, |
| - | Curl_cf_def_data_pending, |
| - | Curl_cf_def_send, |
| - | Curl_cf_def_recv, |
| - | Curl_cf_def_cntrl, |
| - | Curl_cf_def_conn_is_alive, |
| - | Curl_cf_def_conn_keep_alive, |
| - | Curl_cf_def_query, |
| - | }; |
| - | |
| - | static CURLcode cf_setup_create(struct Curl_cfilter **pcf, |
| - | struct Curl_easy *data, |
| - | uint8_t transport, |
| - | int ssl_mode) |
| + | /** |
| + | * Update connection statistics |
| + | */ |
| + | static void conn_report_connect_stats(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data) |
| { | |
| - | struct Curl_cfilter *cf = NULL; |
| - | struct cf_setup_ctx *ctx; |
| - | CURLcode result = CURLE_OK; |
| - | |
| - | (void)data; |
| - | ctx = curlx_calloc(1, sizeof(*ctx)); |
| - | if(!ctx) { |
| - | result = CURLE_OUT_OF_MEMORY; |
| - | goto out; |
| - | } |
| - | ctx->state = CF_SETUP_INIT; |
| - | ctx->ssl_mode = ssl_mode; |
| - | ctx->transport = transport; |
| - | |
| - | result = Curl_cf_create(&cf, &Curl_cft_setup, ctx); |
| - | if(result) |
| - | goto out; |
| - | ctx = NULL; |
| - | |
| - | out: |
| - | *pcf = result ? NULL : cf; |
| - | if(ctx) { |
| - | curlx_free(ctx); |
| + | if(cf) { |
| + | struct curltime connected; |
| + | struct curltime appconnected; |
| + | |
| + | memset(&connected, 0, sizeof(connected)); |
| + | cf->cft->query(cf, data, CF_QUERY_TIMER_CONNECT, NULL, &connected); |
| + | if(connected.tv_sec || connected.tv_usec) |
| + | Curl_pgrsTimeWas(data, TIMER_CONNECT, connected); |
| + | |
| + | memset(&appconnected, 0, sizeof(appconnected)); |
| + | cf->cft->query(cf, data, CF_QUERY_TIMER_APPCONNECT, NULL, &appconnected); |
| + | if(appconnected.tv_sec || appconnected.tv_usec) |
| + | Curl_pgrsTimeWas(data, TIMER_APPCONNECT, appconnected); |
| } | |
| - | return result; |
| } | |
| - | static CURLcode cf_setup_add(struct Curl_easy *data, |
| - | struct connectdata *conn, |
| - | int sockindex, |
| - | uint8_t transport, |
| - | int ssl_mode) |
| + | CURLcode Curl_conn_connect(struct Curl_easy *data, |
| + | int sockindex, |
| + | bool blocking, |
| + | bool *done) |
| { | |
| + | #define CF_CONN_NUM_POLLS_ON_STACK 5 |
| + | struct pollfd a_few_on_stack[CF_CONN_NUM_POLLS_ON_STACK]; |
| + | struct easy_pollset ps; |
| + | struct curl_pollfds cpfds; |
| struct Curl_cfilter *cf; | |
| CURLcode result = CURLE_OK; | |
| DEBUGASSERT(data); | |
| - | result = cf_setup_create(&cf, data, transport, ssl_mode); |
| - | if(result) |
| - | goto out; |
| - | Curl_conn_cf_add(data, conn, sockindex, cf); |
| - | out: |
| - | return result; |
| - | } |
| - | |
| - | CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at, |
| - | struct Curl_easy *data, |
| - | uint8_t transport, |
| - | int ssl_mode) |
| - | { |
| - | struct Curl_cfilter *cf; |
| - | CURLcode result; |
| + | DEBUGASSERT(data->conn); |
| + | if(!CONN_SOCK_IDX_VALID(sockindex)) |
| + | return CURLE_BAD_FUNCTION_ARGUMENT; |
| - | DEBUGASSERT(data); |
| - | result = cf_setup_create(&cf, data, transport, ssl_mode); |
| - | if(result) |
| - | goto out; |
| - | Curl_conn_cf_insert_after(cf_at, cf); |
| - | out: |
| - | return result; |
| - | } |
| + | if(data->conn->scheme->flags & PROTOPT_NONETWORK) { |
| + | *done = TRUE; |
| + | return CURLE_OK; |
| + | } |
| - | CURLcode Curl_conn_setup(struct Curl_easy *data, |
| - | struct connectdata *conn, |
| - | int sockindex, |
| - | int ssl_mode) |
| - | { |
| - | CURLcode result = CURLE_OK; |
| - | struct Curl_peer *peer = Curl_conn_get_first_peer(conn, sockindex); |
| - | uint8_t dns_queries; |
| + | cf = data->conn->cfilter[sockindex]; |
| + | if(!cf) { |
| + | *done = FALSE; |
| + | return CURLE_FAILED_INIT; |
| + | } |
| - | DEBUGASSERT(data); |
| - | DEBUGASSERT(conn->scheme); |
| - | DEBUGASSERT(!conn->cfilter[sockindex]); |
| + | *done = (bool)cf->connected; |
| + | if(*done) |
| + | return CURLE_OK; |
| - | if(!peer) |
| - | return CURLE_FAILED_INIT; |
| + | Curl_pollset_init(&ps); |
| + | Curl_pollfds_init(&cpfds, a_few_on_stack, CF_CONN_NUM_POLLS_ON_STACK); |
| + | while(!*done) { |
| + | if(Curl_conn_needs_flush(data, sockindex)) { |
| + | DEBUGF(infof(data, "Curl_conn_connect(index=%d), flush", sockindex)); |
| + | result = Curl_conn_flush(data, sockindex); |
| + | if(result && (result != CURLE_AGAIN)) |
| + | goto out; |
| + | } |
| - | #ifndef CURL_DISABLE_HTTP |
| - | if(!conn->cfilter[sockindex] && |
| - | conn->scheme->protocol == CURLPROTO_HTTPS) { |
| - | DEBUGASSERT(ssl_mode != CURL_CF_SSL_DISABLE); |
| - | result = Curl_cf_https_setup(data, conn, sockindex); |
| - | if(result) |
| + | result = cf->cft->do_connect(cf, data, done); |
| + | CURL_TRC_CF(data, cf, "Curl_conn_connect(block=%d) -> %d, done=%d", |
| + | blocking, (int)result, *done); |
| + | if(!result && *done) { |
| + | /* A final sanity check on connection security */ |
| + | if((data->state.origin->scheme->flags & PROTOPT_SSL) && |
| + | (sockindex == FIRSTSOCKET) && |
| + | !Curl_conn_is_ssl(data->conn, FIRSTSOCKET)) { |
| + | DEBUGASSERT(0); |
| + | failf(data, "transfer requires SSL, but not connected via SSL"); |
| + | result = CURLE_FAILED_INIT; |
| + | goto out; |
| + | } |
| + | /* Now that the complete filter chain is connected, let all filters |
| + | * persist information at the connection. E.g. cf-socket sets the |
| + | * socket and ip related information. */ |
| + | Curl_conn_cntrl_update_info(data, data->conn); |
| + | conn_report_connect_stats(cf, data); |
| + | data->conn->keepalive = *Curl_pgrs_now(data); |
| + | VERBOSE(result = conn_connect_trace(data, cf)); |
| + | VERBOSE(Curl_conn_trc_filters(data, sockindex, "connected")); |
| + | Curl_conn_remove_setup_filters(data, sockindex); |
| + | VERBOSE(Curl_conn_trc_filters(data, sockindex, "reduced to")); |
| goto out; | |
| - | } |
| - | #endif /* !CURL_DISABLE_HTTP */ |
| + | } |
| + | else if(result) { |
| + | CURL_TRC_CF(data, cf, "Curl_conn_connect(), filter returned %d", |
| + | (int)result); |
| + | VERBOSE(Curl_conn_trc_filters(data, sockindex, "failed to connect")); |
| + | conn_report_connect_stats(cf, data); |
| + | goto out; |
| + | } |
| - | /* Still no cfilter set, apply default. */ |
| - | if(!conn->cfilter[sockindex]) { |
| - | result = cf_setup_add(data, conn, sockindex, |
| - | conn->transport_wanted, ssl_mode); |
| - | if(result) |
| + | if(!blocking) |
| goto out; | |
| + | else { |
| + | /* check allowed time left */ |
| + | const timediff_t timeout_ms = Curl_timeleft_ms(data); |
| + | curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); |
| + | int rc; |
| + | |
| + | if(timeout_ms < 0) { |
| + | /* no need to continue if time already is up */ |
| + | failf(data, "connect timeout"); |
| + | result = CURLE_OPERATION_TIMEDOUT; |
| + | goto out; |
| + | } |
| + | |
| + | CURL_TRC_CF(data, cf, "Curl_conn_connect(block=1), do poll"); |
| + | Curl_pollset_reset(&ps); |
| + | Curl_pollfds_reset(&cpfds); |
| + | /* In general, we want to send after connect, wait on that. */ |
| + | if(sockfd != CURL_SOCKET_BAD) |
| + | result = Curl_pollset_set_out_only(data, &ps, sockfd); |
| + | if(!result) |
| + | result = Curl_conn_adjust_pollset(data, data->conn, &ps); |
| + | if(result) |
| + | goto out; |
| + | result = Curl_pollfds_add_ps(&cpfds, &ps); |
| + | if(result) |
| + | goto out; |
| + | |
| + | rc = Curl_poll(cpfds.pfds, cpfds.n, |
| + | CURLMIN(timeout_ms, (cpfds.n ? 1000 : 10))); |
| + | CURL_TRC_CF(data, cf, "Curl_conn_connect(block=1), Curl_poll() -> %d", |
| + | rc); |
| + | if(rc < 0) { |
| + | result = CURLE_COULDNT_CONNECT; |
| + | goto out; |
| + | } |
| + | /* continue iterating */ |
| + | } |
| } | |
| - | dns_queries = Curl_resolv_dns_queries(data, conn->ip_version); |
| - | #ifdef USE_HTTPSRR |
| - | if(sockindex == FIRSTSOCKET) |
| - | dns_queries |= CURL_DNSQ_HTTPS; |
| - | #endif |
| - | result = Curl_cf_dns_add(data, conn, sockindex, peer, dns_queries, |
| - | conn->transport_wanted); |
| - | DEBUGASSERT(conn->cfilter[sockindex]); |
| out: | |
| + | Curl_pollset_cleanup(&ps); |
| + | Curl_pollfds_cleanup(&cpfds); |
| return result; | |
| } | |
| @@ -28,8 +28,6 @@ | |
| #include "hostip.h" | |
| #include "curlx/timeval.h" | |
| - | struct Curl_dns_entry; |
| - | struct ip_quadruple; |
| struct Curl_peer; | |
| struct Curl_str; | |
| @@ -105,11 +103,6 @@ void Curl_conncontrol(struct connectdata *conn, | |
| #define connkeep(x, y) Curl_conncontrol(x, CONNCTRL_KEEP) | |
| #endif | |
| - | CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at, |
| - | struct Curl_easy *data, |
| - | uint8_t transport, |
| - | int ssl_mode); |
| - | |
| /** | |
| * Setup the cfilters at `sockindex` in connection `conn`. | |
| * If no filter chain is installed yet, inspects the configuration | |
| @@ -120,6 +113,16 @@ CURLcode Curl_conn_setup(struct Curl_easy *data, | |
| int sockindex, | |
| int ssl_mode); | |
| + | /** |
| + | * Bring the filter chain at `sockindex` for connection `data->conn` into |
| + | * connected state. Which will set `*done` to TRUE. |
| + | * This can be called on an already connected chain with no side effects. |
| + | * When not `blocking`, calls may return without error and `*done != TRUE`, |
| + | * while the individual filters negotiated the connection. |
| + | */ |
| + | CURLcode Curl_conn_connect(struct Curl_easy *data, int sockindex, |
| + | bool blocking, bool *done); |
| + | |
| /* Set conn to allow multiplexing. */ | |
| void Curl_conn_set_multiplex(struct connectdata *conn); | |
| @@ -138,6 +141,4 @@ struct Curl_peer *Curl_conn_get_destination(struct connectdata *conn, | |
| struct Curl_peer *Curl_conn_get_first_peer(struct connectdata *conn, | |
| int sockindex); | |
| - | extern struct Curl_cftype Curl_cft_setup; |
| - | |
| #endif /* HEADER_CURL_CONNECT_H */ | |
| @@ -30,7 +30,7 @@ | |
| #include "cf-dns.h" | |
| #include "cf-socket.h" | |
| - | #include "connect.h" |
| + | #include "cf-setup.h" |
| #include "http2.h" | |
| #include "http_proxy.h" | |
| #include "cf-h1-proxy.h" | |