← Commit list

f497b25672

Stefan Eissing
Stefan Eissing
2026-06-15
SUCCESS Duration: 179s

Subject

cf-setup: own source file

Binary

No binary (build failed)

Architecture & Compiler Flags

Architecture: x86_64-linux-gnu Compiler: gcc (Ubuntu 11.4.0-1ubuntu1~22.04.3) 11.4.0

—

Build Log

(log yok)

Commit Diff GitHub →

lib/Makefile.inc+2 -0
@@ -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 \
lib/cf-https-connect.c+1 -0
@@ -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"
lib/cf-setup.c+476 -0
@@ -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;
+}
lib/cf-setup.h+46 -0
@@ -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 */
lib/cfilters.c+4 -170
@@ -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))
lib/cfilters.h+6 -10
@@ -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.
lib/connect.c+171 -471
@@ -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;
}
lib/connect.h+10 -9
@@ -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 */
lib/curl_trc.c+1 -1
@@ -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"