From: Stefan Eissing Date: Mon, 14 Jul 2025 09:41:59 +0000 (+0200) Subject: connection: clarify `transport` X-Git-Tag: curl-8_15_0~18 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=e9ae1bd404e9ec095ebebb9a89d62566555c36b4;p=thirdparty%2Fcurl.git connection: clarify `transport` The `transport` to use for a transfer, e.g. TCP/QUIC/UNIX/UDP, is initially selected by options and protocol used. This is set at the `struct connectdata` as `transport` member. During connection establishment, this transport may change due to Alt-Svc or Happy-Eyeballing. Most common is the switch from TCP to QUIC. Rename the connection member to `transport_wanted` and add a way to query the connection for the transport in use via a new connection filter query. The filter query can also be used in the happy eyeballing attempts when code needs to know which transport is used by the "filter below". This happens in wolfssl initialization, as one example. Closes #17923 --- diff --git a/lib/asyn-ares.c b/lib/asyn-ares.c index 4ea2e0724c..007f1d7123 100644 --- a/lib/asyn-ares.c +++ b/lib/asyn-ares.c @@ -48,6 +48,7 @@ #endif #include "urldata.h" +#include "cfilters.h" #include "sendf.h" #include "hostip.h" #include "hash.h" @@ -757,7 +758,8 @@ struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data, (pf == PF_UNSPEC) ? "A+AAAA" : ((pf == PF_INET) ? "A" : "AAAA")); hints.ai_family = pf; - hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP) ? + hints.ai_socktype = + (Curl_conn_get_transport(data, data->conn) == TRNSPRT_TCP) ? SOCK_STREAM : SOCK_DGRAM; /* Since the service is a numerical one, set the hint flags * accordingly to save a call to getservbyname in inside C-Ares diff --git a/lib/asyn-thrdd.c b/lib/asyn-thrdd.c index 454e31b8b8..1ede868820 100644 --- a/lib/asyn-thrdd.c +++ b/lib/asyn-thrdd.c @@ -55,6 +55,7 @@ #endif #include "urldata.h" +#include "cfilters.h" #include "sendf.h" #include "hostip.h" #include "hash.h" @@ -741,7 +742,8 @@ struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data, memset(&hints, 0, sizeof(hints)); hints.ai_family = pf; - hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP) ? + hints.ai_socktype = + (Curl_conn_get_transport(data, data->conn) == TRNSPRT_TCP) ? SOCK_STREAM : SOCK_DGRAM; /* fire up a new resolver thread! */ diff --git a/lib/cf-https-connect.c b/lib/cf-https-connect.c index 474769d1cb..b89ed80e55 100644 --- a/lib/cf-https-connect.c +++ b/lib/cf-https-connect.c @@ -55,6 +55,7 @@ struct cf_hc_baller { CURLcode result; struct curltime started; int reply_ms; + unsigned char transport; enum alpnid alpn_id; BIT(shutdown); }; @@ -122,12 +123,15 @@ struct cf_hc_ctx { }; static void cf_hc_baller_assign(struct cf_hc_baller *b, - enum alpnid alpn_id) + enum alpnid alpn_id, + unsigned char def_transport) { b->alpn_id = alpn_id; + b->transport = def_transport; switch(b->alpn_id) { case ALPN_h3: b->name = "h3"; + b->transport = TRNSPRT_QUIC; break; case ALPN_h2: b->name = "h2"; @@ -218,6 +222,7 @@ static CURLcode baller_connected(struct Curl_cfilter *cf, winner->name, (int)curlx_timediff(curlx_now(), winner->started)); + /* install the winning filter below this one. */ cf->next = winner->cf; winner->cf = NULL; @@ -312,7 +317,7 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf, DEBUGASSERT(!ctx->ballers[i].cf); CURL_TRC_CF(data, cf, "connect, init"); ctx->started = now; - cf_hc_baller_init(&ctx->ballers[0], cf, data, cf->conn->transport); + cf_hc_baller_init(&ctx->ballers[0], cf, data, ctx->ballers[0].transport); if(ctx->baller_count > 1) { Curl_expire(data, ctx->soft_eyeballs_timeout_ms, EXPIRE_ALPN_EYEBALLS); CURL_TRC_CF(data, cf, "set next attempt to start in %" FMT_TIMEDIFF_T @@ -331,7 +336,7 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf, } if(time_to_start_next(cf, data, 1, now)) { - cf_hc_baller_init(&ctx->ballers[1], cf, data, cf->conn->transport); + cf_hc_baller_init(&ctx->ballers[1], cf, data, ctx->ballers[1].transport); } if((ctx->baller_count > 1) && cf_hc_baller_is_active(&ctx->ballers[1])) { @@ -574,7 +579,8 @@ struct Curl_cftype Curl_cft_http_connect = { static CURLcode cf_hc_create(struct Curl_cfilter **pcf, struct Curl_easy *data, - enum alpnid *alpnids, size_t alpn_count) + enum alpnid *alpnids, size_t alpn_count, + unsigned char def_transport) { struct Curl_cfilter *cf = NULL; struct cf_hc_ctx *ctx; @@ -596,7 +602,7 @@ static CURLcode cf_hc_create(struct Curl_cfilter **pcf, goto out; } for(i = 0; i < alpn_count; ++i) - cf_hc_baller_assign(&ctx->ballers[i], alpnids[i]); + cf_hc_baller_assign(&ctx->ballers[i], alpnids[i], def_transport); for(; i < CURL_ARRAYSIZE(ctx->ballers); ++i) ctx->ballers[i].alpn_id = ALPN_none; ctx->baller_count = alpn_count; @@ -616,13 +622,14 @@ out: static CURLcode cf_http_connect_add(struct Curl_easy *data, struct connectdata *conn, int sockindex, - enum alpnid *alpn_ids, size_t alpn_count) + enum alpnid *alpn_ids, size_t alpn_count, + unsigned char def_transport) { struct Curl_cfilter *cf; CURLcode result = CURLE_OK; DEBUGASSERT(data); - result = cf_hc_create(&cf, data, alpn_ids, alpn_count); + result = cf_hc_create(&cf, data, alpn_ids, alpn_count, def_transport); if(result) goto out; Curl_conn_cf_add(data, conn, sockindex, cf); @@ -679,7 +686,7 @@ CURLcode Curl_cf_https_setup(struct Curl_easy *data, continue; switch(alpn) { case ALPN_h3: - if(Curl_conn_may_http3(data, conn)) + if(Curl_conn_may_http3(data, conn, conn->transport_wanted)) break; /* not possible */ if(data->state.http_neg.allowed & CURL_HTTP_V3x) { CURL_TRC_CF(data, cf, "adding h3 via HTTPS-RR"); @@ -708,7 +715,7 @@ CURLcode Curl_cf_https_setup(struct Curl_easy *data, if((alpn_count < CURL_ARRAYSIZE(alpn_ids)) && (data->state.http_neg.wanted & CURL_HTTP_V3x) && !cf_https_alpns_contain(ALPN_h3, alpn_ids, alpn_count)) { - result = Curl_conn_may_http3(data, conn); + result = Curl_conn_may_http3(data, conn, conn->transport_wanted); if(!result) { CURL_TRC_CF(data, cf, "adding wanted h3"); alpn_ids[alpn_count++] = ALPN_h3; @@ -733,7 +740,9 @@ CURLcode Curl_cf_https_setup(struct Curl_easy *data, /* If we identified ALPNs to use, install our filter. Otherwise, * install nothing, so our call will use a default connect setup. */ if(alpn_count) { - result = cf_http_connect_add(data, conn, sockindex, alpn_ids, alpn_count); + result = cf_http_connect_add(data, conn, sockindex, + alpn_ids, alpn_count, + conn->transport_wanted); } out: diff --git a/lib/cf-socket.c b/lib/cf-socket.c index 2a7311eb3b..273258feaf 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -1683,6 +1683,10 @@ static CURLcode cf_socket_query(struct Curl_cfilter *cf, DEBUGASSERT(pres2); *((curl_socket_t *)pres2) = ctx->sock; return CURLE_OK; + case CF_QUERY_TRANSPORT: + DEBUGASSERT(pres1); + *pres1 = ctx->transport; + return CURLE_OK; case CF_QUERY_REMOTE_ADDR: DEBUGASSERT(pres2); *((const struct Curl_sockaddr_ex **)pres2) = cf->connected ? @@ -2193,7 +2197,7 @@ CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data, result = CURLE_OUT_OF_MEMORY; goto out; } - ctx->transport = conn->transport; + ctx->transport = TRNSPRT_TCP; ctx->sock = *s; ctx->listening = TRUE; ctx->accepted = FALSE; diff --git a/lib/cfilters.c b/lib/cfilters.c index e3247f72c9..01f1e28218 100644 --- a/lib/cfilters.c +++ b/lib/cfilters.c @@ -606,6 +606,13 @@ bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex) return FALSE; } +unsigned char Curl_conn_get_transport(struct Curl_easy *data, + struct connectdata *conn) +{ + struct Curl_cfilter *cf = conn->cfilter[FIRSTSOCKET]; + return Curl_conn_cf_get_transport(cf, data); +} + unsigned char Curl_conn_http_version(struct Curl_easy *data, struct connectdata *conn) { @@ -794,6 +801,15 @@ curl_socket_t Curl_conn_cf_get_socket(struct Curl_cfilter *cf, return CURL_SOCKET_BAD; } +unsigned char Curl_conn_cf_get_transport(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + int transport = 0; + if(cf && !cf->cft->query(cf, data, CF_QUERY_TRANSPORT, &transport, NULL)) + return (unsigned char)transport; + return (unsigned char)(data->conn ? data->conn->transport_wanted : 0); +} + static const struct Curl_sockaddr_ex * cf_get_remote_addr(struct Curl_cfilter *cf, struct Curl_easy *data) { diff --git a/lib/cfilters.h b/lib/cfilters.h index b28ca998e0..39d906d673 100644 --- a/lib/cfilters.h +++ b/lib/cfilters.h @@ -175,6 +175,7 @@ typedef CURLcode Curl_cft_cntrl(struct Curl_cfilter *cf, #define CF_QUERY_HOST_PORT 11 /* port const char * */ #define CF_QUERY_SSL_INFO 12 /* - struct curl_tlssessioninfo * */ #define CF_QUERY_SSL_CTX_INFO 13 /* - struct curl_tlssessioninfo * */ +#define CF_QUERY_TRANSPORT 14 /* TRNSPRT_* - * */ /** * Query the cfilter for properties. Filters ignorant of a query will @@ -350,6 +351,9 @@ CURLcode Curl_conn_cf_get_ip_info(struct Curl_cfilter *cf, bool Curl_conn_cf_needs_flush(struct Curl_cfilter *cf, struct Curl_easy *data); +unsigned char Curl_conn_cf_get_transport(struct Curl_cfilter *cf, + struct Curl_easy *data); + #define CURL_CF_SSL_DEFAULT -1 #define CURL_CF_SSL_DISABLE 0 #define CURL_CF_SSL_ENABLE 1 @@ -410,6 +414,10 @@ bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex); unsigned char Curl_conn_http_version(struct Curl_easy *data, struct connectdata *conn); +/* Get the TRNSPRT_* the connection is using */ +unsigned char Curl_conn_get_transport(struct Curl_easy *data, + struct connectdata *conn); + /** * Close the filter chain at `sockindex` for connection `data->conn`. * Filters remain in place and may be connected again afterwards. diff --git a/lib/connect.c b/lib/connect.c index c894fe62b0..5d9486394a 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -1515,7 +1515,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, ssl_mode); + result = cf_setup_add(data, conn, sockindex, + conn->transport_wanted, ssl_mode); if(result) goto out; } diff --git a/lib/ftp.c b/lib/ftp.c index 5e505887b1..06c486bc91 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -1065,7 +1065,8 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, /* step 2, create a socket for the requested address */ error = 0; for(ai = res; ai; ai = ai->ai_next) { - if(Curl_socket_open(data, ai, NULL, conn->transport, &portsock)) { + if(Curl_socket_open(data, ai, NULL, + Curl_conn_get_transport(data, conn), &portsock)) { error = SOCKERRNO; continue; } diff --git a/lib/hostip6.c b/lib/hostip6.c index 35cc2d737b..ce7f5050ea 100644 --- a/lib/hostip6.c +++ b/lib/hostip6.c @@ -44,6 +44,7 @@ #endif #include "urldata.h" +#include "cfilters.h" #include "sendf.h" #include "hostip.h" #include "hash.h" @@ -104,7 +105,8 @@ struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data, memset(&hints, 0, sizeof(hints)); hints.ai_family = pf; - hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP) ? + hints.ai_socktype = + (Curl_conn_get_transport(data, data->conn) == TRNSPRT_TCP) ? SOCK_STREAM : SOCK_DGRAM; #ifndef USE_RESOLVE_ON_IPS diff --git a/lib/http.c b/lib/http.c index 32e54c2683..e5a0696274 100644 --- a/lib/http.c +++ b/lib/http.c @@ -232,7 +232,7 @@ CURLcode Curl_http_setup_conn(struct Curl_easy *data, connkeep(conn, "HTTP default"); if(data->state.http_neg.wanted == CURL_HTTP_V3x) { /* only HTTP/3, needs to work */ - CURLcode result = Curl_conn_may_http3(data, conn); + CURLcode result = Curl_conn_may_http3(data, conn, conn->transport_wanted); if(result) return result; } diff --git a/lib/tftp.c b/lib/tftp.c index 5ed7258d38..5ea986dbb4 100644 --- a/lib/tftp.c +++ b/lib/tftp.c @@ -1369,7 +1369,7 @@ static CURLcode tftp_setup_connection(struct Curl_easy *data, { char *type; - conn->transport = TRNSPRT_UDP; + conn->transport_wanted = TRNSPRT_UDP; /* TFTP URLs support an extension like ";mode=" that * we will try to get now! */ diff --git a/lib/url.c b/lib/url.c index 1f73c1e3ac..e4f250f54a 100644 --- a/lib/url.c +++ b/lib/url.c @@ -1464,7 +1464,7 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) #endif conn->ip_version = data->set.ipver; conn->connect_only = data->set.connect_only; - conn->transport = TRNSPRT_TCP; /* most of them are TCP streams */ + conn->transport_wanted = TRNSPRT_TCP; /* most of them are TCP streams */ /* Initialize the attached xfers bitset */ Curl_uint_spbset_init(&conn->xfers_attached); @@ -3235,7 +3235,7 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, neg->wanted = neg->allowed = CURL_HTTP_V2x; break; case ALPN_h3: - conn->transport = TRNSPRT_QUIC; + conn->transport_wanted = TRNSPRT_QUIC; neg->wanted = neg->allowed = CURL_HTTP_V3x; break; default: /* should not be possible */ @@ -3312,7 +3312,7 @@ static CURLcode resolve_server(struct Curl_easy *data, if(unix_path) { /* This only works if previous transport is TRNSPRT_TCP. Check it? */ - conn->transport = TRNSPRT_UNIX; + conn->transport_wanted = TRNSPRT_UNIX; return resolve_unix(data, conn, unix_path, pdns); } } diff --git a/lib/urldata.h b/lib/urldata.h index 63bee65dc9..e54721dd98 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -785,7 +785,10 @@ struct connectdata { #ifndef CURL_DISABLE_PROXY unsigned char proxy_alpn; /* APLN of proxy tunnel, CURL_HTTP_VERSION* */ #endif - unsigned char transport; /* one of the TRNSPRT_* defines */ + unsigned char transport_wanted; /* one of the TRNSPRT_* defines. Not + necessarily the transport the connection ends using due to Alt-Svc + and happy eyeballing. Use `Curl_conn_get_transport() for actual value + once the connection is set up. */ unsigned char ip_version; /* copied from the Curl_easy at creation time */ /* HTTP version last responded with by the server. * 0 at start, then one of 09, 10, 11, etc. */ diff --git a/lib/vquic/vquic.c b/lib/vquic/vquic.c index 1eb67f516f..691c0d3fa7 100644 --- a/lib/vquic/vquic.c +++ b/lib/vquic/vquic.c @@ -703,9 +703,10 @@ CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf, } CURLcode Curl_conn_may_http3(struct Curl_easy *data, - const struct connectdata *conn) + const struct connectdata *conn, + unsigned char transport) { - if(conn->transport == TRNSPRT_UNIX) { + if(transport == TRNSPRT_UNIX) { /* cannot do QUIC over a Unix domain socket */ return CURLE_QUIC_CONNECT_ERROR; } @@ -730,10 +731,12 @@ CURLcode Curl_conn_may_http3(struct Curl_easy *data, #else /* USE_HTTP3 */ CURLcode Curl_conn_may_http3(struct Curl_easy *data, - const struct connectdata *conn) + const struct connectdata *conn, + unsigned char transport) { (void)conn; (void)data; + (void)transport; DEBUGF(infof(data, "QUIC is not supported in this build")); return CURLE_NOT_BUILT_IN; } diff --git a/lib/vquic/vquic.h b/lib/vquic/vquic.h index dbf63b1f6f..3577a857e3 100644 --- a/lib/vquic/vquic.h +++ b/lib/vquic/vquic.h @@ -54,6 +54,7 @@ extern struct Curl_cftype Curl_cft_http3; #endif /* !USE_HTTP3 */ CURLcode Curl_conn_may_http3(struct Curl_easy *data, - const struct connectdata *conn); + const struct connectdata *conn, + unsigned char transport); #endif /* HEADER_CURL_VQUIC_QUIC_H */ diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c index 8005fa3f59..c3560e8b8d 100644 --- a/lib/vtls/wolfssl.c +++ b/lib/vtls/wolfssl.c @@ -964,6 +964,7 @@ CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx, size_t idx = 0; #endif CURLcode result = CURLE_FAILED_INIT; + unsigned char transport; DEBUGASSERT(!wctx->ssl_ctx); DEBUGASSERT(!wctx->ssl); @@ -973,6 +974,8 @@ CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx, goto out; } Curl_alpn_copy(&alpns, alpns_requested); + DEBUGASSERT(cf->next); + transport = Curl_conn_cf_get_transport(cf->next, data); #if LIBWOLFSSL_VERSION_HEX < 0x04002000 /* 4.2.0 (2019) */ req_method = wolfSSLv23_client_method(); @@ -1103,7 +1106,7 @@ CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx, #endif curves = conn_config->curves; - if(!curves && cf->conn->transport == TRNSPRT_QUIC) + if(!curves && (transport == TRNSPRT_QUIC)) curves = (char *)CURL_UNCONST(QUIC_GROUPS); if(curves) { @@ -1244,8 +1247,7 @@ CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx, } #endif - if(ssl_config->primary.cache_session && - cf->conn->transport != TRNSPRT_QUIC) { + if(ssl_config->primary.cache_session && (transport != TRNSPRT_QUIC)) { /* Register to get notified when a new session is received */ wolfSSL_CTX_sess_set_new_cb(wctx->ssl_ctx, wssl_vtls_new_session_cb); } @@ -1291,7 +1293,7 @@ CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx, wolfSSL_set_app_data(wctx->ssl, ssl_user_data); #ifdef WOLFSSL_QUIC - if(cf->conn->transport == TRNSPRT_QUIC) + if(transport == TRNSPRT_QUIC) wolfSSL_set_quic_use_legacy_codepoint(wctx->ssl, 0); #endif