h3: HTTPS-RR use in HTTP/3

When HTTPS-RR is needed for the HTTP/3 handshake, delay the connect
until it arrives. Relevant only for TLS backends that support ECH, for
now.

Closes #21253
This commit is contained in:
Stefan Eissing 2026-04-07 13:53:42 +02:00 committed by Daniel Stenberg
parent 3bde26dac8
commit d99df64405
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
6 changed files with 52 additions and 15 deletions

View File

@ -52,6 +52,7 @@
#include "rand.h"
#include "multiif.h"
#include "cfilters.h"
#include "cf-dns.h"
#include "cf-socket.h"
#include "connect.h"
#include "progress.h"
@ -2594,6 +2595,18 @@ static CURLcode cf_ngtcp2_on_session_reuse(struct Curl_cfilter *cf,
return result;
}
static bool cf_ngtcp2_need_httpsrr(struct Curl_easy *data)
{
#ifdef USE_OPENSSL
return Curl_ossl_need_httpsrr(data);
#elif defined(USE_WOLFSSL)
return Curl_wssl_need_httpsrr(data);
#else
(void)data;
return FALSE;
#endif
}
/*
* Might be called twice for happy eyeballs.
*/
@ -2708,8 +2721,14 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf,
}
*done = FALSE;
pktx_init(&pktx, cf, data);
if(cf_ngtcp2_need_httpsrr(data) &&
!Curl_conn_dns_resolved_https(data, cf->sockindex)) {
CURL_TRC_CF(data, cf, "need HTTPS-RR, delaying connect");
return CURLE_OK;
}
pktx_init(&pktx, cf, data);
CF_DATA_SAVE(save, cf, data);
if(!ctx->qconn) {

View File

@ -32,6 +32,7 @@
#include "uint-hash.h"
#include "urldata.h"
#include "cfilters.h"
#include "cf-dns.h"
#include "cf-socket.h"
#include "curl_trc.h"
#include "rand.h"
@ -1378,6 +1379,12 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf,
}
*done = FALSE;
if(Curl_ossl_need_httpsrr(data) &&
!Curl_conn_dns_resolved_https(data, cf->sockindex)) {
CURL_TRC_CF(data, cf, "need HTTPS-RR, delaying connect");
return CURLE_OK;
}
vquic_ctx_update_time(&ctx->q, Curl_pgrs_now(data));
if(!ctx->qconn) {

View File

@ -3424,7 +3424,7 @@ ossl_init_session_and_alpns(struct ossl_ctx *octx,
}
#ifdef HAVE_SSL_SET1_ECH_CONFIG_LIST
static bool ossl_ech_need_httpsrr(struct Curl_easy *data)
bool Curl_ossl_need_httpsrr(struct Curl_easy *data)
{
if(!CURLECH_ENABLED(data))
return FALSE;
@ -3506,7 +3506,7 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx,
const unsigned char *ecl = rinfo->echconfiglist;
size_t elen = rinfo->echconfiglist_len;
infof(data, "ECH: ECHConfig from DoH HTTPS RR");
infof(data, "ECH: ECHConfig from HTTPS RR");
if(SSL_set1_ech_config_list(octx->ssl, ecl, elen) != 1) {
infof(data, "ECH: SSL_set1_ech_config_list failed");
if(data->set.tls_ech & CURLECH_HARD)
@ -3550,7 +3550,13 @@ static CURLcode ossl_init_ech(struct ossl_ctx *octx,
return CURLE_OK;
}
#endif /* HAVE_SSL_SET1_ECH_CONFIG_LIST */
#else /* HAVE_SSL_SET1_ECH_CONFIG_LIST */
bool Curl_ossl_need_httpsrr(struct Curl_easy *data)
{
(void)data;
return FALSE;
}
#endif /* else HAVE_SSL_SET1_ECH_CONFIG_LIST */
static CURLcode ossl_init_ssl(struct ossl_ctx *octx,
struct Curl_cfilter *cf,
@ -4943,15 +4949,11 @@ static CURLcode ossl_connect(struct Curl_cfilter *cf,
connssl->io_need = CURL_SSL_IO_NEED_NONE;
if(ssl_connect_1 == connssl->connecting_state) {
#ifdef HAVE_SSL_SET1_ECH_CONFIG_LIST
/* if we do ECH and need the HTTPS-RR information for it,
* we delay the connect until it arrives or DNS resolve fails. */
if(ossl_ech_need_httpsrr(data) &&
if(Curl_ossl_need_httpsrr(data) &&
!Curl_conn_dns_resolved_https(data, cf->sockindex)) {
CURL_TRC_CF(data, cf, "need HTTPS-RR for ECH, delaying connect");
CURL_TRC_CF(data, cf, "need HTTPS-RR, delaying connect");
return CURLE_OK;
}
#endif
CURL_TRC_CF(data, cf, "ossl_connect, step1");
result = ossl_connect_step1(cf, data);
if(result)

View File

@ -147,6 +147,9 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
void *ssl_user_data,
Curl_ossl_init_session_reuse_cb *sess_reuse_cb);
/* Is a resolved HTTPS-RR needed for initializing OpenSSL? */
bool Curl_ossl_need_httpsrr(struct Curl_easy *data);
#ifndef HAVE_OPENSSL3
#define SSL_get1_peer_certificate SSL_get_peer_certificate
#endif

View File

@ -1297,7 +1297,7 @@ static CURLcode wssl_init_ech(struct wssl_ctx *wctx,
const unsigned char *ecl = rinfo->echconfiglist;
size_t elen = rinfo->echconfiglist_len;
infof(data, "ECH: ECHConfig from DoH HTTPS RR");
infof(data, "ECH: ECHConfig from HTTPS RR");
if(wolfSSL_SetEchConfigs(wctx->ssl, ecl, (word32)elen) !=
WOLFSSL_SUCCESS) {
infof(data, "ECH: wolfSSL_SetEchConfigs failed");
@ -1487,17 +1487,20 @@ out:
return result;
}
#ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG
static bool wssl_ech_need_httpsrr(struct Curl_easy *data)
bool Curl_wssl_need_httpsrr(struct Curl_easy *data)
{
#ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG
if(!CURLECH_ENABLED(data))
return FALSE;
if((data->set.tls_ech & CURLECH_GREASE) ||
(data->set.tls_ech & CURLECH_CLA_CFG))
return FALSE;
return TRUE;
}
#else
(void)data;
return FALSE;
#endif
}
/*
* This function loads all the client/CA certificates and CRLs. Setup the TLS
@ -2171,7 +2174,7 @@ static CURLcode wssl_connect(struct Curl_cfilter *cf,
#ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG
/* if we do ECH and need the HTTPS-RR information for it,
* we delay the connect until it arrives or DNS resolve fails. */
if(wssl_ech_need_httpsrr(data) &&
if(Curl_wssl_need_httpsrr(data) &&
!Curl_conn_dns_resolved_https(data, cf->sockindex)) {
CURL_TRC_CF(data, cf, "need HTTPS-RR for ECH, delaying connect");
return CURLE_OK;

View File

@ -71,6 +71,9 @@ CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx,
void *ssl_user_data,
Curl_wssl_init_session_reuse_cb *sess_reuse_cb);
/* Is a resolved HTTPS-RR needed for initializing wolfSSL? */
bool Curl_wssl_need_httpsrr(struct Curl_easy *data);
CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct wssl_ctx *wssl);