]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
gnutls: use common gnutls init and verify code for ngtcp2
authorStefan Eissing <stefan@eissing.org>
Wed, 30 Nov 2022 13:42:37 +0000 (14:42 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Thu, 1 Dec 2022 09:57:36 +0000 (10:57 +0100)
Closes #10007

lib/vquic/ngtcp2.c
lib/vquic/ngtcp2.h
lib/vtls/gtls.c
lib/vtls/gtls.h

index 33cf474ae80120ffcad9abbc6229f4c376f68d05..f16b469946247f9d401470cb205a29df1cf22156 100644 (file)
@@ -27,6 +27,7 @@
 #ifdef USE_NGTCP2
 #include <ngtcp2/ngtcp2.h>
 #include <nghttp3/nghttp3.h>
+
 #ifdef USE_OPENSSL
 #include <openssl/err.h>
 #ifdef OPENSSL_IS_BORINGSSL
@@ -42,6 +43,7 @@
 #include <ngtcp2/ngtcp2_crypto_wolfssl.h>
 #include "vtls/wolfssl.h"
 #endif
+
 #include "urldata.h"
 #include "sendf.h"
 #include "strdup.h"
@@ -321,13 +323,17 @@ static CURLcode quic_set_client_cert(struct Curl_easy *data,
 
 /** SSL callbacks ***/
 
-static int quic_init_ssl(struct quicsocket *qs)
+static CURLcode quic_init_ssl(struct quicsocket *qs,
+                              struct Curl_easy *data,
+                              struct connectdata *conn)
 {
   const uint8_t *alpn = NULL;
   size_t alpnlen = 0;
   /* this will need some attention when HTTPS proxy over QUIC get fixed */
   const char * const hostname = qs->conn->host.name;
 
+  (void)data;
+  (void)conn;
   DEBUGASSERT(!qs->ssl);
   qs->ssl = SSL_new(qs->sslctx);
 
@@ -342,64 +348,49 @@ static int quic_init_ssl(struct quicsocket *qs)
 
   /* set SNI */
   SSL_set_tlsext_host_name(qs->ssl, hostname);
-  return 0;
+  return CURLE_OK;
 }
 #elif defined(USE_GNUTLS)
-static int quic_init_ssl(struct quicsocket *qs)
+static CURLcode quic_init_ssl(struct quicsocket *qs,
+                              struct Curl_easy *data,
+                              struct connectdata *conn)
 {
+  CURLcode result;
   gnutls_datum_t alpn[2];
   /* this will need some attention when HTTPS proxy over QUIC get fixed */
   const char * const hostname = qs->conn->host.name;
+  long * const pverifyresult = &data->set.ssl.certverifyresult;
   int rc;
 
-  DEBUGASSERT(!qs->ssl);
+  DEBUGASSERT(qs->gtls == NULL);
+  qs->gtls = calloc(1, sizeof(*(qs->gtls)));
+  if(!qs->gtls)
+    return CURLE_OUT_OF_MEMORY;
 
-  gnutls_init(&qs->ssl, GNUTLS_CLIENT);
-  gnutls_session_set_ptr(qs->ssl, &qs->conn_ref);
+  result = gtls_client_init(data, &conn->ssl_config, &data->set.ssl,
+                            hostname, qs->gtls, pverifyresult);
+  if(result)
+    return result;
+
+  gnutls_session_set_ptr(qs->gtls->session, &qs->conn_ref);
 
-  if(ngtcp2_crypto_gnutls_configure_client_session(qs->ssl) != 0) {
+  if(ngtcp2_crypto_gnutls_configure_client_session(qs->gtls->session) != 0) {
     H3BUGF(fprintf(stderr,
                    "ngtcp2_crypto_gnutls_configure_client_session failed\n"));
-    return 1;
+    return CURLE_QUIC_CONNECT_ERROR;
   }
 
-  rc = gnutls_priority_set_direct(qs->ssl, QUIC_PRIORITY, NULL);
+  rc = gnutls_priority_set_direct(qs->gtls->session, QUIC_PRIORITY, NULL);
   if(rc < 0) {
     H3BUGF(fprintf(stderr, "gnutls_priority_set_direct failed: %s\n",
                    gnutls_strerror(rc)));
-    return 1;
+    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(qs->ssl, keylog_callback);
-  }
-
-  if(qs->cred)
-    gnutls_certificate_free_credentials(qs->cred);
-
-  rc = gnutls_certificate_allocate_credentials(&qs->cred);
-  if(rc < 0) {
-    H3BUGF(fprintf(stderr,
-                   "gnutls_certificate_allocate_credentials failed: %s\n",
-                   gnutls_strerror(rc)));
-    return 1;
-  }
-
-  rc = gnutls_certificate_set_x509_system_trust(qs->cred);
-  if(rc < 0) {
-    H3BUGF(fprintf(stderr,
-                   "gnutls_certificate_set_x509_system_trust failed: %s\n",
-                   gnutls_strerror(rc)));
-    return 1;
-  }
-
-  rc = gnutls_credentials_set(qs->ssl, GNUTLS_CRD_CERTIFICATE, qs->cred);
-  if(rc < 0) {
-    H3BUGF(fprintf(stderr, "gnutls_credentials_set failed: %s\n",
-                   gnutls_strerror(rc)));
-    return 1;
+    gnutls_session_set_keylog_function(qs->gtls->session, keylog_callback);
   }
 
   /* strip the first byte (the length) from NGHTTP3_ALPN_H3 */
@@ -408,11 +399,9 @@ static int quic_init_ssl(struct quicsocket *qs)
   alpn[1].data = (unsigned char *)H3_ALPN_H3 + 1;
   alpn[1].size = sizeof(H3_ALPN_H3) - 2;
 
-  gnutls_alpn_set_protocols(qs->ssl, alpn, 2, GNUTLS_ALPN_MANDATORY);
+  gnutls_alpn_set_protocols(qs->gtls->session, alpn, 2, GNUTLS_ALPN_MANDATORY);
 
-  /* set SNI */
-  gnutls_server_name_set(qs->ssl, GNUTLS_NAME_DNS, hostname, strlen(hostname));
-  return 0;
+  return CURLE_OK;
 }
 #elif defined(USE_WOLFSSL)
 
@@ -487,13 +476,17 @@ static WOLFSSL_CTX *quic_ssl_ctx(struct Curl_easy *data)
 
 /** SSL callbacks ***/
 
-static int quic_init_ssl(struct quicsocket *qs)
+static CURLcode quic_init_ssl(struct quicsocket *qs,
+                              struct Curl_easy *data,
+                              struct connectdata *conn)
 {
   const uint8_t *alpn = NULL;
   size_t alpnlen = 0;
   /* this will need some attention when HTTPS proxy over QUIC get fixed */
   const char * const hostname = qs->conn->host.name;
 
+  (void)data;
+  (void)conn;
   DEBUGASSERT(!qs->ssl);
   qs->ssl = SSL_new(qs->sslctx);
 
@@ -510,7 +503,7 @@ static int quic_init_ssl(struct quicsocket *qs)
   wolfSSL_UseSNI(qs->ssl, WOLFSSL_SNI_HOST_NAME,
                  hostname, (unsigned short)strlen(hostname));
 
-  return 0;
+  return CURLE_OK;
 }
 #endif /* defined(USE_WOLFSSL) */
 
@@ -815,8 +808,9 @@ CURLcode Curl_quic_connect(struct Curl_easy *data,
     return CURLE_QUIC_CONNECT_ERROR;
 #endif
 
-  if(quic_init_ssl(qs))
-    return CURLE_QUIC_CONNECT_ERROR;
+  result = quic_init_ssl(qs, data, conn);
+  if(result)
+    return result;
 
   qs->dcid.datalen = NGTCP2_MAX_CIDLEN;
   result = Curl_rand(data, qs->dcid.data, NGTCP2_MAX_CIDLEN);
@@ -848,7 +842,11 @@ CURLcode Curl_quic_connect(struct Curl_easy *data,
   if(rc)
     return CURLE_QUIC_CONNECT_ERROR;
 
+#ifdef USE_GNUTLS
+  ngtcp2_conn_set_tls_native_handle(qs->qconn, qs->gtls->session);
+#else
   ngtcp2_conn_set_tls_native_handle(qs->qconn, qs->ssl);
+#endif
 
   ngtcp2_connection_close_error_default(&qs->last_error);
 
@@ -935,29 +933,29 @@ static void qs_disconnect(struct quicsocket *qs)
     close(qs->qlogfd);
     qs->qlogfd = -1;
   }
-  if(qs->ssl)
 #ifdef USE_OPENSSL
+  if(qs->ssl)
     SSL_free(qs->ssl);
+  qs->ssl = NULL;
+  SSL_CTX_free(qs->sslctx);
 #elif defined(USE_GNUTLS)
-    gnutls_deinit(qs->ssl);
+  if(qs->gtls) {
+    if(qs->gtls->cred)
+      gnutls_certificate_free_credentials(qs->gtls->cred);
+    if(qs->gtls->session)
+      gnutls_deinit(qs->gtls->session);
+    free(qs->gtls);
+    qs->gtls = NULL;
+  }
 #elif defined(USE_WOLFSSL)
+  if(qs->ssl)
     wolfSSL_free(qs->ssl);
-#endif
   qs->ssl = NULL;
-#ifdef USE_GNUTLS
-  if(qs->cred) {
-    gnutls_certificate_free_credentials(qs->cred);
-    qs->cred = NULL;
-  }
+  wolfSSL_CTX_free(qs->sslctx);
 #endif
   free(qs->pktbuf);
   nghttp3_conn_del(qs->h3conn);
   ngtcp2_conn_del(qs->qconn);
-#ifdef USE_OPENSSL
-  SSL_CTX_free(qs->sslctx);
-#elif defined(USE_WOLFSSL)
-  wolfSSL_CTX_free(qs->sslctx);
-#endif
 }
 
 void Curl_quic_disconnect(struct Curl_easy *data,
@@ -1675,6 +1673,15 @@ static CURLcode ng_has_connected(struct Curl_easy *data,
                                  struct connectdata *conn, int tempindex)
 {
   CURLcode result = CURLE_OK;
+  const char *hostname, *disp_hostname;
+  int port;
+  char *snihost;
+
+  Curl_conn_get_host(data, FIRSTSOCKET, &hostname, &disp_hostname, &port);
+  snihost = Curl_ssl_snihost(data, hostname, NULL);
+  if(!snihost)
+      return CURLE_PEER_FAILED_VERIFICATION;
+
   conn->recv[FIRSTSOCKET] = ngh3_stream_recv;
   conn->send[FIRSTSOCKET] = ngh3_stream_send;
   conn->handler = &Curl_handler_http3;
@@ -1694,22 +1701,18 @@ static CURLcode ng_has_connected(struct Curl_easy *data,
     X509_free(server_cert);
     if(result)
       return result;
-    infof(data, "Verified certificate just fine");
 #elif defined(USE_GNUTLS)
-    result = Curl_gtls_verifyserver(conn->cfilter[FIRSTSOCKET],
-                                    data, conn->quic->ssl);
+    result = Curl_gtls_verifyserver(data, conn->quic->gtls->session,
+                                    &conn->ssl_config, &data->set.ssl,
+                                    hostname, disp_hostname,
+                                    data->set.str[STRING_SSL_PINNEDPUBLICKEY]);
+    if(result)
+      return result;
 #elif defined(USE_WOLFSSL)
-    const char *hostname, *disp_hostname;
-    int port;
-    char *snihost;
-
-    Curl_conn_get_host(data, FIRSTSOCKET, &hostname, &disp_hostname, &port);
-    snihost = Curl_ssl_snihost(data, hostname, NULL);
-    if(!snihost ||
-       (wolfSSL_check_domain_name(conn->quic->ssl, snihost) == SSL_FAILURE))
+    if(wolfSSL_check_domain_name(conn->quic->ssl, snihost) == SSL_FAILURE)
       return CURLE_PEER_FAILED_VERIFICATION;
-    infof(data, "Verified certificate just fine");
 #endif
+    infof(data, "Verified certificate just fine");
   }
   else
     infof(data, "Skipped certificate verification");
index 6539f5fef3d90e746f1227faa8a9b321b6b76f45..2265999e22f0bc06e0438e8ed156a3cbec191667 100644 (file)
 #include <nghttp3/nghttp3.h>
 #ifdef USE_OPENSSL
 #include <openssl/ssl.h>
-#elif defined(USE_GNUTLS)
-#include <gnutls/gnutls.h>
 #elif defined(USE_WOLFSSL)
 #include <wolfssl/options.h>
 #include <wolfssl/ssl.h>
 #include <wolfssl/quic.h>
 #endif
 
+struct gtls_instance;
+
 struct blocked_pkt {
   const uint8_t *pkt;
   size_t pktlen;
@@ -64,8 +64,7 @@ struct quicsocket {
   SSL_CTX *sslctx;
   SSL *ssl;
 #elif defined(USE_GNUTLS)
-  gnutls_certificate_credentials_t cred;
-  gnutls_session_t ssl;
+  struct gtls_instance *gtls;
 #elif defined(USE_WOLFSSL)
   WOLFSSL_CTX *sslctx;
   WOLFSSL *ssl;
index b55b707d1165e92fa83663a42bf014c9a34eb20a..104dce60935bbc6637bfcc68b7590a703e3d64f8 100644 (file)
 /* The last #include file should be: */
 #include "memdebug.h"
 
-#ifdef HAVE_GNUTLS_SRP
-/* the function exists */
-#ifdef USE_TLS_SRP
-/* the functionality is not disabled */
-#define USE_GNUTLS_SRP
-#endif
-#endif
-
 /* Enable GnuTLS debugging by defining GTLSDEBUG */
 /*#define GTLSDEBUG */
 
@@ -85,11 +77,7 @@ static bool gtls_inited = FALSE;
 # include <gnutls/ocsp.h>
 
 struct ssl_backend_data {
-  gnutls_session_t session;
-  gnutls_certificate_credentials_t cred;
-#ifdef USE_GNUTLS_SRP
-  gnutls_srp_client_credentials_t srp_client_cred;
-#endif
+  struct gtls_instance gtls;
 };
 
 static ssize_t gtls_push(void *s, const void *buf, size_t blen)
@@ -103,7 +91,7 @@ static ssize_t gtls_push(void *s, const void *buf, size_t blen)
   DEBUGASSERT(data);
   nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result);
   if(nwritten < 0) {
-    gnutls_transport_set_errno(connssl->backend->session,
+    gnutls_transport_set_errno(connssl->backend->gtls.session,
                                (CURLE_AGAIN == result)? EAGAIN : EINVAL);
     nwritten = -1;
   }
@@ -121,7 +109,7 @@ static ssize_t gtls_pull(void *s, void *buf, size_t blen)
   DEBUGASSERT(data);
   nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result);
   if(nread < 0) {
-    gnutls_transport_set_errno(connssl->backend->session,
+    gnutls_transport_set_errno(connssl->backend->gtls.session,
                                (CURLE_AGAIN == result)? EAGAIN : EINVAL);
     nread = -1;
   }
@@ -229,7 +217,7 @@ static CURLcode handshake(struct Curl_cfilter *cf,
   curl_socket_t sockfd = cf->conn->sock[cf->sockindex];
 
   DEBUGASSERT(backend);
-  session = backend->session;
+  session = backend->gtls.session;
 
   for(;;) {
     timediff_t timeout_ms;
@@ -334,12 +322,11 @@ static gnutls_x509_crt_fmt_t do_file_type(const char *type)
 #define GNUTLS_SRP "+SRP"
 
 static CURLcode
-set_ssl_version_min_max(struct Curl_cfilter *cf,
-                        struct Curl_easy *data,
+set_ssl_version_min_max(struct Curl_easy *data,
+                        struct ssl_primary_config *conn_config,
                         const char **prioritylist,
                         const char *tls13support)
 {
-  struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   long ssl_version = conn_config->version;
   long ssl_version_max = conn_config->version_max;
 
@@ -407,20 +394,16 @@ set_ssl_version_min_max(struct Curl_cfilter *cf,
   return CURLE_SSL_CONNECT_ERROR;
 }
 
-static CURLcode
-gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
+CURLcode gtls_client_init(struct Curl_easy *data,
+                          struct ssl_primary_config *config,
+                          struct ssl_config_data *ssl_config,
+                          const char *hostname,
+                          struct gtls_instance *gtls,
+                          long *pverifyresult)
 {
-  struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = 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);
   unsigned int init_flags;
-  gnutls_session_t session;
   int rc;
   bool sni = TRUE; /* default is SNI enabled */
-  void *transport_ptr = NULL;
-  gnutls_push_func gnutls_transport_push = NULL;
-  gnutls_pull_func gnutls_transport_pull = NULL;
 #ifdef ENABLE_IPV6
   struct in6_addr addr;
 #else
@@ -428,54 +411,44 @@ gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
 #endif
   const char *prioritylist;
   const char *err = NULL;
-  const char *hostname = connssl->hostname;
-  long * const certverifyresult = &ssl_config->certverifyresult;
   const char *tls13support;
   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;
-
   if(!gtls_inited)
     gtls_init();
 
-  /* Initialize certverifyresult to OK */
-  *certverifyresult = 0;
+  *pverifyresult = 0;
 
-  if(conn_config->version == CURL_SSLVERSION_SSLv2) {
+  if(config->version == CURL_SSLVERSION_SSLv2) {
     failf(data, "GnuTLS does not support SSLv2");
     return CURLE_SSL_CONNECT_ERROR;
   }
-  else if(conn_config->version == CURL_SSLVERSION_SSLv3)
+  else if(config->version == CURL_SSLVERSION_SSLv3)
     sni = FALSE; /* SSLv3 has no SNI */
 
   /* allocate a cred struct */
-  rc = gnutls_certificate_allocate_credentials(&backend->cred);
+  rc = gnutls_certificate_allocate_credentials(&gtls->cred);
   if(rc != GNUTLS_E_SUCCESS) {
     failf(data, "gnutls_cert_all_cred() failed: %s", gnutls_strerror(rc));
     return CURLE_SSL_CONNECT_ERROR;
   }
 
 #ifdef USE_GNUTLS_SRP
-  if((ssl_config->primary.authtype == CURL_TLSAUTH_SRP) &&
+  if((config->authtype == CURL_TLSAUTH_SRP) &&
      Curl_auth_allowed_to_host(data)) {
-    infof(data, "Using TLS-SRP username: %s",
-          ssl_config->primary.username);
+    infof(data, "Using TLS-SRP username: %s", config->username);
 
-    rc = gnutls_srp_allocate_client_credentials(&backend->srp_client_cred);
+    rc = gnutls_srp_allocate_client_credentials(
+           &gtls->srp_client_cred);
     if(rc != GNUTLS_E_SUCCESS) {
       failf(data, "gnutls_srp_allocate_client_cred() failed: %s",
             gnutls_strerror(rc));
       return CURLE_OUT_OF_MEMORY;
     }
 
-    rc = gnutls_srp_set_client_credentials(backend->srp_client_cred,
-                                           ssl_config->primary.username,
-                                           ssl_config->primary.password);
+    rc = gnutls_srp_set_client_credentials(gtls->srp_client_cred,
+                                           config->username,
+                                           config->password);
     if(rc != GNUTLS_E_SUCCESS) {
       failf(data, "gnutls_srp_set_client_cred() failed: %s",
             gnutls_strerror(rc));
@@ -484,67 +457,63 @@ gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
   }
 #endif
 
-  if(conn_config->CAfile) {
+  if(config->CAfile) {
     /* set the trusted CA cert bundle file */
-    gnutls_certificate_set_verify_flags(backend->cred,
+    gnutls_certificate_set_verify_flags(gtls->cred,
                                         GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
 
-    rc = gnutls_certificate_set_x509_trust_file(backend->cred,
-                                                conn_config->CAfile,
+    rc = gnutls_certificate_set_x509_trust_file(gtls->cred,
+                                                config->CAfile,
                                                 GNUTLS_X509_FMT_PEM);
     if(rc < 0) {
       infof(data, "error reading ca cert file %s (%s)",
-            conn_config->CAfile, gnutls_strerror(rc));
-      if(conn_config->verifypeer) {
-        *certverifyresult = rc;
+            config->CAfile, gnutls_strerror(rc));
+      if(config->verifypeer) {
+        *pverifyresult = rc;
         return CURLE_SSL_CACERT_BADFILE;
       }
     }
     else
-      infof(data, "found %d certificates in %s", rc,
-            conn_config->CAfile);
+      infof(data, "found %d certificates in %s", rc, config->CAfile);
   }
 
-  if(conn_config->CApath) {
+  if(config->CApath) {
     /* set the trusted CA cert directory */
-    rc = gnutls_certificate_set_x509_trust_dir(backend->cred,
-                                               conn_config->CApath,
+    rc = gnutls_certificate_set_x509_trust_dir(gtls->cred,
+                                               config->CApath,
                                                GNUTLS_X509_FMT_PEM);
     if(rc < 0) {
       infof(data, "error reading ca cert file %s (%s)",
-            conn_config->CApath, gnutls_strerror(rc));
-      if(conn_config->verifypeer) {
-        *certverifyresult = rc;
+            config->CApath, gnutls_strerror(rc));
+      if(config->verifypeer) {
+        *pverifyresult = rc;
         return CURLE_SSL_CACERT_BADFILE;
       }
     }
     else
-      infof(data, "found %d certificates in %s",
-            rc, conn_config->CApath);
+      infof(data, "found %d certificates in %s", rc, config->CApath);
   }
 
 #ifdef CURL_CA_FALLBACK
   /* use system ca certificate store as fallback */
-  if(conn_config->verifypeer &&
-     !(conn_config->CAfile || conn_config->CApath)) {
+  if(config->verifypeer && !(config->CAfile || config->CApath)) {
     /* this ignores errors on purpose */
-    gnutls_certificate_set_x509_system_trust(backend->cred);
+    gnutls_certificate_set_x509_system_trust(gtls->cred);
   }
 #endif
 
-  if(ssl_config->primary.CRLfile) {
+  if(config->CRLfile) {
     /* set the CRL list file */
-    rc = gnutls_certificate_set_x509_crl_file(backend->cred,
-                                              ssl_config->primary.CRLfile,
+    rc = gnutls_certificate_set_x509_crl_file(gtls->cred,
+                                              config->CRLfile,
                                               GNUTLS_X509_FMT_PEM);
     if(rc < 0) {
       failf(data, "error reading crl file %s (%s)",
-            ssl_config->primary.CRLfile, gnutls_strerror(rc));
+            config->CRLfile, gnutls_strerror(rc));
       return CURLE_SSL_CRL_BADFILE;
     }
     else
-      infof(data, "found %d CRL in %s",
-            rc, ssl_config->primary.CRLfile);
+      infof(data, "found %d CRL in %s", rc, config->CRLfile);
   }
 
   /* Initialize TLS session as a client */
@@ -559,15 +528,12 @@ gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
   init_flags |= GNUTLS_NO_TICKETS;
 #endif
 
-  rc = gnutls_init(&backend->session, init_flags);
+  rc = gnutls_init(&gtls->session, init_flags);
   if(rc != GNUTLS_E_SUCCESS) {
     failf(data, "gnutls_init() failed: %d", rc);
     return CURLE_SSL_CONNECT_ERROR;
   }
 
-  /* convenient assign */
-  session = backend->session;
-
   if((0 == Curl_inet_pton(AF_INET, hostname, &addr)) &&
 #ifdef ENABLE_IPV6
      (0 == Curl_inet_pton(AF_INET6, hostname, &addr)) &&
@@ -575,15 +541,15 @@ gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
      sni) {
     size_t snilen;
     char *snihost = Curl_ssl_snihost(data, hostname, &snilen);
-    if(!snihost || gnutls_server_name_set(session, GNUTLS_NAME_DNS, snihost,
-                                          snilen) < 0) {
+    if(!snihost || gnutls_server_name_set(gtls->session, GNUTLS_NAME_DNS,
+                                          snihost, snilen) < 0) {
       failf(data, "Failed to set SNI");
       return CURLE_SSL_CONNECT_ERROR;
     }
   }
 
   /* Use default priorities */
-  rc = gnutls_set_default_priority(session);
+  rc = gnutls_set_default_priority(gtls->session);
   if(rc != GNUTLS_E_SUCCESS)
     return CURLE_SSL_CONNECT_ERROR;
 
@@ -594,13 +560,13 @@ gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
    * removed if a run-time error indicates that SRP is not supported by this
    * GnuTLS version */
 
-  if(conn_config->version == CURL_SSLVERSION_SSLv2 ||
-     conn_config->version == CURL_SSLVERSION_SSLv3) {
+  if(config->version == CURL_SSLVERSION_SSLv2 ||
+     config->version == CURL_SSLVERSION_SSLv3) {
     failf(data, "GnuTLS does not support SSLv2 or SSLv3");
     return CURLE_SSL_CONNECT_ERROR;
   }
 
-  if(conn_config->version == CURL_SSLVERSION_TLSv1_3) {
+  if(config->version == CURL_SSLVERSION_TLSv1_3) {
     if(!tls13support) {
       failf(data, "This GnuTLS installation does not support TLS 1.3");
       return CURLE_SSL_CONNECT_ERROR;
@@ -608,14 +574,14 @@ gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
   }
 
   /* At this point we know we have a supported TLS version, so set it */
-  result = set_ssl_version_min_max(cf, data, &prioritylist, tls13support);
+  result = set_ssl_version_min_max(data, config, &prioritylist, tls13support);
   if(result)
     return result;
 
 #ifdef USE_GNUTLS_SRP
   /* Only add SRP to the cipher list if SRP is requested. Otherwise
    * GnuTLS will disable TLS 1.3 support. */
-  if(ssl_config->primary.authtype == CURL_TLSAUTH_SRP) {
+  if(config->authtype == CURL_TLSAUTH_SRP) {
     size_t len = strlen(prioritylist);
 
     char *prioritysrp = malloc(len + sizeof(GNUTLS_SRP) + 1);
@@ -623,7 +589,7 @@ gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
       return CURLE_OUT_OF_MEMORY;
     strcpy(prioritysrp, prioritylist);
     strcpy(prioritysrp + len, ":" GNUTLS_SRP);
-    rc = gnutls_priority_set_direct(session, prioritysrp, &err);
+    rc = gnutls_priority_set_direct(gtls->session, prioritysrp, &err);
     free(prioritysrp);
 
     if((rc == GNUTLS_E_INVALID_REQUEST) && err) {
@@ -633,7 +599,7 @@ gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
   else {
 #endif
     infof(data, "GnuTLS ciphers: %s", prioritylist);
-    rc = gnutls_priority_set_direct(session, prioritylist, &err);
+    rc = gnutls_priority_set_direct(gtls->session, prioritylist, &err);
 #ifdef USE_GNUTLS_SRP
   }
 #endif
@@ -644,35 +610,7 @@ gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
     return CURLE_SSL_CONNECT_ERROR;
   }
 
-  if(cf->conn->bits.tls_enable_alpn) {
-    int cur = 0;
-    gnutls_datum_t protocols[2];
-
-#ifdef USE_HTTP2
-    if(data->state.httpwant >= CURL_HTTP_VERSION_2
-#ifndef CURL_DISABLE_PROXY
-       && (!Curl_ssl_cf_is_proxy(cf) || !cf->conn->bits.tunnel_proxy)
-#endif
-       ) {
-      protocols[cur].data = (unsigned char *)ALPN_H2;
-      protocols[cur].size = ALPN_H2_LENGTH;
-      cur++;
-      infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2);
-    }
-#endif
-
-    protocols[cur].data = (unsigned char *)ALPN_HTTP_1_1;
-    protocols[cur].size = ALPN_HTTP_1_1_LENGTH;
-    cur++;
-    infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1);
-
-    if(gnutls_alpn_set_protocols(session, protocols, cur, 0)) {
-      failf(data, "failed setting ALPN");
-      return CURLE_SSL_CONNECT_ERROR;
-    }
-  }
-
-  if(ssl_config->primary.clientcert) {
+  if(config->clientcert) {
     if(ssl_config->key_passwd) {
       const unsigned int supported_key_encryption_algorithms =
         GNUTLS_PKCS_USE_PKCS12_3DES | GNUTLS_PKCS_USE_PKCS12_ARCFOUR |
@@ -680,10 +618,9 @@ gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
         GNUTLS_PKCS_USE_PBES2_AES_128 | GNUTLS_PKCS_USE_PBES2_AES_192 |
         GNUTLS_PKCS_USE_PBES2_AES_256;
       rc = gnutls_certificate_set_x509_key_file2(
-           backend->cred,
-           ssl_config->primary.clientcert,
-           ssl_config->key ?
-           ssl_config->key : ssl_config->primary.clientcert,
+           gtls->cred,
+           config->clientcert,
+           ssl_config->key ? ssl_config->key : config->clientcert,
            do_file_type(ssl_config->cert_type),
            ssl_config->key_passwd,
            supported_key_encryption_algorithms);
@@ -696,10 +633,9 @@ gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
     }
     else {
       if(gnutls_certificate_set_x509_key_file(
-           backend->cred,
-           ssl_config->primary.clientcert,
-           ssl_config->key ?
-           ssl_config->key : ssl_config->primary.clientcert,
+           gtls->cred,
+           config->clientcert,
+           ssl_config->key ? ssl_config->key : config->clientcert,
            do_file_type(ssl_config->cert_type) ) !=
          GNUTLS_E_SUCCESS) {
         failf(data, "error reading X.509 key or certificate file");
@@ -710,9 +646,9 @@ gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
 
 #ifdef USE_GNUTLS_SRP
   /* put the credentials to the current session */
-  if(ssl_config->primary.authtype == CURL_TLSAUTH_SRP) {
-    rc = gnutls_credentials_set(session, GNUTLS_CRD_SRP,
-                                backend->srp_client_cred);
+  if(config->authtype == CURL_TLSAUTH_SRP) {
+    rc = gnutls_credentials_set(gtls->session, GNUTLS_CRD_SRP,
+                                gtls->srp_client_cred);
     if(rc != GNUTLS_E_SUCCESS) {
       failf(data, "gnutls_credentials_set() failed: %s", gnutls_strerror(rc));
       return CURLE_SSL_CONNECT_ERROR;
@@ -721,44 +657,88 @@ gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
   else
 #endif
   {
-    rc = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE,
-                                backend->cred);
+    rc = gnutls_credentials_set(gtls->session, GNUTLS_CRD_CERTIFICATE,
+                                gtls->cred);
     if(rc != GNUTLS_E_SUCCESS) {
       failf(data, "gnutls_credentials_set() failed: %s", gnutls_strerror(rc));
       return CURLE_SSL_CONNECT_ERROR;
     }
   }
 
-  /* push/pull through filter chain */
-  transport_ptr = cf;
-  gnutls_transport_push = gtls_push;
-  gnutls_transport_pull = gtls_pull;
+  if(config->verifystatus) {
+    rc = gnutls_ocsp_status_request_enable_client(gtls->session,
+                                                  NULL, 0, NULL);
+    if(rc != GNUTLS_E_SUCCESS) {
+      failf(data, "gnutls_ocsp_status_request_enable_client() failed: %d", rc);
+      return CURLE_SSL_CONNECT_ERROR;
+    }
+  }
+
+  return CURLE_OK;
+}
+
+static CURLcode
+gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+  struct ssl_connect_data *connssl = cf->ctx;
+  struct ssl_backend_data *backend = 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);
 
-  /* set the connection handle */
-  gnutls_transport_set_ptr(session, transport_ptr);
+  if(connssl->state == ssl_connection_complete)
+    /* to make us tolerant against being called more than once for the
+       same connection */
+    return CURLE_OK;
 
-  /* register callback functions to send and receive data. */
-  gnutls_transport_set_push_function(session, gnutls_transport_push);
-  gnutls_transport_set_pull_function(session, gnutls_transport_pull);
+  result = gtls_client_init(data, conn_config, ssl_config,
+                            connssl->hostname,
+                            &backend->gtls, pverifyresult);
+  if(result)
+    return result;
 
-  if(conn_config->verifystatus) {
-    rc = gnutls_ocsp_status_request_enable_client(session, NULL, 0, NULL);
-    if(rc != GNUTLS_E_SUCCESS) {
-      failf(data, "gnutls_ocsp_status_request_enable_client() failed: %d", rc);
+  if(cf->conn->bits.tls_enable_alpn) {
+    int cur = 0;
+    gnutls_datum_t protocols[2];
+
+#ifdef USE_HTTP2
+    if(data->state.httpwant >= CURL_HTTP_VERSION_2
+#ifndef CURL_DISABLE_PROXY
+       && (!Curl_ssl_cf_is_proxy(cf) || !cf->conn->bits.tunnel_proxy)
+#endif
+       ) {
+      protocols[cur].data = (unsigned char *)ALPN_H2;
+      protocols[cur].size = ALPN_H2_LENGTH;
+      cur++;
+      infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2);
+    }
+#endif
+
+    protocols[cur].data = (unsigned char *)ALPN_HTTP_1_1;
+    protocols[cur].size = ALPN_HTTP_1_1_LENGTH;
+    cur++;
+    infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1);
+
+    if(gnutls_alpn_set_protocols(backend->gtls.session, protocols, cur, 0)) {
+      failf(data, "failed setting ALPN");
       return CURLE_SSL_CONNECT_ERROR;
     }
   }
 
   /* This might be a reconnect, so we check for a session ID in the cache
      to speed up things */
-  if(ssl_config->primary.sessionid) {
+  if(conn_config->sessionid) {
     void *ssl_sessionid;
     size_t ssl_idsize;
 
     Curl_ssl_sessionid_lock(data);
     if(!Curl_ssl_getsessionid(cf, data, &ssl_sessionid, &ssl_idsize)) {
       /* we got a session id, use it! */
-      gnutls_session_set_data(session, ssl_sessionid, ssl_idsize);
+      gnutls_session_set_data(backend->gtls.session,
+                              ssl_sessionid, ssl_idsize);
 
       /* Informational message */
       infof(data, "SSL re-using session ID");
@@ -766,6 +746,11 @@ gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
     Curl_ssl_sessionid_unlock(data);
   }
 
+  /* register callback functions and handle to send and receive data. */
+  gnutls_transport_set_ptr(backend->gtls.session, cf);
+  gnutls_transport_set_push_function(backend->gtls.session, gtls_push);
+  gnutls_transport_set_pull_function(backend->gtls.session, gtls_pull);
+
   return CURLE_OK;
 }
 
@@ -828,13 +813,14 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data,
 }
 
 CURLcode
-Curl_gtls_verifyserver(struct Curl_cfilter *cf,
-                       struct Curl_easy *data,
-                       gnutls_session_t session)
+Curl_gtls_verifyserver(struct Curl_easy *data,
+                       gnutls_session_t session,
+                       struct ssl_primary_config *config,
+                       struct ssl_config_data *ssl_config,
+                       const char *hostname,
+                       const char *dispname,
+                       const char *pinned_key)
 {
-  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);
   unsigned int cert_list_size;
   const gnutls_datum_t *chainp;
   unsigned int verify_status = 0;
@@ -846,14 +832,12 @@ Curl_gtls_verifyserver(struct Curl_cfilter *cf,
   time_t certclock;
   const char *ptr;
   int rc;
-  gnutls_datum_t proto;
   CURLcode result = CURLE_OK;
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
   unsigned int algo;
   unsigned int bits;
   gnutls_protocol_t version = gnutls_protocol_get_version(session);
 #endif
-  const char *hostname = connssl->hostname;
   long * const certverifyresult = &ssl_config->certverifyresult;
 
   /* the name of the cipher suite used, e.g. ECDHE_RSA_AES_256_GCM_SHA384. */
@@ -872,13 +856,13 @@ Curl_gtls_verifyserver(struct Curl_cfilter *cf,
 
   chainp = gnutls_certificate_get_peers(session, &cert_list_size);
   if(!chainp) {
-    if(conn_config->verifypeer ||
-       conn_config->verifyhost ||
-       conn_config->issuercert) {
+    if(config->verifypeer ||
+       config->verifyhost ||
+       config->issuercert) {
 #ifdef USE_GNUTLS_SRP
       if(ssl_config->primary.authtype == CURL_TLSAUTH_SRP
          && ssl_config->primary.username
-         && !conn_config->verifypeer
+         && !config->verifypeer
          && gnutls_cipher_get(session)) {
         /* no peer cert, but auth is ok if we have SRP user and cipher and no
            peer verify */
@@ -912,7 +896,7 @@ Curl_gtls_verifyserver(struct Curl_cfilter *cf,
     }
   }
 
-  if(conn_config->verifypeer) {
+  if(config->verifypeer) {
     /* This function will try to verify the peer's certificate and return its
        status (trusted, invalid etc.). The value of status should be one or
        more of the gnutls_certificate_status_t enumerated elements bitwise
@@ -931,9 +915,9 @@ Curl_gtls_verifyserver(struct Curl_cfilter *cf,
 
     /* verify_status is a bitmask of gnutls_certificate_status bits */
     if(verify_status & GNUTLS_CERT_INVALID) {
-      if(conn_config->verifypeer) {
+      if(config->verifypeer) {
         failf(data, "server certificate verification failed. CAfile: %s "
-              "CRLfile: %s", conn_config->CAfile ? conn_config->CAfile:
+              "CRLfile: %s", config->CAfile ? config->CAfile:
               "none",
               ssl_config->primary.CRLfile ?
               ssl_config->primary.CRLfile : "none");
@@ -948,7 +932,7 @@ Curl_gtls_verifyserver(struct Curl_cfilter *cf,
   else
     infof(data, "  server certificate verification SKIPPED");
 
-  if(conn_config->verifystatus) {
+  if(config->verifystatus) {
     if(gnutls_ocsp_status_request_is_checked(session, 0) == 0) {
       gnutls_datum_t status_request;
       gnutls_ocsp_resp_t ocsp_resp;
@@ -1059,21 +1043,21 @@ Curl_gtls_verifyserver(struct Curl_cfilter *cf,
        gnutls_x509_crt_t format */
     gnutls_x509_crt_import(x509_cert, chainp, GNUTLS_X509_FMT_DER);
 
-  if(conn_config->issuercert) {
+  if(config->issuercert) {
     gnutls_x509_crt_init(&x509_issuer);
-    issuerp = load_file(conn_config->issuercert);
+    issuerp = load_file(config->issuercert);
     gnutls_x509_crt_import(x509_issuer, &issuerp, GNUTLS_X509_FMT_PEM);
     rc = gnutls_x509_crt_check_issuer(x509_cert, x509_issuer);
     gnutls_x509_crt_deinit(x509_issuer);
     unload_file(issuerp);
     if(rc <= 0) {
       failf(data, "server certificate issuer check failed (IssuerCert: %s)",
-            conn_config->issuercert?conn_config->issuercert:"none");
+            config->issuercert?config->issuercert:"none");
       gnutls_x509_crt_deinit(x509_cert);
       return CURLE_SSL_ISSUER_ERROR;
     }
     infof(data, "  server certificate issuer check OK (Issuer Cert: %s)",
-          conn_config->issuercert?conn_config->issuercert:"none");
+          config->issuercert?config->issuercert:"none");
   }
 
   size = sizeof(certname);
@@ -1136,15 +1120,15 @@ Curl_gtls_verifyserver(struct Curl_cfilter *cf,
   }
 #endif
   if(!rc) {
-    if(conn_config->verifyhost) {
+    if(config->verifyhost) {
       failf(data, "SSL: certificate subject name (%s) does not match "
-            "target host name '%s'", certname, connssl->dispname);
+            "target host name '%s'", certname, dispname);
       gnutls_x509_crt_deinit(x509_cert);
       return CURLE_PEER_FAILED_VERIFICATION;
     }
     else
       infof(data, "  common name: %s (does not match '%s')",
-            certname, connssl->dispname);
+            certname, dispname);
   }
   else
     infof(data, "  common name: %s (matched)", certname);
@@ -1153,7 +1137,7 @@ Curl_gtls_verifyserver(struct Curl_cfilter *cf,
   certclock = gnutls_x509_crt_get_expiration_time(x509_cert);
 
   if(certclock == (time_t)-1) {
-    if(conn_config->verifypeer) {
+    if(config->verifypeer) {
       failf(data, "server cert expiration date verify failed");
       *certverifyresult = GNUTLS_CERT_EXPIRED;
       gnutls_x509_crt_deinit(x509_cert);
@@ -1164,7 +1148,7 @@ Curl_gtls_verifyserver(struct Curl_cfilter *cf,
   }
   else {
     if(certclock < time(NULL)) {
-      if(conn_config->verifypeer) {
+      if(config->verifypeer) {
         failf(data, "server certificate expiration date has passed.");
         *certverifyresult = GNUTLS_CERT_EXPIRED;
         gnutls_x509_crt_deinit(x509_cert);
@@ -1180,7 +1164,7 @@ Curl_gtls_verifyserver(struct Curl_cfilter *cf,
   certclock = gnutls_x509_crt_get_activation_time(x509_cert);
 
   if(certclock == (time_t)-1) {
-    if(conn_config->verifypeer) {
+    if(config->verifypeer) {
       failf(data, "server cert activation date verify failed");
       *certverifyresult = GNUTLS_CERT_NOT_ACTIVATED;
       gnutls_x509_crt_deinit(x509_cert);
@@ -1191,7 +1175,7 @@ Curl_gtls_verifyserver(struct Curl_cfilter *cf,
   }
   else {
     if(certclock > time(NULL)) {
-      if(conn_config->verifypeer) {
+      if(config->verifypeer) {
         failf(data, "server certificate not activated yet.");
         *certverifyresult = GNUTLS_CERT_NOT_ACTIVATED;
         gnutls_x509_crt_deinit(x509_cert);
@@ -1204,11 +1188,8 @@ Curl_gtls_verifyserver(struct Curl_cfilter *cf,
       infof(data, "  server certificate activation date OK");
   }
 
-  ptr = Curl_ssl_cf_is_proxy(cf)?
-    data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]:
-    data->set.str[STRING_SSL_PINNEDPUBLICKEY];
-  if(ptr) {
-    result = pkp_pin_peer_pubkey(data, x509_cert, ptr);
+  if(pinned_key) {
+    result = pkp_pin_peer_pubkey(data, x509_cert, pinned_key);
     if(result != CURLE_OK) {
       failf(data, "SSL: public key does not match pinned public key");
       gnutls_x509_crt_deinit(x509_cert);
@@ -1264,7 +1245,31 @@ Curl_gtls_verifyserver(struct Curl_cfilter *cf,
 
   gnutls_x509_crt_deinit(x509_cert);
 
+  return result;
+}
+
+static CURLcode gtls_verifyserver(struct Curl_cfilter *cf,
+                                  struct Curl_easy *data,
+                                  gnutls_session_t session)
+{
+  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);
+  const char *pinned_key = Curl_ssl_cf_is_proxy(cf)?
+    data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]:
+    data->set.str[STRING_SSL_PINNEDPUBLICKEY];
+  CURLcode result;
+
+  result = Curl_gtls_verifyserver(data, session, conn_config, ssl_config,
+                                  connssl->hostname, connssl->dispname,
+                                  pinned_key);
+  if(result)
+    goto out;
+
   if(cf->conn->bits.tls_enable_alpn) {
+    gnutls_datum_t proto;
+    int rc;
+
     rc = gnutls_alpn_get_selected_protocol(session, &proto);
     if(rc == 0) {
       infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, proto.size,
@@ -1290,8 +1295,6 @@ Curl_gtls_verifyserver(struct Curl_cfilter *cf,
                         BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
   }
 
-  connssl->state = ssl_connection_complete;
-
   if(ssl_config->primary.sessionid) {
     /* we always unconditionally get the session id here, as even if we
        already got it from the cache and asked to use it in the connection, it
@@ -1334,10 +1337,10 @@ Curl_gtls_verifyserver(struct Curl_cfilter *cf,
       result = CURLE_OUT_OF_MEMORY;
   }
 
+out:
   return result;
 }
 
-
 /*
  * This function is called after the TCP connect has completed. Setup the TLS
  * layer and do all necessary magic.
@@ -1378,12 +1381,13 @@ gtls_connect_common(struct Curl_cfilter *cf,
     struct ssl_backend_data *backend = connssl->backend;
     gnutls_session_t session;
     DEBUGASSERT(backend);
-    session = backend->session;
-    rc = Curl_gtls_verifyserver(cf, data, session);
+    session = backend->gtls.session;
+    rc = gtls_verifyserver(cf, data, session);
     if(rc) {
       result = rc;
       goto out;
     }
+    connssl->state = ssl_connection_complete;
   }
 
 out:
@@ -1421,8 +1425,8 @@ static bool gtls_data_pending(struct Curl_cfilter *cf,
 
   (void)data;
   DEBUGASSERT(ctx && ctx->backend);
-  if(ctx->backend->session &&
-     0 != gnutls_record_check_pending(ctx->backend->session))
+  if(ctx->backend->gtls.session &&
+     0 != gnutls_record_check_pending(ctx->backend->gtls.session))
     return TRUE;
   return FALSE;
 }
@@ -1439,7 +1443,7 @@ static ssize_t gtls_send(struct Curl_cfilter *cf,
 
   (void)data;
   DEBUGASSERT(backend);
-  rc = gnutls_record_send(backend->session, mem, len);
+  rc = gnutls_record_send(backend->gtls.session, mem, len);
 
   if(rc < 0) {
     *curlcode = (rc == GNUTLS_E_AGAIN)
@@ -1461,23 +1465,23 @@ static void gtls_close(struct Curl_cfilter *cf,
   (void) data;
   DEBUGASSERT(backend);
 
-  if(backend->session) {
+  if(backend->gtls.session) {
     char buf[32];
     /* Maybe the server has already sent a close notify alert.
        Read it to avoid an RST on the TCP connection. */
-    (void)gnutls_record_recv(backend->session, buf, sizeof(buf));
-    gnutls_bye(backend->session, GNUTLS_SHUT_WR);
-    gnutls_deinit(backend->session);
-    backend->session = NULL;
+    (void)gnutls_record_recv(backend->gtls.session, buf, sizeof(buf));
+    gnutls_bye(backend->gtls.session, GNUTLS_SHUT_WR);
+    gnutls_deinit(backend->gtls.session);
+    backend->gtls.session = NULL;
   }
-  if(backend->cred) {
-    gnutls_certificate_free_credentials(backend->cred);
-    backend->cred = NULL;
+  if(backend->gtls.cred) {
+    gnutls_certificate_free_credentials(backend->gtls.cred);
+    backend->gtls.cred = NULL;
   }
 #ifdef USE_GNUTLS_SRP
-  if(backend->srp_client_cred) {
-    gnutls_srp_free_client_credentials(backend->srp_client_cred);
-    backend->srp_client_cred = NULL;
+  if(backend->gtls.srp_client_cred) {
+    gnutls_srp_free_client_credentials(backend->gtls.srp_client_cred);
+    backend->gtls.srp_client_cred = NULL;
   }
 #endif
 }
@@ -1503,10 +1507,10 @@ static int gtls_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)
-    gnutls_bye(backend->session, GNUTLS_SHUT_WR);
+    gnutls_bye(backend->gtls.session, GNUTLS_SHUT_WR);
 #endif
 
-  if(backend->session) {
+  if(backend->gtls.session) {
     ssize_t result;
     bool done = FALSE;
     char buf[120];
@@ -1517,7 +1521,7 @@ static int gtls_shutdown(struct Curl_cfilter *cf,
       if(what > 0) {
         /* Something to read, let's do it and hope that it is the close
            notify alert from the server */
-        result = gnutls_record_recv(backend->session,
+        result = gnutls_record_recv(backend->gtls.session,
                                     buf, sizeof(buf));
         switch(result) {
         case 0:
@@ -1547,18 +1551,18 @@ static int gtls_shutdown(struct Curl_cfilter *cf,
         done = TRUE;
       }
     }
-    gnutls_deinit(backend->session);
+    gnutls_deinit(backend->gtls.session);
   }
-  gnutls_certificate_free_credentials(backend->cred);
+  gnutls_certificate_free_credentials(backend->gtls.cred);
 
 #ifdef USE_GNUTLS_SRP
   if(ssl_config->primary.authtype == CURL_TLSAUTH_SRP
      && ssl_config->primary.username != NULL)
-    gnutls_srp_free_client_credentials(backend->srp_client_cred);
+    gnutls_srp_free_client_credentials(backend->gtls.srp_client_cred);
 #endif
 
-  backend->cred = NULL;
-  backend->session = NULL;
+  backend->gtls.cred = NULL;
+  backend->gtls.session = NULL;
 
   return retval;
 }
@@ -1576,7 +1580,7 @@ static ssize_t gtls_recv(struct Curl_cfilter *cf,
   (void)data;
   DEBUGASSERT(backend);
 
-  ret = gnutls_record_recv(backend->session, buf, buffersize);
+  ret = gnutls_record_recv(backend->gtls.session, buf, buffersize);
   if((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED)) {
     *curlcode = CURLE_AGAIN;
     ret = -1;
@@ -1652,7 +1656,7 @@ static void *gtls_get_internals(struct ssl_connect_data *connssl,
   struct ssl_backend_data *backend = connssl->backend;
   (void)info;
   DEBUGASSERT(backend);
-  return backend->session;
+  return backend->gtls.session;
 }
 
 const struct Curl_ssl Curl_ssl_gnutls = {
index af00b454363299f677f9b5be46d1213bbe0314e9..49c1c47637653c07ec1348eb72ba34c8245e7573 100644 (file)
  ***************************************************************************/
 
 #include "curl_setup.h"
+#include <curl/curl.h>
 
 #ifdef USE_GNUTLS
 
-#include "urldata.h"
-#include "cfilters.h"
 #include <gnutls/gnutls.h>
+
+#ifdef HAVE_GNUTLS_SRP
+/* the function exists */
+#ifdef USE_TLS_SRP
+/* the functionality is not disabled */
+#define USE_GNUTLS_SRP
+#endif
+#endif
+
+struct Curl_easy;
+struct Curl_cfilter;
+struct ssl_primary_config;
+struct ssl_config_data;
+
+struct gtls_instance {
+  gnutls_session_t session;
+  gnutls_certificate_credentials_t cred;
+#ifdef USE_GNUTLS_SRP
+  gnutls_srp_client_credentials_t srp_client_cred;
+#endif
+};
+
 CURLcode
-Curl_gtls_verifyserver(struct Curl_cfilter *cf,
-                       struct Curl_easy *data,
-                       gnutls_session_t session);
+gtls_client_init(struct Curl_easy *data,
+                 struct ssl_primary_config *config,
+                 struct ssl_config_data *ssl_config,
+                 const char *hostname,
+                 struct gtls_instance *gtls,
+                 long *pverifyresult);
+
+CURLcode
+Curl_gtls_verifyserver(struct Curl_easy *data,
+                       gnutls_session_t session,
+                       struct ssl_primary_config *config,
+                       struct ssl_config_data *ssl_config,
+                       const char *hostname,
+                       const char *dispname,
+                       const char *pinned_key);
+
 extern const struct Curl_ssl Curl_ssl_gnutls;
 
 #endif /* USE_GNUTLS */