text: /***************************************************************************
   *                                  _   _ ____  _
   *  Project                     ___| | | |  _ \| |
   *                             / __| | | | |_) | |
   *                            | (__| |_| |  _ <| |___
   *                             \___|\___/|_| \_\_____|
   *
   * Copyright (C) 2010 - 2011, Hoi-Ho Chan, <hoiho.chan@gmail.com>
   * Copyright (C) 2012 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
   *
   * This software is licensed as described in the file COPYING, which
   * you should have received as part of this distribution. The terms
   * are also available at http://curl.haxx.se/docs/copyright.html.
   *
   * You may opt to use, copy, modify, merge, publish, distribute and/or sell
   * copies of the Software, and permit persons to whom the Software is
   * furnished to do so, under the terms of the COPYING file.
   *
   * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
   * KIND, either express or implied.
   *
   ***************************************************************************/
  
  /*
   * Source file for all mbedTSL-specific code for the TLS/SSL layer. No code
   * but vtls.c should ever call or use these functions.
   *
   */
  
  #include "curl_setup.h"
  
  #ifdef USE_MBEDTLS
  
  #include <mbedtls/net.h>
  #include <mbedtls/ssl.h>
  #include <mbedtls/certs.h>
  #include <mbedtls/x509.h>
  #include <mbedtls/version.h>
  
  #include <mbedtls/error.h>
  #include <mbedtls/entropy.h>
  #include <mbedtls/ctr_drbg.h>
  
  #include "urldata.h"
  #include "sendf.h"
  #include "inet_pton.h"
  #include "mbedtls.h"
  #include "vtls.h"
  #include "parsedate.h"
  #include "connect.h" /* for the connect timeout */
  #include "select.h"
  #include "rawstr.h"
  #include "polarssl_threadlock.h"
  
  #define _MPRINTF_REPLACE /* use our functions only */
  #include <curl/mprintf.h>
  #include "curl_memory.h"
  /* The last #include file should be: */
  #include "memdebug.h"
  
  /* apply threading? */
  #if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32)
  #define THREADING_SUPPORT
  #endif
  
  #if defined(THREADING_SUPPORT)
  static mbedtls_entropy_context entropy;
  
  static int  entropy_init_initialized  = 0;
  
  /* start of entropy_init_mutex() */
  static void entropy_init_mutex(mbedtls_entropy_context *ctx)
  {
    /* lock 0 = entropy_init_mutex() */
    Curl_polarsslthreadlock_lock_function(0);
    if(entropy_init_initialized == 0) {
      mbedtls_entropy_init(ctx);
      entropy_init_initialized = 1;
    }
    Curl_polarsslthreadlock_unlock_function(0);
  }
  /* end of entropy_init_mutex() */
  
  /* start of entropy_func_mutex() */
  static int entropy_func_mutex(void *data, unsigned char *output, size_t len)
  {
    int ret;
    /* lock 1 = entropy_func_mutex() */
    Curl_polarsslthreadlock_lock_function(1);
    ret = mbedtls_entropy_func(data, output, len);
    Curl_polarsslthreadlock_unlock_function(1);
  
    return ret;
  }
  /* end of entropy_func_mutex() */
  
  #endif /* THREADING_SUPPORT */
  
  /* Define this to enable lots of debugging for mbedTLS */
  #undef MBEDTLS_DEBUG
  
  #ifdef MBEDTLS_DEBUG
  static void mbedtls_debug(void *context, int level, const char *line)
  {
    struct SessionHandle *data = NULL;
  
    if(!context)
      return;
  
    data = (struct SessionHandle *)context;
  
    infof(data, "%s", line);
    (void) level;
  }
  #else
  #endif
  
  /* ALPN for http2? */
  #ifdef USE_NGHTTP2
  #  undef HAS_ALPN
  #  ifdef MBEDTLS_SSL_ALPN
  #    define HAS_ALPN
  #  endif
  #endif
  
  
  /*
   *  profile
   */
  const mbedtls_x509_crt_profile mbedtls_x509_crt_profile_fr =
  {
      /* Hashes from SHA-1 and above */
      MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA1 ) |
      MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_RIPEMD160 ) |
      MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA224 ) |
      MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA256 ) |
      MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA384 ) |
      MBEDTLS_X509_ID_FLAG( MBEDTLS_MD_SHA512 ),
      0xFFFFFFF, /* Any PK alg    */
      0xFFFFFFF, /* Any curve     */
      1024,      /* RSA min key len */
  };
  
  static Curl_recv mbedtls_recv;
  static Curl_send mbedtls_send;
  
  static CURLcode
  mbedtls_connect_step1(struct connectdata *conn,
                       int sockindex)
  {
    struct SessionHandle *data = conn->data;
    struct ssl_connect_data* connssl = &conn->ssl[sockindex];
  
    bool sni = TRUE; /* default is SNI enabled */
    int ret = -1;
  #ifdef ENABLE_IPV6
    struct in6_addr addr;
  #else
    struct in_addr addr;
  #endif
    void *old_session = NULL;
    size_t old_session_size = 0;
    char errorbuf[128];
    errorbuf[0]=0;
  
    /* mbedTLS only supports SSLv3 and TLSv1 */
    if(data->set.ssl.version == CURL_SSLVERSION_SSLv2) {
      failf(data, "mbedTLS does not support SSLv2");
      return CURLE_SSL_CONNECT_ERROR;
    }
    else if(data->set.ssl.version == CURL_SSLVERSION_SSLv3)
      sni = FALSE; /* SSLv3 has no SNI */
  
  #ifdef THREADING_SUPPORT
    entropy_init_mutex(&entropy);
    mbedtls_ctr_drbg_init(&connssl->ctr_drbg);
  
    ret = mbedtls_ctr_drbg_seed(&connssl->ctr_drbg, entropy_func_mutex,
                                &entropy, connssl->ssn.id,
                                connssl->ssn.id_len);
    if(ret) {
  #ifdef MBEDTLS_ERROR_C
      mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
  #endif /* MBEDTLS_ERROR_C */
      failf(data, "Failed - mbedTLS: ctr_drbg_init returned (-0x%04X) %s\n",
            -ret, errorbuf);
    }
  #else
    mbedtls_entropy_init(&connssl->entropy);
    mbedtls_ctr_drbg_init(&connssl->ctr_drbg);
  
    ret = mbedtls_ctr_drbg_seed(&connssl->ctr_drbg, mbedtls_entropy_func,
                                &connssl->entropy, connssl->ssn.id,
                                connssl->ssn.id_len);
    if(ret) {
  #ifdef MBEDTLS_ERROR_C
      mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
  #endif /* MBEDTLS_ERROR_C */
      failf(data, "Failed - mbedTLS: ctr_drbg_init returned (-0x%04X) %s\n",
            -ret, errorbuf);
    }
  #endif /* THREADING_SUPPORT */
  
    /* Load the trusted CA */
    memset(&connssl->cacert, 0, sizeof(mbedtls_x509_crt));
  
    if(data->set.str[STRING_SSL_CAFILE]) {
      ret = mbedtls_x509_crt_parse_file(&connssl->cacert,
                                        data->set.str[STRING_SSL_CAFILE]);
  
      if(ret<0) {
  #ifdef MBEDTLS_ERROR_C
        mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
  #endif /* MBEDTLS_ERROR_C */
        failf(data, "Error reading ca cert file %s - mbedTLS: (-0x%04X) %s",
              data->set.str[STRING_SSL_CAFILE], -ret, errorbuf);
  
        if(data->set.ssl.verifypeer)
          return CURLE_SSL_CACERT_BADFILE;
      }
    }
  
    if(data->set.str[STRING_SSL_CAPATH]) {
      ret = mbedtls_x509_crt_parse_path(&connssl->cacert,
                                        data->set.str[STRING_SSL_CAPATH]);
  
      if(ret<0) {
  #ifdef MBEDTLS_ERROR_C
        mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
  #endif /* MBEDTLS_ERROR_C */
        failf(data, "Error reading ca cert path %s - mbedTLS: (-0x%04X) %s",
              data->set.str[STRING_SSL_CAPATH], -ret, errorbuf);
  
        if(data->set.ssl.verifypeer)
          return CURLE_SSL_CACERT_BADFILE;
      }
    }
  
    /* Load the client certificate */
    memset(&connssl->clicert, 0, sizeof(mbedtls_x509_crt));
  
    if(data->set.str[STRING_CERT]) {
      ret = mbedtls_x509_crt_parse_file(&connssl->clicert,
                                        data->set.str[STRING_CERT]);
  
      if(ret) {
  #ifdef MBEDTLS_ERROR_C
        mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
  #endif /* MBEDTLS_ERROR_C */
        failf(data, "Error reading client cert file %s - mbedTLS: (-0x%04X) %s",
              data->set.str[STRING_CERT], -ret, errorbuf);
  
        return CURLE_SSL_CERTPROBLEM;
      }
    }
  
    /* Load the client private key */
    if(data->set.str[STRING_KEY]) {
      mbedtls_pk_init(&connssl->pk);
      ret = mbedtls_pk_parse_keyfile(&connssl->pk, data->set.str[STRING_KEY],
                                     data->set.str[STRING_KEY_PASSWD]);
      if(ret == 0 && !mbedtls_pk_can_do(&connssl->pk, MBEDTLS_PK_RSA))
        ret = MBEDTLS_ERR_PK_TYPE_MISMATCH;
  
      if(ret) {
  #ifdef MBEDTLS_ERROR_C
        mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
  #endif /* MBEDTLS_ERROR_C */
        failf(data, "Error reading private key %s - mbedTLS: (-0x%04X) %s",
              data->set.str[STRING_KEY], -ret, errorbuf);
  
        return CURLE_SSL_CERTPROBLEM;
      }
    }
  
    /* Load the CRL */
    memset(&connssl->crl, 0, sizeof(mbedtls_x509_crl));
  
    if(data->set.str[STRING_SSL_CRLFILE]) {
      ret = mbedtls_x509_crl_parse_file(&connssl->crl,
                                data->set.str[STRING_SSL_CRLFILE]);
  
      if(ret) {
  #ifdef MBEDTLS_ERROR_C
        mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
  #endif /* MBEDTLS_ERROR_C */
        failf(data, "Error reading CRL file %s - mbedTLS: (-0x%04X) %s",
              data->set.str[STRING_SSL_CRLFILE], -ret, errorbuf);
  
        return CURLE_SSL_CRL_BADFILE;
      }
    }
  
    infof(data, "mbedTLS: Connecting to %s:%d\n",
          conn->host.name, conn->remote_port);
  
    mbedtls_ssl_config_init(&connssl->config);
  
    mbedtls_ssl_init(&connssl->ssl);
    if(mbedtls_ssl_setup(&connssl->ssl, &connssl->config)) {
      failf(data, "mbedTLS: ssl_init failed");
      return CURLE_SSL_CONNECT_ERROR;
    }
    ret = mbedtls_ssl_config_defaults(&connssl->config,
                                      MBEDTLS_SSL_IS_CLIENT,
                                      MBEDTLS_SSL_TRANSPORT_STREAM,
                                      MBEDTLS_SSL_PRESET_DEFAULT);
    if(ret) {
      failf(data, "mbedTLS: ssl_config failed");
      return CURLE_SSL_CONNECT_ERROR;
    }
  
    /* new profile with RSA min key len = 1024 ... */
    mbedtls_ssl_conf_cert_profile( &connssl->config,
                                   &mbedtls_x509_crt_profile_fr);
  
    switch(data->set.ssl.version) {
    case CURL_SSLVERSION_SSLv3:
      mbedtls_ssl_conf_min_version(&connssl->config, MBEDTLS_SSL_MAJOR_VERSION_3,
                                   MBEDTLS_SSL_MINOR_VERSION_0);
      infof(data, "mbedTLS: Forced min. SSL Version to be SSLv3\n");
      break;
    case CURL_SSLVERSION_TLSv1_0:
      mbedtls_ssl_conf_min_version(&connssl->config, MBEDTLS_SSL_MAJOR_VERSION_3,
                                   MBEDTLS_SSL_MINOR_VERSION_1);
      infof(data, "mbedTLS: Forced min. SSL Version to be TLS 1.0\n");
      break;
    case CURL_SSLVERSION_TLSv1_1:
      mbedtls_ssl_conf_min_version(&connssl->config, MBEDTLS_SSL_MAJOR_VERSION_3,
                                   MBEDTLS_SSL_MINOR_VERSION_2);
      infof(data, "mbedTLS: Forced min. SSL Version to be TLS 1.1\n");
      break;
    case CURL_SSLVERSION_TLSv1_2:
      mbedtls_ssl_conf_min_version(&connssl->config, MBEDTLS_SSL_MAJOR_VERSION_3,
                                   MBEDTLS_SSL_MINOR_VERSION_3);
      infof(data, "mbedTLS: Forced min. SSL Version to be TLS 1.2\n");
      break;
    }
  
    mbedtls_ssl_conf_authmode(&connssl->config, MBEDTLS_SSL_VERIFY_OPTIONAL);
  
    mbedtls_ssl_conf_rng(&connssl->config, mbedtls_ctr_drbg_random,
                &connssl->ctr_drbg);
    mbedtls_ssl_set_bio(&connssl->ssl, &conn->sock[sockindex],
                mbedtls_net_send,
                mbedtls_net_recv,
                NULL /*  rev_timeout() */);
  
    mbedtls_ssl_conf_ciphersuites(&connssl->config,
                                  mbedtls_ssl_list_ciphersuites());
    if(!Curl_ssl_getsessionid(conn, &old_session, &old_session_size)) {
      memcpy(&connssl->ssn, old_session, old_session_size);
      infof(data, "mbedTLS re-using session\n");
    }
  
    mbedtls_ssl_set_session(&connssl->ssl,
                    &connssl->ssn);
  
    mbedtls_ssl_conf_ca_chain(&connssl->config,
                     &connssl->cacert,
                     &connssl->crl);
  
    if(data->set.str[STRING_KEY]) {
      mbedtls_ssl_conf_own_cert(&connssl->config,
                           &connssl->clicert, &connssl->pk);
    }
    if(!Curl_inet_pton(AF_INET, conn->host.name, &addr) &&
  #ifdef ENABLE_IPV6
       !Curl_inet_pton(AF_INET6, conn->host.name, &addr) &&
  #endif
       sni && mbedtls_ssl_set_hostname(&connssl->ssl, conn->host.name)) {
       infof(data, "WARNING: failed to configure "
                   "server name indication (SNI) TLS extension\n");
    }
  
  #ifdef HAS_ALPN
    if(data->set.httpversion == CURL_HTTP_VERSION_2_0) {
      if(data->set.ssl_enable_alpn) {
        static const char* protocols[] = {
          NGHTTP2_PROTO_VERSION_ID, ALPN_HTTP_1_1, NULL
        };
        mbedtls_ssl_conf_alpn_protocols(&connssl->config, protocols);
        infof(data, "ALPN, offering %s, %s\n", protocols[0],
              protocols[1]);
      }
    }
  #endif
  
  #ifdef MBEDTLS_DEBUG
    mbedtls_ssl_conf_dbg(&connssl->ssl, mbedtls_debug, data);
  #endif
  
    connssl->connecting_state = ssl_connect_2;
  
    return CURLE_OK;
  }
  
  static CURLcode
  mbedtls_connect_step2(struct connectdata *conn,
                       int sockindex)
  {
    int ret;
    struct SessionHandle *data = conn->data;
    struct ssl_connect_data* connssl = &conn->ssl[sockindex];
    char buffer[1024];
  
  #ifdef HAS_ALPN
    const char* next_protocol;
  #endif
  
    char errorbuf[128];
    errorbuf[0] = 0;
  
    conn->recv[sockindex] = mbedtls_recv;
    conn->send[sockindex] = mbedtls_send;
  
    ret = mbedtls_ssl_handshake(&connssl->ssl);
  
    if(ret == MBEDTLS_ERR_SSL_WANT_READ) {
      connssl->connecting_state = ssl_connect_2_reading;
      return CURLE_OK;
    }
    else if(ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
      connssl->connecting_state = ssl_connect_2_writing;
      return CURLE_OK;
    }
    else if(ret) {
  #ifdef MBEDTLS_ERROR_C
      mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
  #endif /* MBEDTLS_ERROR_C */
      failf(data, "ssl_handshake returned - mbedTLS: (-0x%04X) %s",
            -ret, errorbuf);
      return CURLE_SSL_CONNECT_ERROR;
    }
  
    infof(data, "mbedTLS: Handshake complete, cipher is %s\n",
          mbedtls_ssl_get_ciphersuite(&conn->ssl[sockindex].ssl)
      );
  
    ret = mbedtls_ssl_get_verify_result(&conn->ssl[sockindex].ssl);
  
    if(ret && data->set.ssl.verifypeer) {
      if(ret & MBEDTLS_X509_BADCERT_EXPIRED)
        failf(data, "Cert verify failed: BADCERT_EXPIRED");
  
      if(ret & MBEDTLS_X509_BADCERT_REVOKED) {
        failf(data, "Cert verify failed: BADCERT_REVOKED");
        return CURLE_SSL_CACERT;
      }
  
      if(ret & MBEDTLS_X509_BADCERT_CN_MISMATCH)
        failf(data, "Cert verify failed: BADCERT_CN_MISMATCH");
  
      if(ret & MBEDTLS_X509_BADCERT_NOT_TRUSTED)
        failf(data, "Cert verify failed: BADCERT_NOT_TRUSTED");
  
      return CURLE_PEER_FAILED_VERIFICATION;
    }
  
    if(mbedtls_ssl_get_peer_cert(&(connssl->ssl))) {
      /* If the session was resumed, there will be no peer certs */
      memset(buffer, 0, sizeof(buffer));
  
      if(mbedtls_x509_crt_info(buffer, sizeof(buffer), (char *)"* ",
                       mbedtls_ssl_get_peer_cert(&(connssl->ssl))) != -1)
        infof(data, "Dumping cert info:\n%s\n", buffer);
    }
  
  #ifdef HAS_ALPN
    if(data->set.ssl_enable_alpn) {
      next_protocol = mbedtls_ssl_get_alpn_protocol(&connssl->ssl);
  
      if(next_protocol != NULL) {
        infof(data, "ALPN, server accepted to use %s\n", next_protocol);
  
        if(strncmp(next_protocol, NGHTTP2_PROTO_VERSION_ID,
                    NGHTTP2_PROTO_VERSION_ID_LEN)) {
          conn->negnpn = CURL_HTTP_VERSION_2_0;
        }
        else if(strncmp(next_protocol, ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH)) {
          conn->negnpn = CURL_HTTP_VERSION_1_1;
        }
      }
      else {
        infof(data, "ALPN, server did not agree to a protocol\n");
      }
    }
  #endif
  
    connssl->connecting_state = ssl_connect_3;
    infof(data, "SSL connected\n");
  
    return CURLE_OK;
  }
  
  static CURLcode
  mbedtls_connect_step3(struct connectdata *conn,
                       int sockindex)
  {
    CURLcode retcode = CURLE_OK;
    struct ssl_connect_data *connssl = &conn->ssl[sockindex];
    struct SessionHandle *data = conn->data;
    void *old_ssl_sessionid = NULL;
    mbedtls_ssl_session *our_ssl_sessionid = &conn->ssl[sockindex].ssn;
    int incache;
  
    DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
  
    /* Save the current session data for possible re-use */
    incache = !(Curl_ssl_getsessionid(conn, &old_ssl_sessionid, NULL));
    if(incache) {
      if(old_ssl_sessionid != our_ssl_sessionid) {
        infof(data, "old SSL session ID is stale, removing\n");
        Curl_ssl_delsessionid(conn, old_ssl_sessionid);
        incache = FALSE;
      }
    }
    if(!incache) {
      void *new_session = malloc(sizeof(mbedtls_ssl_session));
  
      if(new_session) {
        memcpy(new_session, our_ssl_sessionid,
               sizeof(mbedtls_ssl_session));
  
        retcode = Curl_ssl_addsessionid(conn, new_session,
                                     sizeof(mbedtls_ssl_session));
      }
      else {
        retcode = CURLE_OUT_OF_MEMORY;
      }
  
      if(retcode) {
        failf(data, "failed to store ssl session");
        return retcode;
      }
    }
  
    connssl->connecting_state = ssl_connect_done;
  
    return CURLE_OK;
  }
  
  static ssize_t mbedtls_send(struct connectdata *conn,
                               int sockindex,
                               const void *mem,
                               size_t len,
                               CURLcode *curlcode)
  {
    int ret = -1;
  
    ret = mbedtls_ssl_write(&conn->ssl[sockindex].ssl,
                    (unsigned char *)mem, len);
  
    if(ret < 0) {
      *curlcode = (ret == MBEDTLS_ERR_SSL_WANT_WRITE) ?
        CURLE_AGAIN : CURLE_SEND_ERROR;
      ret = -1;
    }
  
    return ret;
  }
  
  void Curl_mbedtls_close_all(struct SessionHandle *data)
  {
    (void)data;
  }
  
  void Curl_mbedtls_close(struct connectdata *conn, int sockindex)
  {
    /* mbedtls_rsa_free(&conn->ssl[sockindex].rsa); */
    mbedtls_x509_crt_free(&conn->ssl[sockindex].clicert);
    mbedtls_x509_crt_free(&conn->ssl[sockindex].cacert);
    mbedtls_x509_crl_free(&conn->ssl[sockindex].crl);
    mbedtls_ssl_free(&conn->ssl[sockindex].ssl);
  }
  
  static ssize_t mbedtls_recv(struct connectdata *conn,
                               int num,
                               char *buf,
                               size_t buffersize,
                               CURLcode *curlcode)
  {
    int ret = -1;
    ssize_t len = -1;
  
    memset(buf, 0, buffersize);
    ret = mbedtls_ssl_read(&conn->ssl[num].ssl, (unsigned char *)buf,
                           buffersize);
  
    if(ret <= 0) {
      if(ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY)
        return 0;
  
      *curlcode = (ret == MBEDTLS_ERR_SSL_WANT_READ) ?
        CURLE_AGAIN : CURLE_RECV_ERROR;
      return -1;
    }
  
    len = ret;
  
    return len;
  }
  
  void Curl_mbedtls_session_free(void *ptr)
  {
    free(ptr);
  }
  
  size_t Curl_mbedtls_version(char *buffer, size_t size)
  {
    unsigned int version = mbedtls_version_get_number();
    return snprintf(buffer, size, "mbedTLS/%d.%d.%d", version>>24,
                    (version>>16)&0xff, (version>>8)&0xff);
  }
  
  static CURLcode
  mbedtls_connect_common(struct connectdata *conn,
                          int sockindex,
                          bool nonblocking,
                          bool *done)
  {
    CURLcode retcode;
    struct SessionHandle *data = conn->data;
    struct ssl_connect_data *connssl = &conn->ssl[sockindex];
    curl_socket_t sockfd = conn->sock[sockindex];
    long timeout_ms;
    int what;
  
    /* check if the connection has already been established */
    if(ssl_connection_complete == connssl->state) {
      *done = TRUE;
      return CURLE_OK;
    }
  
    if(ssl_connect_1==connssl->connecting_state) {
      /* Find out how much more time we're allowed */
      timeout_ms = Curl_timeleft(data, NULL, TRUE);
  
      if(timeout_ms < 0) {
        /* no need to continue if time already is up */
        failf(data, "SSL connection timeout");
        return CURLE_OPERATION_TIMEDOUT;
      }
      retcode = mbedtls_connect_step1(conn, sockindex);
      if(retcode)
        return retcode;
    }
  
    while(ssl_connect_2 == connssl->connecting_state ||
          ssl_connect_2_reading == connssl->connecting_state ||
          ssl_connect_2_writing == connssl->connecting_state) {
  
      /* check allowed time left */
      timeout_ms = Curl_timeleft(data, NULL, TRUE);
  
      if(timeout_ms < 0) {
        /* no need to continue if time already is up */
        failf(data, "SSL connection timeout");
        return CURLE_OPERATION_TIMEDOUT;
      }
  
      /* if ssl is expecting something, check if it's available. */
      if(connssl->connecting_state == ssl_connect_2_reading
         || connssl->connecting_state == ssl_connect_2_writing) {
  
        curl_socket_t writefd = ssl_connect_2_writing==
          connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
        curl_socket_t readfd = ssl_connect_2_reading==
          connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
  
        what = Curl_socket_ready(readfd, writefd, nonblocking ? 0 : timeout_ms);
        if(what < 0) {
          /* fatal error */
          failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
          return CURLE_SSL_CONNECT_ERROR;
        }
        else if(0 == what) {
          if(nonblocking) {
            *done = FALSE;
            return CURLE_OK;
          }
          else {
            /* timeout */
            failf(data, "SSL connection timeout");
            return CURLE_OPERATION_TIMEDOUT;
          }
        }
        /* socket is readable or writable */
      }
  
      /* Run transaction, and return to the caller if it failed or if
       * this connection is part of a multi handle and this loop would
       * execute again. This permits the owner of a multi handle to
       * abort a connection attempt before step2 has completed while
       * ensuring that a client using select() or epoll() will always
       * have a valid fdset to wait on.
       */
      retcode = mbedtls_connect_step2(conn, sockindex);
      if(retcode || (nonblocking &&
                     (ssl_connect_2 == connssl->connecting_state ||
                      ssl_connect_2_reading == connssl->connecting_state ||
                      ssl_connect_2_writing == connssl->connecting_state)))
        return retcode;
  
    } /* repeat step2 until all transactions are done. */
  
    if(ssl_connect_3==connssl->connecting_state) {
      retcode = mbedtls_connect_step3(conn, sockindex);
      if(retcode)
        return retcode;
    }
  
    if(ssl_connect_done==connssl->connecting_state) {
      connssl->state = ssl_connection_complete;
      conn->recv[sockindex] = mbedtls_recv;
      conn->send[sockindex] = mbedtls_send;
      *done = TRUE;
    }
    else
      *done = FALSE;
  
    /* Reset our connect state machine */
    connssl->connecting_state = ssl_connect_1;
  
    return CURLE_OK;
  }
  
  CURLcode
  Curl_mbedtls_connect_nonblocking(struct connectdata *conn,
                                  int sockindex,
                                  bool *done)
  {
    return mbedtls_connect_common(conn, sockindex, TRUE, done);
  }
  
  
  CURLcode
  Curl_mbedtls_connect(struct connectdata *conn,
                      int sockindex)
  {
    CURLcode retcode;
    bool done = FALSE;
  
    retcode = mbedtls_connect_common(conn, sockindex, FALSE, &done);
    if(retcode)
      return retcode;
  
    DEBUGASSERT(done);
  
    return CURLE_OK;
  }
  
  /*
   * return 0 error initializing SSL
   * return 1 SSL initialized successfully
   */
  int mbedtls_init(void)
  {
    return Curl_polarsslthreadlock_thread_setup();
  }
  
  void mbedtls_cleanup(void)
  {
    (void)Curl_polarsslthreadlock_thread_cleanup();
  }
  
  #endif /* USE_MBEDTLS */
  