From f497b25672227df564e523097845f7169724520e Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Mon, 15 Jun 2026 13:48:24 +0200 Subject: [PATCH] cf-setup: own source file Move the setup filter into its own source file. Move the main connect driving function, `Curl_conn_connect()`, from cfilters.c to connect.c. Closes #22022 --- lib/Makefile.inc | 2 + lib/cf-https-connect.c | 1 + lib/cf-setup.c | 476 ++++++++++++++++++++++++++++++ lib/cf-setup.h | 46 +++ lib/cfilters.c | 174 +---------- lib/cfilters.h | 16 +- lib/connect.c | 644 +++++++++++------------------------------ lib/connect.h | 19 +- lib/curl_trc.c | 2 +- 9 files changed, 718 insertions(+), 662 deletions(-) create mode 100644 lib/cf-setup.c create mode 100644 lib/cf-setup.h diff --git a/lib/Makefile.inc b/lib/Makefile.inc index 90e33e044b..061a317b8d 100644 --- a/lib/Makefile.inc +++ b/lib/Makefile.inc @@ -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 \ diff --git a/lib/cf-https-connect.c b/lib/cf-https-connect.c index 2ee96c86b1..3c326e95ef 100644 --- a/lib/cf-https-connect.c +++ b/lib/cf-https-connect.c @@ -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" diff --git a/lib/cf-setup.c b/lib/cf-setup.c new file mode 100644 index 0000000000..119294a89a --- /dev/null +++ b/lib/cf-setup.c @@ -0,0 +1,476 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , 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; +} diff --git a/lib/cf-setup.h b/lib/cf-setup.h new file mode 100644 index 0000000000..80e20064b8 --- /dev/null +++ b/lib/cf-setup.h @@ -0,0 +1,46 @@ +#ifndef HEADER_CURL_CF_SETUP_H +#define HEADER_CURL_CF_SETUP_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , 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 */ diff --git a/lib/cfilters.c b/lib/cfilters.c index 7205093201..b6278eff12 100644 --- a/lib/cfilters.c +++ b/lib/cfilters.c @@ -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)) diff --git a/lib/cfilters.h b/lib/cfilters.h index f3e19087b5..a9e00d4899 100644 --- a/lib/cfilters.h +++ b/lib/cfilters.h @@ -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. diff --git a/lib/connect.c b/lib/connect.c index 8466c81092..a2b86c7458 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -23,49 +23,20 @@ ***************************************************************************/ #include "curl_setup.h" -#ifdef HAVE_NETINET_IN_H -#include /* may need it */ -#endif -#ifdef HAVE_LINUX_TCP_H -#include -#elif defined(HAVE_NETINET_TCP_H) -#include -#endif -#ifdef HAVE_SYS_IOCTL_H -#include -#endif -#ifdef HAVE_NETDB_H -#include -#endif -#ifdef HAVE_ARPA_INET_H -#include -#endif - -#ifdef __VMS -#include -#include -#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,444 +241,6 @@ 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) -{ - 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 && - 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 -#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; -} - -static CURLcode 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; -} - CURLcode Curl_conn_setup(struct Curl_easy *data, struct connectdata *conn, int sockindex, @@ -736,8 +269,8 @@ CURLcode Curl_conn_setup(struct Curl_easy *data, /* Still no cfilter set, apply default. */ if(!conn->cfilter[sockindex]) { - result = cf_setup_add(data, conn, sockindex, - conn->transport_wanted, ssl_mode); + result = Curl_cf_setup_add(data, conn, sockindex, + conn->transport_wanted, ssl_mode); if(result) goto out; } @@ -754,6 +287,173 @@ out: return result; } +#ifdef CURLVERBOSE +static CURLcode conn_connect_trace(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 + +/** + * 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); + } +} + +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)) + goto out; + } + + 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; + } + 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; +} + void Curl_conn_set_multiplex(struct connectdata *conn) { if(!conn->bits.multiplex) { diff --git a/lib/connect.h b/lib/connect.h index 9ae8aec59f..95bde1eb4c 100644 --- a/lib/connect.h +++ b/lib/connect.h @@ -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 */ diff --git a/lib/curl_trc.c b/lib/curl_trc.c index a30c87ad02..127bc8c5cc 100644 --- a/lib/curl_trc.c +++ b/lib/curl_trc.c @@ -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"