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
This commit is contained in:
Stefan Eissing 2025-07-14 11:41:59 +02:00 committed by Daniel Stenberg
parent 674ad27f77
commit e9ae1bd404
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
16 changed files with 84 additions and 30 deletions

View File

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

View File

@ -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! */

View File

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

View File

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

View File

@ -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)
{

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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=<typecode>" that
* we will try to get now! */

View File

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

View File

@ -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. */

View File

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

View File

@ -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 */

View File

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