]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
tls: use shared init code for TCP+QUIC
authorStefan Eissing <stefan@eissing.org>
Fri, 22 Mar 2024 12:07:25 +0000 (13:07 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Tue, 9 Apr 2024 07:08:05 +0000 (09:08 +0200)
Closes #13172

27 files changed:
lib/cf-h1-proxy.c
lib/cf-h2-proxy.c
lib/cf-haproxy.c
lib/cfilters.h
lib/http_proxy.c
lib/socks.c
lib/urldata.h
lib/vquic/curl_ngtcp2.c
lib/vquic/curl_osslq.c
lib/vquic/curl_quiche.c
lib/vquic/vquic-tls.c
lib/vquic/vquic-tls.h
lib/vtls/bearssl.c
lib/vtls/gtls.c
lib/vtls/gtls.h
lib/vtls/keylog.c
lib/vtls/mbedtls.c
lib/vtls/openssl.c
lib/vtls/openssl.h
lib/vtls/schannel.c
lib/vtls/sectransp.c
lib/vtls/vtls.c
lib/vtls/vtls.h
lib/vtls/vtls_int.h
lib/vtls/wolfssl.c
tests/http/clients/tls-session-reuse.c
tests/http/test_02_download.py

index ed6322c10d031f77156676dcd51190ad027460f4..689db978918550ad0a6257203378590d2aaa15eb 100644 (file)
@@ -1068,7 +1068,7 @@ static void cf_h1_proxy_close(struct Curl_cfilter *cf,
 
 struct Curl_cftype Curl_cft_h1_proxy = {
   "H1-PROXY",
-  CF_TYPE_IP_CONNECT,
+  CF_TYPE_IP_CONNECT|CF_TYPE_PROXY,
   0,
   cf_h1_proxy_destroy,
   cf_h1_proxy_connect,
index 78dc222fa8bf2f514148f97c541de2b793129635..0bff15f38cc2f8d39ba5fe9f5e07a3c74d377017 100644 (file)
@@ -1532,7 +1532,7 @@ static bool cf_h2_proxy_is_alive(struct Curl_cfilter *cf,
 
 struct Curl_cftype Curl_cft_h2_proxy = {
   "H2-PROXY",
-  CF_TYPE_IP_CONNECT,
+  CF_TYPE_IP_CONNECT|CF_TYPE_PROXY,
   CURL_LOG_LVL_NONE,
   cf_h2_proxy_destroy,
   cf_h2_proxy_connect,
index 404392291cc12529530694850ff40c5d66da5878..2abc4d754ea37266c8644e43ad129181b0e24c0c 100644 (file)
@@ -189,7 +189,7 @@ static void cf_haproxy_adjust_pollset(struct Curl_cfilter *cf,
 
 struct Curl_cftype Curl_cft_haproxy = {
   "HAPROXY",
-  0,
+  CF_TYPE_PROXY,
   0,
   cf_haproxy_destroy,
   cf_haproxy_connect,
index c90b1f4bc2bdb3c0f1ca2a794eeb34399ab89b23..2d6c7b6afb81068e142ec435a349c14d5c70bbe2 100644 (file)
@@ -178,10 +178,12 @@ typedef CURLcode Curl_cft_query(struct Curl_cfilter *cf,
  *                     connection, etc.
  * CF_TYPE_SSL:        provide SSL/TLS
  * CF_TYPE_MULTIPLEX:  provides multiplexing of easy handles
+ * CF_TYPE_PROXY       provides proxying
  */
 #define CF_TYPE_IP_CONNECT  (1 << 0)
 #define CF_TYPE_SSL         (1 << 1)
 #define CF_TYPE_MULTIPLEX   (1 << 2)
+#define CF_TYPE_PROXY       (1 << 3)
 
 /* A connection filter type, e.g. specific implementation. */
 struct Curl_cftype {
index 113c43a4135252e209867e2cb70700d4105747eb..7c035d4b60e701ae8a6ad6d3fa709f4652c5072f 100644 (file)
@@ -293,7 +293,7 @@ static void http_proxy_cf_close(struct Curl_cfilter *cf,
 
 struct Curl_cftype Curl_cft_http_proxy = {
   "HTTP-PROXY",
-  CF_TYPE_IP_CONNECT,
+  CF_TYPE_IP_CONNECT|CF_TYPE_PROXY,
   0,
   http_proxy_cf_destroy,
   http_proxy_cf_connect,
index bd5962a8701877d1885e7ee1f74406626583005a..e6c6a6480fdebce1a79c4b80d1bea330935dbeeb 100644 (file)
@@ -1244,7 +1244,7 @@ static void socks_cf_get_host(struct Curl_cfilter *cf,
 
 struct Curl_cftype Curl_cft_socks_proxy = {
   "SOCKS-PROXYY",
-  CF_TYPE_IP_CONNECT,
+  CF_TYPE_IP_CONNECT|CF_TYPE_PROXY,
   0,
   socks_proxy_cf_destroy,
   socks_proxy_cf_connect,
index 95a3add703e07aa8b7a17856e969447551a7789a..90f4d1bb574cc2119bae3504a1e092199f0f81a3 100644 (file)
@@ -279,6 +279,8 @@ struct ssl_peer {
   char *dispname;        /* display version of hostname */
   char *sni;             /* SNI version of hostname or NULL if not usable */
   ssl_peer_type type;    /* type of the peer information */
+  int port;              /* port we are talking to */
+  int transport;         /* TCP or QUIC */
 };
 
 struct ssl_primary_config {
@@ -344,6 +346,7 @@ struct Curl_ssl_session {
   long age;         /* just a number, the higher the more recent */
   int remote_port;  /* remote port */
   int conn_to_port; /* remote port for the connection (may be -1) */
+  int transport;    /* TCP or QUIC */
   struct ssl_primary_config ssl_config; /* setup for this session */
 };
 
index d2e0a12658284513fe151452d0cc1f8f308c5b70..ed53f88a2766bd0a04c501dbf11ea7892897a67b 100644 (file)
@@ -113,7 +113,7 @@ void Curl_ngtcp2_ver(char *p, size_t len)
 struct cf_ngtcp2_ctx {
   struct cf_quic_ctx q;
   struct ssl_peer peer;
-  struct quic_tls_ctx tls;
+  struct curl_tls_ctx tls;
   ngtcp2_path connected_path;
   ngtcp2_conn *qconn;
   ngtcp2_cid dcid;
@@ -1909,25 +1909,60 @@ static void cf_ngtcp2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
   (void)save;
 }
 
-static CURLcode tls_ctx_setup(struct quic_tls_ctx *ctx,
-                              struct Curl_cfilter *cf,
-                              struct Curl_easy *data)
+#ifdef USE_OPENSSL
+/* The "new session" callback must return zero if the session can be removed
+ * or non-zero if the session has been put into the session cache.
+ */
+static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
 {
+  struct Curl_cfilter *cf;
+  struct cf_ngtcp2_ctx *ctx;
+  struct Curl_easy *data;
+  ngtcp2_crypto_conn_ref *cref;
+
+  cref = (ngtcp2_crypto_conn_ref *)SSL_get_app_data(ssl);
+  cf = cref? cref->user_data : NULL;
+  ctx = cf? cf->ctx : NULL;
+  data = cf? CF_DATA_CURRENT(cf) : NULL;
+  if(cf && data && ctx) {
+    CURLcode result = Curl_ossl_add_session(cf, data, &ctx->peer,
+                                            ssl_sessionid);
+    return result? 0 : 1;
+  }
+  return 0;
+}
+#endif /* USE_OPENSSL */
+
+static CURLcode tls_ctx_setup(struct Curl_cfilter *cf,
+                              struct Curl_easy *data,
+                              void *user_data)
+{
+  struct curl_tls_ctx *ctx = user_data;
   (void)cf;
 #ifdef USE_OPENSSL
 #if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
-  if(ngtcp2_crypto_boringssl_configure_client_context(ctx->ssl_ctx) != 0) {
+  if(ngtcp2_crypto_boringssl_configure_client_context(ctx->ossl.ssl_ctx)
+     != 0) {
     failf(data, "ngtcp2_crypto_boringssl_configure_client_context failed");
     return CURLE_FAILED_INIT;
   }
 #else
-  if(ngtcp2_crypto_quictls_configure_client_context(ctx->ssl_ctx) != 0) {
+  if(ngtcp2_crypto_quictls_configure_client_context(ctx->ossl.ssl_ctx) != 0) {
     failf(data, "ngtcp2_crypto_quictls_configure_client_context failed");
     return CURLE_FAILED_INIT;
   }
 #endif /* !OPENSSL_IS_BORINGSSL && !OPENSSL_IS_AWSLC */
+  /* Enable the session cache because it's a prerequisite for the
+   * "new session" callback. Use the "external storage" mode to prevent
+   * OpenSSL from creating an internal session cache.
+   */
+  SSL_CTX_set_session_cache_mode(ctx->ossl.ssl_ctx,
+                                 SSL_SESS_CACHE_CLIENT |
+                                 SSL_SESS_CACHE_NO_INTERNAL);
+  SSL_CTX_sess_set_new_cb(ctx->ossl.ssl_ctx, ossl_new_session_cb);
+
 #elif defined(USE_GNUTLS)
-  if(ngtcp2_crypto_gnutls_configure_client_session(ctx->gtls->session) != 0) {
+  if(ngtcp2_crypto_gnutls_configure_client_session(ctx->gtls.session) != 0) {
     failf(data, "ngtcp2_crypto_gnutls_configure_client_session failed");
     return CURLE_FAILED_INIT;
   }
@@ -1960,17 +1995,21 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf,
   Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE,
                   H3_STREAM_POOL_SPARES);
 
-  result = Curl_ssl_peer_init(&ctx->peer, cf);
+  result = Curl_ssl_peer_init(&ctx->peer, cf, TRNSPRT_QUIC);
   if(result)
     return result;
 
 #define H3_ALPN "\x2h3\x5h3-29"
   result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer,
                                H3_ALPN, sizeof(H3_ALPN) - 1,
-                               tls_ctx_setup, &ctx->conn_ref);
+                               tls_ctx_setup, &ctx->tls, &ctx->conn_ref);
   if(result)
     return result;
 
+#ifdef USE_OPENSSL
+  SSL_set_quic_use_legacy_codepoint(ctx->tls.ossl.ssl, 0);
+#endif
+
   ctx->dcid.datalen = NGTCP2_MAX_CIDLEN;
   result = Curl_rand(data, ctx->dcid.data, NGTCP2_MAX_CIDLEN);
   if(result)
@@ -2012,8 +2051,10 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf,
   if(rc)
     return CURLE_QUIC_CONNECT_ERROR;
 
-#ifdef USE_GNUTLS
-  ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.gtls->session);
+#ifdef USE_OPENSSL
+  ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.ossl.ssl);
+#elif defined(USE_GNUTLS)
+  ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.gtls.session);
 #else
   ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.ssl);
 #endif
index 61434e37d986bfc486408584d1450b11bab8343f..b7ca302230a46032283e77cdcabd19ae718090f0 100644 (file)
@@ -281,7 +281,7 @@ static void cf_osslq_h3conn_cleanup(struct cf_osslq_h3conn *h3)
 struct cf_osslq_ctx {
   struct cf_quic_ctx q;
   struct ssl_peer peer;
-  struct quic_tls_ctx tls;
+  struct curl_tls_ctx tls;
   struct cf_call_data call_data;
   struct cf_osslq_h3conn h3;
   struct curltime started_at;        /* time the current attempt started */
@@ -318,7 +318,7 @@ static void cf_osslq_close(struct Curl_cfilter *cf, struct Curl_easy *data)
   struct cf_call_data save;
 
   CF_DATA_SAVE(save, cf, data);
-  if(ctx && ctx->tls.ssl) {
+  if(ctx && ctx->tls.ossl.ssl) {
     /* TODO: send connection close */
     CURL_TRC_CF(data, cf, "cf_osslq_close()");
     cf_osslq_ctx_clear(ctx);
@@ -403,7 +403,7 @@ static CURLcode cf_osslq_ssl_err(struct Curl_cfilter *cf,
       (reason == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED))) {
     result = CURLE_PEER_FAILED_VERIFICATION;
 
-    lerr = SSL_get_verify_result(ctx->tls.ssl);
+    lerr = SSL_get_verify_result(ctx->tls.ossl.ssl);
     if(lerr != X509_V_OK) {
       ssl_config->certverifyresult = lerr;
       msnprintf(ebuf, sizeof(ebuf),
@@ -1047,14 +1047,14 @@ static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf,
 
   Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE,
                   H3_STREAM_POOL_SPARES);
-  result = Curl_ssl_peer_init(&ctx->peer, cf);
+  result = Curl_ssl_peer_init(&ctx->peer, cf, TRNSPRT_QUIC);
   if(result)
     goto out;
 
 #define H3_ALPN "\x2h3"
   result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer,
                                H3_ALPN, sizeof(H3_ALPN) - 1,
-                               NULL, NULL);
+                               NULL, NULL, NULL);
   if(result)
     goto out;
 
@@ -1098,12 +1098,12 @@ static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf,
     goto out;
   }
 
-  if(!SSL_set1_initial_peer_addr(ctx->tls.ssl, baddr)) {
+  if(!SSL_set1_initial_peer_addr(ctx->tls.ossl.ssl, baddr)) {
     failf(data, "failed to set the initial peer address");
     result = CURLE_FAILED_INIT;
     goto out;
   }
-  if(!SSL_set_blocking_mode(ctx->tls.ssl, 0)) {
+  if(!SSL_set_blocking_mode(ctx->tls.ossl.ssl, 0)) {
     failf(data, "failed to turn off blocking mode");
     result = CURLE_FAILED_INIT;
     goto out;
@@ -1111,7 +1111,8 @@ static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf,
 
 #ifdef SSL_VALUE_QUIC_IDLE_TIMEOUT
   /* Added in OpenSSL v3.3.x */
-  if(!SSL_set_feature_request_uint(ctx->tls.ssl, SSL_VALUE_QUIC_IDLE_TIMEOUT,
+  if(!SSL_set_feature_request_uint(ctx->tls.ossl.ssl,
+                                   SSL_VALUE_QUIC_IDLE_TIMEOUT,
                                    CURL_QUIC_MAX_IDLE_MS)) {
     CURL_TRC_CF(data, cf, "error setting idle timeout, ");
     result = CURLE_FAILED_INIT;
@@ -1119,13 +1120,13 @@ static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf,
   }
 #endif
 
-  SSL_set_bio(ctx->tls.ssl, bio, bio);
+  SSL_set_bio(ctx->tls.ossl.ssl, bio, bio);
   bio = NULL;
-  SSL_set_connect_state(ctx->tls.ssl);
-  SSL_set_incoming_stream_policy(ctx->tls.ssl,
+  SSL_set_connect_state(ctx->tls.ossl.ssl);
+  SSL_set_incoming_stream_policy(ctx->tls.ossl.ssl,
                                  SSL_INCOMING_STREAM_POLICY_ACCEPT, 0);
   /* setup the H3 things on top of the QUIC connection */
-  result = cf_osslq_h3conn_init(ctx, ctx->tls.ssl, cf);
+  result = cf_osslq_h3conn_init(ctx, ctx->tls.ossl.ssl, cf);
 
 out:
   if(bio)
@@ -1288,22 +1289,23 @@ static CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
   struct cf_osslq_ctx *ctx = cf->ctx;
   CURLcode result = CURLE_OK;
 
-  if(!ctx->tls.ssl)
+  if(!ctx->tls.ossl.ssl)
     goto out;
 
   ERR_clear_error();
 
   /* 1. Check for new incoming streams */
   while(1) {
-    SSL *snew = SSL_accept_stream(ctx->tls.ssl, SSL_ACCEPT_STREAM_NO_BLOCK);
+    SSL *snew = SSL_accept_stream(ctx->tls.ossl.ssl,
+                                  SSL_ACCEPT_STREAM_NO_BLOCK);
     if(!snew)
       break;
 
     (void)cf_osslq_h3conn_add_stream(&ctx->h3, snew, cf, data);
   }
 
-  if(!SSL_handle_events(ctx->tls.ssl)) {
-    int detail = SSL_get_error(ctx->tls.ssl, 0);
+  if(!SSL_handle_events(ctx->tls.ossl.ssl)) {
+    int detail = SSL_get_error(ctx->tls.ossl.ssl, 0);
     result = cf_osslq_ssl_err(cf, data, detail, CURLE_RECV_ERROR);
   }
 
@@ -1370,7 +1372,7 @@ static CURLcode h3_send_streams(struct Curl_cfilter *cf,
   struct cf_osslq_ctx *ctx = cf->ctx;
   CURLcode result = CURLE_OK;
 
-  if(!ctx->tls.ssl || !ctx->h3.conn)
+  if(!ctx->tls.ossl.ssl || !ctx->h3.conn)
     goto out;
 
   for(;;) {
@@ -1493,7 +1495,7 @@ static CURLcode cf_progress_egress(struct Curl_cfilter *cf,
   struct cf_osslq_ctx *ctx = cf->ctx;
   CURLcode result = CURLE_OK;
 
-  if(!ctx->tls.ssl)
+  if(!ctx->tls.ossl.ssl)
     goto out;
 
   ERR_clear_error();
@@ -1501,8 +1503,8 @@ static CURLcode cf_progress_egress(struct Curl_cfilter *cf,
   if(result)
     goto out;
 
-  if(!SSL_handle_events(ctx->tls.ssl)) {
-    int detail = SSL_get_error(ctx->tls.ssl, 0);
+  if(!SSL_handle_events(ctx->tls.ossl.ssl)) {
+    int detail = SSL_get_error(ctx->tls.ossl.ssl, 0);
     result = cf_osslq_ssl_err(cf, data, detail, CURLE_SEND_ERROR);
   }
 
@@ -1522,8 +1524,8 @@ static CURLcode check_and_set_expiry(struct Curl_cfilter *cf,
   timediff_t timeoutms;
   int is_infinite = TRUE;
 
-  if(ctx->tls.ssl &&
-    SSL_get_event_timeout(ctx->tls.ssl, &tv, &is_infinite) &&
+  if(ctx->tls.ossl.ssl &&
+    SSL_get_event_timeout(ctx->tls.ossl.ssl, &tv, &is_infinite) &&
     !is_infinite) {
     timeoutms = curlx_tvtoms(&tv);
     /* QUIC want to be called again latest at the returned timeout */
@@ -1534,7 +1536,7 @@ static CURLcode check_and_set_expiry(struct Curl_cfilter *cf,
       result = cf_progress_egress(cf, data);
       if(result)
         goto out;
-      if(SSL_get_event_timeout(ctx->tls.ssl, &tv, &is_infinite)) {
+      if(SSL_get_event_timeout(ctx->tls.ossl.ssl, &tv, &is_infinite)) {
         timeoutms = curlx_tvtoms(&tv);
       }
     }
@@ -1579,7 +1581,7 @@ static CURLcode cf_osslq_connect(struct Curl_cfilter *cf,
     goto out;
   }
 
-  if(!ctx->tls.ssl) {
+  if(!ctx->tls.ossl.ssl) {
     ctx->started_at = now;
     result = cf_osslq_ctx_start(cf, data);
     if(result)
@@ -1595,7 +1597,7 @@ static CURLcode cf_osslq_connect(struct Curl_cfilter *cf,
   }
 
   ERR_clear_error();
-  err = SSL_do_handshake(ctx->tls.ssl);
+  err = SSL_do_handshake(ctx->tls.ossl.ssl);
 
   if(err == 1) {
     /* connected */
@@ -1613,7 +1615,7 @@ static CURLcode cf_osslq_connect(struct Curl_cfilter *cf,
     }
   }
   else {
-    int detail = SSL_get_error(ctx->tls.ssl, err);
+    int detail = SSL_get_error(ctx->tls.ossl.ssl, err);
     switch(detail) {
     case SSL_ERROR_WANT_READ:
       ctx->q.last_io = now;
@@ -1644,7 +1646,8 @@ static CURLcode cf_osslq_connect(struct Curl_cfilter *cf,
   }
 
 out:
-  if(result == CURLE_RECV_ERROR && ctx->tls.ssl && ctx->protocol_shutdown) {
+  if(result == CURLE_RECV_ERROR && ctx->tls.ossl.ssl &&
+     ctx->protocol_shutdown) {
     /* When a QUIC server instance is shutting down, it may send us a
      * CONNECTION_CLOSE right away. Our connection then enters the DRAINING
      * state. The CONNECT may work in the near future again. Indicate
@@ -1732,7 +1735,7 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf,
   }
 
   DEBUGASSERT(stream->s.id == -1);
-  *err = cf_osslq_stream_open(&stream->s, ctx->tls.ssl, 0,
+  *err = cf_osslq_stream_open(&stream->s, ctx->tls.ossl.ssl, 0,
                               &ctx->stream_bufcp, data);
   if(*err) {
     failf(data, "can't get bidi streams");
@@ -1810,7 +1813,7 @@ static ssize_t cf_osslq_send(struct Curl_cfilter *cf, struct Curl_easy *data,
 
   CF_DATA_SAVE(save, cf, data);
   DEBUGASSERT(cf->connected);
-  DEBUGASSERT(ctx->tls.ssl);
+  DEBUGASSERT(ctx->tls.ossl.ssl);
   DEBUGASSERT(ctx->h3.conn);
   *err = CURLE_OK;
 
@@ -1952,7 +1955,7 @@ static ssize_t cf_osslq_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
   CF_DATA_SAVE(save, cf, data);
   DEBUGASSERT(cf->connected);
   DEBUGASSERT(ctx);
-  DEBUGASSERT(ctx->tls.ssl);
+  DEBUGASSERT(ctx->tls.ossl.ssl);
   DEBUGASSERT(ctx->h3.conn);
   *err = CURLE_OK;
 
@@ -2088,7 +2091,7 @@ static bool cf_osslq_conn_is_alive(struct Curl_cfilter *cf,
 
   CF_DATA_SAVE(save, cf, data);
   *input_pending = FALSE;
-  if(!ctx->tls.ssl)
+  if(!ctx->tls.ossl.ssl)
     goto out;
 
 #ifdef SSL_VALUE_QUIC_IDLE_TIMEOUT
@@ -2096,7 +2099,8 @@ static bool cf_osslq_conn_is_alive(struct Curl_cfilter *cf,
   {
     timediff_t idletime;
     uint64_t idle_ms = ctx->max_idle_ms;
-    if(!SSL_get_value_uint(ctx->tls.ssl, SSL_VALUE_CLASS_FEATURE_NEGOTIATED,
+    if(!SSL_get_value_uint(ctx->tls.ossl.ssl,
+                           SSL_VALUE_CLASS_FEATURE_NEGOTIATED,
                            SSL_VALUE_QUIC_IDLE_TIMEOUT, &idle_ms)) {
       CURL_TRC_CF(data, cf, "error getting negotiated idle timeout, "
                   "assume connection is dead.");
@@ -2136,15 +2140,15 @@ static void cf_osslq_adjust_pollset(struct Curl_cfilter *cf,
 {
   struct cf_osslq_ctx *ctx = cf->ctx;
 
-  if(!ctx->tls.ssl) {
+  if(!ctx->tls.ossl.ssl) {
     /* NOP */
   }
   else if(!cf->connected) {
     /* during handshake, transfer has not started yet. we always
      * add our socket for polling if SSL wants to send/recv */
     Curl_pollset_set(data, ps, ctx->q.sockfd,
-                     SSL_net_read_desired(ctx->tls.ssl),
-                     SSL_net_write_desired(ctx->tls.ssl));
+                     SSL_net_read_desired(ctx->tls.ossl.ssl),
+                     SSL_net_write_desired(ctx->tls.ossl.ssl));
   }
   else {
     /* once connected, we only modify the socket if it is present.
@@ -2153,8 +2157,8 @@ static void cf_osslq_adjust_pollset(struct Curl_cfilter *cf,
     Curl_pollset_check(data, ps, ctx->q.sockfd, &want_recv, &want_send);
     if(want_recv || want_send) {
       Curl_pollset_set(data, ps, ctx->q.sockfd,
-                       SSL_net_read_desired(ctx->tls.ssl),
-                       SSL_net_write_desired(ctx->tls.ssl));
+                       SSL_net_read_desired(ctx->tls.ossl.ssl),
+                       SSL_net_write_desired(ctx->tls.ossl.ssl));
     }
   }
 }
@@ -2170,7 +2174,7 @@ static CURLcode cf_osslq_query(struct Curl_cfilter *cf,
 #ifdef SSL_VALUE_QUIC_STREAM_BIDI_LOCAL_AVAIL
     /* Added in OpenSSL v3.3.x */
     uint64_t v;
-    if(!SSL_get_value_uint(ctx->tls.ssl, SSL_VALUE_CLASS_GENERIC,
+    if(!SSL_get_value_uint(ctx->tls.ossl.ssl, SSL_VALUE_CLASS_GENERIC,
                            SSL_VALUE_QUIC_STREAM_BIDI_LOCAL_AVAIL, &v)) {
       CURL_TRC_CF(data, cf, "error getting available local bidi streams");
       return CURLE_HTTP3;
index 3015db063adfe4114fb03ba124bb1f7033a946c2..aca100162363b22945cac6e7940d5906984492a1 100644 (file)
@@ -88,7 +88,7 @@ void Curl_quiche_ver(char *p, size_t len)
 struct cf_quiche_ctx {
   struct cf_quic_ctx q;
   struct ssl_peer peer;
-  struct quic_tls_ctx tls;
+  struct curl_tls_ctx tls;
   quiche_conn *qconn;
   quiche_config *cfg;
   quiche_h3_conn *h3c;
@@ -123,8 +123,8 @@ static void cf_quiche_ctx_clear(struct cf_quiche_ctx *ctx)
       quiche_conn_free(ctx->qconn);
     if(ctx->cfg)
       quiche_config_free(ctx->cfg);
-    /* quiche just freed ctx->tls.ssl */
-    ctx->tls.ssl = NULL;
+    /* quiche just freed it */
+    ctx->tls.ossl.ssl = NULL;
     Curl_vquic_tls_cleanup(&ctx->tls);
     Curl_ssl_peer_cleanup(&ctx->peer);
     vquic_ctx_free(&ctx->q);
@@ -565,7 +565,7 @@ static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen,
       return CURLE_OK;
     }
     else if(QUICHE_ERR_TLS_FAIL == nread) {
-      long verify_ok = SSL_get_verify_result(ctx->tls.ssl);
+      long verify_ok = SSL_get_verify_result(ctx->tls.ossl.ssl);
       if(verify_ok != X509_V_OK) {
         failf(r->data, "SSL certificate problem: %s",
               X509_verify_cert_error_string(verify_ok));
@@ -1202,7 +1202,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf,
   if(result)
     return result;
 
-  result = Curl_ssl_peer_init(&ctx->peer, cf);
+  result = Curl_ssl_peer_init(&ctx->peer, cf, TRNSPRT_QUIC);
   if(result)
     return result;
 
@@ -1237,7 +1237,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf,
   result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer,
                                QUICHE_H3_APPLICATION_PROTOCOL,
                                sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1,
-                               NULL, cf);
+                               NULL, NULL, cf);
   if(result)
     return result;
 
@@ -1257,7 +1257,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf,
                                       (struct sockaddr *)&ctx->q.local_addr,
                                       ctx->q.local_addrlen,
                                       &sockaddr->sa_addr, sockaddr->addrlen,
-                                      ctx->cfg, ctx->tls.ssl, false);
+                                      ctx->cfg, ctx->tls.ossl.ssl, false);
   if(!ctx->qconn) {
     failf(data, "can't create quiche connection");
     return CURLE_OUT_OF_MEMORY;
index ecba607a9ff3f1b3df275bd50a6f6e0f25d976ad..191ac22cec2fb152429d82fa3c723df1885d12ab 100644 (file)
 #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
 #endif
 
-#ifdef USE_OPENSSL
-#define QUIC_CIPHERS                                                          \
-  "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_"               \
-  "POLY1305_SHA256:TLS_AES_128_CCM_SHA256"
-#define QUIC_GROUPS "P-256:X25519:P-384:P-521"
-#elif defined(USE_GNUTLS)
-#define QUIC_PRIORITY \
-  "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:" \
-  "+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:+GROUP-SECP256R1:" \
-  "+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1:" \
-  "%DISABLE_TLS13_COMPAT_MODE"
-#elif defined(USE_WOLFSSL)
+#if defined(USE_WOLFSSL)
+
 #define QUIC_CIPHERS                                                          \
   "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_"               \
   "POLY1305_SHA256:TLS_AES_128_CCM_SHA256"
 #define QUIC_GROUPS "P-256:P-384:P-521"
-#endif
-
-
-#ifdef USE_OPENSSL
-
-static void keylog_callback(const SSL *ssl, const char *line)
-{
-  (void)ssl;
-  Curl_tls_keylog_write_line(line);
-}
-
-static CURLcode curl_ossl_init_ctx(struct quic_tls_ctx *ctx,
-                                   struct Curl_cfilter *cf,
-                                   struct Curl_easy *data,
-                                   Curl_vquic_tls_ctx_setup *ctx_setup)
-{
-  struct ssl_primary_config *conn_config;
-  CURLcode result = CURLE_FAILED_INIT;
-
-  DEBUGASSERT(!ctx->ssl_ctx);
-#ifdef USE_OPENSSL_QUIC
-  ctx->ssl_ctx = SSL_CTX_new(OSSL_QUIC_client_method());
-#else
-  ctx->ssl_ctx = SSL_CTX_new(TLS_method());
-#endif
-  if(!ctx->ssl_ctx) {
-    result = CURLE_OUT_OF_MEMORY;
-    goto out;
-  }
-  conn_config = Curl_ssl_cf_get_primary_config(cf);
-  if(!conn_config) {
-    result = CURLE_FAILED_INIT;
-    goto out;
-  }
-
-  if(ctx_setup) {
-    result = ctx_setup(ctx, cf, data);
-    if(result)
-      goto out;
-  }
-
-  SSL_CTX_set_default_verify_paths(ctx->ssl_ctx);
-
-  {
-    const char *curves = conn_config->curves ?
-      conn_config->curves : QUIC_GROUPS;
-    if(!SSL_CTX_set1_curves_list(ctx->ssl_ctx, curves)) {
-      failf(data, "failed setting curves list for QUIC: '%s'", curves);
-      return CURLE_SSL_CIPHER;
-    }
-  }
-
-#ifndef OPENSSL_IS_BORINGSSL
-  {
-    const char *ciphers13 = conn_config->cipher_list13 ?
-      conn_config->cipher_list13 : QUIC_CIPHERS;
-    if(SSL_CTX_set_ciphersuites(ctx->ssl_ctx, ciphers13) != 1) {
-      failf(data, "failed setting QUIC cipher suite: %s", ciphers13);
-      return CURLE_SSL_CIPHER;
-    }
-    infof(data, "QUIC cipher selection: %s", ciphers13);
-  }
-#endif
-
-  /* Open the file if a TLS or QUIC backend has not done this before. */
-  Curl_tls_keylog_open();
-  if(Curl_tls_keylog_enabled()) {
-    SSL_CTX_set_keylog_callback(ctx->ssl_ctx, keylog_callback);
-  }
-
-  /* OpenSSL always tries to verify the peer, this only says whether it should
-   * fail to connect if the verification fails, or if it should continue
-   * anyway. In the latter case the result of the verification is checked with
-   * SSL_get_verify_result() below. */
-  SSL_CTX_set_verify(ctx->ssl_ctx, conn_config->verifypeer ?
-                     SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL);
-
-  /* give application a chance to interfere with SSL set up. */
-  if(data->set.ssl.fsslctx) {
-    /* When a user callback is installed to modify the SSL_CTX,
-     * we need to do the full initialization before calling it.
-     * See: #11800 */
-    if(!ctx->x509_store_setup) {
-      result = Curl_ssl_setup_x509_store(cf, data, ctx->ssl_ctx);
-      if(result)
-        goto out;
-      ctx->x509_store_setup = TRUE;
-    }
-    Curl_set_in_callback(data, true);
-    result = (*data->set.ssl.fsslctx)(data, ctx->ssl_ctx,
-                                      data->set.ssl.fsslctxp);
-    Curl_set_in_callback(data, false);
-    if(result) {
-      failf(data, "error signaled by ssl ctx callback");
-      goto out;
-    }
-  }
-  result = CURLE_OK;
-
-out:
-  if(result && ctx->ssl_ctx) {
-    SSL_CTX_free(ctx->ssl_ctx);
-    ctx->ssl_ctx = NULL;
-  }
-  return result;
-}
-
-static CURLcode curl_ossl_set_client_cert(struct quic_tls_ctx *ctx,
-                                     struct Curl_cfilter *cf,
-                                     struct Curl_easy *data)
-{
-  SSL_CTX *ssl_ctx = ctx->ssl_ctx;
-  const struct ssl_config_data *ssl_config;
-
-  ssl_config = Curl_ssl_cf_get_config(cf, data);
-  DEBUGASSERT(ssl_config);
-
-  if(ssl_config->primary.clientcert ||
-     ssl_config->primary.cert_blob ||
-     ssl_config->cert_type) {
-    return Curl_ossl_set_client_cert(
-        data, ssl_ctx, ssl_config->primary.clientcert,
-        ssl_config->primary.cert_blob, ssl_config->cert_type,
-        ssl_config->key, ssl_config->key_blob,
-        ssl_config->key_type, ssl_config->key_passwd);
-  }
-
-  return CURLE_OK;
-}
-
-/** SSL callbacks ***/
-
-static CURLcode curl_ossl_init_ssl(struct quic_tls_ctx *ctx,
-                                   struct Curl_easy *data,
-                                   struct ssl_peer *peer,
-                                   const char *alpn, size_t alpn_len,
-                                   void *user_data)
-{
-  DEBUGASSERT(!ctx->ssl);
-  ctx->ssl = SSL_new(ctx->ssl_ctx);
-
-  SSL_set_app_data(ctx->ssl, user_data);
-  SSL_set_connect_state(ctx->ssl);
-#ifndef USE_OPENSSL_QUIC
-  SSL_set_quic_use_legacy_codepoint(ctx->ssl, 0);
-#endif
-
-  if(alpn)
-    SSL_set_alpn_protos(ctx->ssl, (const uint8_t *)alpn, (int)alpn_len);
-
-  if(peer->sni) {
-    if(!SSL_set_tlsext_host_name(ctx->ssl, peer->sni)) {
-      failf(data, "Failed set SNI");
-      SSL_free(ctx->ssl);
-      ctx->ssl = NULL;
-      return CURLE_QUIC_CONNECT_ERROR;
-    }
-  }
-  return CURLE_OK;
-}
-
-#elif defined(USE_GNUTLS)
-static int keylog_callback(gnutls_session_t session, const char *label,
-                    const gnutls_datum_t *secret)
-{
-  gnutls_datum_t crandom;
-  gnutls_datum_t srandom;
-
-  gnutls_session_get_random(session, &crandom, &srandom);
-  if(crandom.size != 32) {
-    return -1;
-  }
-
-  Curl_tls_keylog_write(label, crandom.data, secret->data, secret->size);
-  return 0;
-}
-
-static CURLcode curl_gtls_init_ctx(struct quic_tls_ctx *ctx,
-                                   struct Curl_cfilter *cf,
-                                   struct Curl_easy *data,
-                                   struct ssl_peer *peer,
-                                   const char *alpn, size_t alpn_len,
-                                   Curl_vquic_tls_ctx_setup *ctx_setup,
-                                   void *user_data)
-{
-  struct ssl_primary_config *conn_config;
-  CURLcode result;
-  gnutls_datum_t alpns[5];
-  /* this will need some attention when HTTPS proxy over QUIC get fixed */
-  long * const pverifyresult = &data->set.ssl.certverifyresult;
-  int rc;
-
-  conn_config = Curl_ssl_cf_get_primary_config(cf);
-  if(!conn_config)
-    return CURLE_FAILED_INIT;
-
-  DEBUGASSERT(ctx->gtls == NULL);
-  ctx->gtls = calloc(1, sizeof(*(ctx->gtls)));
-  if(!ctx->gtls)
-    return CURLE_OUT_OF_MEMORY;
-
-  result = gtls_client_init(data, conn_config, &data->set.ssl,
-                            peer, ctx->gtls, pverifyresult);
-  if(result)
-    return result;
-
-  gnutls_session_set_ptr(ctx->gtls->session, user_data);
-
-  if(ctx_setup) {
-    result = ctx_setup(ctx, cf, data);
-    if(result)
-      return result;
-  }
-
-  rc = gnutls_priority_set_direct(ctx->gtls->session, QUIC_PRIORITY, NULL);
-  if(rc < 0) {
-    CURL_TRC_CF(data, cf, "gnutls_priority_set_direct failed: %s\n",
-                gnutls_strerror(rc));
-    return CURLE_QUIC_CONNECT_ERROR;
-  }
-
-  /* Open the file if a TLS or QUIC backend has not done this before. */
-  Curl_tls_keylog_open();
-  if(Curl_tls_keylog_enabled()) {
-    gnutls_session_set_keylog_function(ctx->gtls->session, keylog_callback);
-  }
-
-  /* convert the ALPN string from our arguments to a list of strings
-   * that gnutls wants and will convert internally back to this very
-   * string for sending to the server. nice. */
-  if(alpn) {
-    size_t i, alen = alpn_len;
-    unsigned char *s = (unsigned char *)alpn;
-    unsigned char slen;
-    for(i = 0; (i < ARRAYSIZE(alpns)) && alen; ++i) {
-      slen = s[0];
-      if(slen >= alen)
-        return CURLE_FAILED_INIT;
-      alpns[i].data = s + 1;
-      alpns[i].size = slen;
-      s += slen + 1;
-      alen -= (size_t)slen + 1;
-    }
-    if(alen) /* not all alpn chars used, wrong format or too many */
-        return CURLE_FAILED_INIT;
-    if(i) {
-      gnutls_alpn_set_protocols(ctx->gtls->session,
-                                alpns, (unsigned int)i,
-                                GNUTLS_ALPN_MANDATORY);
-    }
-  }
-
-  return CURLE_OK;
-}
-#elif defined(USE_WOLFSSL)
 
 #if defined(HAVE_SECRET_CALLBACK)
 static void keylog_callback(const WOLFSSL *ssl, const char *line)
@@ -341,10 +76,11 @@ static void keylog_callback(const WOLFSSL *ssl, const char *line)
 }
 #endif
 
-static CURLcode curl_wssl_init_ctx(struct quic_tls_ctx *ctx,
+static CURLcode curl_wssl_init_ctx(struct curl_tls_ctx *ctx,
                                    struct Curl_cfilter *cf,
                                    struct Curl_easy *data,
-                                   Curl_vquic_tls_ctx_setup *ctx_setup)
+                                   Curl_vquic_tls_ctx_setup *cb_setup,
+                                   void *cb_user_data)
 {
   struct ssl_primary_config *conn_config;
   CURLcode result = CURLE_FAILED_INIT;
@@ -361,8 +97,8 @@ static CURLcode curl_wssl_init_ctx(struct quic_tls_ctx *ctx,
     goto out;
   }
 
-  if(ctx_setup) {
-    result = ctx_setup(ctx, cf, data);
+  if(cb_setup) {
+    result = cb_setup(cf, data, cb_user_data);
     if(result)
       goto out;
   }
@@ -458,7 +194,7 @@ out:
 
 /** SSL callbacks ***/
 
-static CURLcode curl_wssl_init_ssl(struct quic_tls_ctx *ctx,
+static CURLcode curl_wssl_init_ssl(struct curl_tls_ctx *ctx,
                                    struct Curl_easy *data,
                                    struct ssl_peer *peer,
                                    const char *alpn, size_t alpn_len,
@@ -486,57 +222,50 @@ static CURLcode curl_wssl_init_ssl(struct quic_tls_ctx *ctx,
 }
 #endif /* defined(USE_WOLFSSL) */
 
-CURLcode Curl_vquic_tls_init(struct quic_tls_ctx *ctx,
+CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx,
                              struct Curl_cfilter *cf,
                              struct Curl_easy *data,
                              struct ssl_peer *peer,
                              const char *alpn, size_t alpn_len,
-                             Curl_vquic_tls_ctx_setup *ctx_setup,
-                             void *user_data)
+                             Curl_vquic_tls_ctx_setup *cb_setup,
+                             void *cb_user_data, void *ssl_user_data)
 {
   CURLcode result;
 
 #ifdef USE_OPENSSL
-  result = curl_ossl_init_ctx(ctx, cf, data, ctx_setup);
-  if(result)
-    return result;
-
-  result = curl_ossl_set_client_cert(ctx, cf, data);
-  if(result)
-    return result;
-
-  return curl_ossl_init_ssl(ctx, data, peer, alpn, alpn_len, user_data);
+  (void)result;
+  return Curl_ossl_ctx_init(&ctx->ossl, cf, data, peer, TRNSPRT_QUIC,
+                            (const unsigned char *)alpn, alpn_len,
+                            cb_setup, cb_user_data, NULL, ssl_user_data);
 #elif defined(USE_GNUTLS)
   (void)result;
-  return curl_gtls_init_ctx(ctx, cf, data, peer, alpn, alpn_len,
-                            ctx_setup, user_data);
+  return Curl_gtls_ctx_init(&ctx->gtls, cf, data, peer,
+                            (const unsigned char *)alpn, alpn_len,
+                            cb_setup, cb_user_data, ssl_user_data);
 #elif defined(USE_WOLFSSL)
-  result = curl_wssl_init_ctx(ctx, cf, data, ctx_setup);
+  result = curl_wssl_init_ctx(ctx, cf, data, cb_setup, cb_user_data);
   if(result)
     return result;
 
-  return curl_wssl_init_ssl(ctx, data, peer, alpn, alpn_len, user_data);
+  return curl_wssl_init_ssl(ctx, data, peer, alpn, alpn_len, ssl_user_data);
 #else
 #error "no TLS lib in used, should not happen"
   return CURLE_FAILED_INIT;
 #endif
 }
 
-void Curl_vquic_tls_cleanup(struct quic_tls_ctx *ctx)
+void Curl_vquic_tls_cleanup(struct curl_tls_ctx *ctx)
 {
 #ifdef USE_OPENSSL
-  if(ctx->ssl)
-    SSL_free(ctx->ssl);
-  if(ctx->ssl_ctx)
-    SSL_CTX_free(ctx->ssl_ctx);
+  if(ctx->ossl.ssl)
+    SSL_free(ctx->ossl.ssl);
+  if(ctx->ossl.ssl_ctx)
+    SSL_CTX_free(ctx->ossl.ssl_ctx);
 #elif defined(USE_GNUTLS)
-  if(ctx->gtls) {
-    if(ctx->gtls->cred)
-      gnutls_certificate_free_credentials(ctx->gtls->cred);
-    if(ctx->gtls->session)
-      gnutls_deinit(ctx->gtls->session);
-    free(ctx->gtls);
-  }
+  if(ctx->gtls.cred)
+    gnutls_certificate_free_credentials(ctx->gtls.cred);
+  if(ctx->gtls.session)
+    gnutls_deinit(ctx->gtls.session);
 #elif defined(USE_WOLFSSL)
   if(ctx->ssl)
     wolfSSL_free(ctx->ssl);
@@ -546,16 +275,16 @@ void Curl_vquic_tls_cleanup(struct quic_tls_ctx *ctx)
   memset(ctx, 0, sizeof(*ctx));
 }
 
-CURLcode Curl_vquic_tls_before_recv(struct quic_tls_ctx *ctx,
+CURLcode Curl_vquic_tls_before_recv(struct curl_tls_ctx *ctx,
                                     struct Curl_cfilter *cf,
                                     struct Curl_easy *data)
 {
 #ifdef USE_OPENSSL
-  if(!ctx->x509_store_setup) {
-    CURLcode result = Curl_ssl_setup_x509_store(cf, data, ctx->ssl_ctx);
+  if(!ctx->ossl.x509_store_setup) {
+    CURLcode result = Curl_ssl_setup_x509_store(cf, data, ctx->ossl.ssl_ctx);
     if(result)
       return result;
-    ctx->x509_store_setup = TRUE;
+    ctx->ossl.x509_store_setup = TRUE;
   }
 #else
   (void)ctx; (void)cf; (void)data;
@@ -563,7 +292,7 @@ CURLcode Curl_vquic_tls_before_recv(struct quic_tls_ctx *ctx,
   return CURLE_OK;
 }
 
-CURLcode Curl_vquic_tls_verify_peer(struct quic_tls_ctx *ctx,
+CURLcode Curl_vquic_tls_verify_peer(struct curl_tls_ctx *ctx,
                                     struct Curl_cfilter *cf,
                                     struct Curl_easy *data,
                                     struct ssl_peer *peer)
@@ -575,36 +304,24 @@ CURLcode Curl_vquic_tls_verify_peer(struct quic_tls_ctx *ctx,
   if(!conn_config)
     return CURLE_FAILED_INIT;
 
-  if(conn_config->verifyhost) {
 #ifdef USE_OPENSSL
-    X509 *server_cert;
-    server_cert = SSL_get1_peer_certificate(ctx->ssl);
-    if(!server_cert) {
-      return CURLE_PEER_FAILED_VERIFICATION;
-    }
-    result = Curl_ossl_verifyhost(data, cf->conn, peer, server_cert);
-    X509_free(server_cert);
-    if(result)
-      return result;
+  (void)conn_config;
+  result = Curl_oss_check_peer_cert(cf, data, &ctx->ossl, peer);
 #elif defined(USE_GNUTLS)
-    result = Curl_gtls_verifyserver(data, ctx->gtls->session,
+  if(conn_config->verifyhost) {
+    result = Curl_gtls_verifyserver(data, ctx->gtls.session,
                                     conn_config, &data->set.ssl, peer,
                                     data->set.str[STRING_SSL_PINNEDPUBLICKEY]);
     if(result)
       return result;
+  }
 #elif defined(USE_WOLFSSL)
+  (void)data;
+  if(conn_config->verifyhost) {
     if(!peer->sni ||
        wolfSSL_check_domain_name(ctx->ssl, peer->sni) == SSL_FAILURE)
       return CURLE_PEER_FAILED_VERIFICATION;
-#endif
-    infof(data, "Verified certificate just fine");
   }
-  else
-    infof(data, "Skipped certificate verification");
-#ifdef USE_OPENSSL
-  if(data->set.ssl.certinfo)
-    /* asked to gather certificate info */
-    (void)Curl_ossl_certchain(data, ctx->ssl);
 #endif
   return result;
 }
index 9c0dfd8d5286720510bae217ef697e13f3d0bb59..be18fccc5e075b7a804aead4771bfbc74509a93f 100644 (file)
 
 #include "curl_setup.h"
 #include "bufq.h"
+#include "vtls/openssl.h"
 
 #if defined(ENABLE_QUIC) && \
   (defined(USE_OPENSSL) || defined(USE_GNUTLS) || defined(USE_WOLFSSL))
 
-struct quic_tls_ctx {
+struct curl_tls_ctx {
 #ifdef USE_OPENSSL
-  SSL_CTX *ssl_ctx;
-  SSL *ssl;
+  struct ossl_ctx ossl;
 #elif defined(USE_GNUTLS)
-  struct gtls_instance *gtls;
+  struct gtls_ctx gtls;
 #elif defined(USE_WOLFSSL)
   WOLFSSL_CTX *ssl_ctx;
   WOLFSSL *ssl;
 #endif
-  BIT(x509_store_setup);             /* if x509 store has been set up */
 };
 
 /**
@@ -50,9 +49,9 @@ struct quic_tls_ctx {
  * - openssl/wolfssl: SSL_CTX* has just been created
  * - gnutls: gtls_client_init() has run
  */
-typedef CURLcode Curl_vquic_tls_ctx_setup(struct quic_tls_ctx *ctx,
-                                          struct Curl_cfilter *cf,
-                                          struct Curl_easy *data);
+typedef CURLcode Curl_vquic_tls_ctx_setup(struct Curl_cfilter *cf,
+                                          struct Curl_easy *data,
+                                          void *cb_user_data);
 
 /**
  * Initialize the QUIC TLS instances based of the SSL configurations
@@ -64,23 +63,25 @@ typedef CURLcode Curl_vquic_tls_ctx_setup(struct quic_tls_ctx *ctx,
  * @param alpn        the ALPN string in protocol format ((len+bytes+)+),
  *                    may be NULL
  * @param alpn_len    the overall number of bytes in `alpn`
- * @param ctx_setup   optional callback for very early TLS config
- * @param user_data   optional pointer to set in TLS application context
+ * @param cb_setup    optional callback for very early TLS config
+ Â± @param cb_user_data user_data param for callback
+ * @param ssl_user_data  optional pointer to set in TLS application context
  */
-CURLcode Curl_vquic_tls_init(struct quic_tls_ctx *ctx,
+CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx,
                              struct Curl_cfilter *cf,
                              struct Curl_easy *data,
                              struct ssl_peer *peer,
                              const char *alpn, size_t alpn_len,
-                             Curl_vquic_tls_ctx_setup *ctx_setup,
-                             void *user_data);
+                             Curl_vquic_tls_ctx_setup *cb_setup,
+                             void *cb_user_data,
+                             void *ssl_user_data);
 
 /**
  * Cleanup all data that has been initialized.
  */
-void Curl_vquic_tls_cleanup(struct quic_tls_ctx *ctx);
+void Curl_vquic_tls_cleanup(struct curl_tls_ctx *ctx);
 
-CURLcode Curl_vquic_tls_before_recv(struct quic_tls_ctx *ctx,
+CURLcode Curl_vquic_tls_before_recv(struct curl_tls_ctx *ctx,
                                     struct Curl_cfilter *cf,
                                     struct Curl_easy *data);
 
@@ -88,7 +89,7 @@ CURLcode Curl_vquic_tls_before_recv(struct quic_tls_ctx *ctx,
  * After the QUIC basic handshake has been, verify that the peer
  * (and its certificate) fulfill our requirements.
  */
-CURLcode Curl_vquic_tls_verify_peer(struct quic_tls_ctx *ctx,
+CURLcode Curl_vquic_tls_verify_peer(struct curl_tls_ctx *ctx,
                                     struct Curl_cfilter *cf,
                                     struct Curl_easy *data,
                                     struct ssl_peer *peer);
index 9b003d695820a21d058f39b699f6458ff6c64c26..e9e2ce86cc654a557986241a82fc58119ff6ecbb 100644 (file)
@@ -686,7 +686,7 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf,
 
     CURL_TRC_CF(data, cf, "connect_step1, check session cache");
     Curl_ssl_sessionid_lock(data);
-    if(!Curl_ssl_getsessionid(cf, data, &session, NULL)) {
+    if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, &session, NULL)) {
       br_ssl_engine_set_session_parameters(&backend->ctx.eng, session);
       session_set = 1;
       infof(data, "BearSSL: reusing session ID");
@@ -905,10 +905,11 @@ static CURLcode bearssl_connect_step3(struct Curl_cfilter *cf,
       return CURLE_OUT_OF_MEMORY;
     br_ssl_engine_get_session_parameters(&backend->ctx.eng, session);
     Curl_ssl_sessionid_lock(data);
-    incache = !(Curl_ssl_getsessionid(cf, data, &oldsession, NULL));
+    incache = !(Curl_ssl_getsessionid(cf, data, &connssl->peer,
+                                      &oldsession, NULL));
     if(incache)
       Curl_ssl_delsessionid(data, oldsession);
-    ret = Curl_ssl_addsessionid(cf, data, session, 0, &added);
+    ret = Curl_ssl_addsessionid(cf, data, &connssl->peer, session, 0, &added);
     Curl_ssl_sessionid_unlock(data);
     if(!added)
       free(session);
index 7db1ce9dafd4cd9531ae55dfd47dae2d4e51a018..16d44c17c02f54efece17c0edf939684f3e958e6 100644 (file)
@@ -43,6 +43,7 @@
 #include "urldata.h"
 #include "sendf.h"
 #include "inet_pton.h"
+#include "keylog.h"
 #include "gtls.h"
 #include "vtls.h"
 #include "vtls_int.h"
 /* The last #include file should be: */
 #include "memdebug.h"
 
+#ifndef ARRAYSIZE
+#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
+#endif
+
+#define QUIC_PRIORITY \
+  "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:" \
+  "+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:+GROUP-SECP256R1:" \
+  "+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1:" \
+  "%DISABLE_TLS13_COMPAT_MODE"
+
 /* Enable GnuTLS debugging by defining GTLSDEBUG */
 /*#define GTLSDEBUG */
 
@@ -77,7 +88,7 @@ static bool gtls_inited = FALSE;
 # include <gnutls/ocsp.h>
 
 struct gtls_ssl_backend_data {
-  struct gtls_instance gtls;
+  struct gtls_ctx gtls;
 };
 
 static ssize_t gtls_push(void *s, const void *buf, size_t blen)
@@ -330,6 +341,7 @@ static gnutls_x509_crt_fmt_t do_file_type(const char *type)
 
 static CURLcode
 set_ssl_version_min_max(struct Curl_easy *data,
+                        struct ssl_peer *peer,
                         struct ssl_primary_config *conn_config,
                         const char **prioritylist,
                         const char *tls13support)
@@ -337,6 +349,16 @@ set_ssl_version_min_max(struct Curl_easy *data,
   long ssl_version = conn_config->version;
   long ssl_version_max = conn_config->version_max;
 
+  if(peer->transport == TRNSPRT_QUIC) {
+    if((ssl_version != CURL_SSLVERSION_DEFAULT) &&
+       (ssl_version < CURL_SSLVERSION_TLSv1_3)) {
+      failf(data, "QUIC needs at least TLS version 1.3");
+      return CURLE_SSL_CONNECT_ERROR;
+     }
+    *prioritylist = QUIC_PRIORITY;
+    return CURLE_OK;
+  }
+
   if((ssl_version == CURL_SSLVERSION_DEFAULT) ||
      (ssl_version == CURL_SSLVERSION_TLSv1))
     ssl_version = CURL_SSLVERSION_TLSv1_0;
@@ -401,12 +423,12 @@ set_ssl_version_min_max(struct Curl_easy *data,
   return CURLE_SSL_CONNECT_ERROR;
 }
 
-CURLcode gtls_client_init(struct Curl_easy *data,
-                          struct ssl_primary_config *config,
-                          struct ssl_config_data *ssl_config,
-                          struct ssl_peer *peer,
-                          struct gtls_instance *gtls,
-                          long *pverifyresult)
+static CURLcode gtls_client_init(struct Curl_easy *data,
+                                 struct ssl_primary_config *config,
+                                 struct ssl_config_data *ssl_config,
+                                 struct ssl_peer *peer,
+                                 struct gtls_ctx *gtls,
+                                 long *pverifyresult)
 {
   unsigned int init_flags;
   int rc;
@@ -578,7 +600,8 @@ CURLcode gtls_client_init(struct Curl_easy *data,
   }
 
   /* At this point we know we have a supported TLS version, so set it */
-  result = set_ssl_version_min_max(data, config, &prioritylist, tls13support);
+  result = set_ssl_version_min_max(data, peer,
+                                   config, &prioritylist, tls13support);
   if(result)
     return result;
 
@@ -677,46 +700,81 @@ CURLcode gtls_client_init(struct Curl_easy *data,
   return CURLE_OK;
 }
 
-static CURLcode
-gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
+static int keylog_callback(gnutls_session_t session, const char *label,
+                           const gnutls_datum_t *secret)
+{
+  gnutls_datum_t crandom;
+  gnutls_datum_t srandom;
+
+  gnutls_session_get_random(session, &crandom, &srandom);
+  if(crandom.size != 32) {
+    return -1;
+  }
+
+  Curl_tls_keylog_write(label, crandom.data, secret->data, secret->size);
+  return 0;
+}
+
+CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx,
+                            struct Curl_cfilter *cf,
+                            struct Curl_easy *data,
+                            struct ssl_peer *peer,
+                            const unsigned char *alpn, size_t alpn_len,
+                            Curl_gtls_ctx_setup_cb *cb_setup,
+                            void *cb_user_data,
+                            void *ssl_user_data)
 {
-  struct ssl_connect_data *connssl = cf->ctx;
-  struct gtls_ssl_backend_data *backend =
-    (struct gtls_ssl_backend_data *)connssl->backend;
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
   long * const pverifyresult = &ssl_config->certverifyresult;
   CURLcode result;
 
-  DEBUGASSERT(backend);
-
-  if(connssl->state == ssl_connection_complete)
-    /* to make us tolerant against being called more than once for the
-       same connection */
-    return CURLE_OK;
+  DEBUGASSERT(gctx);
 
-  result = gtls_client_init(data, conn_config, ssl_config,
-                            &connssl->peer,
-                            &backend->gtls, pverifyresult);
+  result = gtls_client_init(data, conn_config, ssl_config, peer,
+                            gctx, pverifyresult);
   if(result)
     return result;
 
-  if(connssl->alpn) {
-    struct alpn_proto_buf proto;
-    gnutls_datum_t alpn[ALPN_ENTRIES_MAX];
-    size_t i;
+  gnutls_session_set_ptr(gctx->session, ssl_user_data);
+
+  if(cb_setup) {
+    result = cb_setup(cf, data, cb_user_data);
+    if(result)
+      return result;
+  }
 
-    for(i = 0; i < connssl->alpn->count; ++i) {
-      alpn[i].data = (unsigned char *)connssl->alpn->entries[i];
-      alpn[i].size = (unsigned)strlen(connssl->alpn->entries[i]);
+  /* Open the file if a TLS or QUIC backend has not done this before. */
+  Curl_tls_keylog_open();
+  if(Curl_tls_keylog_enabled()) {
+    gnutls_session_set_keylog_function(gctx->session, keylog_callback);
+  }
+
+  /* convert the ALPN string from our arguments to a list of strings
+   * that gnutls wants and will convert internally back to this very
+   * string for sending to the server. nice. */
+  if(alpn) {
+    gnutls_datum_t alpns[5];
+    size_t i, alen = alpn_len;
+    unsigned char *s = (unsigned char *)alpn;
+    unsigned char slen;
+    for(i = 0; (i < ARRAYSIZE(alpns)) && alen; ++i) {
+      slen = s[0];
+      if(slen >= alen)
+        return CURLE_FAILED_INIT;
+      alpns[i].data = s + 1;
+      alpns[i].size = slen;
+      s += slen + 1;
+      alen -= (size_t)slen + 1;
     }
-    if(gnutls_alpn_set_protocols(backend->gtls.session, alpn,
-                                 (unsigned)connssl->alpn->count, 0)) {
+    if(alen) /* not all alpn chars used, wrong format or too many */
+        return CURLE_FAILED_INIT;
+    if(i && gnutls_alpn_set_protocols(gctx->session,
+                                      alpns, (unsigned int)i,
+                                      GNUTLS_ALPN_MANDATORY)) {
       failf(data, "failed setting ALPN");
       return CURLE_SSL_CONNECT_ERROR;
     }
-    Curl_alpn_to_proto_str(&proto, connssl->alpn);
-    infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
   }
 
   /* This might be a reconnect, so we check for a session ID in the cache
@@ -726,16 +784,49 @@ gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
     size_t ssl_idsize;
 
     Curl_ssl_sessionid_lock(data);
-    if(!Curl_ssl_getsessionid(cf, data, &ssl_sessionid, &ssl_idsize)) {
+    if(!Curl_ssl_getsessionid(cf, data, peer, &ssl_sessionid, &ssl_idsize)) {
       /* we got a session id, use it! */
-      gnutls_session_set_data(backend->gtls.session,
-                              ssl_sessionid, ssl_idsize);
+      gnutls_session_set_data(gctx->session, ssl_sessionid, ssl_idsize);
 
       /* Informational message */
       infof(data, "SSL reusing session ID");
     }
     Curl_ssl_sessionid_unlock(data);
   }
+  return CURLE_OK;
+}
+
+static CURLcode
+gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+  struct ssl_connect_data *connssl = cf->ctx;
+  struct gtls_ssl_backend_data *backend =
+    (struct gtls_ssl_backend_data *)connssl->backend;
+  struct alpn_proto_buf proto;
+  CURLcode result;
+
+  DEBUGASSERT(backend);
+  DEBUGASSERT(ssl_connect_1 == connssl->connecting_state);
+
+  if(connssl->state == ssl_connection_complete)
+    /* to make us tolerant against being called more than once for the
+       same connection */
+    return CURLE_OK;
+
+  memset(&proto, 0, sizeof(proto));
+  if(connssl->alpn) {
+    result = Curl_alpn_to_proto_buf(&proto, connssl->alpn);
+    if(result) {
+      failf(data, "Error determining ALPN");
+      return CURLE_SSL_CONNECT_ERROR;
+    }
+  }
+
+  result = Curl_gtls_ctx_init(&backend->gtls, cf, data, &connssl->peer,
+                              proto.data, proto.len, NULL, NULL, cf);
+  if(result)
+    return result;
+
 
   /* register callback functions and handle to send and receive data. */
   gnutls_transport_set_ptr(backend->gtls.session, cf);
@@ -1291,7 +1382,8 @@ static CURLcode gtls_verifyserver(struct Curl_cfilter *cf,
       gnutls_session_get_data(session, connect_sessionid, &connect_idsize);
 
       Curl_ssl_sessionid_lock(data);
-      incache = !(Curl_ssl_getsessionid(cf, data, &ssl_sessionid, NULL));
+      incache = !(Curl_ssl_getsessionid(cf, data, &connssl->peer,
+                                        &ssl_sessionid, NULL));
       if(incache) {
         /* there was one before in the cache, so instead of risking that the
            previous one was rejected, we just kill that and store the new */
@@ -1299,8 +1391,9 @@ static CURLcode gtls_verifyserver(struct Curl_cfilter *cf,
       }
 
       /* store this session id */
-      result = Curl_ssl_addsessionid(cf, data, connect_sessionid,
-                                     connect_idsize, &added);
+      result = Curl_ssl_addsessionid(cf, data, &connssl->peer,
+                                     connect_sessionid, connect_idsize,
+                                     &added);
       Curl_ssl_sessionid_unlock(data);
       if(!added)
         free(connect_sessionid);
index 1a81c01e93bccdac8035d8b43b2edee30d600634..766cd40ac884c6c73b796436d4cd240cb88c9447 100644 (file)
@@ -45,7 +45,7 @@ struct ssl_primary_config;
 struct ssl_config_data;
 struct ssl_peer;
 
-struct gtls_instance {
+struct gtls_ctx {
   gnutls_session_t session;
   gnutls_certificate_credentials_t cred;
 #ifdef USE_GNUTLS_SRP
@@ -53,13 +53,18 @@ struct gtls_instance {
 #endif
 };
 
-CURLcode
-gtls_client_init(struct Curl_easy *data,
-                 struct ssl_primary_config *config,
-                 struct ssl_config_data *ssl_config,
-                 struct ssl_peer *peer,
-                 struct gtls_instance *gtls,
-                 long *pverifyresult);
+typedef CURLcode Curl_gtls_ctx_setup_cb(struct Curl_cfilter *cf,
+                                        struct Curl_easy *data,
+                                        void *user_data);
+
+CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx,
+                            struct Curl_cfilter *cf,
+                            struct Curl_easy *data,
+                            struct ssl_peer *peer,
+                            const unsigned char *alpn, size_t alpn_len,
+                            Curl_gtls_ctx_setup_cb *cb_setup,
+                            void *cb_user_data,
+                            void *ssl_user_data);
 
 CURLcode
 Curl_gtls_verifyserver(struct Curl_easy *data,
index fbcb25cfb61d7999d21c26787dc9d4928ed093e8..ab7baaaeca8865fee026333dc79488f70dcd0245 100644 (file)
@@ -24,6 +24,7 @@
 #include "curl_setup.h"
 
 #if defined(USE_OPENSSL) || \
+  defined(USE_GNUTLS) || \
   defined(USE_WOLFSSL) || \
   (defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || \
   defined(USE_QUICHE)
index 438cb7c6fa10faae193290acee133f88bd0701fa..4967beac369c01dab4704df56811bb133b72ccec 100644 (file)
@@ -589,7 +589,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
   }
 #endif
 
-  infof(data, "mbedTLS: Connecting to %s:%d", hostname, connssl->port);
+  infof(data, "mbedTLS: Connecting to %s:%d", hostname, connssl->peer.port);
 
   mbedtls_ssl_config_init(&backend->config);
   ret = mbedtls_ssl_config_defaults(&backend->config,
@@ -667,7 +667,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
     void *old_session = NULL;
 
     Curl_ssl_sessionid_lock(data);
-    if(!Curl_ssl_getsessionid(cf, data, &old_session, NULL)) {
+    if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, &old_session, NULL)) {
       ret = mbedtls_ssl_set_session(&backend->ssl, old_session);
       if(ret) {
         Curl_ssl_sessionid_unlock(data);
@@ -955,11 +955,12 @@ mbed_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
 
     /* If there's already a matching session in the cache, delete it */
     Curl_ssl_sessionid_lock(data);
-    if(!Curl_ssl_getsessionid(cf, data, &old_ssl_sessionid, NULL))
+    if(!Curl_ssl_getsessionid(cf, data, &connssl->peer,
+                              &old_ssl_sessionid, NULL))
       Curl_ssl_delsessionid(data, old_ssl_sessionid);
 
-    retcode = Curl_ssl_addsessionid(cf, data, our_ssl_sessionid,
-                                    0, &added);
+    retcode = Curl_ssl_addsessionid(cf, data, &connssl->peer,
+                                    our_ssl_sessionid, 0, &added);
     Curl_ssl_sessionid_unlock(data);
     if(!added) {
       mbedtls_ssl_session_free(our_ssl_sessionid);
index 84b95ae8adeb3862a5948431de6cebff1b748d48..58395cb68e12a3bc6ca5eb93e272a141bf223849 100644 (file)
@@ -298,20 +298,6 @@ typedef unsigned long sslerr_t;
 #define USE_PRE_1_1_API (OPENSSL_VERSION_NUMBER < 0x10100000L)
 #endif /* !LIBRESSL_VERSION_NUMBER */
 
-struct ossl_ssl_backend_data {
-  /* these ones requires specific SSL-types */
-  SSL_CTX* ctx;
-  SSL*     handle;
-  X509*    server_cert;
-  BIO_METHOD *bio_method;
-  CURLcode io_result;       /* result of last BIO cfilter operation */
-#ifndef HAVE_KEYLOG_CALLBACK
-  /* Set to true once a valid keylog entry has been created to avoid dupes. */
-  bool     keylog_done;
-#endif
-  bool x509_store_setup;            /* x509 store has been set up */
-};
-
 #if defined(HAVE_SSL_X509_STORE_SHARE)
 struct multi_ssl_backend_data {
   char *CAfile;         /* CAfile path used to generate X509 store */
@@ -726,8 +712,7 @@ static int ossl_bio_cf_out_write(BIO *bio, const char *buf, int blen)
 {
   struct Curl_cfilter *cf = BIO_get_data(bio);
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ossl_ssl_backend_data *backend =
-    (struct ossl_ssl_backend_data *)connssl->backend;
+  struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
   struct Curl_easy *data = CF_DATA_CURRENT(cf);
   ssize_t nwritten;
   CURLcode result = CURLE_SEND_ERROR;
@@ -737,7 +722,7 @@ static int ossl_bio_cf_out_write(BIO *bio, const char *buf, int blen)
   CURL_TRC_CF(data, cf, "ossl_bio_cf_out_write(len=%d) -> %d, err=%d",
               blen, (int)nwritten, result);
   BIO_clear_retry_flags(bio);
-  backend->io_result = result;
+  octx->io_result = result;
   if(nwritten < 0) {
     if(CURLE_AGAIN == result)
       BIO_set_retry_write(bio);
@@ -749,8 +734,7 @@ static int ossl_bio_cf_in_read(BIO *bio, char *buf, int blen)
 {
   struct Curl_cfilter *cf = BIO_get_data(bio);
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ossl_ssl_backend_data *backend =
-    (struct ossl_ssl_backend_data *)connssl->backend;
+  struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
   struct Curl_easy *data = CF_DATA_CURRENT(cf);
   ssize_t nread;
   CURLcode result = CURLE_RECV_ERROR;
@@ -764,7 +748,7 @@ static int ossl_bio_cf_in_read(BIO *bio, char *buf, int blen)
   CURL_TRC_CF(data, cf, "ossl_bio_cf_in_read(len=%d) -> %d, err=%d",
               blen, (int)nread, result);
   BIO_clear_retry_flags(bio);
-  backend->io_result = result;
+  octx->io_result = result;
   if(nread < 0) {
     if(CURLE_AGAIN == result)
       BIO_set_retry_read(bio);
@@ -775,13 +759,13 @@ static int ossl_bio_cf_in_read(BIO *bio, char *buf, int blen)
 
   /* Before returning server replies to the SSL instance, we need
    * to have setup the x509 store or verification will fail. */
-  if(!backend->x509_store_setup) {
-    result = Curl_ssl_setup_x509_store(cf, data, backend->ctx);
+  if(!octx->x509_store_setup) {
+    result = Curl_ssl_setup_x509_store(cf, data, octx->ssl_ctx);
     if(result) {
-      backend->io_result = result;
+      octx->io_result = result;
       return -1;
     }
-    backend->x509_store_setup = TRUE;
+    octx->x509_store_setup = TRUE;
   }
 
   return (int)nread;
@@ -1883,13 +1867,12 @@ static struct curl_slist *ossl_engines_list(struct Curl_easy *data)
 static void ossl_close(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ossl_ssl_backend_data *backend =
-    (struct ossl_ssl_backend_data *)connssl->backend;
+  struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
 
   (void)data;
-  DEBUGASSERT(backend);
+  DEBUGASSERT(octx);
 
-  if(backend->handle) {
+  if(octx->ssl) {
     /* Send the TLS shutdown if we are still connected *and* if
      * the peer did not already close the connection. */
     if(cf->next && cf->next->connected && !connssl->peer_closed) {
@@ -1900,8 +1883,8 @@ static void ossl_close(struct Curl_cfilter *cf, struct Curl_easy *data)
       /* Maybe the server has already sent a close notify alert.
          Read it to avoid an RST on the TCP connection. */
       ERR_clear_error();
-      nread = SSL_read(backend->handle, buf, (int)sizeof(buf));
-      err = SSL_get_error(backend->handle, nread);
+      nread = SSL_read(octx->ssl, buf, (int)sizeof(buf));
+      err = SSL_get_error(octx->ssl, nread);
       if(!nread && err == SSL_ERROR_ZERO_RETURN) {
         CURLcode result;
         ssize_t n;
@@ -1924,12 +1907,12 @@ static void ossl_close(struct Curl_cfilter *cf, struct Curl_easy *data)
         CURL_TRC_CF(data, cf, "not from sending TLS shutdown on "
                     "connection closed by peer");
       }
-      else if(SSL_shutdown(backend->handle) == 1) {
+      else if(SSL_shutdown(octx->ssl) == 1) {
         CURL_TRC_CF(data, cf, "SSL shutdown finished");
       }
       else {
-        nread = SSL_read(backend->handle, buf, (int)sizeof(buf));
-        err = SSL_get_error(backend->handle, nread);
+        nread = SSL_read(octx->ssl, buf, (int)sizeof(buf));
+        err = SSL_get_error(octx->ssl, nread);
         switch(err) {
         case SSL_ERROR_NONE: /* this is not an error */
         case SSL_ERROR_ZERO_RETURN: /* no more data */
@@ -1955,20 +1938,20 @@ static void ossl_close(struct Curl_cfilter *cf, struct Curl_easy *data)
       }
 
       ERR_clear_error();
-      SSL_set_connect_state(backend->handle);
+      SSL_set_connect_state(octx->ssl);
     }
 
-    SSL_free(backend->handle);
-    backend->handle = NULL;
+    SSL_free(octx->ssl);
+    octx->ssl = NULL;
   }
-  if(backend->ctx) {
-    SSL_CTX_free(backend->ctx);
-    backend->ctx = NULL;
-    backend->x509_store_setup = FALSE;
+  if(octx->ssl_ctx) {
+    SSL_CTX_free(octx->ssl_ctx);
+    octx->ssl_ctx = NULL;
+    octx->x509_store_setup = FALSE;
   }
-  if(backend->bio_method) {
-    ossl_bio_cf_method_free(backend->bio_method);
-    backend->bio_method = NULL;
+  if(octx->bio_method) {
+    ossl_bio_cf_method_free(octx->bio_method);
+    octx->bio_method = NULL;
   }
 }
 
@@ -1988,11 +1971,10 @@ static int ossl_shutdown(struct Curl_cfilter *cf,
   int buffsize;
   int err;
   bool done = FALSE;
-  struct ossl_ssl_backend_data *backend =
-    (struct ossl_ssl_backend_data *)connssl->backend;
+  struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
   int loop = 10;
 
-  DEBUGASSERT(backend);
+  DEBUGASSERT(octx);
 
 #ifndef CURL_DISABLE_FTP
   /* This has only been tested on the proftpd server, and the mod_tls code
@@ -2001,10 +1983,10 @@ static int ossl_shutdown(struct Curl_cfilter *cf,
      we do not send one. Let's hope other servers do the same... */
 
   if(data->set.ftp_ccc == CURLFTPSSL_CCC_ACTIVE)
-    (void)SSL_shutdown(backend->handle);
+    (void)SSL_shutdown(octx->ssl);
 #endif
 
-  if(backend->handle) {
+  if(octx->ssl) {
     buffsize = (int)sizeof(buf);
     while(!done && loop--) {
       int what = SOCKET_READABLE(Curl_conn_cf_get_socket(cf, data),
@@ -2014,8 +1996,8 @@ static int ossl_shutdown(struct Curl_cfilter *cf,
 
         /* Something to read, let's do it and hope that it is the close
            notify alert from the server */
-        nread = SSL_read(backend->handle, buf, buffsize);
-        err = SSL_get_error(backend->handle, nread);
+        nread = SSL_read(octx->ssl, buf, buffsize);
+        err = SSL_get_error(octx->ssl, nread);
 
         switch(err) {
         case SSL_ERROR_NONE: /* this is not an error */
@@ -2060,7 +2042,7 @@ static int ossl_shutdown(struct Curl_cfilter *cf,
 
     if(data->set.verbose) {
 #ifdef HAVE_SSL_GET_SHUTDOWN
-      switch(SSL_get_shutdown(backend->handle)) {
+      switch(SSL_get_shutdown(octx->ssl)) {
       case SSL_SENT_SHUTDOWN:
         infof(data, "SSL_get_shutdown() returned SSL_SENT_SHUTDOWN");
         break;
@@ -2075,8 +2057,8 @@ static int ossl_shutdown(struct Curl_cfilter *cf,
 #endif
     }
 
-    SSL_free(backend->handle);
-    backend->handle = NULL;
+    SSL_free(octx->ssl);
+    octx->ssl = NULL;
   }
   return retval;
 }
@@ -2381,8 +2363,7 @@ static CURLcode verifystatus(struct Curl_cfilter *cf,
   OCSP_BASICRESP *br = NULL;
   X509_STORE     *st = NULL;
   STACK_OF(X509) *ch = NULL;
-  struct ossl_ssl_backend_data *backend =
-    (struct ossl_ssl_backend_data *)connssl->backend;
+  struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
   X509 *cert;
   OCSP_CERTID *id = NULL;
   int cert_status, crl_reason;
@@ -2390,9 +2371,9 @@ static CURLcode verifystatus(struct Curl_cfilter *cf,
   int ret;
   long len;
 
-  DEBUGASSERT(backend);
+  DEBUGASSERT(octx);
 
-  len = SSL_get_tlsext_status_ocsp_resp(backend->handle, &status);
+  len = SSL_get_tlsext_status_ocsp_resp(octx->ssl, &status);
 
   if(!status) {
     failf(data, "No OCSP response received");
@@ -2422,13 +2403,13 @@ static CURLcode verifystatus(struct Curl_cfilter *cf,
     goto end;
   }
 
-  ch = SSL_get_peer_cert_chain(backend->handle);
+  ch = SSL_get_peer_cert_chain(octx->ssl);
   if(!ch) {
     failf(data, "Could not get peer certificate chain");
     result = CURLE_SSL_INVALIDCERTSTATUS;
     goto end;
   }
-  st = SSL_CTX_get_cert_store(backend->ctx);
+  st = SSL_CTX_get_cert_store(octx->ssl_ctx);
 
 #if ((OPENSSL_VERSION_NUMBER <= 0x1000201fL) /* Fixed after 1.0.2a */ || \
      (defined(LIBRESSL_VERSION_NUMBER) &&                               \
@@ -2465,7 +2446,7 @@ static CURLcode verifystatus(struct Curl_cfilter *cf,
   }
 
   /* Compute the certificate's ID */
-  cert = SSL_get1_peer_certificate(backend->handle);
+  cert = SSL_get1_peer_certificate(octx->ssl);
   if(!cert) {
     failf(data, "Error getting peer certificate");
     result = CURLE_SSL_INVALIDCERTSTATUS;
@@ -2880,10 +2861,9 @@ ossl_set_ssl_version_min_max_legacy(ctx_option_t *ctx_options,
 #ifdef TLS1_3_VERSION
   {
     struct ssl_connect_data *connssl = cf->ctx;
-    struct ossl_ssl_backend_data *backend =
-      (struct ossl_ssl_backend_data *)connssl->backend;
-    DEBUGASSERT(backend);
-    SSL_CTX_set_max_proto_version(backend->ctx, TLS1_3_VERSION);
+    struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
+    DEBUGASSERT(octx);
+    SSL_CTX_set_max_proto_version(octx->ssl_ctx, TLS1_3_VERSION);
     *ctx_options |= SSL_OP_NO_TLSv1_2;
   }
 #else
@@ -2941,24 +2921,17 @@ ossl_set_ssl_version_min_max_legacy(ctx_option_t *ctx_options,
 }
 #endif
 
-/* The "new session" callback must return zero if the session can be removed
- * or non-zero if the session has been put into the session cache.
- */
-static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
+CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf,
+                               struct Curl_easy *data,
+                               const struct ssl_peer *peer,
+                               SSL_SESSION *ssl_sessionid)
 {
-  int res = 0;
-  struct Curl_easy *data;
-  struct Curl_cfilter *cf;
   const struct ssl_config_data *config;
-  struct ssl_connect_data *connssl;
   bool isproxy;
+  CURLcode result = CURLE_WRITE_ERROR;
 
-  cf = (struct Curl_cfilter*) SSL_get_app_data(ssl);
-  connssl = cf? cf->ctx : NULL;
-  data = connssl? CF_DATA_CURRENT(cf) : NULL;
-  /* The sockindex has been stored as a pointer to an array element */
   if(!cf || !data)
-    return 0;
+    return result;
 
   isproxy = Curl_ssl_cf_is_proxy(cf);
 
@@ -2972,7 +2945,8 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
     if(isproxy)
       incache = FALSE;
     else
-      incache = !(Curl_ssl_getsessionid(cf, data, &old_ssl_sessionid, NULL));
+      incache = !(Curl_ssl_getsessionid(cf, data, peer,
+                                        &old_ssl_sessionid, NULL));
     if(incache) {
       if(old_ssl_sessionid != ssl_sessionid) {
         infof(data, "old SSL session ID is stale, removing");
@@ -2982,11 +2956,11 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
     }
 
     if(!incache) {
-      if(!Curl_ssl_addsessionid(cf, data, ssl_sessionid,
+      if(!Curl_ssl_addsessionid(cf, data, peer, ssl_sessionid,
                                 0 /* unknown size */, &added)) {
         if(added) {
           /* the session has been put into the session cache */
-          res = 1;
+          result = CURLE_OK;
         }
       }
       else
@@ -2995,7 +2969,24 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
     Curl_ssl_sessionid_unlock(data);
   }
 
-  return res;
+  return result;
+}
+
+/* The "new session" callback must return zero if the session can be removed
+ * or non-zero if the session has been put into the session cache.
+ */
+static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
+{
+  struct Curl_cfilter *cf;
+  struct Curl_easy *data;
+  struct ssl_connect_data *connssl;
+  CURLcode result;
+
+  cf = (struct Curl_cfilter*) SSL_get_app_data(ssl);
+  connssl = cf? cf->ctx : NULL;
+  data = connssl? CF_DATA_CURRENT(cf) : NULL;
+  result = Curl_ossl_add_session(cf, data, &connssl->peer, ssl_sessionid);
+  return result? 0 : 1;
 }
 
 static CURLcode load_cacert_from_memory(X509_STORE *store,
@@ -3493,29 +3484,30 @@ CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf,
 }
 #endif /* HAVE_SSL_X509_STORE_SHARE */
 
-static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
-                                   struct Curl_easy *data)
+CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
+                            struct Curl_cfilter *cf,
+                            struct Curl_easy *data,
+                            struct ssl_peer *peer,
+                            int transport, /* TCP or QUIC */
+                            const unsigned char *alpn, size_t alpn_len,
+                            Curl_ossl_ctx_setup_cb *cb_setup,
+                            void *cb_user_data,
+                            Curl_ossl_new_session_cb *cb_new_session,
+                            void *ssl_user_data)
 {
   CURLcode result = CURLE_OK;
-  char *ciphers;
+  const char *ciphers;
   SSL_METHOD_QUAL SSL_METHOD *req_method = NULL;
-  struct ssl_connect_data *connssl = cf->ctx;
   ctx_option_t ctx_options = 0;
   void *ssl_sessionid = NULL;
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
-  BIO *bio;
   const long int ssl_version = conn_config->version;
   char * const ssl_cert = ssl_config->primary.clientcert;
   const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob;
   const char * const ssl_cert_type = ssl_config->cert_type;
   const bool verifypeer = conn_config->verifypeer;
   char error_buffer[256];
-  struct ossl_ssl_backend_data *backend =
-    (struct ossl_ssl_backend_data *)connssl->backend;
-
-  DEBUGASSERT(ssl_connect_1 == connssl->connecting_state);
-  DEBUGASSERT(backend);
 
   /* Make funny stuff to get random input */
   result = ossl_seed(data);
@@ -3524,52 +3516,72 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
 
   ssl_config->certverifyresult = !X509_V_OK;
 
-  /* check to see if we've been told to use an explicit SSL/TLS version */
-
-  switch(ssl_version) {
-  case CURL_SSLVERSION_DEFAULT:
-  case CURL_SSLVERSION_TLSv1:
-  case CURL_SSLVERSION_TLSv1_0:
-  case CURL_SSLVERSION_TLSv1_1:
-  case CURL_SSLVERSION_TLSv1_2:
-  case CURL_SSLVERSION_TLSv1_3:
-    /* it will be handled later with the context options */
-#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
-    req_method = TLS_client_method();
+  switch(transport) {
+  case TRNSPRT_TCP:
+    /* check to see if we've been told to use an explicit SSL/TLS version */
+    switch(ssl_version) {
+    case CURL_SSLVERSION_DEFAULT:
+    case CURL_SSLVERSION_TLSv1:
+    case CURL_SSLVERSION_TLSv1_0:
+    case CURL_SSLVERSION_TLSv1_1:
+    case CURL_SSLVERSION_TLSv1_2:
+    case CURL_SSLVERSION_TLSv1_3:
+      /* it will be handled later with the context options */
+  #if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
+      req_method = TLS_client_method();
+  #else
+      req_method = SSLv23_client_method();
+  #endif
+      break;
+    case CURL_SSLVERSION_SSLv2:
+      failf(data, "No SSLv2 support");
+      return CURLE_NOT_BUILT_IN;
+    case CURL_SSLVERSION_SSLv3:
+      failf(data, "No SSLv3 support");
+      return CURLE_NOT_BUILT_IN;
+    default:
+      failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
+      return CURLE_SSL_CONNECT_ERROR;
+    }
+    break;
+  case TRNSPRT_QUIC:
+    if((ssl_version != CURL_SSLVERSION_DEFAULT) &&
+       (ssl_version < CURL_SSLVERSION_TLSv1_3)) {
+      failf(data, "QUIC needs at least TLS version 1.3");
+      return CURLE_SSL_CONNECT_ERROR;
+     }
+#ifdef USE_OPENSSL_QUIC
+    req_method = OSSL_QUIC_client_method();
 #else
-    req_method = SSLv23_client_method();
+    req_method = TLS_method();
 #endif
     break;
-  case CURL_SSLVERSION_SSLv2:
-    failf(data, "No SSLv2 support");
-    return CURLE_NOT_BUILT_IN;
-  case CURL_SSLVERSION_SSLv3:
-    failf(data, "No SSLv3 support");
-    return CURLE_NOT_BUILT_IN;
   default:
-    failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
+    failf(data, "unsupported transport %d in SSL init", transport);
     return CURLE_SSL_CONNECT_ERROR;
   }
 
-  if(backend->ctx) {
-    /* This happens when an error was encountered before in this
-     * step and we are called to do it again. Get rid of any leftover
-     * from the previous call. */
-    ossl_close(cf, data);
-  }
-  backend->ctx = SSL_CTX_new(req_method);
 
-  if(!backend->ctx) {
+  DEBUGASSERT(!octx->ssl_ctx);
+  octx->ssl_ctx = SSL_CTX_new(req_method);
+
+  if(!octx->ssl_ctx) {
     failf(data, "SSL: couldn't create a context: %s",
           ossl_strerror(ERR_peek_error(), error_buffer, sizeof(error_buffer)));
     return CURLE_OUT_OF_MEMORY;
   }
 
+  if(cb_setup) {
+    result = cb_setup(cf, data, cb_user_data);
+    if(result)
+      return result;
+  }
+
 #ifdef SSL_CTRL_SET_MSG_CALLBACK
   if(data->set.fdebug && data->set.verbose) {
     /* the SSL trace callback is only used for verbose logging */
-    SSL_CTX_set_msg_callback(backend->ctx, ossl_trace);
-    SSL_CTX_set_msg_callback_arg(backend->ctx, cf);
+    SSL_CTX_set_msg_callback(octx->ssl_ctx, ossl_trace);
+    SSL_CTX_set_msg_callback_arg(octx->ssl_ctx, cf);
   }
 #endif
 
@@ -3649,7 +3661,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
     ctx_options |= SSL_OP_NO_SSLv3;
 
 #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) /* 1.1.0 */
-    result = ossl_set_ssl_version_min_max(cf, backend->ctx);
+    result = ossl_set_ssl_version_min_max(cf, octx->ssl_ctx);
 #else
     result = ossl_set_ssl_version_min_max_legacy(&ctx_options, cf, data);
 #endif
@@ -3662,26 +3674,20 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
     return CURLE_SSL_CONNECT_ERROR;
   }
 
-  SSL_CTX_set_options(backend->ctx, ctx_options);
+  SSL_CTX_set_options(octx->ssl_ctx, ctx_options);
 
 #ifdef HAS_ALPN
-  if(connssl->alpn) {
-    struct alpn_proto_buf proto;
-
-    result = Curl_alpn_to_proto_buf(&proto, connssl->alpn);
-    if(result ||
-       SSL_CTX_set_alpn_protos(backend->ctx, proto.data, proto.len)) {
+  if(alpn) {
+    if(SSL_CTX_set_alpn_protos(octx->ssl_ctx, alpn, (int)alpn_len)) {
       failf(data, "Error setting ALPN");
       return CURLE_SSL_CONNECT_ERROR;
     }
-    Curl_alpn_to_proto_str(&proto, connssl->alpn);
-    infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
   }
 #endif
 
   if(ssl_cert || ssl_cert_blob || ssl_cert_type) {
     if(!result &&
-       !cert_stuff(data, backend->ctx,
+       !cert_stuff(data, octx->ssl_ctx,
                    ssl_cert, ssl_cert_blob, ssl_cert_type,
                    ssl_config->key, ssl_config->key_blob,
                    ssl_config->key_type, ssl_config->key_passwd))
@@ -3692,10 +3698,10 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
   }
 
   ciphers = conn_config->cipher_list;
-  if(!ciphers)
-    ciphers = (char *)DEFAULT_CIPHER_SELECTION;
+  if(!ciphers && (peer->transport != TRNSPRT_QUIC))
+    ciphers = DEFAULT_CIPHER_SELECTION;
   if(ciphers) {
-    if(!SSL_CTX_set_cipher_list(backend->ctx, ciphers)) {
+    if(!SSL_CTX_set_cipher_list(octx->ssl_ctx, ciphers)) {
       failf(data, "failed setting cipher list: %s", ciphers);
       return CURLE_SSL_CIPHER;
     }
@@ -3704,9 +3710,9 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
 
 #ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
   {
-    char *ciphers13 = conn_config->cipher_list13;
+    const char *ciphers13 = conn_config->cipher_list13;
     if(ciphers13) {
-      if(!SSL_CTX_set_ciphersuites(backend->ctx, ciphers13)) {
+      if(!SSL_CTX_set_ciphersuites(octx->ssl_ctx, ciphers13)) {
         failf(data, "failed setting TLS 1.3 cipher suite: %s", ciphers13);
         return CURLE_SSL_CIPHER;
       }
@@ -3717,14 +3723,14 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
 
 #ifdef HAVE_SSL_CTX_SET_POST_HANDSHAKE_AUTH
   /* OpenSSL 1.1.1 requires clients to opt-in for PHA */
-  SSL_CTX_set_post_handshake_auth(backend->ctx, 1);
+  SSL_CTX_set_post_handshake_auth(octx->ssl_ctx, 1);
 #endif
 
 #ifdef HAVE_SSL_CTX_SET_EC_CURVES
   {
-    char *curves = conn_config->curves;
+    const char *curves = conn_config->curves;
     if(curves) {
-      if(!SSL_CTX_set1_curves_list(backend->ctx, curves)) {
+      if(!SSL_CTX_set1_curves_list(octx->ssl_ctx, curves)) {
         failf(data, "failed setting curves list: '%s'", curves);
         return CURLE_SSL_CIPHER;
       }
@@ -3738,18 +3744,18 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
     char * const ssl_password = ssl_config->primary.password;
     infof(data, "Using TLS-SRP username: %s", ssl_username);
 
-    if(!SSL_CTX_set_srp_username(backend->ctx, ssl_username)) {
+    if(!SSL_CTX_set_srp_username(octx->ssl_ctx, ssl_username)) {
       failf(data, "Unable to set SRP user name");
       return CURLE_BAD_FUNCTION_ARGUMENT;
     }
-    if(!SSL_CTX_set_srp_password(backend->ctx, ssl_password)) {
+    if(!SSL_CTX_set_srp_password(octx->ssl_ctx, ssl_password)) {
       failf(data, "failed setting SRP password");
       return CURLE_BAD_FUNCTION_ARGUMENT;
     }
     if(!conn_config->cipher_list) {
       infof(data, "Setting cipher list SRP");
 
-      if(!SSL_CTX_set_cipher_list(backend->ctx, "SRP")) {
+      if(!SSL_CTX_set_cipher_list(octx->ssl_ctx, "SRP")) {
         failf(data, "failed setting SRP cipher list");
         return CURLE_SSL_CIPHER;
       }
@@ -3761,38 +3767,40 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
    * fail to connect if the verification fails, or if it should continue
    * anyway. In the latter case the result of the verification is checked with
    * SSL_get_verify_result() below. */
-  SSL_CTX_set_verify(backend->ctx,
+  SSL_CTX_set_verify(octx->ssl_ctx,
                      verifypeer ? SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL);
 
   /* Enable logging of secrets to the file specified in env SSLKEYLOGFILE. */
 #ifdef HAVE_KEYLOG_CALLBACK
   if(Curl_tls_keylog_enabled()) {
-    SSL_CTX_set_keylog_callback(backend->ctx, ossl_keylog_callback);
+    SSL_CTX_set_keylog_callback(octx->ssl_ctx, ossl_keylog_callback);
   }
 #endif
 
-  /* Enable the session cache because it's a prerequisite for the "new session"
-   * callback. Use the "external storage" mode to prevent OpenSSL from creating
-   * an internal session cache.
-   */
-  SSL_CTX_set_session_cache_mode(backend->ctx,
-                                 SSL_SESS_CACHE_CLIENT |
-                                 SSL_SESS_CACHE_NO_INTERNAL);
-  SSL_CTX_sess_set_new_cb(backend->ctx, ossl_new_session_cb);
+  if(cb_new_session) {
+    /* Enable the session cache because it's a prerequisite for the
+     * "new session" callback. Use the "external storage" mode to prevent
+     * OpenSSL from creating an internal session cache.
+     */
+    SSL_CTX_set_session_cache_mode(octx->ssl_ctx,
+                                   SSL_SESS_CACHE_CLIENT |
+                                   SSL_SESS_CACHE_NO_INTERNAL);
+    SSL_CTX_sess_set_new_cb(octx->ssl_ctx, cb_new_session);
+  }
 
   /* give application a chance to interfere with SSL set up. */
   if(data->set.ssl.fsslctx) {
     /* When a user callback is installed to modify the SSL_CTX,
      * we need to do the full initialization before calling it.
      * See: #11800 */
-    if(!backend->x509_store_setup) {
-      result = Curl_ssl_setup_x509_store(cf, data, backend->ctx);
+    if(!octx->x509_store_setup) {
+      result = Curl_ssl_setup_x509_store(cf, data, octx->ssl_ctx);
       if(result)
         return result;
-      backend->x509_store_setup = TRUE;
+      octx->x509_store_setup = TRUE;
     }
     Curl_set_in_callback(data, true);
-    result = (*data->set.ssl.fsslctx)(data, backend->ctx,
+    result = (*data->set.ssl.fsslctx)(data, octx->ssl_ctx,
                                       data->set.ssl.fsslctxp);
     Curl_set_in_callback(data, false);
     if(result) {
@@ -3802,47 +3810,45 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
   }
 
   /* Let's make an SSL structure */
-  if(backend->handle)
-    SSL_free(backend->handle);
-  backend->handle = SSL_new(backend->ctx);
-  if(!backend->handle) {
+  if(octx->ssl)
+    SSL_free(octx->ssl);
+  octx->ssl = SSL_new(octx->ssl_ctx);
+  if(!octx->ssl) {
     failf(data, "SSL: couldn't create a context (handle)");
     return CURLE_OUT_OF_MEMORY;
   }
 
-  SSL_set_app_data(backend->handle, cf);
+  SSL_set_app_data(octx->ssl, ssl_user_data);
 
 #if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
   !defined(OPENSSL_NO_OCSP)
   if(conn_config->verifystatus)
-    SSL_set_tlsext_status_type(backend->handle, TLSEXT_STATUSTYPE_ocsp);
+    SSL_set_tlsext_status_type(octx->ssl, TLSEXT_STATUSTYPE_ocsp);
 #endif
 
 #if (defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)) && \
     defined(ALLOW_RENEG)
-  SSL_set_renegotiate_mode(backend->handle, ssl_renegotiate_freely);
+  SSL_set_renegotiate_mode(octx->ssl, ssl_renegotiate_freely);
 #endif
 
-  SSL_set_connect_state(backend->handle);
+  SSL_set_connect_state(octx->ssl);
 
-  backend->server_cert = 0x0;
+  octx->server_cert = 0x0;
 #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
-  if(connssl->peer.sni) {
-    if(!SSL_set_tlsext_host_name(backend->handle, connssl->peer.sni)) {
+  if(peer->sni) {
+    if(!SSL_set_tlsext_host_name(octx->ssl, peer->sni)) {
       failf(data, "Failed set SNI");
       return CURLE_SSL_CONNECT_ERROR;
     }
   }
 #endif
 
-  SSL_set_app_data(backend->handle, cf);
-
-  connssl->reused_session = FALSE;
-  if(ssl_config->primary.sessionid) {
+  octx->reused_session = FALSE;
+  if(ssl_config->primary.sessionid && transport == TRNSPRT_TCP) {
     Curl_ssl_sessionid_lock(data);
-    if(!Curl_ssl_getsessionid(cf, data, &ssl_sessionid, NULL)) {
+    if(!Curl_ssl_getsessionid(cf, data, peer, &ssl_sessionid, NULL)) {
       /* we got a session id, use it! */
-      if(!SSL_set_session(backend->handle, ssl_sessionid)) {
+      if(!SSL_set_session(octx->ssl, ssl_sessionid)) {
         Curl_ssl_sessionid_unlock(data);
         failf(data, "SSL: SSL_set_session failed: %s",
               ossl_strerror(ERR_get_error(), error_buffer,
@@ -3851,15 +3857,46 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
       }
       /* Informational message */
       infof(data, "SSL reusing session ID");
-      connssl->reused_session = TRUE;
+      octx->reused_session = TRUE;
     }
     Curl_ssl_sessionid_unlock(data);
   }
 
-  backend->bio_method = ossl_bio_cf_method_create();
-  if(!backend->bio_method)
+  return CURLE_OK;
+}
+
+static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
+                                   struct Curl_easy *data)
+{
+  struct ssl_connect_data *connssl = cf->ctx;
+  struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
+  struct alpn_proto_buf proto;
+  BIO *bio;
+  CURLcode result;
+
+  DEBUGASSERT(ssl_connect_1 == connssl->connecting_state);
+  DEBUGASSERT(octx);
+  memset(&proto, 0, sizeof(proto));
+#ifdef HAS_ALPN
+  if(connssl->alpn) {
+    result = Curl_alpn_to_proto_buf(&proto, connssl->alpn);
+    if(result) {
+      failf(data, "Error determining ALPN");
+      return CURLE_SSL_CONNECT_ERROR;
+    }
+  }
+#endif
+
+  result = Curl_ossl_ctx_init(octx, cf, data, &connssl->peer, TRNSPRT_TCP,
+                              proto.data, proto.len, NULL, NULL,
+                              ossl_new_session_cb, cf);
+  if(result)
+    return result;
+
+  octx->bio_method = ossl_bio_cf_method_create();
+  if(!octx->bio_method)
     return CURLE_OUT_OF_MEMORY;
-  bio = BIO_new(backend->bio_method);
+  bio = BIO_new(octx->bio_method);
   if(!bio)
     return CURLE_OUT_OF_MEMORY;
 
@@ -3871,13 +3908,17 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
    * We check on the function in configure, since libressl and friends
    * each have their own versions to add support for this. */
   BIO_up_ref(bio);
-  SSL_set0_rbio(backend->handle, bio);
-  SSL_set0_wbio(backend->handle, bio);
+  SSL_set0_rbio(octx->ssl, bio);
+  SSL_set0_wbio(octx->ssl, bio);
 #else
-  SSL_set_bio(backend->handle, bio, bio);
+  SSL_set_bio(octx->ssl, bio, bio);
 #endif
-  connssl->connecting_state = ssl_connect_2;
 
+  if(connssl->alpn) {
+    Curl_alpn_to_proto_str(&proto, connssl->alpn);
+    infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
+  }
+  connssl->connecting_state = ssl_connect_2;
   return CURLE_OK;
 }
 
@@ -3886,25 +3927,24 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
 {
   int err;
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ossl_ssl_backend_data *backend =
-    (struct ossl_ssl_backend_data *)connssl->backend;
+  struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
   DEBUGASSERT(ssl_connect_2 == connssl->connecting_state
               || ssl_connect_2_reading == connssl->connecting_state
               || ssl_connect_2_writing == connssl->connecting_state);
-  DEBUGASSERT(backend);
+  DEBUGASSERT(octx);
 
   ERR_clear_error();
 
-  err = SSL_connect(backend->handle);
+  err = SSL_connect(octx->ssl);
 
-  if(!backend->x509_store_setup) {
+  if(!octx->x509_store_setup) {
     /* After having send off the ClientHello, we prepare the x509
      * store to verify the coming certificate from the server */
-    CURLcode result = Curl_ssl_setup_x509_store(cf, data, backend->ctx);
+    CURLcode result = Curl_ssl_setup_x509_store(cf, data, octx->ssl_ctx);
     if(result)
       return result;
-    backend->x509_store_setup = TRUE;
+    octx->x509_store_setup = TRUE;
   }
 
 #ifndef HAVE_KEYLOG_CALLBACK
@@ -3912,7 +3952,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
     /* If key logging is enabled, wait for the handshake to complete and then
      * proceed with logging secrets (for TLS 1.2 or older).
      */
-    ossl_log_tls12_secret(backend->handle, &backend->keylog_done);
+    ossl_log_tls12_secret(octx->ssl, &octx->keylog_done);
   }
 #endif
 
@@ -3920,7 +3960,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
      0  is "not successful but was shut down controlled"
      <0 is "handshake was not successful, because a fatal error occurred" */
   if(1 != err) {
-    int detail = SSL_get_error(backend->handle, err);
+    int detail = SSL_get_error(octx->ssl, err);
 
     if(SSL_ERROR_WANT_READ == detail) {
       connssl->connecting_state = ssl_connect_2_reading;
@@ -3942,7 +3982,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
       return CURLE_OK;
     }
 #endif
-    if(backend->io_result == CURLE_AGAIN) {
+    if(octx->io_result == CURLE_AGAIN) {
       return CURLE_OK;
     }
     else {
@@ -3970,7 +4010,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
           (reason == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED))) {
         result = CURLE_PEER_FAILED_VERIFICATION;
 
-        lerr = SSL_get_verify_result(backend->handle);
+        lerr = SSL_get_verify_result(octx->ssl);
         if(lerr != X509_V_OK) {
           ssl_config->certverifyresult = lerr;
           msnprintf(error_buffer, sizeof(error_buffer),
@@ -4012,7 +4052,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
           Curl_strerror(sockerr, extramsg, sizeof(extramsg));
         failf(data, OSSL_PACKAGE " SSL_connect: %s in connection to %s:%d ",
               extramsg[0] ? extramsg : SSL_ERROR_to_str(detail),
-              connssl->peer.hostname, connssl->port);
+              connssl->peer.hostname, connssl->peer.port);
         return result;
       }
 
@@ -4030,19 +4070,19 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
     connssl->connecting_state = ssl_connect_3;
 
 #if (OPENSSL_VERSION_NUMBER >= 0x30000000L)
-    SSL_get_peer_signature_type_nid(backend->handle, &psigtype_nid);
+    SSL_get_peer_signature_type_nid(octx->ssl, &psigtype_nid);
 #if (OPENSSL_VERSION_NUMBER >= 0x30200000L)
-    negotiated_group_name = SSL_get0_group_name(backend->handle);
+    negotiated_group_name = SSL_get0_group_name(octx->ssl);
 #else
     negotiated_group_name =
-      OBJ_nid2sn(SSL_get_negotiated_group(backend->handle) & 0x0000FFFF);
+      OBJ_nid2sn(SSL_get_negotiated_group(octx->ssl) & 0x0000FFFF);
 #endif
 #endif
 
     /* Informational message */
     infof(data, "SSL connection using %s / %s / %s / %s",
-          SSL_get_version(backend->handle),
-          SSL_get_cipher(backend->handle),
+          SSL_get_version(octx->ssl),
+          SSL_get_cipher(octx->ssl),
           negotiated_group_name? negotiated_group_name : "[blank]",
           OBJ_nid2sn(psigtype_nid));
 
@@ -4053,7 +4093,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
     if(connssl->alpn) {
       const unsigned char *neg_protocol;
       unsigned int len;
-      SSL_get0_alpn_selected(backend->handle, &neg_protocol, &len);
+      SSL_get0_alpn_selected(octx->ssl, &neg_protocol, &len);
 
       return Curl_alpn_set_negotiated(cf, data, neg_protocol, len);
     }
@@ -4190,20 +4230,12 @@ static void infof_certstack(struct Curl_easy *data, const SSL *ssl)
 #define infof_certstack(data, ssl)
 #endif
 
-/*
- * Get the server cert, verify it and show it, etc., only call failf() if the
- * 'strict' argument is TRUE as otherwise all this is for informational
- * purposes only!
- *
- * We check certificates to authenticate the server; otherwise we risk
- * man-in-the-middle attack.
- */
-static CURLcode servercert(struct Curl_cfilter *cf,
-                           struct Curl_easy *data,
-                           bool strict)
+CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf,
+                                  struct Curl_easy *data,
+                                  struct ossl_ctx *octx,
+                                  struct ssl_peer *peer)
 {
   struct connectdata *conn = cf->conn;
-  struct ssl_connect_data *connssl = cf->ctx;
   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   CURLcode result = CURLE_OK;
@@ -4215,10 +4247,9 @@ static CURLcode servercert(struct Curl_cfilter *cf,
   char buffer[2048];
   const char *ptr;
   BIO *mem = BIO_new(BIO_s_mem());
-  struct ossl_ssl_backend_data *backend =
-    (struct ossl_ssl_backend_data *)connssl->backend;
+  bool strict = (conn_config->verifypeer || conn_config->verifyhost);
 
-  DEBUGASSERT(backend);
+  DEBUGASSERT(octx);
 
   if(!mem) {
     failf(data,
@@ -4231,10 +4262,10 @@ static CURLcode servercert(struct Curl_cfilter *cf,
 
   if(data->set.ssl.certinfo)
     /* asked to gather certificate info */
-    (void)Curl_ossl_certchain(data, backend->handle);
+    (void)Curl_ossl_certchain(data, octx->ssl);
 
-  backend->server_cert = SSL_get1_peer_certificate(backend->handle);
-  if(!backend->server_cert) {
+  octx->server_cert = SSL_get1_peer_certificate(octx->ssl);
+  if(!octx->server_cert) {
     BIO_free(mem);
     if(!strict)
       return CURLE_OK;
@@ -4246,19 +4277,19 @@ static CURLcode servercert(struct Curl_cfilter *cf,
   infof(data, "%s certificate:",
         Curl_ssl_cf_is_proxy(cf)? "Proxy" : "Server");
 
-  rc = x509_name_oneline(X509_get_subject_name(backend->server_cert),
+  rc = x509_name_oneline(X509_get_subject_name(octx->server_cert),
                          buffer, sizeof(buffer));
   infof(data, " subject: %s", rc?"[NONE]":buffer);
 
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
   {
     long len;
-    ASN1_TIME_print(mem, X509_get0_notBefore(backend->server_cert));
+    ASN1_TIME_print(mem, X509_get0_notBefore(octx->server_cert));
     len = BIO_get_mem_data(mem, (char **) &ptr);
     infof(data, " start date: %.*s", (int)len, ptr);
     (void)BIO_reset(mem);
 
-    ASN1_TIME_print(mem, X509_get0_notAfter(backend->server_cert));
+    ASN1_TIME_print(mem, X509_get0_notAfter(octx->server_cert));
     len = BIO_get_mem_data(mem, (char **) &ptr);
     infof(data, " expire date: %.*s", (int)len, ptr);
     (void)BIO_reset(mem);
@@ -4268,16 +4299,15 @@ static CURLcode servercert(struct Curl_cfilter *cf,
   BIO_free(mem);
 
   if(conn_config->verifyhost) {
-    result = Curl_ossl_verifyhost(data, conn, &connssl->peer,
-                                  backend->server_cert);
+    result = Curl_ossl_verifyhost(data, conn, peer, octx->server_cert);
     if(result) {
-      X509_free(backend->server_cert);
-      backend->server_cert = NULL;
+      X509_free(octx->server_cert);
+      octx->server_cert = NULL;
       return result;
     }
   }
 
-  rc = x509_name_oneline(X509_get_issuer_name(backend->server_cert),
+  rc = x509_name_oneline(X509_get_issuer_name(octx->server_cert),
                          buffer, sizeof(buffer));
   if(rc) {
     if(strict)
@@ -4301,8 +4331,8 @@ static CURLcode servercert(struct Curl_cfilter *cf,
                 " error %s",
                 ossl_strerror(ERR_get_error(), error_buffer,
                               sizeof(error_buffer)) );
-          X509_free(backend->server_cert);
-          backend->server_cert = NULL;
+          X509_free(octx->server_cert);
+          octx->server_cert = NULL;
           return CURLE_OUT_OF_MEMORY;
         }
       }
@@ -4314,8 +4344,8 @@ static CURLcode servercert(struct Curl_cfilter *cf,
                 " error %s",
                 ossl_strerror(ERR_get_error(), error_buffer,
                               sizeof(error_buffer)) );
-          X509_free(backend->server_cert);
-          backend->server_cert = NULL;
+          X509_free(octx->server_cert);
+          octx->server_cert = NULL;
           return CURLE_OUT_OF_MEMORY;
         }
 
@@ -4324,8 +4354,8 @@ static CURLcode servercert(struct Curl_cfilter *cf,
             failf(data, "SSL: Unable to open issuer cert (%s)",
                   conn_config->issuercert);
           BIO_free(fp);
-          X509_free(backend->server_cert);
-          backend->server_cert = NULL;
+          X509_free(octx->server_cert);
+          octx->server_cert = NULL;
           return CURLE_SSL_ISSUER_ERROR;
         }
       }
@@ -4337,19 +4367,19 @@ static CURLcode servercert(struct Curl_cfilter *cf,
                 conn_config->issuercert);
         BIO_free(fp);
         X509_free(issuer);
-        X509_free(backend->server_cert);
-        backend->server_cert = NULL;
+        X509_free(octx->server_cert);
+        octx->server_cert = NULL;
         return CURLE_SSL_ISSUER_ERROR;
       }
 
-      if(X509_check_issued(issuer, backend->server_cert) != X509_V_OK) {
+      if(X509_check_issued(issuer, octx->server_cert) != X509_V_OK) {
         if(strict)
           failf(data, "SSL: Certificate issuer check failed (%s)",
                 conn_config->issuercert);
         BIO_free(fp);
         X509_free(issuer);
-        X509_free(backend->server_cert);
-        backend->server_cert = NULL;
+        X509_free(octx->server_cert);
+        octx->server_cert = NULL;
         return CURLE_SSL_ISSUER_ERROR;
       }
 
@@ -4359,7 +4389,7 @@ static CURLcode servercert(struct Curl_cfilter *cf,
       X509_free(issuer);
     }
 
-    lerr = SSL_get_verify_result(backend->handle);
+    lerr = SSL_get_verify_result(octx->ssl);
     ssl_config->certverifyresult = lerr;
     if(lerr != X509_V_OK) {
       if(conn_config->verifypeer) {
@@ -4379,11 +4409,11 @@ static CURLcode servercert(struct Curl_cfilter *cf,
       infof(data, " SSL certificate verify ok.");
   }
 
-  infof_certstack(data, backend->handle);
+  infof_certstack(data, octx->ssl);
 
 #if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
   !defined(OPENSSL_NO_OCSP)
-  if(conn_config->verifystatus && !connssl->reused_session) {
+  if(conn_config->verifystatus && !octx->reused_session) {
     /* don't do this after Session ID reuse */
     result = verifystatus(cf, data);
     if(result) {
@@ -4393,7 +4423,8 @@ static CURLcode servercert(struct Curl_cfilter *cf,
         void *old_ssl_sessionid = NULL;
         bool incache;
         Curl_ssl_sessionid_lock(data);
-        incache = !(Curl_ssl_getsessionid(cf, data, &old_ssl_sessionid, NULL));
+        incache = !(Curl_ssl_getsessionid(cf, data, peer,
+                                          &old_ssl_sessionid, NULL));
         if(incache) {
           infof(data, "Remove session ID again from cache");
           Curl_ssl_delsessionid(data, old_ssl_sessionid);
@@ -4401,8 +4432,8 @@ static CURLcode servercert(struct Curl_cfilter *cf,
         Curl_ssl_sessionid_unlock(data);
       }
 
-      X509_free(backend->server_cert);
-      backend->server_cert = NULL;
+      X509_free(octx->server_cert);
+      octx->server_cert = NULL;
       return result;
     }
   }
@@ -4420,14 +4451,13 @@ static CURLcode servercert(struct Curl_cfilter *cf,
   ptr = data->set.str[STRING_SSL_PINNEDPUBLICKEY];
 #endif
   if(!result && ptr) {
-    result = ossl_pkp_pin_peer_pubkey(data, backend->server_cert, ptr);
+    result = ossl_pkp_pin_peer_pubkey(data, octx->server_cert, ptr);
     if(result)
       failf(data, "SSL: public key does not match pinned public key");
   }
 
-  X509_free(backend->server_cert);
-  backend->server_cert = NULL;
-  connssl->connecting_state = ssl_connect_done;
+  X509_free(octx->server_cert);
+  octx->server_cert = NULL;
 
   return result;
 }
@@ -4437,7 +4467,7 @@ static CURLcode ossl_connect_step3(struct Curl_cfilter *cf,
 {
   CURLcode result = CURLE_OK;
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+  struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
 
   DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
 
@@ -4448,9 +4478,7 @@ static CURLcode ossl_connect_step3(struct Curl_cfilter *cf,
    * operations.
    */
 
-  result = servercert(cf, data, conn_config->verifypeer ||
-                      conn_config->verifyhost);
-
+  result = Curl_oss_check_peer_cert(cf, data, octx, &connssl->peer);
   if(!result)
     connssl->connecting_state = ssl_connect_done;
 
@@ -4590,12 +4618,11 @@ static bool ossl_data_pending(struct Curl_cfilter *cf,
                               const struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ossl_ssl_backend_data *backend =
-    (struct ossl_ssl_backend_data *)connssl->backend;
+  struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
 
   (void)data;
-  DEBUGASSERT(connssl && backend);
-  if(backend->handle && SSL_pending(backend->handle))
+  DEBUGASSERT(connssl && octx);
+  if(octx->ssl && SSL_pending(octx->ssl))
     return TRUE;
   return FALSE;
 }
@@ -4614,19 +4641,18 @@ static ssize_t ossl_send(struct Curl_cfilter *cf,
   int memlen;
   int rc;
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ossl_ssl_backend_data *backend =
-    (struct ossl_ssl_backend_data *)connssl->backend;
+  struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
 
   (void)data;
-  DEBUGASSERT(backend);
+  DEBUGASSERT(octx);
 
   ERR_clear_error();
 
   memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len;
-  rc = SSL_write(backend->handle, mem, memlen);
+  rc = SSL_write(octx->ssl, mem, memlen);
 
   if(rc <= 0) {
-    err = SSL_get_error(backend->handle, rc);
+    err = SSL_get_error(octx->ssl, rc);
 
     switch(err) {
     case SSL_ERROR_WANT_READ:
@@ -4641,7 +4667,7 @@ static ssize_t ossl_send(struct Curl_cfilter *cf,
     {
       int sockerr = SOCKERRNO;
 
-      if(backend->io_result == CURLE_AGAIN) {
+      if(octx->io_result == CURLE_AGAIN) {
         *curlcode = CURLE_AGAIN;
         rc = -1;
         goto out;
@@ -4698,20 +4724,19 @@ static ssize_t ossl_recv(struct Curl_cfilter *cf,
   int buffsize;
   struct connectdata *conn = cf->conn;
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ossl_ssl_backend_data *backend =
-    (struct ossl_ssl_backend_data *)connssl->backend;
+  struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
 
   (void)data;
-  DEBUGASSERT(backend);
+  DEBUGASSERT(octx);
 
   ERR_clear_error();
 
   buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize;
-  nread = (ssize_t)SSL_read(backend->handle, buf, buffsize);
+  nread = (ssize_t)SSL_read(octx->ssl, buf, buffsize);
 
   if(nread <= 0) {
     /* failed SSL_read */
-    int err = SSL_get_error(backend->handle, (int)nread);
+    int err = SSL_get_error(octx->ssl, (int)nread);
 
     switch(err) {
     case SSL_ERROR_NONE: /* this is not an error */
@@ -4733,7 +4758,7 @@ static ssize_t ossl_recv(struct Curl_cfilter *cf,
       /* openssl/ssl.h for SSL_ERROR_SYSCALL says "look at error stack/return
          value/errno" */
       /* https://www.openssl.org/docs/crypto/ERR_get_error.html */
-      if(backend->io_result == CURLE_AGAIN) {
+      if(octx->io_result == CURLE_AGAIN) {
         *curlcode = CURLE_AGAIN;
         nread = -1;
         goto out;
@@ -4924,11 +4949,10 @@ static void *ossl_get_internals(struct ssl_connect_data *connssl,
                                 CURLINFO info)
 {
   /* Legacy: CURLINFO_TLS_SESSION must return an SSL_CTX pointer. */
-  struct ossl_ssl_backend_data *backend =
-    (struct ossl_ssl_backend_data *)connssl->backend;
-  DEBUGASSERT(backend);
+  struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
+  DEBUGASSERT(octx);
   return info == CURLINFO_TLS_SESSION ?
-    (void *)backend->ctx : (void *)backend->handle;
+    (void *)octx->ssl_ctx : (void *)octx->ssl;
 }
 
 static void ossl_free_multi_ssl_backend_data(
@@ -4958,7 +4982,7 @@ const struct Curl_ssl Curl_ssl_openssl = {
 #endif
   SSLSUPP_HTTPS_PROXY,
 
-  sizeof(struct ossl_ssl_backend_data),
+  sizeof(struct ossl_ctx),
 
   ossl_init,                /* init */
   ossl_cleanup,             /* cleanup */
index e802363a4ada97ecf7b2450952a1bc12e705eb72..47bb282a9b4d5a670c5e86fa8b5520e2789e0e07 100644 (file)
 
 #include "urldata.h"
 
+/* Struct to hold a Curl OpenSSL instance */
+struct ossl_ctx {
+  /* these ones requires specific SSL-types */
+  SSL_CTX* ssl_ctx;
+  SSL*     ssl;
+  X509*    server_cert;
+  BIO_METHOD *bio_method;
+  CURLcode io_result;       /* result of last BIO cfilter operation */
+#ifndef HAVE_KEYLOG_CALLBACK
+  /* Set to true once a valid keylog entry has been created to avoid dupes. */
+  BIT(keylog_done);
+#endif
+  BIT(x509_store_setup);            /* x509 store has been set up */
+  BIT(reused_session);              /* session-ID was reused for this */
+};
+
+typedef CURLcode Curl_ossl_ctx_setup_cb(struct Curl_cfilter *cf,
+                                        struct Curl_easy *data,
+                                        void *user_data);
+
+typedef int Curl_ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid);
+
+CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
+                            struct Curl_cfilter *cf,
+                            struct Curl_easy *data,
+                            struct ssl_peer *peer,
+                            int transport, /* TCP or QUIC */
+                            const unsigned char *alpn, size_t alpn_len,
+                            Curl_ossl_ctx_setup_cb *cb_setup,
+                            void *cb_user_data,
+                            Curl_ossl_new_session_cb *cb_new_session,
+                            void *ssl_user_data);
+
 #if (OPENSSL_VERSION_NUMBER < 0x30000000L)
 #define SSL_get1_peer_certificate SSL_get_peer_certificate
 #endif
@@ -66,5 +99,23 @@ CURLcode Curl_ossl_ctx_configure(struct Curl_cfilter *cf,
                                  struct Curl_easy *data,
                                  SSL_CTX *ssl_ctx);
 
+/*
+ * Add a new session to the cache.
+ */
+CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf,
+                               struct Curl_easy *data,
+                               const struct ssl_peer *peer,
+                               SSL_SESSION *ssl_sessionid);
+
+/*
+ * Get the server cert, verify it and show it, etc., only call failf() if
+ * ssl config verifypeer or -host is set. Otherwise all this is for
+ * informational purposes only!
+ */
+CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf,
+                                  struct Curl_easy *data,
+                                  struct ossl_ctx *octx,
+                                  struct ssl_peer *peer);
+
 #endif /* USE_OPENSSL */
 #endif /* HEADER_CURL_SSLUSE_H */
index 76b855d928c121a6362f93eab30dba6e3027ac80..cec7fa9710278cfd80bb633617292d63ac93e22d 100644 (file)
@@ -1071,7 +1071,7 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
   DEBUGASSERT(backend);
   DEBUGF(infof(data,
                "schannel: SSL/TLS connection with %s port %d (step 1/3)",
-               connssl->peer.hostname, connssl->port));
+               connssl->peer.hostname, connssl->peer.port));
 
   if(curlx_verify_windows_version(5, 1, 0, PLATFORM_WINNT,
                                   VERSION_LESS_THAN_EQUAL)) {
@@ -1129,7 +1129,8 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
   /* check for an existing reusable credential handle */
   if(ssl_config->primary.sessionid) {
     Curl_ssl_sessionid_lock(data);
-    if(!Curl_ssl_getsessionid(cf, data, (void **)&old_cred, NULL)) {
+    if(!Curl_ssl_getsessionid(cf, data, &connssl->peer,
+                              (void **)&old_cred, NULL)) {
       backend->cred = old_cred;
       DEBUGF(infof(data, "schannel: reusing existing credential handle"));
 
@@ -1335,7 +1336,7 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
 
   DEBUGF(infof(data,
                "schannel: SSL/TLS connection with %s port %d (step 2/3)",
-               connssl->peer.hostname, connssl->port));
+               connssl->peer.hostname, connssl->peer.port));
 
   if(!backend->cred || !backend->ctxt)
     return CURLE_SSL_CONNECT_ERROR;
@@ -1693,7 +1694,7 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
 
   DEBUGF(infof(data,
                "schannel: SSL/TLS connection with %s port %d (step 3/3)",
-               connssl->peer.hostname, connssl->port));
+               connssl->peer.hostname, connssl->peer.port));
 
   if(!backend->cred)
     return CURLE_SSL_CONNECT_ERROR;
@@ -1755,7 +1756,8 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
     struct Curl_schannel_cred *old_cred = NULL;
 
     Curl_ssl_sessionid_lock(data);
-    incache = !(Curl_ssl_getsessionid(cf, data, (void **)&old_cred, NULL));
+    incache = !(Curl_ssl_getsessionid(cf, data, &connssl->peer,
+                                      (void **)&old_cred, NULL));
     if(incache) {
       if(old_cred != backend->cred) {
         DEBUGF(infof(data,
@@ -1766,7 +1768,7 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
       }
     }
     if(!incache) {
-      result = Curl_ssl_addsessionid(cf, data, backend->cred,
+      result = Curl_ssl_addsessionid(cf, data, &connssl->peer, backend->cred,
                                      sizeof(struct Curl_schannel_cred),
                                      &added);
       if(result) {
@@ -2493,7 +2495,7 @@ static int schannel_shutdown(struct Curl_cfilter *cf,
 
   if(backend->ctxt) {
     infof(data, "schannel: shutting down SSL/TLS connection with %s port %d",
-          connssl->peer.hostname, connssl->port);
+          connssl->peer.hostname, connssl->peer.port);
   }
 
   if(backend->cred && backend->ctxt) {
index 67f534c675ab5538d77ffd3ac70d99fa5d4da106..b7fc7c02224caee1215ebfb4c7a623e783b261af 100644 (file)
@@ -2047,8 +2047,8 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf,
     size_t ssl_sessionid_len;
 
     Curl_ssl_sessionid_lock(data);
-    if(!Curl_ssl_getsessionid(cf, data, (void **)&ssl_sessionid,
-                              &ssl_sessionid_len)) {
+    if(!Curl_ssl_getsessionid(cf, data, &connssl->peer,
+                              (void **)&ssl_sessionid, &ssl_sessionid_len)) {
       /* we got a session id, use it! */
       err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid, ssl_sessionid_len);
       Curl_ssl_sessionid_unlock(data);
@@ -2067,7 +2067,7 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf,
         aprintf("%s:%d:%d:%s:%d",
                 ssl_cafile ? ssl_cafile : "(blob memory)",
                 verifypeer, conn_config->verifyhost, connssl->peer.hostname,
-                connssl->port);
+                connssl->peer.port);
       ssl_sessionid_len = strlen(ssl_sessionid);
 
       err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid, ssl_sessionid_len);
@@ -2077,7 +2077,7 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf,
         return CURLE_SSL_CONNECT_ERROR;
       }
 
-      result = Curl_ssl_addsessionid(cf, data, ssl_sessionid,
+      result = Curl_ssl_addsessionid(cf, data, &connssl->peer, ssl_sessionid,
                                      ssl_sessionid_len, NULL);
       Curl_ssl_sessionid_unlock(data);
       if(result) {
index d13a3cb1b788984a9351ec6e88a1afdda56efa36..28d15953905f37073e02b31be99baab4ba687bf8 100644 (file)
@@ -534,10 +534,10 @@ void Curl_ssl_sessionid_unlock(struct Curl_easy *data)
  */
 bool Curl_ssl_getsessionid(struct Curl_cfilter *cf,
                            struct Curl_easy *data,
+                           const struct ssl_peer *peer,
                            void **ssl_sessionid,
                            size_t *idsize) /* set 0 if unknown */
 {
-  struct ssl_connect_data *connssl = cf->ctx;
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
   struct Curl_ssl_session *check;
@@ -567,14 +567,15 @@ bool Curl_ssl_getsessionid(struct Curl_cfilter *cf,
     if(!check->sessionid)
       /* not session ID means blank entry */
       continue;
-    if(strcasecompare(connssl->peer.hostname, check->name) &&
+    if(strcasecompare(peer->hostname, check->name) &&
        ((!cf->conn->bits.conn_to_host && !check->conn_to_host) ||
         (cf->conn->bits.conn_to_host && check->conn_to_host &&
          strcasecompare(cf->conn->conn_to_host.name, check->conn_to_host))) &&
        ((!cf->conn->bits.conn_to_port && check->conn_to_port == -1) ||
         (cf->conn->bits.conn_to_port && check->conn_to_port != -1 &&
          cf->conn->conn_to_port == check->conn_to_port)) &&
-       (connssl->port == check->remote_port) &&
+       (peer->port == check->remote_port) &&
+       (peer->transport == check->transport) &&
        strcasecompare(cf->conn->handler->scheme, check->scheme) &&
        match_ssl_primary_config(data, conn_config, &check->ssl_config)) {
       /* yes, we have a session ID! */
@@ -591,8 +592,7 @@ bool Curl_ssl_getsessionid(struct Curl_cfilter *cf,
   DEBUGF(infof(data, "%s Session ID in cache for %s %s://%s:%d",
                no_match? "Didn't find": "Found",
                Curl_ssl_cf_is_proxy(cf) ? "proxy" : "host",
-               cf->conn->handler->scheme, connssl->peer.hostname,
-               connssl->port));
+               cf->conn->handler->scheme, peer->hostname, peer->port));
   return no_match;
 }
 
@@ -642,11 +642,11 @@ void Curl_ssl_delsessionid(struct Curl_easy *data, void *ssl_sessionid)
  */
 CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf,
                                struct Curl_easy *data,
+                               const struct ssl_peer *peer,
                                void *ssl_sessionid,
                                size_t idsize,
                                bool *added)
 {
-  struct ssl_connect_data *connssl = cf->ctx;
   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   size_t i;
@@ -668,7 +668,7 @@ CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf,
   (void)ssl_config;
   DEBUGASSERT(ssl_config->primary.sessionid);
 
-  clone_host = strdup(connssl->peer.hostname);
+  clone_host = strdup(peer->hostname);
   if(!clone_host)
     return CURLE_OUT_OF_MEMORY; /* bail out */
 
@@ -723,8 +723,9 @@ CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf,
   store->conn_to_host = clone_conn_to_host; /* clone connect to host name */
   store->conn_to_port = conn_to_port; /* connect to port number */
   /* port number */
-  store->remote_port = connssl->port;
+  store->remote_port = peer->port;
   store->scheme = cf->conn->handler->scheme;
+  store->transport = peer->transport;
 
   if(!clone_ssl_primary_config(conn_config, &store->ssl_config)) {
     Curl_free_primary_ssl_config(&store->ssl_config);
@@ -1549,9 +1550,9 @@ static ssl_peer_type get_peer_type(const char *hostname)
   return CURL_SSL_PEER_DNS;
 }
 
-CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf)
+CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf,
+                            int transport)
 {
-  struct ssl_connect_data *connssl = cf->ctx;
   const char *ehostname, *edispname;
   int eport;
 
@@ -1614,7 +1615,8 @@ CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf)
     }
 
   }
-  connssl->port = eport;
+  peer->port = eport;
+  peer->transport = transport;
   return CURLE_OK;
 }
 
@@ -1667,7 +1669,7 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf,
     goto out;
 
   *done = FALSE;
-  result = Curl_ssl_peer_init(&connssl->peer, cf);
+  result = Curl_ssl_peer_init(&connssl->peer, cf, TRNSPRT_TCP);
   if(result)
     goto out;
 
@@ -1857,7 +1859,7 @@ struct Curl_cftype Curl_cft_ssl = {
 
 struct Curl_cftype Curl_cft_ssl_proxy = {
   "SSL-PROXY",
-  CF_TYPE_SSL,
+  CF_TYPE_SSL|CF_TYPE_PROXY,
   CURL_LOG_LVL_NONE,
   ssl_cf_destroy,
   ssl_cf_connect,
@@ -2033,12 +2035,7 @@ CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data,
 
 bool Curl_ssl_cf_is_proxy(struct Curl_cfilter *cf)
 {
-#ifndef CURL_DISABLE_PROXY
-  return (cf->cft == &Curl_cft_ssl_proxy);
-#else
-  (void)cf;
-  return FALSE;
-#endif
+  return (cf->cft->flags & CF_TYPE_SSL) && (cf->cft->flags & CF_TYPE_PROXY);
 }
 
 struct ssl_config_data *
index 744bbf8fd9eecda8c4ea4c59c2fc805251dc8c65..cd7eeeca174631e8bb159485fb649b563347f309 100644 (file)
@@ -107,7 +107,8 @@ void Curl_ssl_conn_config_update(struct Curl_easy *data, bool for_proxy);
 /**
  * Init SSL peer information for filter. Can be called repeatedly.
  */
-CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf);
+CURLcode Curl_ssl_peer_init(struct ssl_peer *peer,
+                            struct Curl_cfilter *cf, int transport);
 /**
  * Free all allocated data and reset peer information.
  */
index 0361fa95a8ddf72059f53f4fe329bc68d0a201ee..8aca1d33311648e0bf6e871f78a74c67767354b2 100644 (file)
@@ -73,9 +73,7 @@ struct ssl_connect_data {
   void *backend;                    /* vtls backend specific props */
   struct cf_call_data call_data;    /* data handle used in current call */
   struct curltime handshake_done;   /* time when handshake finished */
-  int port;                         /* remote port at origin */
   BIT(use_alpn);                    /* if ALPN shall be used in handshake */
-  BIT(reused_session);              /* session-ID was reused for this */
   BIT(peer_closed);                 /* peer has closed connection */
 };
 
@@ -181,6 +179,7 @@ bool Curl_ssl_cf_is_proxy(struct Curl_cfilter *cf);
  */
 bool Curl_ssl_getsessionid(struct Curl_cfilter *cf,
                            struct Curl_easy *data,
+                           const struct ssl_peer *peer,
                            void **ssl_sessionid,
                            size_t *idsize); /* set 0 if unknown */
 /* add a new session ID
@@ -190,6 +189,7 @@ bool Curl_ssl_getsessionid(struct Curl_cfilter *cf,
  */
 CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf,
                                struct Curl_easy *data,
+                               const struct ssl_peer *peer,
                                void *ssl_sessionid,
                                size_t idsize,
                                bool *added);
index 31ef8c75b671cb8e81dba58b89b1049befa6d5b8..48cf132a378ec3518efa27d636e0ef4914d51c37 100644 (file)
@@ -409,11 +409,11 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
 #if defined(WOLFSSL_ALLOW_TLSV10) && !defined(NO_OLD_TLS)
     req_method = TLSv1_client_method();
     use_sni(TRUE);
+    break;
 #else
     failf(data, "wolfSSL does not support TLS 1.0");
     return CURLE_NOT_BUILT_IN;
 #endif
-    break;
   case CURL_SSLVERSION_TLSv1_1:
 #ifndef NO_OLD_TLS
     req_method = TLSv1_1_client_method();
@@ -481,6 +481,7 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
       return CURLE_SSL_CONNECT_ERROR;
     }
 #endif
+    FALLTHROUGH();
   default:
     break;
   }
@@ -711,7 +712,8 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
     void *ssl_sessionid = NULL;
 
     Curl_ssl_sessionid_lock(data);
-    if(!Curl_ssl_getsessionid(cf, data, &ssl_sessionid, NULL)) {
+    if(!Curl_ssl_getsessionid(cf, data, &connssl->peer,
+                              &ssl_sessionid, NULL)) {
       /* we got a session id, use it! */
       if(!SSL_set_session(backend->handle, ssl_sessionid)) {
         Curl_ssl_delsessionid(data, ssl_sessionid);
@@ -968,7 +970,8 @@ wolfssl_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
 
     if(our_ssl_sessionid) {
       Curl_ssl_sessionid_lock(data);
-      incache = !(Curl_ssl_getsessionid(cf, data, &old_ssl_sessionid, NULL));
+      incache = !(Curl_ssl_getsessionid(cf, data, &connssl->peer,
+                                        &old_ssl_sessionid, NULL));
       if(incache) {
         if(old_ssl_sessionid != our_ssl_sessionid) {
           infof(data, "old SSL session ID is stale, removing");
@@ -978,7 +981,8 @@ wolfssl_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
       }
 
       if(!incache) {
-        result = Curl_ssl_addsessionid(cf, data, our_ssl_sessionid, 0, NULL);
+        result = Curl_ssl_addsessionid(cf, data, &connssl->peer,
+                                       our_ssl_sessionid, 0, NULL);
         if(result) {
           Curl_ssl_sessionid_unlock(data);
           wolfSSL_SESSION_free(our_ssl_sessionid);
index 0309276cf83656c18bc36c76aa43b29a301b0edd..2750ba77763b47fcad99a0edde742ec55939c572 100644 (file)
@@ -142,7 +142,8 @@ static size_t write_cb(char *ptr, size_t size, size_t nmemb, void *opaque)
 }
 
 static void add_transfer(CURLM *multi, CURLSH *share,
-                         struct curl_slist *resolve, const char *url)
+                         struct curl_slist *resolve,
+                         const char *url, int http_version)
 {
   CURL *easy;
   CURLMcode mc;
@@ -159,7 +160,7 @@ static void add_transfer(CURLM *multi, CURLSH *share,
   curl_easy_setopt(easy, CURLOPT_NOSIGNAL, 1L);
   curl_easy_setopt(easy, CURLOPT_AUTOREFERER, 1L);
   curl_easy_setopt(easy, CURLOPT_FAILONERROR, 1L);
-  curl_easy_setopt(easy, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
+  curl_easy_setopt(easy, CURLOPT_HTTP_VERSION, http_version);
   curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, write_cb);
   curl_easy_setopt(easy, CURLOPT_WRITEDATA, NULL);
   curl_easy_setopt(easy, CURLOPT_HTTPGET, 1L);
@@ -190,13 +191,19 @@ int main(int argc, char *argv[])
   int msgs_in_queue;
   int add_more, waits, ongoing = 0;
   char *host, *port;
+  int http_version = CURL_HTTP_VERSION_1_1;
 
-  if(argc != 2) {
-    fprintf(stderr, "%s URL\n", argv[0]);
+  if(argc != 3) {
+    fprintf(stderr, "%s proto URL\n", argv[0]);
     exit(2);
   }
 
-  url = argv[1];
+  if(!strcmp("h2", argv[1]))
+    http_version = CURL_HTTP_VERSION_2;
+  else if(!strcmp("h3", argv[1]))
+    http_version = CURL_HTTP_VERSION_3ONLY;
+
+  url = argv[2];
   cu = curl_url();
   if(!cu) {
     fprintf(stderr, "out of memory\n");
@@ -234,7 +241,7 @@ int main(int argc, char *argv[])
   curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);
 
 
-  add_transfer(multi, share, &resolve, url);
+  add_transfer(multi, share, &resolve, url, http_version);
   ++ongoing;
   add_more = 6;
   waits = 3;
@@ -260,7 +267,7 @@ int main(int argc, char *argv[])
     }
     else {
       while(add_more) {
-        add_transfer(multi, share, &resolve, url);
+        add_transfer(multi, share, &resolve, url, http_version);
         ++ongoing;
         --add_more;
       }
index e608c961261258584598a192af58e81dffe0cbc4..34b863f3c079ac5973be2cb84463431773766ef8 100644 (file)
@@ -434,13 +434,14 @@ class TestDownload:
         assert r.exit_code == 0, f'{client.dump_logs()}'
 
     # Special client that tests TLS session reuse in parallel transfers
-    def test_02_26_session_shared_reuse(self, env: Env, httpd, repeat):
-        curl = CurlClient(env=env)
-        url = f'https://{env.domain1}:{env.https_port}/data-100k'
+    # TODO: just uses a single connection for h2/h3. Not sure how to prevent that
+    @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3'])
+    def test_02_26_session_shared_reuse(self, env: Env, proto, httpd, nghttpx, repeat):
+        url = f'https://{env.authority_for(env.domain1, proto)}/data-100k'
         client = LocalClient(name='tls-session-reuse', env=env)
         if not client.exists():
             pytest.skip(f'example client not built: {client.name}')
-        r = client.run(args=[url])
+        r = client.run(args=[proto, url])
         r.check_exit_code(0)
 
     # test on paused transfers, based on issue #11982