int btrfs_bin_search(struct extent_buffer *eb, const struct btrfs_key *key,
		     int *slot)
{
	if (btrfs_header_level(eb) == 0)
		return generic_bin_search(eb,
					  offsetof(struct btrfs_leaf, items),
					  sizeof(struct btrfs_item),
					  key, btrfs_header_nritems(eb),
					  slot);
	else
		return generic_bin_search(eb,
					  offsetof(struct btrfs_node, ptrs),
					  sizeof(struct btrfs_key_ptr),
					  key, btrfs_header_nritems(eb),
					  slot);
}


struct btrfs_path *btrfs_alloc_path(void)
{
	return kmem_cache_zalloc(btrfs_path_cachep, GFP_NOFS);
}


const char *btrfs_super_csum_driver(u16 csum_type)
{
	/* csum type is validated at mount time */
	return btrfs_csums[csum_type].driver[0] ?
		btrfs_csums[csum_type].driver :
		btrfs_csums[csum_type].name;
}


__tree_mod_log_insert(struct btrfs_fs_info *fs_info, struct tree_mod_elem *tm)
{
	struct rb_root *tm_root;
	struct rb_node **new;
	struct rb_node *parent = NULL;
	struct tree_mod_elem *cur;

	lockdep_assert_held_write(&fs_info->tree_mod_log_lock);

	tm->seq = btrfs_inc_tree_mod_seq(fs_info);

	tm_root = &fs_info->tree_mod_log;
	new = &tm_root->rb_node;
	while (*new) {
		cur = rb_entry(*new, struct tree_mod_elem, node);
		parent = *new;
		if (cur->logical < tm->logical)
			new = &((*new)->rb_left);
		else if (cur->logical > tm->logical)
			new = &((*new)->rb_right);
		else if (cur->seq < tm->seq)
			new = &((*new)->rb_left);
		else if (cur->seq > tm->seq)
			new = &((*new)->rb_right);
		else
			return -EEXIST;
	}

	rb_link_node(&tm->node, parent, new);
	rb_insert_color(&tm->node, tm_root);
	return 0;
}


u64 btrfs_get_tree_mod_seq(struct btrfs_fs_info *fs_info,
			   struct seq_list *elem)
{
	write_lock(&fs_info->tree_mod_log_lock);
	if (!elem->seq) {
		elem->seq = btrfs_inc_tree_mod_seq(fs_info);
		list_add_tail(&elem->list, &fs_info->tree_mod_seq_list);
	}
	write_unlock(&fs_info->tree_mod_log_lock);

	return elem->seq;
}


CURLcode Curl_readwrite(struct connectdata *conn,
                        struct Curl_easy *data,
                        bool *done,
                        bool *comeback)
{
  struct SingleRequest *k = &data->req;
  CURLcode result;
  int didwhat = 0;

  curl_socket_t fd_read;
  curl_socket_t fd_write;
  int select_res = conn->cselect_bits;

  conn->cselect_bits = 0;

  /* only use the proper socket if the *_HOLD bit is not set simultaneously as
     then we are in rate limiting state in that transfer direction */

  if((k->keepon & KEEP_RECVBITS) == KEEP_RECV)
    fd_read = conn->sockfd;
  else
    fd_read = CURL_SOCKET_BAD;

  if((k->keepon & KEEP_SENDBITS) == KEEP_SEND)
    fd_write = conn->writesockfd;
  else
    fd_write = CURL_SOCKET_BAD;

  if(data->state.drain) {
    select_res |= CURL_CSELECT_IN;
    DEBUGF(infof(data, "Curl_readwrite: forcibly told to drain data\n"));
  }

  if(!select_res) /* Call for select()/poll() only, if read/write/error
                     status is not known. */
    select_res = Curl_socket_check(fd_read, CURL_SOCKET_BAD, fd_write, 0);

  if(select_res == CURL_CSELECT_ERR) {
    failf(data, "select/poll returned error");
    return CURLE_SEND_ERROR;
  }

#ifdef USE_HYPER
  if(conn->datastream)
    return conn->datastream(data, conn, &didwhat, done, select_res);
#endif
  /* We go ahead and do a read if we have a readable socket or if
     the stream was rewound (in which case we have data in a
     buffer) */
  if((k->keepon & KEEP_RECV) && (select_res & CURL_CSELECT_IN)) {
    result = readwrite_data(data, conn, k, &didwhat, done, comeback);
    if(result || *done)
      return result;
  }

  /* If we still have writing to do, we check if we have a writable socket. */
  if((k->keepon & KEEP_SEND) && (select_res & CURL_CSELECT_OUT)) {
    /* write */

    result = readwrite_upload(data, conn, &didwhat);
    if(result)
      return result;
  }

  k->now = Curl_now();
  if(!didwhat) {
    /* no read no write, this is a timeout? */
    if(k->exp100 == EXP100_AWAITING_CONTINUE) {
      /* This should allow some time for the header to arrive, but only a
         very short time as otherwise it'll be too much wasted time too
         often. */

      /* Quoting RFC2616, section "8.2.3 Use of the 100 (Continue) Status":

         Therefore, when a client sends this header field to an origin server
         (possibly via a proxy) from which it has never seen a 100 (Continue)
         status, the client SHOULD NOT wait for an indefinite period before
         sending the request body.

      */

      timediff_t ms = Curl_timediff(k->now, k->start100);
      if(ms >= data->set.expect_100_timeout) {
        /* we've waited long enough, continue anyway */
        k->exp100 = EXP100_SEND_DATA;
        k->keepon |= KEEP_SEND;
        Curl_expire_done(data, EXPIRE_100_TIMEOUT);
        infof(data, "Done waiting for 100-continue\n");
      }
    }
  }

  if(Curl_pgrsUpdate(data))
    result = CURLE_ABORTED_BY_CALLBACK;
  else
    result = Curl_speedcheck(data, k->now);
  if(result)
    return result;

  if(k->keepon) {
    if(0 > Curl_timeleft(data, &k->now, FALSE)) {
      if(k->size != -1) {
        failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T
              " milliseconds with %" CURL_FORMAT_CURL_OFF_T " out of %"
              CURL_FORMAT_CURL_OFF_T " bytes received",
              Curl_timediff(k->now, data->progress.t_startsingle),
              k->bytecount, k->size);
      }
      else {
        failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T
              " milliseconds with %" CURL_FORMAT_CURL_OFF_T " bytes received",
              Curl_timediff(k->now, data->progress.t_startsingle),
              k->bytecount);
      }
      return CURLE_OPERATION_TIMEDOUT;
    }
  }
  else {
    /*
     * The transfer has been performed. Just make some general checks before
     * returning.
     */

    if(!(data->set.opt_no_body) && (k->size != -1) &&
       (k->bytecount != k->size) &&
#ifdef CURL_DO_LINEEND_CONV
       /* Most FTP servers don't adjust their file SIZE response for CRLFs,
          so we'll check to see if the discrepancy can be explained
          by the number of CRLFs we've changed to LFs.
       */
       (k->bytecount != (k->size + data->state.crlf_conversions)) &&
#endif /* CURL_DO_LINEEND_CONV */
       !k->newurl) {
      failf(data, "transfer closed with %" CURL_FORMAT_CURL_OFF_T
            " bytes remaining to read", k->size - k->bytecount);
      return CURLE_PARTIAL_FILE;
    }
    if(!(data->set.opt_no_body) && k->chunk &&
       (conn->chunk.state != CHUNK_STOP)) {
      /*
       * In chunked mode, return an error if the connection is closed prior to
       * the empty (terminating) chunk is read.
       *
       * The condition above used to check for
       * conn->proto.http->chunk.datasize != 0 which is true after reading
       * *any* chunk, not just the empty chunk.
       *
       */
      failf(data, "transfer closed with outstanding read data remaining");
      return CURLE_PARTIAL_FILE;
    }
    if(Curl_pgrsUpdate(data))
      return CURLE_ABORTED_BY_CALLBACK;
  }

  /* Now update the "done" boolean we return */
  *done = (0 == (k->keepon&(KEEP_RECV|KEEP_SEND|
                            KEEP_RECV_PAUSE|KEEP_SEND_PAUSE))) ? TRUE : FALSE;

  return CURLE_OK;
}


static CURLcode ossl_connect_step1(struct Curl_easy *data,
                                   struct connectdata *conn, int sockindex)
{
  CURLcode result = CURLE_OK;
  char *ciphers;
  SSL_METHOD_QUAL SSL_METHOD *req_method = NULL;
  X509_LOOKUP *lookup = NULL;
  curl_socket_t sockfd = conn->sock[sockindex];
  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
  ctx_option_t ctx_options = 0;

#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
  bool sni;
  const char * const hostname = SSL_HOST_NAME();

#ifdef ENABLE_IPV6
  struct in6_addr addr;
#else
  struct in_addr addr;
#endif
#endif
  const long int ssl_version = SSL_CONN_CONFIG(version);
#ifdef USE_OPENSSL_SRP
  const enum CURL_TLSAUTH ssl_authtype = SSL_SET_OPTION(authtype);
#endif
  char * const ssl_cert = SSL_SET_OPTION(primary.clientcert);
  const struct curl_blob *ssl_cert_blob = SSL_SET_OPTION(primary.cert_blob);
  const char * const ssl_cert_type = SSL_SET_OPTION(cert_type);
  const char * const ssl_cafile = SSL_CONN_CONFIG(CAfile);
  const char * const ssl_capath = SSL_CONN_CONFIG(CApath);
  const bool verifypeer = SSL_CONN_CONFIG(verifypeer);
  const char * const ssl_crlfile = SSL_SET_OPTION(CRLfile);
  char error_buffer[256];
  struct ssl_backend_data *backend = connssl->backend;
  bool imported_native_ca = false;

  DEBUGASSERT(ssl_connect_1 == connssl->connecting_state);

  /* Make funny stuff to get random input */
  result = ossl_seed(data);
  if(result)
    return result;

  SSL_SET_OPTION_LVALUE(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();
#else
    req_method = SSLv23_client_method();
#endif
    use_sni(TRUE);
    break;
  case CURL_SSLVERSION_SSLv2:
#ifdef OPENSSL_NO_SSL2
    failf(data, OSSL_PACKAGE " was built without SSLv2 support");
    return CURLE_NOT_BUILT_IN;
#else
#ifdef USE_OPENSSL_SRP
    if(ssl_authtype == CURL_TLSAUTH_SRP)
      return CURLE_SSL_CONNECT_ERROR;
#endif
    req_method = SSLv2_client_method();
    use_sni(FALSE);
    break;
#endif
  case CURL_SSLVERSION_SSLv3:
#ifdef OPENSSL_NO_SSL3_METHOD
    failf(data, OSSL_PACKAGE " was built without SSLv3 support");
    return CURLE_NOT_BUILT_IN;
#else
#ifdef USE_OPENSSL_SRP
    if(ssl_authtype == CURL_TLSAUTH_SRP)
      return CURLE_SSL_CONNECT_ERROR;
#endif
    req_method = SSLv3_client_method();
    use_sni(FALSE);
    break;
#endif
  default:
    failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
    return CURLE_SSL_CONNECT_ERROR;
  }

  if(backend->ctx)
    SSL_CTX_free(backend->ctx);
  backend->ctx = SSL_CTX_new(req_method);

  if(!backend->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;
  }

#ifdef SSL_MODE_RELEASE_BUFFERS
  SSL_CTX_set_mode(backend->ctx, SSL_MODE_RELEASE_BUFFERS);
#endif

#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, conn);
    set_logger(conn, data);
  }
#endif

  /* OpenSSL contains code to work-around lots of bugs and flaws in various
     SSL-implementations. SSL_CTX_set_options() is used to enabled those
     work-arounds. The man page for this option states that SSL_OP_ALL enables
     all the work-arounds and that "It is usually safe to use SSL_OP_ALL to
     enable the bug workaround options if compatibility with somewhat broken
     implementations is desired."

     The "-no_ticket" option was introduced in Openssl0.9.8j. It's a flag to
     disable "rfc4507bis session ticket support".  rfc4507bis was later turned
     into the proper RFC5077 it seems: https://tools.ietf.org/html/rfc5077

     The enabled extension concerns the session management. I wonder how often
     libcurl stops a connection and then resumes a TLS session. also, sending
     the session data is some overhead. .I suggest that you just use your
     proposed patch (which explicitly disables TICKET).

     If someone writes an application with libcurl and openssl who wants to
     enable the feature, one can do this in the SSL callback.

     SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG option enabling allowed proper
     interoperability with web server Netscape Enterprise Server 2.0.1 which
     was released back in 1996.

     Due to CVE-2010-4180, option SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG has
     become ineffective as of OpenSSL 0.9.8q and 1.0.0c. In order to mitigate
     CVE-2010-4180 when using previous OpenSSL versions we no longer enable
     this option regardless of OpenSSL version and SSL_OP_ALL definition.

     OpenSSL added a work-around for a SSL 3.0/TLS 1.0 CBC vulnerability
     (https://www.openssl.org/~bodo/tls-cbc.txt). In 0.9.6e they added a bit to
     SSL_OP_ALL that _disables_ that work-around despite the fact that
     SSL_OP_ALL is documented to do "rather harmless" workarounds. In order to
     keep the secure work-around, the SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS bit
     must not be set.
  */

  ctx_options = SSL_OP_ALL;

#ifdef SSL_OP_NO_TICKET
  ctx_options |= SSL_OP_NO_TICKET;
#endif

#ifdef SSL_OP_NO_COMPRESSION
  ctx_options |= SSL_OP_NO_COMPRESSION;
#endif

#ifdef SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
  /* mitigate CVE-2010-4180 */
  ctx_options &= ~SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG;
#endif

#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
  /* unless the user explicitly ask to allow the protocol vulnerability we
     use the work-around */
  if(!SSL_SET_OPTION(enable_beast))
    ctx_options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
#endif

  switch(ssl_version) {
    /* "--sslv2" option means SSLv2 only, disable all others */
    case CURL_SSLVERSION_SSLv2:
#if OPENSSL_VERSION_NUMBER >= 0x10100000L /* 1.1.0 */
      SSL_CTX_set_min_proto_version(backend->ctx, SSL2_VERSION);
      SSL_CTX_set_max_proto_version(backend->ctx, SSL2_VERSION);
#else
      ctx_options |= SSL_OP_NO_SSLv3;
      ctx_options |= SSL_OP_NO_TLSv1;
#  if OPENSSL_VERSION_NUMBER >= 0x1000100FL
      ctx_options |= SSL_OP_NO_TLSv1_1;
      ctx_options |= SSL_OP_NO_TLSv1_2;
#    ifdef TLS1_3_VERSION
      ctx_options |= SSL_OP_NO_TLSv1_3;
#    endif
#  endif
#endif
      break;

    /* "--sslv3" option means SSLv3 only, disable all others */
    case CURL_SSLVERSION_SSLv3:
#if OPENSSL_VERSION_NUMBER >= 0x10100000L /* 1.1.0 */
      SSL_CTX_set_min_proto_version(backend->ctx, SSL3_VERSION);
      SSL_CTX_set_max_proto_version(backend->ctx, SSL3_VERSION);
#else
      ctx_options |= SSL_OP_NO_SSLv2;
      ctx_options |= SSL_OP_NO_TLSv1;
#  if OPENSSL_VERSION_NUMBER >= 0x1000100FL
      ctx_options |= SSL_OP_NO_TLSv1_1;
      ctx_options |= SSL_OP_NO_TLSv1_2;
#    ifdef TLS1_3_VERSION
      ctx_options |= SSL_OP_NO_TLSv1_3;
#    endif
#  endif
#endif
      break;

    /* "--tlsv<x.y>" options mean TLS >= version <x.y> */
    case CURL_SSLVERSION_DEFAULT:
    case CURL_SSLVERSION_TLSv1: /* TLS >= version 1.0 */
    case CURL_SSLVERSION_TLSv1_0: /* TLS >= version 1.0 */
    case CURL_SSLVERSION_TLSv1_1: /* TLS >= version 1.1 */
    case CURL_SSLVERSION_TLSv1_2: /* TLS >= version 1.2 */
    case CURL_SSLVERSION_TLSv1_3: /* TLS >= version 1.3 */
      /* asking for any TLS version as the minimum, means no SSL versions
        allowed */
      ctx_options |= SSL_OP_NO_SSLv2;
      ctx_options |= SSL_OP_NO_SSLv3;

#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) /* 1.1.0 */
      result = set_ssl_version_min_max(backend->ctx, conn);
#else
      result = set_ssl_version_min_max_legacy(&ctx_options, data, conn,
                                              sockindex);
#endif
      if(result != CURLE_OK)
        return result;
      break;

    default:
      failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
      return CURLE_SSL_CONNECT_ERROR;
  }

  SSL_CTX_set_options(backend->ctx, ctx_options);

#ifdef HAS_NPN
  if(conn->bits.tls_enable_npn)
    SSL_CTX_set_next_proto_select_cb(backend->ctx, select_next_proto_cb, data);
#endif

#ifdef HAS_ALPN
  if(conn->bits.tls_enable_alpn) {
    int cur = 0;
    unsigned char protocols[128];

#ifdef USE_NGHTTP2
    if(data->state.httpwant >= CURL_HTTP_VERSION_2
#ifndef CURL_DISABLE_PROXY
       && (!SSL_IS_PROXY() || !conn->bits.tunnel_proxy)
#endif
      ) {
      protocols[cur++] = NGHTTP2_PROTO_VERSION_ID_LEN;

      memcpy(&protocols[cur], NGHTTP2_PROTO_VERSION_ID,
          NGHTTP2_PROTO_VERSION_ID_LEN);
      cur += NGHTTP2_PROTO_VERSION_ID_LEN;
      infof(data, "ALPN, offering %s\n", NGHTTP2_PROTO_VERSION_ID);
    }
#endif

    protocols[cur++] = ALPN_HTTP_1_1_LENGTH;
    memcpy(&protocols[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH);
    cur += ALPN_HTTP_1_1_LENGTH;
    infof(data, "ALPN, offering %s\n", ALPN_HTTP_1_1);

    /* expects length prefixed preference ordered list of protocols in wire
     * format
     */
    if(SSL_CTX_set_alpn_protos(backend->ctx, protocols, cur)) {
      failf(data, "Error setting ALPN");
      return CURLE_SSL_CONNECT_ERROR;
    }
  }
#endif

  if(ssl_cert || ssl_cert_blob || ssl_cert_type) {
    BIO *ssl_cert_bio = NULL;
    BIO *ssl_key_bio = NULL;
    if(ssl_cert_blob) {
      /* the typecast of blob->len is fine since it is guaranteed to never be
         larger than CURL_MAX_INPUT_LENGTH */
      ssl_cert_bio = BIO_new_mem_buf(ssl_cert_blob->data,
                                     (int)ssl_cert_blob->len);
      if(!ssl_cert_bio)
        result = CURLE_OUT_OF_MEMORY;
    }
    if(!result && SSL_SET_OPTION(key_blob)) {
      ssl_key_bio = BIO_new_mem_buf(SSL_SET_OPTION(key_blob)->data,
                                    (int)SSL_SET_OPTION(key_blob)->len);
      if(!ssl_key_bio)
        result = CURLE_OUT_OF_MEMORY;
    }
    if(!result &&
       !cert_stuff(data, backend->ctx,
                   ssl_cert, ssl_cert_bio, ssl_cert_type,
                   SSL_SET_OPTION(key), ssl_key_bio,
                   SSL_SET_OPTION(key_type), SSL_SET_OPTION(key_passwd)))
      result = CURLE_SSL_CERTPROBLEM;
    if(ssl_cert_bio)
      BIO_free(ssl_cert_bio);
    if(ssl_key_bio)
      BIO_free(ssl_key_bio);
    if(result)
      /* failf() is already done in cert_stuff() */
      return result;
  }

  ciphers = SSL_CONN_CONFIG(cipher_list);
  if(!ciphers)
    ciphers = (char *)DEFAULT_CIPHER_SELECTION;
  if(ciphers) {
    if(!SSL_CTX_set_cipher_list(backend->ctx, ciphers)) {
      failf(data, "failed setting cipher list: %s", ciphers);
      return CURLE_SSL_CIPHER;
    }
    infof(data, "Cipher selection: %s\n", ciphers);
  }

#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
  {
    char *ciphers13 = SSL_CONN_CONFIG(cipher_list13);
    if(ciphers13) {
      if(!SSL_CTX_set_ciphersuites(backend->ctx, ciphers13)) {
        failf(data, "failed setting TLS 1.3 cipher suite: %s", ciphers13);
        return CURLE_SSL_CIPHER;
      }
      infof(data, "TLS 1.3 cipher selection: %s\n", ciphers13);
    }
  }
#endif

#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);
#endif

#ifdef HAVE_SSL_CTX_SET_EC_CURVES
  {
    char *curves = SSL_CONN_CONFIG(curves);
    if(curves) {
      if(!SSL_CTX_set1_curves_list(backend->ctx, curves)) {
        failf(data, "failed setting curves list: '%s'", curves);
        return CURLE_SSL_CIPHER;
      }
    }
  }
#endif

#ifdef USE_OPENSSL_SRP
  if(ssl_authtype == CURL_TLSAUTH_SRP) {
    char * const ssl_username = SSL_SET_OPTION(username);

    infof(data, "Using TLS-SRP username: %s\n", ssl_username);

    if(!SSL_CTX_set_srp_username(backend->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_SET_OPTION(password))) {
      failf(data, "failed setting SRP password");
      return CURLE_BAD_FUNCTION_ARGUMENT;
    }
    if(!SSL_CONN_CONFIG(cipher_list)) {
      infof(data, "Setting cipher list SRP\n");

      if(!SSL_CTX_set_cipher_list(backend->ctx, "SRP")) {
        failf(data, "failed setting SRP cipher list");
        return CURLE_SSL_CIPHER;
      }
    }
  }
#endif

#if defined(USE_WIN32_CRYPTO)
  /* Import certificates from the Windows root certificate store if requested.
     https://stackoverflow.com/questions/9507184/
     https://github.com/d3x0r/SACK/blob/master/src/netlib/ssl_layer.c#L1037
     https://tools.ietf.org/html/rfc5280 */
  if((SSL_CONN_CONFIG(verifypeer) || SSL_CONN_CONFIG(verifyhost)) &&
     (SSL_SET_OPTION(native_ca_store))) {
    X509_STORE *store = SSL_CTX_get_cert_store(backend->ctx);
    HCERTSTORE hStore = CertOpenSystemStore((HCRYPTPROV_LEGACY)NULL,
                                            TEXT("ROOT"));

    if(hStore) {
      PCCERT_CONTEXT pContext = NULL;
      /* The array of enhanced key usage OIDs will vary per certificate and is
         declared outside of the loop so that rather than malloc/free each
         iteration we can grow it with realloc, when necessary. */
      CERT_ENHKEY_USAGE *enhkey_usage = NULL;
      DWORD enhkey_usage_size = 0;

      /* This loop makes a best effort to import all valid certificates from
         the MS root store. If a certificate cannot be imported it is skipped.
         'result' is used to store only hard-fail conditions (such as out of
         memory) that cause an early break. */
      result = CURLE_OK;
      for(;;) {
        X509 *x509;
        FILETIME now;
        BYTE key_usage[2];
        DWORD req_size;
        const unsigned char *encoded_cert;
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
        char cert_name[256];
#endif

        pContext = CertEnumCertificatesInStore(hStore, pContext);
        if(!pContext)
          break;

#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
        if(!CertGetNameStringA(pContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0,
                               NULL, cert_name, sizeof(cert_name))) {
          strcpy(cert_name, "Unknown");
        }
        infof(data, "SSL: Checking cert \"%s\"\n", cert_name);
#endif

        encoded_cert = (const unsigned char *)pContext->pbCertEncoded;
        if(!encoded_cert)
          continue;

        GetSystemTimeAsFileTime(&now);
        if(CompareFileTime(&pContext->pCertInfo->NotBefore, &now) > 0 ||
           CompareFileTime(&now, &pContext->pCertInfo->NotAfter) > 0)
          continue;

        /* If key usage exists check for signing attribute */
        if(CertGetIntendedKeyUsage(pContext->dwCertEncodingType,
                                   pContext->pCertInfo,
                                   key_usage, sizeof(key_usage))) {
          if(!(key_usage[0] & CERT_KEY_CERT_SIGN_KEY_USAGE))
            continue;
        }
        else if(GetLastError())
          continue;

        /* If enhanced key usage exists check for server auth attribute.
         *
         * Note "In a Microsoft environment, a certificate might also have EKU
         * extended properties that specify valid uses for the certificate."
         * The call below checks both, and behavior varies depending on what is
         * found. For more details see CertGetEnhancedKeyUsage doc.
         */
        if(CertGetEnhancedKeyUsage(pContext, 0, NULL, &req_size)) {
          if(req_size && req_size > enhkey_usage_size) {
            void *tmp = realloc(enhkey_usage, req_size);

            if(!tmp) {
              failf(data, "SSL: Out of memory allocating for OID list");
              result = CURLE_OUT_OF_MEMORY;
              break;
            }

            enhkey_usage = (CERT_ENHKEY_USAGE *)tmp;
            enhkey_usage_size = req_size;
          }

          if(CertGetEnhancedKeyUsage(pContext, 0, enhkey_usage, &req_size)) {
            if(!enhkey_usage->cUsageIdentifier) {
              /* "If GetLastError returns CRYPT_E_NOT_FOUND, the certificate is
                 good for all uses. If it returns zero, the certificate has no
                 valid uses." */
              if((HRESULT)GetLastError() != CRYPT_E_NOT_FOUND)
                continue;
            }
            else {
              DWORD i;
              bool found = false;

              for(i = 0; i < enhkey_usage->cUsageIdentifier; ++i) {
                if(!strcmp("1.3.6.1.5.5.7.3.1" /* OID server auth */,
                           enhkey_usage->rgpszUsageIdentifier[i])) {
                  found = true;
                  break;
                }
              }

              if(!found)
                continue;
            }
          }
          else
            continue;
        }
        else
          continue;

        x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
        if(!x509)
          continue;

        /* Try to import the certificate. This may fail for legitimate reasons
           such as duplicate certificate, which is allowed by MS but not
           OpenSSL. */
        if(X509_STORE_add_cert(store, x509) == 1) {
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
          infof(data, "SSL: Imported cert \"%s\"\n", cert_name);
#endif
          imported_native_ca = true;
        }
        X509_free(x509);
      }

      free(enhkey_usage);
      CertFreeCertificateContext(pContext);
      CertCloseStore(hStore, 0);

      if(result)
        return result;
    }
    if(imported_native_ca)
      infof(data, "successfully imported windows ca store\n");
    else
      infof(data, "error importing windows ca store, continuing anyway\n");
  }
#endif

#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
  /* OpenSSL 3.0.0 has deprecated SSL_CTX_load_verify_locations */
  {
    if(ssl_cafile) {
      if(!SSL_CTX_load_verify_file(backend->ctx, ssl_cafile)) {
        if(verifypeer && !imported_native_ca) {
          /* Fail if we insist on successfully verifying the server. */
          failf(data, "error setting certificate file: %s", ssl_cafile);
          return CURLE_SSL_CACERT_BADFILE;
        }
        /* Continue with a warning if no certificate verif is required. */
        infof(data, "error setting certificate file, continuing anyway\n");
      }
      infof(data, " CAfile: %s\n", ssl_cafile);
    }
    if(ssl_capath) {
      if(!SSL_CTX_load_verify_dir(backend->ctx, ssl_capath)) {
        if(verifypeer && !imported_native_ca) {
          /* Fail if we insist on successfully verifying the server. */
          failf(data, "error setting certificate path: %s", ssl_capath);
          return CURLE_SSL_CACERT_BADFILE;
        }
        /* Continue with a warning if no certificate verif is required. */
        infof(data, "error setting certificate path, continuing anyway\n");
      }
      infof(data, " CApath: %s\n", ssl_capath);
    }
  }
#else
  if(ssl_cafile || ssl_capath) {
    /* tell SSL where to find CA certificates that are used to verify
       the servers certificate. */
    if(!SSL_CTX_load_verify_locations(backend->ctx, ssl_cafile, ssl_capath)) {
      if(verifypeer && !imported_native_ca) {
        /* Fail if we insist on successfully verifying the server. */
        failf(data, "error setting certificate verify locations:"
              "  CAfile: %s CApath: %s",
              ssl_cafile ? ssl_cafile : "none",
              ssl_capath ? ssl_capath : "none");
        return CURLE_SSL_CACERT_BADFILE;
      }
      /* Just continue with a warning if no strict certificate verification
         is required. */
      infof(data, "error setting certificate verify locations,"
            " continuing anyway:\n");
    }
    else {
      /* Everything is fine. */
      infof(data, "successfully set certificate verify locations:\n");
    }
    infof(data, " CAfile: %s\n", ssl_cafile ? ssl_cafile : "none");
    infof(data, " CApath: %s\n", ssl_capath ? ssl_capath : "none");
  }
#endif

#ifdef CURL_CA_FALLBACK
  if(verifypeer && !ssl_cafile && !ssl_capath && !imported_native_ca) {
    /* verifying the peer without any CA certificates won't
       work so use openssl's built in default as fallback */
    SSL_CTX_set_default_verify_paths(backend->ctx);
  }
#endif

  if(ssl_crlfile) {
    /* tell SSL where to find CRL file that is used to check certificate
     * revocation */
    lookup = X509_STORE_add_lookup(SSL_CTX_get_cert_store(backend->ctx),
                                 X509_LOOKUP_file());
    if(!lookup ||
       (!X509_load_crl_file(lookup, ssl_crlfile, X509_FILETYPE_PEM)) ) {
      failf(data, "error loading CRL file: %s", ssl_crlfile);
      return CURLE_SSL_CRL_BADFILE;
    }
    /* Everything is fine. */
    infof(data, "successfully load CRL file:\n");
    X509_STORE_set_flags(SSL_CTX_get_cert_store(backend->ctx),
                         X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL);

    infof(data, "  CRLfile: %s\n", ssl_crlfile);
  }

  if(verifypeer) {
    /* Try building a chain using issuers in the trusted store first to avoid
       problems with server-sent legacy intermediates.  Newer versions of
       OpenSSL do alternate chain checking by default but we do not know how to
       determine that in a reliable manner.
       https://rt.openssl.org/Ticket/Display.html?id=3621&user=guest&pass=guest
    */
#if defined(X509_V_FLAG_TRUSTED_FIRST)
    X509_STORE_set_flags(SSL_CTX_get_cert_store(backend->ctx),
                         X509_V_FLAG_TRUSTED_FIRST);
#endif
#ifdef X509_V_FLAG_PARTIAL_CHAIN
    if(!SSL_SET_OPTION(no_partialchain) && !ssl_crlfile) {
      /* Have intermediate certificates in the trust store be treated as
         trust-anchors, in the same way as self-signed root CA certificates
         are. This allows users to verify servers using the intermediate cert
         only, instead of needing the whole chain.

         Due to OpenSSL bug https://github.com/openssl/openssl/issues/5081 we
         cannot do partial chains with CRL check.
      */
      X509_STORE_set_flags(SSL_CTX_get_cert_store(backend->ctx),
                           X509_V_FLAG_PARTIAL_CHAIN);
    }
#endif
  }

  /* SSL 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(backend->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);
  }
#endif

  /* Enable the session cache because it's a prerequisite for the "new session"
   * callback. Use the "external storage" mode to avoid that OpenSSL creates
   * 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);

  /* give application a chance to interfere with SSL set up. */
  if(data->set.ssl.fsslctx) {
    Curl_set_in_callback(data, true);
    result = (*data->set.ssl.fsslctx)(data, backend->ctx,
                                      data->set.ssl.fsslctxp);
    Curl_set_in_callback(data, false);
    if(result) {
      failf(data, "error signaled by ssl ctx callback");
      return result;
    }
  }

  /* Lets make an SSL structure */
  if(backend->handle)
    SSL_free(backend->handle);
  backend->handle = SSL_new(backend->ctx);
  if(!backend->handle) {
    failf(data, "SSL: couldn't create a context (handle)!");
    return CURLE_OUT_OF_MEMORY;
  }

#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
    !defined(OPENSSL_NO_OCSP)
  if(SSL_CONN_CONFIG(verifystatus))
    SSL_set_tlsext_status_type(backend->handle, TLSEXT_STATUSTYPE_ocsp);
#endif

#if defined(OPENSSL_IS_BORINGSSL) && defined(ALLOW_RENEG)
  SSL_set_renegotiate_mode(backend->handle, ssl_renegotiate_freely);
#endif

  SSL_set_connect_state(backend->handle);

  backend->server_cert = 0x0;
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
  if((0 == Curl_inet_pton(AF_INET, hostname, &addr)) &&
#ifdef ENABLE_IPV6
     (0 == Curl_inet_pton(AF_INET6, hostname, &addr)) &&
#endif
     sni) {
    size_t nlen = strlen(hostname);
    if((long)nlen >= data->set.buffer_size)
      /* this is seriously messed up */
      return CURLE_SSL_CONNECT_ERROR;

    /* RFC 6066 section 3 says the SNI field is case insensitive, but browsers
       send the data lowercase and subsequently there are now numerous servers
       out there that don't work unless the name is lowercased */
    Curl_strntolower(data->state.buffer, hostname, nlen);
    data->state.buffer[nlen] = 0;
    if(!SSL_set_tlsext_host_name(backend->handle, data->state.buffer))
      infof(data, "WARNING: failed to configure server name indication (SNI) "
            "TLS extension\n");
  }
#endif

  /* Check if there's a cached ID we can/should use here! */
  if(SSL_SET_OPTION(primary.sessionid)) {
    void *ssl_sessionid = NULL;
    int data_idx = ossl_get_ssl_data_index();
    int connectdata_idx = ossl_get_ssl_conn_index();
    int sockindex_idx = ossl_get_ssl_sockindex_index();
    int proxy_idx = ossl_get_proxy_index();

    if(data_idx >= 0 && connectdata_idx >= 0 && sockindex_idx >= 0 &&
       proxy_idx >= 0) {
      /* Store the data needed for the "new session" callback.
       * The sockindex is stored as a pointer to an array element. */
      SSL_set_ex_data(backend->handle, data_idx, data);
      SSL_set_ex_data(backend->handle, connectdata_idx, conn);
      SSL_set_ex_data(backend->handle, sockindex_idx, conn->sock + sockindex);
#ifndef CURL_DISABLE_PROXY
      SSL_set_ex_data(backend->handle, proxy_idx, SSL_IS_PROXY() ? (void *) 1:
                      NULL);
#else
      SSL_set_ex_data(backend->handle, proxy_idx, NULL);
#endif

    }

    Curl_ssl_sessionid_lock(data);
    if(!Curl_ssl_getsessionid(data, conn, SSL_IS_PROXY() ? TRUE : FALSE,
                              &ssl_sessionid, NULL, sockindex)) {
      /* we got a session id, use it! */
      if(!SSL_set_session(backend->handle, ssl_sessionid)) {
        Curl_ssl_sessionid_unlock(data);
        failf(data, "SSL: SSL_set_session failed: %s",
              ossl_strerror(ERR_get_error(), error_buffer,
                            sizeof(error_buffer)));
        return CURLE_SSL_CONNECT_ERROR;
      }
      /* Informational message */
      infof(data, "SSL re-using session ID\n");
    }
    Curl_ssl_sessionid_unlock(data);
  }

#ifndef CURL_DISABLE_PROXY
  if(conn->proxy_ssl[sockindex].use) {
    BIO *const bio = BIO_new(BIO_f_ssl());
    SSL *handle = conn->proxy_ssl[sockindex].backend->handle;
    DEBUGASSERT(ssl_connection_complete == conn->proxy_ssl[sockindex].state);
    DEBUGASSERT(handle != NULL);
    DEBUGASSERT(bio != NULL);
    BIO_set_ssl(bio, handle, FALSE);
    SSL_set_bio(backend->handle, bio, bio);
  }
  else
#endif
    if(!SSL_set_fd(backend->handle, (int)sockfd)) {
    /* pass the raw socket into the SSL layers */
    failf(data, "SSL: SSL_set_fd failed: %s",
          ossl_strerror(ERR_get_error(), error_buffer, sizeof(error_buffer)));
    return CURLE_SSL_CONNECT_ERROR;
  }

  connssl->connecting_state = ssl_connect_2;

  return CURLE_OK;
}


static void mbedtls_cleanup(void)
{
  (void)Curl_mbedtlsthreadlock_thread_cleanup();
}


void Curl_free_primary_ssl_config(struct ssl_primary_config *sslc)
{
  Curl_safefree(sslc->CApath);
  Curl_safefree(sslc->CAfile);
  Curl_safefree(sslc->clientcert);
  Curl_safefree(sslc->random_file);
  Curl_safefree(sslc->egdsocket);
  Curl_safefree(sslc->cipher_list);
  Curl_safefree(sslc->cipher_list13);
  Curl_safefree(sslc->pinned_key);
  Curl_safefree(sslc->cert_blob);
  Curl_safefree(sslc->curves);
}


wolfssl_connect_common(struct Curl_easy *data,
                      struct connectdata *conn,
                      int sockindex,
                      bool nonblocking,
                      bool *done)
{
  CURLcode result;
  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
  curl_socket_t sockfd = conn->sock[sockindex];
  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 */
    const timediff_t 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;
    }

    result = wolfssl_connect_step1(data, conn, sockindex);
    if(result)
      return result;
  }

  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 */
    const timediff_t 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_check(readfd, CURL_SOCKET_BAD, 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.
     */
    result = wolfssl_connect_step2(data, conn, sockindex);
    if(result || (nonblocking &&
                  (ssl_connect_2 == connssl->connecting_state ||
                   ssl_connect_2_reading == connssl->connecting_state ||
                   ssl_connect_2_writing == connssl->connecting_state)))
      return result;
  } /* repeat step2 until all transactions are done. */

  if(ssl_connect_3 == connssl->connecting_state) {
    result = wolfssl_connect_step3(data, conn, sockindex);
    if(result)
      return result;
  }

  if(ssl_connect_done == connssl->connecting_state) {
    connssl->state = ssl_connection_complete;
    conn->recv[sockindex] = wolfssl_recv;
    conn->send[sockindex] = wolfssl_send;
    *done = TRUE;
  }
  else
    *done = FALSE;

  /* Reset our connect state machine */
  connssl->connecting_state = ssl_connect_1;

  return CURLE_OK;
}


static int do_file_type(const char *type)
{
  if(!type || !type[0])
    return SSL_FILETYPE_PEM;
  if(strcasecompare(type, "PEM"))
    return SSL_FILETYPE_PEM;
  if(strcasecompare(type, "DER"))
    return SSL_FILETYPE_ASN1;
  if(strcasecompare(type, "ENG"))
    return SSL_FILETYPE_ENGINE;
  if(strcasecompare(type, "P12"))
    return SSL_FILETYPE_PKCS12;
  return -1;
}


static int multissl_getsock(struct connectdata *conn, curl_socket_t *socks)
{
  if(multissl_setup(NULL))
    return 0;
  return Curl_ssl->getsock(conn, socks);
}


static size_t multissl_version(char *buffer, size_t size)
{
  static const struct Curl_ssl *selected;
  static char backends[200];
  static size_t backends_len;
  const struct Curl_ssl *current;

  current = Curl_ssl == &Curl_ssl_multi ? available_backends[0] : Curl_ssl;

  if(current != selected) {
    char *p = backends;
    char *end = backends + sizeof(backends);
    int i;

    selected = current;

    backends[0] = '\0';

    for(i = 0; available_backends[i]; ++i) {
      char vb[200];
      bool paren = (selected != available_backends[i]);

      if(available_backends[i]->version(vb, sizeof(vb))) {
        p += msnprintf(p, end - p, "%s%s%s%s", (p != backends ? " " : ""),
                       (paren ? "(" : ""), vb, (paren ? ")" : ""));
      }
    }

    backends_len = p - backends;
  }

  if(!size)
    return 0;

  if(size <= backends_len) {
    strncpy(buffer, backends, size - 1);
    buffer[size - 1] = '\0';
    return size - 1;
  }

  strcpy(buffer, backends);
  return backends_len;
}


bool Curl_ssl_data_pending(const struct connectdata *conn,
                           int connindex)
{
  return Curl_ssl->data_pending(conn, connindex);
}


static void *ossl_get_internals(struct ssl_connect_data *connssl,
                                CURLINFO info)
{
  /* Legacy: CURLINFO_TLS_SESSION must return an SSL_CTX pointer. */
  struct ssl_backend_data *backend = connssl->backend;
  return info == CURLINFO_TLS_SESSION ?
         (void *)backend->ctx : (void *)backend->handle;
}


mesalink_recv(struct Curl_easy *data, int num, char *buf, size_t buffersize,
              CURLcode *curlcode)
{
  struct connectdata *conn = data->conn;
  struct ssl_connect_data *connssl = &conn->ssl[num];
  char error_buffer[MESALINK_MAX_ERROR_SZ];
  int buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize;
  int nread = SSL_read(BACKEND->handle, buf, buffsize);

  if(nread <= 0) {
    int err = SSL_get_error(BACKEND->handle, nread);

    switch(err) {
    case SSL_ERROR_ZERO_RETURN: /* no more data */
    case IO_ERROR_CONNECTION_ABORTED:
      break;
    case SSL_ERROR_WANT_READ:
    case SSL_ERROR_WANT_WRITE:
      /* there's data pending, re-invoke SSL_read() */
      *curlcode = CURLE_AGAIN;
      return -1;
    default:
      failf(data,
            "SSL read: %s, errno %d",
            ERR_error_string_n(err, error_buffer, sizeof(error_buffer)),
            SOCKERRNO);
      *curlcode = CURLE_RECV_ERROR;
      return -1;
    }
  }
  return nread;
}


static bool ossl_cert_status_request(void)
{
#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
    !defined(OPENSSL_NO_OCSP)
  return TRUE;
#else
  return FALSE;
#endif
}


schannel_connect_step2(struct Curl_easy *data, struct connectdata *conn,
                       int sockindex)
{
  int i;
  ssize_t nread = -1, written = -1;
  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
  unsigned char *reallocated_buffer;
  SecBuffer outbuf[3];
  SecBufferDesc outbuf_desc;
  SecBuffer inbuf[2];
  SecBufferDesc inbuf_desc;
  SECURITY_STATUS sspi_status = SEC_E_OK;
  CURLcode result;
  bool doread;
#ifndef CURL_DISABLE_PROXY
  char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
    conn->host.name;
#else
  char * const hostname = conn->host.name;
#endif
  const char *pubkey_ptr;

  doread = (connssl->connecting_state != ssl_connect_2_writing) ? TRUE : FALSE;

  DEBUGF(infof(data,
               "schannel: SSL/TLS connection with %s port %hu (step 2/3)\n",
               hostname, conn->remote_port));

  if(!BACKEND->cred || !BACKEND->ctxt)
    return CURLE_SSL_CONNECT_ERROR;

  /* buffer to store previously received and decrypted data */
  if(BACKEND->decdata_buffer == NULL) {
    BACKEND->decdata_offset = 0;
    BACKEND->decdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE;
    BACKEND->decdata_buffer = malloc(BACKEND->decdata_length);
    if(BACKEND->decdata_buffer == NULL) {
      failf(data, "schannel: unable to allocate memory");
      return CURLE_OUT_OF_MEMORY;
    }
  }

  /* buffer to store previously received and encrypted data */
  if(BACKEND->encdata_buffer == NULL) {
    BACKEND->encdata_is_incomplete = false;
    BACKEND->encdata_offset = 0;
    BACKEND->encdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE;
    BACKEND->encdata_buffer = malloc(BACKEND->encdata_length);
    if(BACKEND->encdata_buffer == NULL) {
      failf(data, "schannel: unable to allocate memory");
      return CURLE_OUT_OF_MEMORY;
    }
  }

  /* if we need a bigger buffer to read a full message, increase buffer now */
  if(BACKEND->encdata_length - BACKEND->encdata_offset <
     CURL_SCHANNEL_BUFFER_FREE_SIZE) {
    /* increase internal encrypted data buffer */
    size_t reallocated_length = BACKEND->encdata_offset +
      CURL_SCHANNEL_BUFFER_FREE_SIZE;
    reallocated_buffer = realloc(BACKEND->encdata_buffer,
                                 reallocated_length);

    if(reallocated_buffer == NULL) {
      failf(data, "schannel: unable to re-allocate memory");
      return CURLE_OUT_OF_MEMORY;
    }
    else {
      BACKEND->encdata_buffer = reallocated_buffer;
      BACKEND->encdata_length = reallocated_length;
    }
  }

  for(;;) {
    TCHAR *host_name;
    if(doread) {
      /* read encrypted handshake data from socket */
      result = Curl_read_plain(conn->sock[sockindex],
                               (char *) (BACKEND->encdata_buffer +
                                         BACKEND->encdata_offset),
                               BACKEND->encdata_length -
                               BACKEND->encdata_offset,
                               &nread);
      if(result == CURLE_AGAIN) {
        if(connssl->connecting_state != ssl_connect_2_writing)
          connssl->connecting_state = ssl_connect_2_reading;
        DEBUGF(infof(data, "schannel: failed to receive handshake, "
                     "need more data\n"));
        return CURLE_OK;
      }
      else if((result != CURLE_OK) || (nread == 0)) {
        failf(data, "schannel: failed to receive handshake, "
              "SSL/TLS connection failed");
        return CURLE_SSL_CONNECT_ERROR;
      }

      /* increase encrypted data buffer offset */
      BACKEND->encdata_offset += nread;
      BACKEND->encdata_is_incomplete = false;
      DEBUGF(infof(data, "schannel: encrypted data got %zd\n", nread));
    }

    DEBUGF(infof(data,
                 "schannel: encrypted data buffer: offset %zu length %zu\n",
                 BACKEND->encdata_offset, BACKEND->encdata_length));

    /* setup input buffers */
    InitSecBuffer(&inbuf[0], SECBUFFER_TOKEN, malloc(BACKEND->encdata_offset),
                  curlx_uztoul(BACKEND->encdata_offset));
    InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0);
    InitSecBufferDesc(&inbuf_desc, inbuf, 2);

    /* setup output buffers */
    InitSecBuffer(&outbuf[0], SECBUFFER_TOKEN, NULL, 0);
    InitSecBuffer(&outbuf[1], SECBUFFER_ALERT, NULL, 0);
    InitSecBuffer(&outbuf[2], SECBUFFER_EMPTY, NULL, 0);
    InitSecBufferDesc(&outbuf_desc, outbuf, 3);

    if(inbuf[0].pvBuffer == NULL) {
      failf(data, "schannel: unable to allocate memory");
      return CURLE_OUT_OF_MEMORY;
    }

    /* copy received handshake data into input buffer */
    memcpy(inbuf[0].pvBuffer, BACKEND->encdata_buffer,
           BACKEND->encdata_offset);

    host_name = curlx_convert_UTF8_to_tchar(hostname);
    if(!host_name)
      return CURLE_OUT_OF_MEMORY;

    /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx
     */
    sspi_status = s_pSecFn->InitializeSecurityContext(
      &BACKEND->cred->cred_handle, &BACKEND->ctxt->ctxt_handle,
      host_name, BACKEND->req_flags, 0, 0, &inbuf_desc, 0, NULL,
      &outbuf_desc, &BACKEND->ret_flags, &BACKEND->ctxt->time_stamp);

    curlx_unicodefree(host_name);

    /* free buffer for received handshake data */
    Curl_safefree(inbuf[0].pvBuffer);

    /* check if the handshake was incomplete */
    if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) {
      BACKEND->encdata_is_incomplete = true;
      connssl->connecting_state = ssl_connect_2_reading;
      DEBUGF(infof(data,
                   "schannel: received incomplete message, need more data\n"));
      return CURLE_OK;
    }

    /* If the server has requested a client certificate, attempt to continue
       the handshake without one. This will allow connections to servers which
       request a client certificate but do not require it. */
    if(sspi_status == SEC_I_INCOMPLETE_CREDENTIALS &&
       !(BACKEND->req_flags & ISC_REQ_USE_SUPPLIED_CREDS)) {
      BACKEND->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS;
      connssl->connecting_state = ssl_connect_2_writing;
      DEBUGF(infof(data,
                   "schannel: a client certificate has been requested\n"));
      return CURLE_OK;
    }

    /* check if the handshake needs to be continued */
    if(sspi_status == SEC_I_CONTINUE_NEEDED || sspi_status == SEC_E_OK) {
      for(i = 0; i < 3; i++) {
        /* search for handshake tokens that need to be send */
        if(outbuf[i].BufferType == SECBUFFER_TOKEN && outbuf[i].cbBuffer > 0) {
          DEBUGF(infof(data, "schannel: sending next handshake data: "
                       "sending %lu bytes...\n", outbuf[i].cbBuffer));

          /* send handshake token to server */
          result = Curl_write_plain(data, conn->sock[sockindex],
                                    outbuf[i].pvBuffer, outbuf[i].cbBuffer,
                                    &written);
          if((result != CURLE_OK) ||
             (outbuf[i].cbBuffer != (size_t) written)) {
            failf(data, "schannel: failed to send next handshake data: "
                  "sent %zd of %lu bytes", written, outbuf[i].cbBuffer);
            return CURLE_SSL_CONNECT_ERROR;
          }
        }

        /* free obsolete buffer */
        if(outbuf[i].pvBuffer != NULL) {
          s_pSecFn->FreeContextBuffer(outbuf[i].pvBuffer);
        }
      }
    }
    else {
      char buffer[STRERROR_LEN];
      switch(sspi_status) {
      case SEC_E_INSUFFICIENT_MEMORY:
        failf(data, "schannel: next InitializeSecurityContext failed: %s",
              Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
        return CURLE_OUT_OF_MEMORY;
      case SEC_E_WRONG_PRINCIPAL:
        failf(data, "schannel: SNI or certificate check failed: %s",
              Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
        return CURLE_PEER_FAILED_VERIFICATION;
      case SEC_E_UNTRUSTED_ROOT:
        failf(data, "schannel: %s",
              Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
        return CURLE_PEER_FAILED_VERIFICATION;
        /*
          case SEC_E_INVALID_HANDLE:
          case SEC_E_INVALID_TOKEN:
          case SEC_E_LOGON_DENIED:
          case SEC_E_TARGET_UNKNOWN:
          case SEC_E_NO_AUTHENTICATING_AUTHORITY:
          case SEC_E_INTERNAL_ERROR:
          case SEC_E_NO_CREDENTIALS:
          case SEC_E_UNSUPPORTED_FUNCTION:
          case SEC_E_APPLICATION_PROTOCOL_MISMATCH:
        */
      default:
        failf(data, "schannel: next InitializeSecurityContext failed: %s",
              Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
        return CURLE_SSL_CONNECT_ERROR;
      }
    }

    /* check if there was additional remaining encrypted data */
    if(inbuf[1].BufferType == SECBUFFER_EXTRA && inbuf[1].cbBuffer > 0) {
      DEBUGF(infof(data, "schannel: encrypted data length: %lu\n",
                   inbuf[1].cbBuffer));
      /*
        There are two cases where we could be getting extra data here:
        1) If we're renegotiating a connection and the handshake is already
        complete (from the server perspective), it can encrypted app data
        (not handshake data) in an extra buffer at this point.
        2) (sspi_status == SEC_I_CONTINUE_NEEDED) We are negotiating a
        connection and this extra data is part of the handshake.
        We should process the data immediately; waiting for the socket to
        be ready may fail since the server is done sending handshake data.
      */
      /* check if the remaining data is less than the total amount
         and therefore begins after the already processed data */
      if(BACKEND->encdata_offset > inbuf[1].cbBuffer) {
        memmove(BACKEND->encdata_buffer,
                (BACKEND->encdata_buffer + BACKEND->encdata_offset) -
                inbuf[1].cbBuffer, inbuf[1].cbBuffer);
        BACKEND->encdata_offset = inbuf[1].cbBuffer;
        if(sspi_status == SEC_I_CONTINUE_NEEDED) {
          doread = FALSE;
          continue;
        }
      }
    }
    else {
      BACKEND->encdata_offset = 0;
    }
    break;
  }

  /* check if the handshake needs to be continued */
  if(sspi_status == SEC_I_CONTINUE_NEEDED) {
    connssl->connecting_state = ssl_connect_2_reading;
    return CURLE_OK;
  }

  /* check if the handshake is complete */
  if(sspi_status == SEC_E_OK) {
    connssl->connecting_state = ssl_connect_3;
    DEBUGF(infof(data, "schannel: SSL/TLS handshake complete\n"));
  }

  pubkey_ptr = SSL_IS_PROXY() ?
    data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] :
    data->set.str[STRING_SSL_PINNEDPUBLICKEY];
  if(pubkey_ptr) {
    result = pkp_pin_peer_pubkey(data, conn, sockindex, pubkey_ptr);
    if(result) {
      failf(data, "SSL: public key does not match pinned public key!");
      return result;
    }
  }

#ifdef HAS_MANUAL_VERIFY_API
  if(conn->ssl_config.verifypeer && BACKEND->use_manual_cred_validation) {
    return Curl_verify_certificate(data, conn, sockindex);
  }
#endif

  return CURLE_OK;
}


static bool gtls_data_pending(const struct connectdata *conn,
                              int connindex)
{
  const struct ssl_connect_data *connssl = &conn->ssl[connindex];
  bool res = FALSE;
  struct ssl_backend_data *backend = connssl->backend;
  if(backend->session &&
     0 != gnutls_record_check_pending(backend->session))
    res = TRUE;

#ifndef CURL_DISABLE_PROXY
  connssl = &conn->proxy_ssl[connindex];
  backend = connssl->backend;
  if(backend->session &&
     0 != gnutls_record_check_pending(backend->session))
    res = TRUE;
#endif

  return res;
}


mbed_connect_common(struct Curl_easy *data,
                    struct connectdata *conn,
                    int sockindex,
                    bool nonblocking,
                    bool *done)
{
  CURLcode retcode;
  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
  curl_socket_t sockfd = conn->sock[sockindex];
  timediff_t 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 = mbed_connect_step1(data, 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_check(readfd, CURL_SOCKET_BAD, 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 = mbed_connect_step2(data, 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 = mbed_connect_step3(data, conn, sockindex);
    if(retcode)
      return retcode;
  }

  if(ssl_connect_done == connssl->connecting_state) {
    connssl->state = ssl_connection_complete;
    conn->recv[sockindex] = mbed_recv;
    conn->send[sockindex] = mbed_send;
    *done = TRUE;
  }
  else
    *done = FALSE;

  /* Reset our connect state machine */
  connssl->connecting_state = ssl_connect_1;

  return CURLE_OK;
}


static CURLcode mbedtls_sha256sum(const unsigned char *input,
                                  size_t inputlen,
                                  unsigned char *sha256sum,
                                  size_t sha256len UNUSED_PARAM)
{
  (void)sha256len;
#if MBEDTLS_VERSION_NUMBER < 0x02070000
  mbedtls_sha256(input, inputlen, sha256sum, 0);
#else
  /* returns 0 on success, otherwise failure */
  if(mbedtls_sha256_ret(input, inputlen, sha256sum, 0) != 0)
    return CURLE_BAD_FUNCTION_ARGUMENT;
#endif
  return CURLE_OK;
}


bool Curl_none_false_start(void)
{
  return FALSE;
}


CURLcode Curl_ssl_addsessionid(struct Curl_easy *data,
                               struct connectdata *conn,
                               bool isProxy,
                               void *ssl_sessionid,
                               size_t idsize,
                               int sockindex)
{
  size_t i;
  struct Curl_ssl_session *store = &data->state.session[0];
  long oldest_age = data->state.session[0].age; /* zero if unused */
  char *clone_host;
  char *clone_conn_to_host;
  int conn_to_port;
  long *general_age;
#ifndef CURL_DISABLE_PROXY
  struct ssl_primary_config * const ssl_config = isProxy ?
    &conn->proxy_ssl_config :
    &conn->ssl_config;
  const char *hostname = isProxy ? conn->http_proxy.host.name :
    conn->host.name;
#else
  /* proxy support disabled */
  const bool isProxy = FALSE;
  struct ssl_primary_config * const ssl_config = &conn->ssl_config;
  const char *hostname = conn->host.name;
  (void)sockindex;
#endif
  (void)sockindex;
  DEBUGASSERT(SSL_SET_OPTION(primary.sessionid));

  clone_host = strdup(hostname);
  if(!clone_host)
    return CURLE_OUT_OF_MEMORY; /* bail out */

  if(conn->bits.conn_to_host) {
    clone_conn_to_host = strdup(conn->conn_to_host.name);
    if(!clone_conn_to_host) {
      free(clone_host);
      return CURLE_OUT_OF_MEMORY; /* bail out */
    }
  }
  else
    clone_conn_to_host = NULL;

  if(conn->bits.conn_to_port)
    conn_to_port = conn->conn_to_port;
  else
    conn_to_port = -1;

  /* Now we should add the session ID and the host name to the cache, (remove
     the oldest if necessary) */

  /* If using shared SSL session, lock! */
  if(SSLSESSION_SHARED(data)) {
    general_age = &data->share->sessionage;
  }
  else {
    general_age = &data->state.sessionage;
  }

  /* find an empty slot for us, or find the oldest */
  for(i = 1; (i < data->set.general_ssl.max_ssl_sessions) &&
        data->state.session[i].sessionid; i++) {
    if(data->state.session[i].age < oldest_age) {
      oldest_age = data->state.session[i].age;
      store = &data->state.session[i];
    }
  }
  if(i == data->set.general_ssl.max_ssl_sessions)
    /* cache is full, we must "kill" the oldest entry! */
    Curl_ssl_kill_session(store);
  else
    store = &data->state.session[i]; /* use this slot */

  /* now init the session struct wisely */
  store->sessionid = ssl_sessionid;
  store->idsize = idsize;
  store->age = *general_age;    /* set current age */
  /* free it if there's one already present */
  free(store->name);
  free(store->conn_to_host);
  store->name = clone_host;               /* clone host name */
  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 = isProxy ? (int)conn->port : conn->remote_port;
  store->scheme = conn->handler->scheme;

  if(!Curl_clone_primary_ssl_config(ssl_config, &store->ssl_config)) {
    Curl_free_primary_ssl_config(&store->ssl_config);
    store->sessionid = NULL; /* let caller free sessionid */
    free(clone_host);
    free(clone_conn_to_host);
    return CURLE_OUT_OF_MEMORY;
  }

  return CURLE_OK;
}


Curl_ssl_connect(struct Curl_easy *data, struct connectdata *conn,
                 int sockindex)
{
  CURLcode result;

#ifndef CURL_DISABLE_PROXY
  if(conn->bits.proxy_ssl_connected[sockindex]) {
    result = ssl_connect_init_proxy(conn, sockindex);
    if(result)
      return result;
  }
#endif

  if(!ssl_prefs_check(data))
    return CURLE_SSL_CONNECT_ERROR;

  /* mark this is being ssl-enabled from here on. */
  conn->ssl[sockindex].use = TRUE;
  conn->ssl[sockindex].state = ssl_connection_negotiating;

  result = Curl_ssl->connect_blocking(data, conn, sockindex);

  if(!result)
    Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSL is connected */

  return result;
}


static CURLcode handshake(struct Curl_easy *data,
                          struct connectdata *conn,
                          int sockindex,
                          bool duringconnect,
                          bool nonblocking)
{
  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
  struct ssl_backend_data *backend = connssl->backend;
  gnutls_session_t session = backend->session;
  curl_socket_t sockfd = conn->sock[sockindex];

  for(;;) {
    timediff_t timeout_ms;
    int rc;

    /* check allowed time left */
    timeout_ms = Curl_timeleft(data, NULL, duringconnect);

    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) {
      int what;
      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_check(readfd, CURL_SOCKET_BAD, writefd,
                               nonblocking?0:
                               timeout_ms?timeout_ms:1000);
      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)
          return CURLE_OK;
        else if(timeout_ms) {
          /* timeout */
          failf(data, "SSL connection timeout at %ld", (long)timeout_ms);
          return CURLE_OPERATION_TIMEDOUT;
        }
      }
      /* socket is readable or writable */
    }

    rc = gnutls_handshake(session);

    if((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) {
      connssl->connecting_state =
        gnutls_record_get_direction(session)?
        ssl_connect_2_writing:ssl_connect_2_reading;
      continue;
    }
    else if((rc < 0) && !gnutls_error_is_fatal(rc)) {
      const char *strerr = NULL;

      if(rc == GNUTLS_E_WARNING_ALERT_RECEIVED) {
        int alert = gnutls_alert_get(session);
        strerr = gnutls_alert_get_name(alert);
      }

      if(strerr == NULL)
        strerr = gnutls_strerror(rc);

      infof(data, "gnutls_handshake() warning: %s\n", strerr);
      continue;
    }
    else if(rc < 0) {
      const char *strerr = NULL;

      if(rc == GNUTLS_E_FATAL_ALERT_RECEIVED) {
        int alert = gnutls_alert_get(session);
        strerr = gnutls_alert_get_name(alert);
      }

      if(strerr == NULL)
        strerr = gnutls_strerror(rc);

      failf(data, "gnutls_handshake() failed: %s", strerr);
      return CURLE_SSL_CONNECT_ERROR;
    }

    /* Reset our connect state machine */
    connssl->connecting_state = ssl_connect_1;
    return CURLE_OK;
  }
}


get_alg_id_by_name(char *name)
{
  char tmp[LONGEST_ALG_ID] = { 0 };
  char *nameEnd = strchr(name, ':');
  size_t n = nameEnd ? min((size_t)(nameEnd - name), LONGEST_ALG_ID - 1) : \
    min(strlen(name), LONGEST_ALG_ID - 1);
  strncpy(tmp, name, n);
  tmp[n] = 0;
  CIPHEROPTION(CALG_MD2);
  CIPHEROPTION(CALG_MD4);
  CIPHEROPTION(CALG_MD5);
  CIPHEROPTION(CALG_SHA);
  CIPHEROPTION(CALG_SHA1);
  CIPHEROPTION(CALG_MAC);
  CIPHEROPTION(CALG_RSA_SIGN);
  CIPHEROPTION(CALG_DSS_SIGN);
/*ifdefs for the options that are defined conditionally in wincrypt.h*/
#ifdef CALG_NO_SIGN
  CIPHEROPTION(CALG_NO_SIGN);
#endif
  CIPHEROPTION(CALG_RSA_KEYX);
  CIPHEROPTION(CALG_DES);
#ifdef CALG_3DES_112
  CIPHEROPTION(CALG_3DES_112);
#endif
  CIPHEROPTION(CALG_3DES);
  CIPHEROPTION(CALG_DESX);
  CIPHEROPTION(CALG_RC2);
  CIPHEROPTION(CALG_RC4);
  CIPHEROPTION(CALG_SEAL);
#ifdef CALG_DH_SF
  CIPHEROPTION(CALG_DH_SF);
#endif
  CIPHEROPTION(CALG_DH_EPHEM);
#ifdef CALG_AGREEDKEY_ANY
  CIPHEROPTION(CALG_AGREEDKEY_ANY);
#endif
#ifdef CALG_HUGHES_MD5
  CIPHEROPTION(CALG_HUGHES_MD5);
#endif
  CIPHEROPTION(CALG_SKIPJACK);
#ifdef CALG_TEK
  CIPHEROPTION(CALG_TEK);
#endif
  CIPHEROPTION(CALG_CYLINK_MEK);
  CIPHEROPTION(CALG_SSL3_SHAMD5);
#ifdef CALG_SSL3_MASTER
  CIPHEROPTION(CALG_SSL3_MASTER);
#endif
#ifdef CALG_SCHANNEL_MASTER_HASH
  CIPHEROPTION(CALG_SCHANNEL_MASTER_HASH);
#endif
#ifdef CALG_SCHANNEL_MAC_KEY
  CIPHEROPTION(CALG_SCHANNEL_MAC_KEY);
#endif
#ifdef CALG_SCHANNEL_ENC_KEY
  CIPHEROPTION(CALG_SCHANNEL_ENC_KEY);
#endif
#ifdef CALG_PCT1_MASTER
  CIPHEROPTION(CALG_PCT1_MASTER);
#endif
#ifdef CALG_SSL2_MASTER
  CIPHEROPTION(CALG_SSL2_MASTER);
#endif
#ifdef CALG_TLS1_MASTER
  CIPHEROPTION(CALG_TLS1_MASTER);
#endif
#ifdef CALG_RC5
  CIPHEROPTION(CALG_RC5);
#endif
#ifdef CALG_HMAC
  CIPHEROPTION(CALG_HMAC);
#endif
#if !defined(__W32API_MAJOR_VERSION) ||                                 \
  !defined(__W32API_MINOR_VERSION) ||                                   \
  defined(__MINGW64_VERSION_MAJOR) ||                                   \
  (__W32API_MAJOR_VERSION > 5)     ||                                   \
  ((__W32API_MAJOR_VERSION == 5) && (__W32API_MINOR_VERSION > 0))
  /* CALG_TLS1PRF has a syntax error in MinGW's w32api up to version 5.0,
     see https://osdn.net/projects/mingw/ticket/38391 */
  CIPHEROPTION(CALG_TLS1PRF);
#endif
#ifdef CALG_HASH_REPLACE_OWF
  CIPHEROPTION(CALG_HASH_REPLACE_OWF);
#endif
#ifdef CALG_AES_128
  CIPHEROPTION(CALG_AES_128);
#endif
#ifdef CALG_AES_192
  CIPHEROPTION(CALG_AES_192);
#endif
#ifdef CALG_AES_256
  CIPHEROPTION(CALG_AES_256);
#endif
#ifdef CALG_AES
  CIPHEROPTION(CALG_AES);
#endif
#ifdef CALG_SHA_256
  CIPHEROPTION(CALG_SHA_256);
#endif
#ifdef CALG_SHA_384
  CIPHEROPTION(CALG_SHA_384);
#endif
#ifdef CALG_SHA_512
  CIPHEROPTION(CALG_SHA_512);
#endif
#ifdef CALG_ECDH
  CIPHEROPTION(CALG_ECDH);
#endif
#ifdef CALG_ECMQV
  CIPHEROPTION(CALG_ECMQV);
#endif
#ifdef CALG_ECDSA
  CIPHEROPTION(CALG_ECDSA);
#endif
#ifdef CALG_ECDH_EPHEM
  CIPHEROPTION(CALG_ECDH_EPHEM);
#endif
  return 0;
}


static ssize_t gtls_pull(void *s, void *buf, size_t len)
{
  curl_socket_t sock = *(curl_socket_t *)s;
  ssize_t ret = sread(sock, buf, len);
  return ret;
}


static void wolfssl_session_free(void *ptr)
{
  (void)ptr;
  /* wolfSSL reuses sessions on own, no free */
}


curve25519_eh_to_x (mp_limb_t *xp, const mp_limb_t *p,
		    mp_limb_t *scratch)
{
#define vp (p + ecc->p.size)
#define wp (p + 2*ecc->p.size)
#define t0 scratch
#define t1 (scratch + ecc->p.size)
#define tp (scratch + 2*ecc->p.size)

  const struct ecc_curve *ecc = &_nettle_curve25519;

  /* If u = U/W and v = V/W are the coordinates of the point on the
     Edwards curve we get the curve25519 x coordinate as

     x = (1+v) / (1-v) = (W + V) / (W - V)
  */
  /* NOTE: For the infinity point, this subtraction gives zero (mod
     p), which isn't invertible. For curve25519, the desired output is
     x = 0, and we should be fine, since ecc_mod_inv for ecc->p returns 0
     in this case. */
  ecc_mod_sub (&ecc->p, t0, wp, vp);
  /* Needs a total of 6*size storage. */
  ecc->p.invert (&ecc->p, t1, t0, tp);

  ecc_mod_add (&ecc->p, t0, wp, vp);
  ecc_mod_mul_canonical (&ecc->p, xp, t0, t1, tp);
#undef vp
#undef wp
#undef t0
#undef t1
#undef tp
}


ecc_mod_mul_canonical (const struct ecc_modulo *m, mp_limb_t *rp,
		       const mp_limb_t *ap, const mp_limb_t *bp, mp_limb_t *tp)
{
  mp_limb_t cy;
  mpn_mul_n (tp, ap, bp, m->size);
  m->reduce (m, tp + m->size, tp);

  cy = mpn_sub_n (rp, tp + m->size, m->m, m->size);
  cnd_copy (cy, rp, tp + m->size, m->size);
}


curve448_eh_to_x (mp_limb_t *xp, const mp_limb_t *p, mp_limb_t *scratch)
{
#define up p
#define vp (p + ecc->p.size)
#define t0 scratch
#define tp (scratch + ecc->p.size)

  const struct ecc_curve *ecc = &_nettle_curve448;

  /* If u = U/W and v = V/W are the coordinates of the point on
     edwards448 we get the curve448 x coordinate as

     x = v^2 / u^2 = (V/W)^2 / (U/W)^2 = (V/U)^2
  */
  /* Needs a total of 5*size storage. */
  ecc->p.invert (&ecc->p, t0, up, tp);
  ecc_mod_mul (&ecc->p, t0, t0, vp, tp);
  ecc_mod_sqr_canonical (&ecc->p, xp, t0, tp);

#undef up
#undef vp
#undef t0
#undef tp
}


ecc_ecdsa_verify_itch (const struct ecc_curve *ecc)
{
  /* Largest storage need is for the ecc->mul call. */
  return 5*ecc->p.size + ecc->mul_itch;
}


ecc_gostdsa_verify_itch (const struct ecc_curve *ecc)
{
  /* Largest storage need is for the ecc->mul call. */
  return 5*ecc->p.size + ecc->mul_itch;
}


struct btf *bpf_get_btf_vmlinux(void)
{
	if (!btf_vmlinux && IS_ENABLED(CONFIG_DEBUG_INFO_BTF)) {
		mutex_lock(&bpf_verifier_lock);
		if (!btf_vmlinux)
			btf_vmlinux = btf_parse_vmlinux();
		mutex_unlock(&bpf_verifier_lock);
	}
	return btf_vmlinux;
}


static int resolve_map_arg_type(struct bpf_verifier_env *env,
				 const struct bpf_call_arg_meta *meta,
				 enum bpf_arg_type *arg_type)
{
	if (!meta->map_ptr) {
		/* kernel subsystem misconfigured verifier */
		verbose(env, "invalid map_ptr to access map->type\n");
		return -EACCES;
	}

	switch (meta->map_ptr->map_type) {
	case BPF_MAP_TYPE_SOCKMAP:
	case BPF_MAP_TYPE_SOCKHASH:
		if (*arg_type == ARG_PTR_TO_MAP_VALUE) {
			*arg_type = ARG_PTR_TO_BTF_ID_SOCK_COMMON;
		} else {
			verbose(env, "invalid arg_type for sockmap/sockhash\n");
			return -EINVAL;
		}
		break;

	default:
		break;
	}
	return 0;
}


static void __mark_reg_known(struct bpf_reg_state *reg, u64 imm)
{
	/* Clear id, off, and union(map_ptr, range) */
	memset(((u8 *)reg) + sizeof(reg->type), 0,
	       offsetof(struct bpf_reg_state, var_off) - sizeof(reg->type));
	___mark_reg_known(reg, imm);
}


static void scalar_min_max_xor(struct bpf_reg_state *dst_reg,
			       struct bpf_reg_state *src_reg)
{
	bool src_known = tnum_is_const(src_reg->var_off);
	bool dst_known = tnum_is_const(dst_reg->var_off);
	s64 smin_val = src_reg->smin_value;

	if (src_known && dst_known) {
		/* dst_reg->var_off.value has been updated earlier */
		__mark_reg_known(dst_reg, dst_reg->var_off.value);
		return;
	}

	/* We get both minimum and maximum from the var_off. */
	dst_reg->umin_value = dst_reg->var_off.value;
	dst_reg->umax_value = dst_reg->var_off.value | dst_reg->var_off.mask;

	if (dst_reg->smin_value >= 0 && smin_val >= 0) {
		/* XORing two positive sign numbers gives a positive,
		 * so safe to cast u64 result into s64.
		 */
		dst_reg->smin_value = dst_reg->umin_value;
		dst_reg->smax_value = dst_reg->umax_value;
	} else {
		dst_reg->smin_value = S64_MIN;
		dst_reg->smax_value = S64_MAX;
	}

	__update_reg_bounds(dst_reg);
}


static void mark_btf_ld_reg(struct bpf_verifier_env *env,
			    struct bpf_reg_state *regs, u32 regno,
			    enum bpf_reg_type reg_type,
			    struct btf *btf, u32 btf_id)
{
	if (reg_type == SCALAR_VALUE) {
		mark_reg_unknown(env, regs, regno);
		return;
	}
	mark_reg_known_zero(env, regs, regno);
	regs[regno].type = PTR_TO_BTF_ID;
	regs[regno].btf = btf;
	regs[regno].btf_id = btf_id;
}


static int is_pkt_ptr_branch_taken(struct bpf_reg_state *dst_reg,
				   struct bpf_reg_state *src_reg,
				   u8 opcode)
{
	struct bpf_reg_state *pkt;

	if (src_reg->type == PTR_TO_PACKET_END) {
		pkt = dst_reg;
	} else if (dst_reg->type == PTR_TO_PACKET_END) {
		pkt = src_reg;
		opcode = flip_opcode(opcode);
	} else {
		return -1;
	}

	if (pkt->range >= 0)
		return -1;

	switch (opcode) {
	case BPF_JLE:
		/* pkt <= pkt_end */
		fallthrough;
	case BPF_JGT:
		/* pkt > pkt_end */
		if (pkt->range == BEYOND_PKT_END)
			/* pkt has at last one extra byte beyond pkt_end */
			return opcode == BPF_JGT;
		break;
	case BPF_JLT:
		/* pkt < pkt_end */
		fallthrough;
	case BPF_JGE:
		/* pkt >= pkt_end */
		if (pkt->range == BEYOND_PKT_END || pkt->range == AT_PKT_END)
			return opcode == BPF_JGE;
		break;
	}
	return -1;
}


static int push_insn(int t, int w, int e, struct bpf_verifier_env *env,
		     bool loop_ok)
{
	int *insn_stack = env->cfg.insn_stack;
	int *insn_state = env->cfg.insn_state;

	if (e == FALLTHROUGH && insn_state[t] >= (DISCOVERED | FALLTHROUGH))
		return DONE_EXPLORING;

	if (e == BRANCH && insn_state[t] >= (DISCOVERED | BRANCH))
		return DONE_EXPLORING;

	if (w < 0 || w >= env->prog->len) {
		verbose_linfo(env, t, "%d: ", t);
		verbose(env, "jump out of range from insn %d to %d\n", t, w);
		return -EINVAL;
	}

	if (e == BRANCH)
		/* mark branch target for state pruning */
		init_explored_state(env, w);

	if (insn_state[w] == 0) {
		/* tree-edge */
		insn_state[t] = DISCOVERED | e;
		insn_state[w] = DISCOVERED;
		if (env->cfg.cur_stack >= env->prog->len)
			return -E2BIG;
		insn_stack[env->cfg.cur_stack++] = w;
		return KEEP_EXPLORING;
	} else if ((insn_state[w] & 0xF0) == DISCOVERED) {
		if (loop_ok && env->bpf_capable)
			return DONE_EXPLORING;
		verbose_linfo(env, t, "%d: ", t);
		verbose_linfo(env, w, "%d: ", w);
		verbose(env, "back-edge from insn %d to %d\n", t, w);
		return -EINVAL;
	} else if (insn_state[w] == EXPLORED) {
		/* forward- or cross-edge */
		insn_state[t] = DISCOVERED | e;
	} else {
		verbose(env, "insn state internal bug\n");
		return -EFAULT;
	}
	return DONE_EXPLORING;
}


static bool __reg64_bound_s32(s64 a)
{
	return a > S32_MIN && a < S32_MAX;
}


static int check_func_proto(const struct bpf_func_proto *fn, int func_id)
{
	return check_raw_mode_ok(fn) &&
	       check_arg_pair_ok(fn) &&
	       check_btf_id_ok(fn) &&
	       check_refcount_ok(fn, func_id) ? 0 : -EINVAL;
}


static int check_pseudo_btf_id(struct bpf_verifier_env *env,
			       struct bpf_insn *insn,
			       struct bpf_insn_aux_data *aux)
{
	const struct btf_var_secinfo *vsi;
	const struct btf_type *datasec;
	const struct btf_type *t;
	const char *sym_name;
	bool percpu = false;
	u32 type, id = insn->imm;
	s32 datasec_id;
	u64 addr;
	int i;

	if (!btf_vmlinux) {
		verbose(env, "kernel is missing BTF, make sure CONFIG_DEBUG_INFO_BTF=y is specified in Kconfig.\n");
		return -EINVAL;
	}

	if (insn[1].imm != 0) {
		verbose(env, "reserved field (insn[1].imm) is used in pseudo_btf_id ldimm64 insn.\n");
		return -EINVAL;
	}

	t = btf_type_by_id(btf_vmlinux, id);
	if (!t) {
		verbose(env, "ldimm64 insn specifies invalid btf_id %d.\n", id);
		return -ENOENT;
	}

	if (!btf_type_is_var(t)) {
		verbose(env, "pseudo btf_id %d in ldimm64 isn't KIND_VAR.\n",
			id);
		return -EINVAL;
	}

	sym_name = btf_name_by_offset(btf_vmlinux, t->name_off);
	addr = kallsyms_lookup_name(sym_name);
	if (!addr) {
		verbose(env, "ldimm64 failed to find the address for kernel symbol '%s'.\n",
			sym_name);
		return -ENOENT;
	}

	datasec_id = btf_find_by_name_kind(btf_vmlinux, ".data..percpu",
					   BTF_KIND_DATASEC);
	if (datasec_id > 0) {
		datasec = btf_type_by_id(btf_vmlinux, datasec_id);
		for_each_vsi(i, datasec, vsi) {
			if (vsi->type == id) {
				percpu = true;
				break;
			}
		}
	}

	insn[0].imm = (u32)addr;
	insn[1].imm = addr >> 32;

	type = t->type;
	t = btf_type_skip_modifiers(btf_vmlinux, type, NULL);
	if (percpu) {
		aux->btf_var.reg_type = PTR_TO_PERCPU_BTF_ID;
		aux->btf_var.btf = btf_vmlinux;
		aux->btf_var.btf_id = type;
	} else if (!btf_type_is_struct(t)) {
		const struct btf_type *ret;
		const char *tname;
		u32 tsize;

		/* resolve the type size of ksym. */
		ret = btf_resolve_size(btf_vmlinux, t, &tsize);
		if (IS_ERR(ret)) {
			tname = btf_name_by_offset(btf_vmlinux, t->name_off);
			verbose(env, "ldimm64 unable to resolve the size of type '%s': %ld\n",
				tname, PTR_ERR(ret));
			return -EINVAL;
		}
		aux->btf_var.reg_type = PTR_TO_MEM;
		aux->btf_var.mem_size = tsize;
	} else {
		aux->btf_var.reg_type = PTR_TO_BTF_ID;
		aux->btf_var.btf = btf_vmlinux;
		aux->btf_var.btf_id = type;
	}
	return 0;
}


HRESULT Http::HrFinalize()
{
	HRESULT hr = hrSuccess;

	HrResponseHeader("Content-Length", stringify(m_strRespBody.length()));

	// force chunked http for long size response, should check version >= 1.1 to disable chunking
	if (m_strRespBody.size() < HTTP_CHUNK_SIZE || m_strHttpVer != "1.1") {
		hr = HrFlushHeaders();
		if (hr != hrSuccess && hr != MAPI_E_END_OF_SESSION) {
			ec_log_debug("Http::HrFinalize flush fail %d", hr);
			m_ulRetCode = 0;
			return hr;
		}
		if (!m_strRespBody.empty()) {
			m_lpChannel->HrWriteString(m_strRespBody);
			ec_log_debug("Response body:\n%s", m_strRespBody.c_str());
		}
	}
	else
	{
		const char *lpstrBody = m_strRespBody.data();
		char lpstrLen[10];
		auto szBodyLen = m_strRespBody.size(); // length of data to be sent to the client
		size_t szBodyWritten = 0; // length of data sent to client
		unsigned int szPart = HTTP_CHUNK_SIZE;						// default length of chunk data to be written

		HrResponseHeader("Transfer-Encoding", "chunked");
		hr = HrFlushHeaders();
		if (hr != hrSuccess && hr != MAPI_E_END_OF_SESSION) {
			ec_log_debug("Http::HrFinalize flush fail(2) %d", hr);
			m_ulRetCode = 0;
			return hr;
		}

		while (szBodyWritten < szBodyLen)
		{
			if ((szBodyWritten + HTTP_CHUNK_SIZE) > szBodyLen)
				szPart = szBodyLen - szBodyWritten;				// change length of data for last chunk
			// send hex length of data and data part
			snprintf(lpstrLen, sizeof(lpstrLen), "%X", szPart);
			m_lpChannel->HrWriteLine(lpstrLen);
			m_lpChannel->HrWriteLine(string_view(lpstrBody, szPart));
			szBodyWritten += szPart;
			lpstrBody += szPart;
		}

		// end of response
		snprintf(lpstrLen, 10, "0\r\n");
		m_lpChannel->HrWriteLine(lpstrLen);
		// just the first part of the body in the log. header shows it's chunked.
		ec_log_debug("%s", m_strRespBody.c_str());
	}

	// if http_log_enable?
	char szTime[32];
	time_t now = time(NULL);
	tm local;
	std::string strAgent;
	localtime_r(&now, &local);
	// @todo we're in C LC_TIME locale to get the correct (month) format, but the timezone will be GMT, which is not wanted.
	strftime(szTime, ARRAY_SIZE(szTime), "%d/%b/%Y:%H:%M:%S %z", &local);
	HrGetHeaderValue("User-Agent", &strAgent);
	ec_log_notice("%s - %s [%s] \"%s\" %d %d \"-\" \"%s\"", m_lpChannel->peer_addr(), m_strUser.empty() ? "-" : m_strUser.c_str(), szTime, m_strAction.c_str(), m_ulRetCode, (int)m_strRespBody.length(), strAgent.c_str());
	m_ulRetCode = 0;
	return hr;
}


HRESULT Http::HrSetKeepAlive(int ulKeepAlive)
{
	m_ulKeepAlive = ulKeepAlive;
	return hrSuccess;
}


HRESULT Http::HrResponseBody(const std::string &strResponse)
{
	m_strRespBody += strResponse;
	// data send in HrFinalize()
	return hrSuccess;
}


_nm_utils_set_testing(NMUtilsTestFlags flags)
{
    g_assert(!NM_FLAGS_ANY(flags, ~NM_UTILS_TEST_ALL));

    /* mask out everything except ALL, and always set GENERAL. */
    flags = (flags & NM_UTILS_TEST_ALL) | (_NM_UTILS_TEST_GENERAL | _NM_UTILS_TEST_INITIALIZED);

    if (!g_atomic_int_compare_and_exchange(&_nm_utils_testing, 0, (int) flags)) {
        /* We only allow setting _nm_utils_set_testing() once, before fetching the
         * value with nm_utils_get_testing(). */
        g_return_if_reached();
    }
}


_stable_id_append(GString *str, const char *substitution)
{
    if (!substitution)
        substitution = "";
    g_string_append_printf(str, "=%zu{%s}", strlen(substitution), substitution);
}


_kc_cb_timeout_grace_period(void *user_data)
{
    KillChildAsyncData *data = user_data;
    int                 ret, errsv;

    data->async.source_timeout_kill_id = 0;

    if ((ret = kill(data->pid, SIGKILL)) != 0) {
        errsv = errno;
        /* ESRCH means, process does not exist or is already a zombie. */
        if (errsv != ESRCH) {
            nm_log_err(LOGD_CORE | data->log_domain,
                       "%s: kill(SIGKILL) returned unexpected return value %d: (%s, %d)",
                       data->log_name,
                       ret,
                       nm_strerror_native(errsv),
                       errsv);
        }
    } else {
        nm_log_dbg(data->log_domain,
                   "%s: process not terminated after %ld usec. Sending SIGKILL signal",
                   data->log_name,
                   (long) (nm_utils_get_monotonic_timestamp_usec() - data->async.wait_start_us));
    }

    return G_SOURCE_REMOVE;
}


nm_match_spec_config(const GSList *specs, guint cur_nm_version, const char *env)
{
    const GSList *iter;
    gboolean      has_match        = FALSE;
    gboolean      has_match_except = FALSE;
    gboolean      has_except       = FALSE;
    gboolean      has_not_except   = FALSE;

    if (!specs)
        return NM_MATCH_SPEC_NO_MATCH;

    for (iter = specs; iter; iter = g_slist_next(iter)) {
        const char *spec_str = iter->data;
        gboolean    except;
        gboolean    v_match;

        if (!spec_str || !*spec_str)
            continue;

        spec_str = match_except(spec_str, &except);

        if (except)
            has_except = TRUE;
        else
            has_not_except = TRUE;

        if ((except && has_match_except) || (!except && has_match)) {
            /* evaluating the match does not give new information. Skip it. */
            continue;
        }

        if (_MATCH_CHECK(spec_str, MATCH_TAG_CONFIG_NM_VERSION))
            v_match = match_config_eval(spec_str, MATCH_TAG_CONFIG_NM_VERSION, cur_nm_version);
        else if (_MATCH_CHECK(spec_str, MATCH_TAG_CONFIG_NM_VERSION_MIN))
            v_match = match_config_eval(spec_str, MATCH_TAG_CONFIG_NM_VERSION_MIN, cur_nm_version);
        else if (_MATCH_CHECK(spec_str, MATCH_TAG_CONFIG_NM_VERSION_MAX))
            v_match = match_config_eval(spec_str, MATCH_TAG_CONFIG_NM_VERSION_MAX, cur_nm_version);
        else if (_MATCH_CHECK(spec_str, MATCH_TAG_CONFIG_ENV))
            v_match = env && env[0] && !strcmp(spec_str, env);
        else
            v_match = FALSE;

        if (!v_match)
            continue;

        if (except)
            has_match_except = TRUE;
        else
            has_match = TRUE;
    }

    return _match_result(has_except, has_not_except, has_match, has_match_except);
}


nm_utils_find_helper(const char *progname, const char *try_first, GError **error)
{
    return nm_utils_file_search_in_paths(progname,
                                         try_first,
                                         NM_PATHS_DEFAULT,
                                         G_FILE_TEST_IS_EXECUTABLE,
                                         NULL,
                                         NULL,
                                         error);
}


_host_id_hash_v2(const guint8 *seed_arr,
                 gsize         seed_len,
                 guint8 *      out_digest /* 32 bytes (NM_UTILS_CHECKSUM_LENGTH_SHA256) */)
{
    nm_auto_free_checksum GChecksum *sum = g_checksum_new(G_CHECKSUM_SHA256);
    const UuidData *                 machine_id_data;
    char                             slen[100];

    /*
        (stat -c '%s' /var/lib/NetworkManager/secret_key;
         echo -n ' ';
         cat /var/lib/NetworkManager/secret_key;
         cat /etc/machine-id | tr -d '\n' | sed -n 's/[a-f0-9-]/\0/pg') | sha256sum
    */

    nm_sprintf_buf(slen, "%" G_GSIZE_FORMAT " ", seed_len);
    g_checksum_update(sum, (const guchar *) slen, strlen(slen));

    g_checksum_update(sum, (const guchar *) seed_arr, seed_len);

    machine_id_data = _machine_id_get(FALSE);
    if (machine_id_data && !machine_id_data->is_fake)
        g_checksum_update(sum, (const guchar *) machine_id_data->str, strlen(machine_id_data->str));

    nm_utils_checksum_get_digest_len(sum, out_digest, NM_UTILS_CHECKSUM_LENGTH_SHA256);
    return out_digest;
}


size_t zmalloc_get_rss(void) {
    struct kinfo_proc info;
    size_t infolen = sizeof(info);
    int mib[4];
    mib[0] = CTL_KERN;
    mib[1] = KERN_PROC;
    mib[2] = KERN_PROC_PID;
    mib[3] = getpid();

    if (sysctl(mib, 4, &info, &infolen, NULL, 0) == 0)
        return (size_t)info.ki_rssize;

    return 0L;
}


SProcXChangeFeedbackControl(ClientPtr client)
{
    REQUEST(xChangeFeedbackControlReq);
    swaps(&stuff->length);
    REQUEST_AT_LEAST_SIZE(xChangeFeedbackControlReq);
    swapl(&stuff->mask);
    return (ProcXChangeFeedbackControl(client));
}


static void gfar_netpoll(struct net_device *dev)
{
	struct gfar_private *priv = netdev_priv(dev);
	int i;

	/* If the device has multiple interrupts, run tx/rx */
	if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
		for (i = 0; i < priv->num_grps; i++) {
			struct gfar_priv_grp *grp = &priv->gfargrp[i];

			disable_irq(gfar_irq(grp, TX)->irq);
			disable_irq(gfar_irq(grp, RX)->irq);
			disable_irq(gfar_irq(grp, ER)->irq);
			gfar_interrupt(gfar_irq(grp, TX)->irq, grp);
			enable_irq(gfar_irq(grp, ER)->irq);
			enable_irq(gfar_irq(grp, RX)->irq);
			enable_irq(gfar_irq(grp, TX)->irq);
		}
	} else {
		for (i = 0; i < priv->num_grps; i++) {
			struct gfar_priv_grp *grp = &priv->gfargrp[i];

			disable_irq(gfar_irq(grp, TX)->irq);
			gfar_interrupt(gfar_irq(grp, TX)->irq, grp);
			enable_irq(gfar_irq(grp, TX)->irq);
		}
	}
}


static void gfar_configure_serdes(struct net_device *dev)
{
	struct gfar_private *priv = netdev_priv(dev);
	struct phy_device *tbiphy;

	if (!priv->tbi_node) {
		dev_warn(&dev->dev, "error: SGMII mode requires that the "
				    "device tree specify a tbi-handle\n");
		return;
	}

	tbiphy = of_phy_find_device(priv->tbi_node);
	if (!tbiphy) {
		dev_err(&dev->dev, "error: Could not get TBI device\n");
		return;
	}

	/* If the link is already up, we must already be ok, and don't need to
	 * configure and reset the TBI<->SerDes link.  Maybe U-Boot configured
	 * everything for us?  Resetting it takes the link down and requires
	 * several seconds for it to come back.
	 */
	if (phy_read(tbiphy, MII_BMSR) & BMSR_LSTATUS) {
		put_device(&tbiphy->mdio.dev);
		return;
	}

	/* Single clk mode, mii mode off(for serdes communication) */
	phy_write(tbiphy, MII_TBICON, TBICON_CLK_SELECT);

	phy_write(tbiphy, MII_ADVERTISE,
		  ADVERTISE_1000XFULL | ADVERTISE_1000XPAUSE |
		  ADVERTISE_1000XPSE_ASYM);

	phy_write(tbiphy, MII_BMCR,
		  BMCR_ANENABLE | BMCR_ANRESTART | BMCR_FULLDPLX |
		  BMCR_SPEED1000);

	put_device(&tbiphy->mdio.dev);
}


static void gfar_init_filer_table(struct gfar_private *priv)
{
	int i = 0x0;
	u32 rqfar = MAX_FILER_IDX;
	u32 rqfcr = 0x0;
	u32 rqfpr = FPR_FILER_MASK;

	/* Default rule */
	rqfcr = RQFCR_CMP_MATCH;
	priv->ftp_rqfcr[rqfar] = rqfcr;
	priv->ftp_rqfpr[rqfar] = rqfpr;
	gfar_write_filer(priv, rqfar, rqfcr, rqfpr);

	rqfar = cluster_entry_per_class(priv, rqfar, RQFPR_IPV6);
	rqfar = cluster_entry_per_class(priv, rqfar, RQFPR_IPV6 | RQFPR_UDP);
	rqfar = cluster_entry_per_class(priv, rqfar, RQFPR_IPV6 | RQFPR_TCP);
	rqfar = cluster_entry_per_class(priv, rqfar, RQFPR_IPV4);
	rqfar = cluster_entry_per_class(priv, rqfar, RQFPR_IPV4 | RQFPR_UDP);
	rqfar = cluster_entry_per_class(priv, rqfar, RQFPR_IPV4 | RQFPR_TCP);

	/* cur_filer_idx indicated the first non-masked rule */
	priv->cur_filer_idx = rqfar;

	/* Rest are masked rules */
	rqfcr = RQFCR_CMP_NOMATCH;
	for (i = 0; i < rqfar; i++) {
		priv->ftp_rqfcr[i] = rqfcr;
		priv->ftp_rqfpr[i] = rqfpr;
		gfar_write_filer(priv, i, rqfcr, rqfpr);
	}
}


static int gfar_alloc_rx_queues(struct gfar_private *priv)
{
	int i;

	for (i = 0; i < priv->num_rx_queues; i++) {
		priv->rx_queue[i] = kzalloc(sizeof(struct gfar_priv_rx_q),
					    GFP_KERNEL);
		if (!priv->rx_queue[i])
			return -ENOMEM;

		priv->rx_queue[i]->qindex = i;
		priv->rx_queue[i]->ndev = priv->ndev;
	}
	return 0;
}


static inline struct txbd8 *skip_txbd(struct txbd8 *bdp, int stride,
				      struct txbd8 *base, int ring_size)
{
	struct txbd8 *new_bd = bdp + stride;

	return (new_bd >= (base + ring_size)) ? (new_bd - ring_size) : new_bd;
}


static void gfar_configure_coalescing_all(struct gfar_private *priv)
{
	gfar_configure_coalescing(priv, 0xFF, 0xFF);
}


static void __gfar_filer_enable(struct gfar_private *priv)
{
	struct gfar __iomem *regs = priv->gfargrp[0].regs;
	u32 temp;

	temp = gfar_read(&regs->rctrl);
	temp |= RCTRL_FILREN | RCTRL_PRSDEP_INIT;
	gfar_write(&regs->rctrl, temp);
}


static int gfar_probe(struct platform_device *ofdev)
{
	struct device_node *np = ofdev->dev.of_node;
	struct net_device *dev = NULL;
	struct gfar_private *priv = NULL;
	int err = 0, i;

	err = gfar_of_init(ofdev, &dev);

	if (err)
		return err;

	priv = netdev_priv(dev);
	priv->ndev = dev;
	priv->ofdev = ofdev;
	priv->dev = &ofdev->dev;
	SET_NETDEV_DEV(dev, &ofdev->dev);

	INIT_WORK(&priv->reset_task, gfar_reset_task);

	platform_set_drvdata(ofdev, priv);

	gfar_detect_errata(priv);

	/* Set the dev->base_addr to the gfar reg region */
	dev->base_addr = (unsigned long) priv->gfargrp[0].regs;

	/* Fill in the dev structure */
	dev->watchdog_timeo = TX_TIMEOUT;
	/* MTU range: 50 - 9586 */
	dev->mtu = 1500;
	dev->min_mtu = 50;
	dev->max_mtu = GFAR_JUMBO_FRAME_SIZE - ETH_HLEN;
	dev->netdev_ops = &gfar_netdev_ops;
	dev->ethtool_ops = &gfar_ethtool_ops;

	/* Register for napi ...We are registering NAPI for each grp */
	for (i = 0; i < priv->num_grps; i++) {
		if (priv->poll_mode == GFAR_SQ_POLLING) {
			netif_napi_add(dev, &priv->gfargrp[i].napi_rx,
				       gfar_poll_rx_sq, GFAR_DEV_WEIGHT);
			netif_tx_napi_add(dev, &priv->gfargrp[i].napi_tx,
				       gfar_poll_tx_sq, 2);
		} else {
			netif_napi_add(dev, &priv->gfargrp[i].napi_rx,
				       gfar_poll_rx, GFAR_DEV_WEIGHT);
			netif_tx_napi_add(dev, &priv->gfargrp[i].napi_tx,
				       gfar_poll_tx, 2);
		}
	}

	if (priv->device_flags & FSL_GIANFAR_DEV_HAS_CSUM) {
		dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_SG |
				   NETIF_F_RXCSUM;
		dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG |
				 NETIF_F_RXCSUM | NETIF_F_HIGHDMA;
	}

	if (priv->device_flags & FSL_GIANFAR_DEV_HAS_VLAN) {
		dev->hw_features |= NETIF_F_HW_VLAN_CTAG_TX |
				    NETIF_F_HW_VLAN_CTAG_RX;
		dev->features |= NETIF_F_HW_VLAN_CTAG_RX;
	}

	dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;

	gfar_init_addr_hash_table(priv);

	/* Insert receive time stamps into padding alignment bytes, and
	 * plus 2 bytes padding to ensure the cpu alignment.
	 */
	if (priv->device_flags & FSL_GIANFAR_DEV_HAS_TIMER)
		priv->padding = 8 + DEFAULT_PADDING;

	if (dev->features & NETIF_F_IP_CSUM ||
	    priv->device_flags & FSL_GIANFAR_DEV_HAS_TIMER)
		dev->needed_headroom = GMAC_FCB_LEN + GMAC_TXPAL_LEN;

	/* Initializing some of the rx/tx queue level parameters */
	for (i = 0; i < priv->num_tx_queues; i++) {
		priv->tx_queue[i]->tx_ring_size = DEFAULT_TX_RING_SIZE;
		priv->tx_queue[i]->num_txbdfree = DEFAULT_TX_RING_SIZE;
		priv->tx_queue[i]->txcoalescing = DEFAULT_TX_COALESCE;
		priv->tx_queue[i]->txic = DEFAULT_TXIC;
	}

	for (i = 0; i < priv->num_rx_queues; i++) {
		priv->rx_queue[i]->rx_ring_size = DEFAULT_RX_RING_SIZE;
		priv->rx_queue[i]->rxcoalescing = DEFAULT_RX_COALESCE;
		priv->rx_queue[i]->rxic = DEFAULT_RXIC;
	}

	/* Always enable rx filer if available */
	priv->rx_filer_enable =
	    (priv->device_flags & FSL_GIANFAR_DEV_HAS_RX_FILER) ? 1 : 0;
	/* Enable most messages by default */
	priv->msg_enable = (NETIF_MSG_IFUP << 1 ) - 1;
	/* use pritority h/w tx queue scheduling for single queue devices */
	if (priv->num_tx_queues == 1)
		priv->prio_sched_en = 1;

	set_bit(GFAR_DOWN, &priv->state);

	gfar_hw_init(priv);

	/* Carrier starts down, phylib will bring it up */
	netif_carrier_off(dev);

	err = register_netdev(dev);

	if (err) {
		pr_err("%s: Cannot register net device, aborting\n", dev->name);
		goto register_fail;
	}

	if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET)
		priv->wol_supported |= GFAR_WOL_MAGIC;

	if ((priv->device_flags & FSL_GIANFAR_DEV_HAS_WAKE_ON_FILER) &&
	    priv->rx_filer_enable)
		priv->wol_supported |= GFAR_WOL_FILER_UCAST;

	device_set_wakeup_capable(&ofdev->dev, priv->wol_supported);

	/* fill out IRQ number and name fields */
	for (i = 0; i < priv->num_grps; i++) {
		struct gfar_priv_grp *grp = &priv->gfargrp[i];
		if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
			sprintf(gfar_irq(grp, TX)->name, "%s%s%c%s",
				dev->name, "_g", '0' + i, "_tx");
			sprintf(gfar_irq(grp, RX)->name, "%s%s%c%s",
				dev->name, "_g", '0' + i, "_rx");
			sprintf(gfar_irq(grp, ER)->name, "%s%s%c%s",
				dev->name, "_g", '0' + i, "_er");
		} else
			strcpy(gfar_irq(grp, TX)->name, dev->name);
	}

	/* Initialize the filer table */
	gfar_init_filer_table(priv);

	/* Print out the device info */
	netdev_info(dev, "mac: %pM\n", dev->dev_addr);

	/* Even more device info helps when determining which kernel
	 * provided which set of benchmarks.
	 */
	netdev_info(dev, "Running with NAPI enabled\n");
	for (i = 0; i < priv->num_rx_queues; i++)
		netdev_info(dev, "RX BD ring size for Q[%d]: %d\n",
			    i, priv->rx_queue[i]->rx_ring_size);
	for (i = 0; i < priv->num_tx_queues; i++)
		netdev_info(dev, "TX BD ring size for Q[%d]: %d\n",
			    i, priv->tx_queue[i]->tx_ring_size);

	return 0;

register_fail:
	if (of_phy_is_fixed_link(np))
		of_phy_deregister_fixed_link(np);
	unmap_group_regs(priv);
	gfar_free_rx_queues(priv);
	gfar_free_tx_queues(priv);
	of_node_put(priv->phy_node);
	of_node_put(priv->tbi_node);
	free_gfar_dev(priv);
	return err;
}


static phy_interface_t gfar_get_interface(struct net_device *dev)
{
	struct gfar_private *priv = netdev_priv(dev);
	struct gfar __iomem *regs = priv->gfargrp[0].regs;
	u32 ecntrl;

	ecntrl = gfar_read(&regs->ecntrl);

	if (ecntrl & ECNTRL_SGMII_MODE)
		return PHY_INTERFACE_MODE_SGMII;

	if (ecntrl & ECNTRL_TBI_MODE) {
		if (ecntrl & ECNTRL_REDUCED_MODE)
			return PHY_INTERFACE_MODE_RTBI;
		else
			return PHY_INTERFACE_MODE_TBI;
	}

	if (ecntrl & ECNTRL_REDUCED_MODE) {
		if (ecntrl & ECNTRL_REDUCED_MII_MODE) {
			return PHY_INTERFACE_MODE_RMII;
		}
		else {
			phy_interface_t interface = priv->interface;

			/* This isn't autodetected right now, so it must
			 * be set by the device tree or platform code.
			 */
			if (interface == PHY_INTERFACE_MODE_RGMII_ID)
				return PHY_INTERFACE_MODE_RGMII_ID;

			return PHY_INTERFACE_MODE_RGMII;
		}
	}

	if (priv->device_flags & FSL_GIANFAR_DEV_HAS_GIGABIT)
		return PHY_INTERFACE_MODE_GMII;

	return PHY_INTERFACE_MODE_MII;
}


static void __gfar_detect_errata_83xx(struct gfar_private *priv)
{
	unsigned int pvr = mfspr(SPRN_PVR);
	unsigned int svr = mfspr(SPRN_SVR);
	unsigned int mod = (svr >> 16) & 0xfff6; /* w/o E suffix */
	unsigned int rev = svr & 0xffff;

	/* MPC8313 Rev 2.0 and higher; All MPC837x */
	if ((pvr == 0x80850010 && mod == 0x80b0 && rev >= 0x0020) ||
	    (pvr == 0x80861010 && (mod & 0xfff9) == 0x80c0))
		priv->errata |= GFAR_ERRATA_74;

	/* MPC8313 and MPC837x all rev */
	if ((pvr == 0x80850010 && mod == 0x80b0) ||
	    (pvr == 0x80861010 && (mod & 0xfff9) == 0x80c0))
		priv->errata |= GFAR_ERRATA_76;

	/* MPC8313 Rev < 2.0 */
	if (pvr == 0x80850010 && mod == 0x80b0 && rev < 0x0020)
		priv->errata |= GFAR_ERRATA_12;
}


static int gfar_of_group_count(struct device_node *np)
{
	struct device_node *child;
	int num = 0;

	for_each_available_child_of_node(np, child)
		if (of_node_name_eq(child, "queue-group"))
			num++;

	return num;
}


static void free_gfar_dev(struct gfar_private *priv)
{
	int i, j;

	for (i = 0; i < priv->num_grps; i++)
		for (j = 0; j < GFAR_NUM_IRQS; j++) {
			kfree(priv->gfargrp[i].irqinfo[j]);
			priv->gfargrp[i].irqinfo[j] = NULL;
		}

	free_netdev(priv->ndev);
}


static int gfar_restore(struct device *dev)
{
	struct gfar_private *priv = dev_get_drvdata(dev);
	struct net_device *ndev = priv->ndev;

	if (!netif_running(ndev)) {
		netif_device_attach(ndev);

		return 0;
	}

	gfar_init_bds(ndev);

	gfar_mac_reset(priv);

	gfar_init_tx_rx_base(priv);

	gfar_start(priv);

	priv->oldlink = 0;
	priv->oldspeed = 0;
	priv->oldduplex = -1;

	if (ndev->phydev)
		phy_start(ndev->phydev);

	netif_device_attach(ndev);
	enable_napi(priv);

	return 0;
}


static int register_grp_irqs(struct gfar_priv_grp *grp)
{
	struct gfar_private *priv = grp->priv;
	struct net_device *dev = priv->ndev;
	int err;

	/* If the device has multiple interrupts, register for
	 * them.  Otherwise, only register for the one
	 */
	if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
		/* Install our interrupt handlers for Error,
		 * Transmit, and Receive
		 */
		err = request_irq(gfar_irq(grp, ER)->irq, gfar_error, 0,
				  gfar_irq(grp, ER)->name, grp);
		if (err < 0) {
			netif_err(priv, intr, dev, "Can't get IRQ %d\n",
				  gfar_irq(grp, ER)->irq);

			goto err_irq_fail;
		}
		enable_irq_wake(gfar_irq(grp, ER)->irq);

		err = request_irq(gfar_irq(grp, TX)->irq, gfar_transmit, 0,
				  gfar_irq(grp, TX)->name, grp);
		if (err < 0) {
			netif_err(priv, intr, dev, "Can't get IRQ %d\n",
				  gfar_irq(grp, TX)->irq);
			goto tx_irq_fail;
		}
		err = request_irq(gfar_irq(grp, RX)->irq, gfar_receive, 0,
				  gfar_irq(grp, RX)->name, grp);
		if (err < 0) {
			netif_err(priv, intr, dev, "Can't get IRQ %d\n",
				  gfar_irq(grp, RX)->irq);
			goto rx_irq_fail;
		}
		enable_irq_wake(gfar_irq(grp, RX)->irq);

	} else {
		err = request_irq(gfar_irq(grp, TX)->irq, gfar_interrupt, 0,
				  gfar_irq(grp, TX)->name, grp);
		if (err < 0) {
			netif_err(priv, intr, dev, "Can't get IRQ %d\n",
				  gfar_irq(grp, TX)->irq);
			goto err_irq_fail;
		}
		enable_irq_wake(gfar_irq(grp, TX)->irq);
	}

	return 0;

rx_irq_fail:
	free_irq(gfar_irq(grp, TX)->irq, grp);
tx_irq_fail:
	free_irq(gfar_irq(grp, ER)->irq, grp);
err_irq_fail:
	return err;

}


static int __init vhost_vdpa_init(void)
{
	int r;

	r = alloc_chrdev_region(&vhost_vdpa_major, 0, VHOST_VDPA_DEV_MAX,
				"vhost-vdpa");
	if (r)
		goto err_alloc_chrdev;

	r = vdpa_register_driver(&vhost_vdpa_driver);
	if (r)
		goto err_vdpa_register_driver;

	return 0;

err_vdpa_register_driver:
	unregister_chrdev_region(vhost_vdpa_major, VHOST_VDPA_DEV_MAX);
err_alloc_chrdev:
	return r;
}


static long vhost_vdpa_get_features(struct vhost_vdpa *v, u64 __user *featurep)
{
	struct vdpa_device *vdpa = v->vdpa;
	const struct vdpa_config_ops *ops = vdpa->config;
	u64 features;

	features = ops->get_features(vdpa);

	if (copy_to_user(featurep, &features, sizeof(features)))
		return -EFAULT;

	return 0;
}