From a1f6ec647cfab8d613897562f8ee5f81a8f6b68d Mon Sep 17 00:00:00 2001
From: Curl Upstream <curl-library@lists.haxx.se>
Date: Wed, 5 Jan 2022 00:35:58 +0100
Subject: curl 2022-01-05 (801bd513)

Code extracted from:

    https://github.com/curl/curl.git

at commit 801bd5138ce31aa0d906fa4e2eabfc599d74e793 (curl-7_81_0).
---
 CMake/CurlTests.c            |  20 --
 CMake/FindMbedTLS.cmake      |   4 +-
 CMake/OtherTests.cmake       |   1 -
 CMakeLists.txt               | 153 ++++----
 include/curl/curl.h          |  42 ++-
 include/curl/curlver.h       |   8 +-
 include/curl/multi.h         |   5 +-
 include/curl/typecheck-gcc.h |   2 +
 include/curl/urlapi.h        |  21 +-
 lib/CMakeLists.txt           |   5 +-
 lib/asyn-ares.c              |   6 +-
 lib/c-hyper.c                | 203 +++++++----
 lib/conncache.c              |  13 +-
 lib/connect.c                |  38 +-
 lib/content_encoding.c       |   3 +-
 lib/cookie.c                 |   2 +-
 lib/curl_config.h.cmake      |   3 -
 lib/curl_des.c               |   4 +-
 lib/curl_gssapi.c            |  10 +-
 lib/curl_hmac.h              |   4 +-
 lib/curl_md5.h               |   8 +-
 lib/curl_ntlm_core.c         |  19 +-
 lib/curl_sasl.c              | 117 +++---
 lib/curl_sasl.h              |  67 ++--
 lib/curl_setup.h             |   2 +-
 lib/curl_sha256.h            |  13 +-
 lib/curl_sspi.c              |   4 +-
 lib/doh.c                    |  25 +-
 lib/easy.c                   |  10 +-
 lib/easyoptions.c            |   8 +-
 lib/ftp.c                    |  24 +-
 lib/hash.c                   |  82 +++--
 lib/hash.h                   |  12 +-
 lib/hostcheck.c              |   2 +-
 lib/hostip.c                 |  31 +-
 lib/hostip.h                 |   4 +-
 lib/http.c                   |  89 +++--
 lib/http.h                   |   6 +-
 lib/http2.c                  |  31 +-
 lib/http_aws_sigv4.c         |  21 +-
 lib/http_ntlm.c              |  11 +-
 lib/http_proxy.c             | 110 ++++--
 lib/http_proxy.h             |   3 +-
 lib/if2ip.c                  |   4 +-
 lib/imap.c                   | 157 ++++----
 lib/inet_pton.c              |   6 +-
 lib/krb5.c                   |   8 +-
 lib/ldap.c                   |   7 +-
 lib/libcurl.rc               |   4 +-
 lib/llist.c                  |   4 +-
 lib/md4.c                    |  17 +-
 lib/md5.c                    |  42 ++-
 lib/mime.c                   |  80 +++--
 lib/mprintf.c                |   2 +-
 lib/multi.c                  | 252 +++++++++----
 lib/multihandle.h            |   2 +
 lib/multiif.h                |   4 +-
 lib/openldap.c               | 833 +++++++++++++++++++++++++------------------
 lib/pop3.c                   | 100 +++---
 lib/select.c                 |   2 +-
 lib/sendf.c                  |   8 +-
 lib/setopt.c                 |  38 ++
 lib/setup-win32.h            |  14 +-
 lib/sha256.c                 | 141 +++++---
 lib/share.c                  |   6 +-
 lib/smtp.c                   | 103 +++---
 lib/socks.c                  |  33 +-
 lib/socks_gssapi.c           |   4 +-
 lib/socks_sspi.c             |   2 +-
 lib/splay.c                  |   4 +-
 lib/strerror.c               | 111 ++++++
 lib/system_win32.c           |   4 +-
 lib/tftp.c                   |   6 +-
 lib/transfer.c               |   2 +-
 lib/url.c                    |  76 ++--
 lib/urlapi-int.h             |   4 +-
 lib/urlapi.c                 | 402 ++++++++++++++-------
 lib/urldata.h                |  14 +-
 lib/vauth/digest.c           |  11 +-
 lib/vauth/ntlm.c             |   4 +-
 lib/version_win32.c          | 113 +++++-
 lib/version_win32.h          |   3 +-
 lib/vquic/ngtcp2.c           | 107 ++++--
 lib/vssh/libssh2.c           | 154 ++++++--
 lib/vssh/wolfssh.c           |   6 +-
 lib/vtls/bearssl.c           |   6 +-
 lib/vtls/gtls.c              |  79 ++--
 lib/vtls/gtls.h              |   8 +-
 lib/vtls/mbedtls.c           |  60 +++-
 lib/vtls/mesalink.c          |  93 ++---
 lib/vtls/nss.c               | 107 +++---
 lib/vtls/openssl.c           | 290 ++++++++-------
 lib/vtls/openssl.h           |   8 +-
 lib/vtls/rustls.c            |  61 ++--
 lib/vtls/schannel.c          | 447 ++++++++++++-----------
 lib/vtls/schannel_verify.c   |   5 +-
 lib/vtls/sectransp.c         |  32 +-
 lib/vtls/vtls.c              |  10 +-
 lib/vtls/vtls.h              |   3 +-
 lib/vtls/wolfssl.c           |  75 +++-
 lib/x509asn1.c               | 127 +++++--
 101 files changed, 3551 insertions(+), 2000 deletions(-)

diff --git a/CMake/CurlTests.c b/CMake/CurlTests.c
index e418146..8666da0 100644
--- a/CMake/CurlTests.c
+++ b/CMake/CurlTests.c
@@ -229,10 +229,6 @@ int main () { ; return 0; }
 #  include <windows.h>
 #  ifdef HAVE_WINSOCK2_H
 #    include <winsock2.h>
-#  else
-#    ifdef HAVE_WINSOCK_H
-#      include <winsock.h>
-#    endif
 #  endif
 #endif
 
@@ -258,10 +254,6 @@ main ()
 #  include <windows.h>
 #  ifdef HAVE_WINSOCK2_H
 #    include <winsock2.h>
-#  else
-#    ifdef HAVE_WINSOCK_H
-#      include <winsock.h>
-#    endif
 #  endif
 #endif
 
@@ -285,10 +277,6 @@ main ()
 #  include <windows.h>
 #  ifdef HAVE_WINSOCK2_H
 #    include <winsock2.h>
-#  else
-#    ifdef HAVE_WINSOCK_H
-#      include <winsock.h>
-#    endif
 #  endif
 #endif
 
@@ -313,10 +301,6 @@ main ()
 #  include <windows.h>
 #  ifdef HAVE_WINSOCK2_H
 #    include <winsock2.h>
-#  else
-#    ifdef HAVE_WINSOCK_H
-#      include <winsock.h>
-#    endif
 #  endif
 #endif
 
@@ -403,10 +387,6 @@ main ()
 #  include <windows.h>
 #  ifdef HAVE_WINSOCK2_H
 #    include <winsock2.h>
-#  else
-#    ifdef HAVE_WINSOCK_H
-#      include <winsock.h>
-#    endif
 #  endif
 #endif
 /* includes start */
diff --git a/CMake/FindMbedTLS.cmake b/CMake/FindMbedTLS.cmake
index 1746093..7bdb197 100644
--- a/CMake/FindMbedTLS.cmake
+++ b/CMake/FindMbedTLS.cmake
@@ -5,7 +5,7 @@
 #                            | (__| |_| |  _ <| |___
 #                             \___|\___/|_| \_\_____|
 #
-# Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
 #
 # This software is licensed as described in the file COPYING, which
 # you should have received as part of this distribution. The terms
@@ -28,7 +28,7 @@ find_library(MBEDCRYPTO_LIBRARY mbedcrypto)
 set(MBEDTLS_LIBRARIES "${MBEDTLS_LIBRARY}" "${MBEDX509_LIBRARY}" "${MBEDCRYPTO_LIBRARY}")
 
 include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(MBEDTLS DEFAULT_MSG
+find_package_handle_standard_args(MbedTLS DEFAULT_MSG
     MBEDTLS_INCLUDE_DIRS MBEDTLS_LIBRARY MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY)
 
 mark_as_advanced(MBEDTLS_INCLUDE_DIRS MBEDTLS_LIBRARY MBEDX509_LIBRARY MBEDCRYPTO_LIBRARY)
diff --git a/CMake/OtherTests.cmake b/CMake/OtherTests.cmake
index 5cddf4a..73c813f 100644
--- a/CMake/OtherTests.cmake
+++ b/CMake/OtherTests.cmake
@@ -33,7 +33,6 @@ set(signature_call_conv)
 if(HAVE_WINDOWS_H)
   add_header_include(HAVE_WINSOCK2_H "winsock2.h")
   add_header_include(HAVE_WINDOWS_H "windows.h")
-  add_header_include(HAVE_WINSOCK_H "winsock.h")
   set(_source_epilogue
       "${_source_epilogue}\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN\n#endif")
   set(signature_call_conv "PASCAL")
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d8084de..125b0a0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -259,6 +259,17 @@ if(ENABLE_IPV6 AND NOT WIN32)
     set(ENABLE_IPV6 OFF
         CACHE BOOL "Define if you want to enable IPv6 support" FORCE)
   endif()
+
+  if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND NOT ENABLE_ARES)
+    set(use_core_foundation ON)
+
+    find_library(SYSTEMCONFIGURATION_FRAMEWORK "SystemConfiguration")
+    if(NOT SYSTEMCONFIGURATION_FRAMEWORK)
+      message(FATAL_ERROR "SystemConfiguration framework not found")
+    endif()
+
+    list(APPEND CURL_LIBS "-framework SystemConfiguration")
+  endif()
 endif()
 
 if(USE_MANUAL)
@@ -341,85 +352,94 @@ if(WIN32)
   check_library_exists_concat("winmm"  getch        HAVE_LIBWINMM)
 endif()
 
-# check SSL libraries
-# TODO support GnuTLS
+# This check below for use of deprecated symbols is only temporary and is to
+# be removed again after a year's service. Remove after November 25, 2022.
+set(CURL_RECONFIG_REQUIRED 0)
+foreach(_LIB GSSAPI OPENLDAP LIBSSH LIBSSH2 BEARSSL MBEDTLS NSS OPENSSL
+        SCHANNEL SECTRANSP WOLFSSL)
+  if(CMAKE_USE_${_LIB})
+    set(CURL_RECONFIG_REQUIRED 1)
+    message(SEND_ERROR "The option CMAKE_USE_${_LIB} was renamed to CURL_USE_${_LIB}.")
+  endif()
+endforeach()
 if(CMAKE_USE_WINSSL)
-  message(FATAL_ERROR "The cmake option CMAKE_USE_WINSSL was renamed to CMAKE_USE_SCHANNEL.")
+  set(CURL_RECONFIG_REQUIRED 1)
+  message(SEND_ERROR "The option CMAKE_USE_WINSSL was renamed to CURL_USE_SCHANNEL.")
 endif()
+if(CURL_RECONFIG_REQUIRED)
+  message(FATAL_ERROR "Reconfig required")
+endif()
+
+# check SSL libraries
+# TODO support GnuTLS
+option(CURL_ENABLE_SSL "Enable SSL support" ON)
 
 if(APPLE)
-  option(CMAKE_USE_SECTRANSP "enable Apple OS native SSL/TLS" OFF)
+  cmake_dependent_option(CURL_USE_SECTRANSP "enable Apple OS native SSL/TLS" OFF CURL_ENABLE_SSL OFF)
 endif()
 if(WIN32)
-  option(CMAKE_USE_SCHANNEL "enable Windows native SSL/TLS" OFF)
+  cmake_dependent_option(CURL_USE_SCHANNEL "enable Windows native SSL/TLS" OFF CURL_ENABLE_SSL OFF)
   cmake_dependent_option(CURL_WINDOWS_SSPI "Use windows libraries to allow NTLM authentication without openssl" ON
-    CMAKE_USE_SCHANNEL OFF)
+    CURL_USE_SCHANNEL OFF)
 endif()
-option(CMAKE_USE_MBEDTLS "Enable mbedTLS for SSL/TLS" OFF)
-option(CMAKE_USE_BEARSSL "Enable BearSSL for SSL/TLS" OFF)
-option(CMAKE_USE_NSS "Enable NSS for SSL/TLS" OFF)
-option(CMAKE_USE_WOLFSSL "enable wolfSSL for SSL/TLS" OFF)
+cmake_dependent_option(CURL_USE_MBEDTLS "Enable mbedTLS for SSL/TLS" OFF CURL_ENABLE_SSL OFF)
+cmake_dependent_option(CURL_USE_BEARSSL "Enable BearSSL for SSL/TLS" OFF CURL_ENABLE_SSL OFF)
+cmake_dependent_option(CURL_USE_NSS "Enable NSS for SSL/TLS" OFF CURL_ENABLE_SSL OFF)
+cmake_dependent_option(CURL_USE_WOLFSSL "enable wolfSSL for SSL/TLS" OFF CURL_ENABLE_SSL OFF)
 
 set(openssl_default ON)
-if(WIN32 OR CMAKE_USE_SECTRANSP OR CMAKE_USE_SCHANNEL OR CMAKE_USE_MBEDTLS OR CMAKE_USE_NSS OR CMAKE_USE_WOLFSSL)
+if(WIN32 OR CURL_USE_SECTRANSP OR CURL_USE_SCHANNEL OR CURL_USE_MBEDTLS OR CURL_USE_NSS OR CURL_USE_WOLFSSL)
   set(openssl_default OFF)
 endif()
-option(CMAKE_USE_OPENSSL "Use OpenSSL code. Experimental" ${openssl_default})
+cmake_dependent_option(CURL_USE_OPENSSL "Use OpenSSL code. Experimental" ${openssl_default} CURL_ENABLE_SSL OFF)
 option(CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG "Disable automatic loading of OpenSSL configuration" OFF)
 
 count_true(enabled_ssl_options_count
-  CMAKE_USE_SCHANNEL
-  CMAKE_USE_SECTRANSP
-  CMAKE_USE_OPENSSL
-  CMAKE_USE_MBEDTLS
-  CMAKE_USE_BEARSSL
-  CMAKE_USE_NSS
-  CMAKE_USE_WOLFSSL
+  CURL_USE_SCHANNEL
+  CURL_USE_SECTRANSP
+  CURL_USE_OPENSSL
+  CURL_USE_MBEDTLS
+  CURL_USE_BEARSSL
+  CURL_USE_NSS
+  CURL_USE_WOLFSSL
 )
 if(enabled_ssl_options_count GREATER "1")
   set(CURL_WITH_MULTI_SSL ON)
 endif()
 
-if(CMAKE_USE_SCHANNEL)
+if(CURL_USE_SCHANNEL)
   set(SSL_ENABLED ON)
   set(USE_SCHANNEL ON) # Windows native SSL/TLS support
-  set(USE_WINDOWS_SSPI ON) # CMAKE_USE_SCHANNEL implies CURL_WINDOWS_SSPI
+  set(USE_WINDOWS_SSPI ON) # CURL_USE_SCHANNEL implies CURL_WINDOWS_SSPI
 endif()
 if(CURL_WINDOWS_SSPI)
   set(USE_WINDOWS_SSPI ON)
   set(CMAKE_REQUIRED_DEFINITIONS "${CMAKE_REQUIRED_DEFINITIONS} -DSECURITY_WIN32")
 endif()
 
-if(CMAKE_USE_DARWINSSL)
-  message(FATAL_ERROR "The cmake option CMAKE_USE_DARWINSSL was renamed to CMAKE_USE_SECTRANSP.")
+if(CURL_USE_SECTRANSP)
+  set(use_core_foundation ON)
+
+  find_library(SECURITY_FRAMEWORK "Security")
+  if(NOT SECURITY_FRAMEWORK)
+     message(FATAL_ERROR "Security framework not found")
+  endif()
+
+  set(SSL_ENABLED ON)
+  set(USE_SECTRANSP ON)
+  list(APPEND CURL_LIBS "-framework Security")
 endif()
 
-if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
+if(use_core_foundation)
   find_library(COREFOUNDATION_FRAMEWORK "CoreFoundation")
   if(NOT COREFOUNDATION_FRAMEWORK)
       message(FATAL_ERROR "CoreFoundation framework not found")
   endif()
 
-  find_library(SYSTEMCONFIGURATION_FRAMEWORK "SystemConfiguration")
-  if(NOT SYSTEMCONFIGURATION_FRAMEWORK)
-     message(FATAL_ERROR "SystemConfiguration framework not found")
-  endif()
-
-  list(APPEND CURL_LIBS "-framework CoreFoundation" "-framework SystemConfiguration")
-
-  if(CMAKE_USE_SECTRANSP)
-    find_library(SECURITY_FRAMEWORK "Security")
-    if(NOT SECURITY_FRAMEWORK)
-       message(FATAL_ERROR "Security framework not found")
-    endif()
-
-    set(SSL_ENABLED ON)
-    set(USE_SECTRANSP ON)
-    list(APPEND CURL_LIBS "-framework Security")
-  endif()
+  list(APPEND CURL_LIBS "-framework CoreFoundation")
 endif()
 
-if(CMAKE_USE_OPENSSL)
+if(CURL_USE_OPENSSL)
   find_package(OpenSSL REQUIRED)
   set(SSL_ENABLED ON)
   set(USE_OPENSSL ON)
@@ -445,9 +465,11 @@ if(CMAKE_USE_OPENSSL)
   check_symbol_exists(RAND_status "${CURL_INCLUDES}" HAVE_RAND_STATUS)
   check_symbol_exists(RAND_screen "${CURL_INCLUDES}" HAVE_RAND_SCREEN)
   check_symbol_exists(RAND_egd    "${CURL_INCLUDES}" HAVE_RAND_EGD)
+
+  add_definitions(-DOPENSSL_SUPPRESS_DEPRECATED)
 endif()
 
-if(CMAKE_USE_MBEDTLS)
+if(CURL_USE_MBEDTLS)
   find_package(MbedTLS REQUIRED)
   set(SSL_ENABLED ON)
   set(USE_MBEDTLS ON)
@@ -455,7 +477,7 @@ if(CMAKE_USE_MBEDTLS)
   include_directories(${MBEDTLS_INCLUDE_DIRS})
 endif()
 
-if(CMAKE_USE_BEARSSL)
+if(CURL_USE_BEARSSL)
   find_package(BearSSL REQUIRED)
   set(SSL_ENABLED ON)
   set(USE_BEARSSL ON)
@@ -463,7 +485,7 @@ if(CMAKE_USE_BEARSSL)
   include_directories(${BEARSSL_INCLUDE_DIRS})
 endif()
 
-if(CMAKE_USE_WOLFSSL)
+if(CURL_USE_WOLFSSL)
   find_package(WolfSSL REQUIRED)
   set(SSL_ENABLED ON)
   set(USE_WOLFSSL ON)
@@ -471,7 +493,7 @@ if(CMAKE_USE_WOLFSSL)
   include_directories(${WolfSSL_INCLUDE_DIRS})
 endif()
 
-if(CMAKE_USE_NSS)
+if(CURL_USE_NSS)
   find_package(NSS REQUIRED)
   include_directories(${NSS_INCLUDE_DIRS})
   list(APPEND CURL_LIBS ${NSS_LIBRARIES})
@@ -552,13 +574,13 @@ if(NOT CURL_DISABLE_LDAP)
     endif()
   endif()
 
-  option(CMAKE_USE_OPENLDAP "Use OpenLDAP code." OFF)
-  mark_as_advanced(CMAKE_USE_OPENLDAP)
+  option(CURL_USE_OPENLDAP "Use OpenLDAP code." OFF)
+  mark_as_advanced(CURL_USE_OPENLDAP)
   set(CMAKE_LDAP_LIB "ldap" CACHE STRING "Name or full path to ldap library")
   set(CMAKE_LBER_LIB "lber" CACHE STRING "Name or full path to lber library")
 
-  if(CMAKE_USE_OPENLDAP AND USE_WIN32_LDAP)
-    message(FATAL_ERROR "Cannot use USE_WIN32_LDAP and CMAKE_USE_OPENLDAP at the same time")
+  if(CURL_USE_OPENLDAP AND USE_WIN32_LDAP)
+    message(FATAL_ERROR "Cannot use USE_WIN32_LDAP and CURL_USE_OPENLDAP at the same time")
   endif()
 
   # Now that we know, we're not using windows LDAP...
@@ -588,7 +610,7 @@ if(NOT CURL_DISABLE_LDAP)
       set(CURL_DISABLE_LDAP ON CACHE BOOL "" FORCE)
       set(CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES_BAK}) #LDAP includes won't be used
     else()
-      if(CMAKE_USE_OPENLDAP)
+      if(CURL_USE_OPENLDAP)
         set(USE_OPENLDAP ON)
       endif()
       if(CMAKE_LDAP_INCLUDE_DIR)
@@ -718,13 +740,13 @@ if(CURL_ZSTD)
 endif()
 
 #libSSH2
-option(CMAKE_USE_LIBSSH2 "Use libSSH2" ON)
-mark_as_advanced(CMAKE_USE_LIBSSH2)
+option(CURL_USE_LIBSSH2 "Use libSSH2" ON)
+mark_as_advanced(CURL_USE_LIBSSH2)
 set(USE_LIBSSH2 OFF)
 set(HAVE_LIBSSH2 OFF)
 set(HAVE_LIBSSH2_H OFF)
 
-if(CMAKE_USE_LIBSSH2)
+if(CURL_USE_LIBSSH2)
   find_package(LibSSH2)
   if(LIBSSH2_FOUND)
     list(APPEND CURL_LIBS ${LIBSSH2_LIBRARY})
@@ -743,9 +765,9 @@ if(CMAKE_USE_LIBSSH2)
 endif()
 
 # libssh
-option(CMAKE_USE_LIBSSH "Use libSSH" OFF)
-mark_as_advanced(CMAKE_USE_LIBSSH)
-if(NOT HAVE_LIBSSH2 AND CMAKE_USE_LIBSSH)
+option(CURL_USE_LIBSSH "Use libSSH" OFF)
+mark_as_advanced(CURL_USE_LIBSSH)
+if(NOT HAVE_LIBSSH2 AND CURL_USE_LIBSSH)
   find_package(libssh CONFIG)
   if(libssh_FOUND)
     message(STATUS "Found libssh ${libssh_VERSION}")
@@ -756,10 +778,10 @@ if(NOT HAVE_LIBSSH2 AND CMAKE_USE_LIBSSH)
   endif()
 endif()
 
-option(CMAKE_USE_GSSAPI "Use GSSAPI implementation (right now only Heimdal is supported with CMake build)" OFF)
-mark_as_advanced(CMAKE_USE_GSSAPI)
+option(CURL_USE_GSSAPI "Use GSSAPI implementation (right now only Heimdal is supported with CMake build)" OFF)
+mark_as_advanced(CURL_USE_GSSAPI)
 
-if(CMAKE_USE_GSSAPI)
+if(CURL_USE_GSSAPI)
   find_package(GSS)
 
   set(HAVE_GSSAPI ${GSS_FOUND})
@@ -908,7 +930,6 @@ endif()
 # Check for header files
 if(NOT UNIX)
   check_include_file_concat("windows.h"      HAVE_WINDOWS_H)
-  check_include_file_concat("winsock.h"      HAVE_WINSOCK_H)
   check_include_file_concat("ws2tcpip.h"     HAVE_WS2TCPIP_H)
   check_include_file_concat("winsock2.h"     HAVE_WINSOCK2_H)
   check_include_file_concat("wincrypt.h"     HAVE_WINCRYPT_H)
@@ -1484,12 +1505,10 @@ set(libdir                  "${CMAKE_INSTALL_PREFIX}/lib")
 foreach(_lib ${CMAKE_C_IMPLICIT_LINK_LIBRARIES} ${CURL_LIBS})
   if(TARGET "${_lib}")
     set(_libname "${_lib}")
-    get_target_property(_libtype "${_libname}" TYPE)
-    if(_libtype STREQUAL INTERFACE_LIBRARY)
-      # Interface libraries can occur when an external project embeds curl and
-      # defined targets such as ZLIB::ZLIB by themselves. Ignore these as
-      # reading the LOCATION property will error out. Assume the user won't need
-      # this information in the .pc file.
+    get_target_property(_imported "${_libname}" IMPORTED)
+    if(NOT _imported)
+      # Reading the LOCATION property on non-imported target will error out.
+      # Assume the user won't need this information in the .pc file.
       continue()
     endif()
     get_target_property(_lib "${_libname}" LOCATION)
diff --git a/include/curl/curl.h b/include/curl/curl.h
index 835c3d8..7b69ce2 100644
--- a/include/curl/curl.h
+++ b/include/curl/curl.h
@@ -46,8 +46,8 @@
 #include <stdio.h>
 #include <limits.h>
 
-#if defined(__FreeBSD__) && (__FreeBSD__ >= 2)
-/* Needed for __FreeBSD_version symbol definition */
+#if (defined(__FreeBSD__) && (__FreeBSD__ >= 2)) || defined(__MidnightBSD__)
+/* Needed for __FreeBSD_version or __MidnightBSD_version symbol definition */
 #include <osreldate.h>
 #endif
 
@@ -73,6 +73,7 @@
     defined(ANDROID) || defined(__ANDROID__) || defined(__OpenBSD__) || \
     defined(__CYGWIN__) || defined(AMIGA) || defined(__NuttX__) || \
    (defined(__FreeBSD_version) && (__FreeBSD_version < 800000)) || \
+   (defined(__MidnightBSD_version) && (__MidnightBSD_version < 100000)) || \
     defined(__VXWORKS__)
 #include <sys/select.h>
 #endif
@@ -470,6 +471,20 @@ typedef int (*curl_debug_callback)
         size_t size,       /* size of the data pointed to */
         void *userptr);    /* whatever the user please */
 
+/* This is the CURLOPT_PREREQFUNCTION callback prototype. */
+typedef int (*curl_prereq_callback)(void *clientp,
+                                    char *conn_primary_ip,
+                                    char *conn_local_ip,
+                                    int conn_primary_port,
+                                    int conn_local_port);
+
+/* Return code for when the pre-request callback has terminated without
+   any errors */
+#define CURL_PREREQFUNC_OK 0
+/* Return code for when the pre-request callback wants to abort the
+   request */
+#define CURL_PREREQFUNC_ABORT 1
+
 /* All possible error codes from all sorts of curl functions. Future versions
    may return other values, stay prepared.
 
@@ -2043,7 +2058,8 @@ typedef enum {
   /* alt-svc cache file name to possibly read from/write to */
   CURLOPT(CURLOPT_ALTSVC, CURLOPTTYPE_STRINGPOINT, 287),
 
-  /* maximum age of a connection to consider it for reuse (in seconds) */
+  /* maximum age (idle time) of a connection to consider it for reuse
+   * (in seconds) */
   CURLOPT(CURLOPT_MAXAGE_CONN, CURLOPTTYPE_LONG, 288),
 
   /* SASL authorisation identity */
@@ -2102,6 +2118,23 @@ typedef enum {
      this option is used only if PROXY_SSL_VERIFYPEER is true */
   CURLOPT(CURLOPT_PROXY_CAINFO_BLOB, CURLOPTTYPE_BLOB, 310),
 
+  /* used by scp/sftp to verify the host's public key */
+  CURLOPT(CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256, CURLOPTTYPE_STRINGPOINT, 311),
+
+  /* Function that will be called immediately before the initial request
+     is made on a connection (after any protocol negotiation step).  */
+  CURLOPT(CURLOPT_PREREQFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 312),
+
+  /* Data passed to the CURLOPT_PREREQFUNCTION callback */
+  CURLOPT(CURLOPT_PREREQDATA, CURLOPTTYPE_CBPOINT, 313),
+
+  /* maximum age (since creation) of a connection to consider it for reuse
+   * (in seconds) */
+  CURLOPT(CURLOPT_MAXLIFETIME_CONN, CURLOPTTYPE_LONG, 314),
+
+  /* Set MIME option flags. */
+  CURLOPT(CURLOPT_MIME_OPTIONS, CURLOPTTYPE_LONG, 315),
+
   CURLOPT_LASTENTRY /* the last unused */
 } CURLoption;
 
@@ -2261,6 +2294,9 @@ CURL_EXTERN int curl_strnequal(const char *s1, const char *s2, size_t n);
 typedef struct curl_mime      curl_mime;      /* Mime context. */
 typedef struct curl_mimepart  curl_mimepart;  /* Mime part context. */
 
+/* CURLMIMEOPT_ defines are for the CURLOPT_MIME_OPTIONS option. */
+#define CURLMIMEOPT_FORMESCAPE  (1<<0) /* Use backslash-escaping for forms. */
+
 /*
  * NAME curl_mime_init()
  *
diff --git a/include/curl/curlver.h b/include/curl/curlver.h
index 393ba12..fe57e43 100644
--- a/include/curl/curlver.h
+++ b/include/curl/curlver.h
@@ -30,13 +30,13 @@
 
 /* This is the version number of the libcurl package from which this header
    file origins: */
-#define LIBCURL_VERSION "7.79.1-DEV"
+#define LIBCURL_VERSION "7.81.0-DEV"
 
 /* The numeric version number is also available "in parts" by using these
    defines: */
 #define LIBCURL_VERSION_MAJOR 7
-#define LIBCURL_VERSION_MINOR 79
-#define LIBCURL_VERSION_PATCH 1
+#define LIBCURL_VERSION_MINOR 81
+#define LIBCURL_VERSION_PATCH 0
 
 /* This is the numeric version of the libcurl version number, meant for easier
    parsing and comparisons by programs. The LIBCURL_VERSION_NUM define will
@@ -57,7 +57,7 @@
    CURL_VERSION_BITS() macro since curl's own configure script greps for it
    and needs it to contain the full number.
 */
-#define LIBCURL_VERSION_NUM 0x074f01
+#define LIBCURL_VERSION_NUM 0x075100
 
 /*
  * This is the date and time when the full source package was created. The
diff --git a/include/curl/multi.h b/include/curl/multi.h
index 37f9829..91cd95d 100644
--- a/include/curl/multi.h
+++ b/include/curl/multi.h
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -73,7 +73,8 @@ typedef enum {
   CURLM_RECURSIVE_API_CALL, /* an api function was called from inside a
                                callback */
   CURLM_WAKEUP_FAILURE,  /* wakeup is unavailable or failed */
-  CURLM_BAD_FUNCTION_ARGUMENT,  /* function called with a bad parameter */
+  CURLM_BAD_FUNCTION_ARGUMENT, /* function called with a bad parameter */
+  CURLM_ABORTED_BY_CALLBACK,
   CURLM_LAST
 } CURLMcode;
 
diff --git a/include/curl/typecheck-gcc.h b/include/curl/typecheck-gcc.h
index 34d0267..9e14d8a 100644
--- a/include/curl/typecheck-gcc.h
+++ b/include/curl/typecheck-gcc.h
@@ -317,6 +317,7 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t,
    (option) == CURLOPT_SERVICE_NAME ||                                        \
    (option) == CURLOPT_SOCKS5_GSSAPI_SERVICE ||                               \
    (option) == CURLOPT_SSH_HOST_PUBLIC_KEY_MD5 ||                             \
+   (option) == CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256 ||                          \
    (option) == CURLOPT_SSH_KNOWNHOSTS ||                                      \
    (option) == CURLOPT_SSH_PRIVATE_KEYFILE ||                                 \
    (option) == CURLOPT_SSH_PUBLIC_KEYFILE ||                                  \
@@ -363,6 +364,7 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t,
    (option) == CURLOPT_INTERLEAVEDATA ||                                      \
    (option) == CURLOPT_IOCTLDATA ||                                           \
    (option) == CURLOPT_OPENSOCKETDATA ||                                      \
+   (option) == CURLOPT_PREREQDATA ||                                          \
    (option) == CURLOPT_PROGRESSDATA ||                                        \
    (option) == CURLOPT_READDATA ||                                            \
    (option) == CURLOPT_SEEKDATA ||                                            \
diff --git a/include/curl/urlapi.h b/include/curl/urlapi.h
index 1d70880..a475f91 100644
--- a/include/curl/urlapi.h
+++ b/include/curl/urlapi.h
@@ -47,7 +47,20 @@ typedef enum {
   CURLUE_NO_HOST,             /* 14 */
   CURLUE_NO_PORT,             /* 15 */
   CURLUE_NO_QUERY,            /* 16 */
-  CURLUE_NO_FRAGMENT          /* 17 */
+  CURLUE_NO_FRAGMENT,         /* 17 */
+  CURLUE_NO_ZONEID,           /* 18 */
+  CURLUE_BAD_FILE_URL,        /* 19 */
+  CURLUE_BAD_FRAGMENT,        /* 20 */
+  CURLUE_BAD_HOSTNAME,        /* 21 */
+  CURLUE_BAD_IPV6,            /* 22 */
+  CURLUE_BAD_LOGIN,           /* 23 */
+  CURLUE_BAD_PASSWORD,        /* 24 */
+  CURLUE_BAD_PATH,            /* 25 */
+  CURLUE_BAD_QUERY,           /* 26 */
+  CURLUE_BAD_SCHEME,          /* 27 */
+  CURLUE_BAD_SLASHES,         /* 28 */
+  CURLUE_BAD_USER,            /* 29 */
+  CURLUE_LAST
 } CURLUcode;
 
 typedef enum {
@@ -118,6 +131,12 @@ CURL_EXTERN CURLUcode curl_url_get(CURLU *handle, CURLUPart what,
 CURL_EXTERN CURLUcode curl_url_set(CURLU *handle, CURLUPart what,
                                    const char *part, unsigned int flags);
 
+/*
+ * curl_url_strerror() turns a CURLUcode value into the equivalent human
+ * readable error string.  This is useful for printing meaningful error
+ * messages.
+ */
+CURL_EXTERN const char *curl_url_strerror(CURLUcode);
 
 #ifdef __cplusplus
 } /* end of extern "C" */
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index 2575288..46973bf 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -95,10 +95,6 @@ endif()
 
 target_link_libraries(${LIB_NAME} ${CURL_LIBS})
 
-if(WIN32)
-  add_definitions(-D_USRDLL)
-endif()
-
 set_target_properties(${LIB_NAME} PROPERTIES
   COMPILE_DEFINITIONS BUILDING_LIBCURL
   OUTPUT_NAME ${LIBCURL_OUTPUT_NAME}
@@ -121,6 +117,7 @@ endif()
 
 if(WIN32)
   if(BUILD_SHARED_LIBS)
+    set_property(TARGET ${LIB_NAME} APPEND PROPERTY COMPILE_DEFINITIONS "_USRDLL")
     if(MSVC)
       # Add "_imp" as a suffix before the extension to avoid conflicting with
       # the statically linked "libcurl.lib"
diff --git a/lib/asyn-ares.c b/lib/asyn-ares.c
index 763a4aa..fd0bb6c 100644
--- a/lib/asyn-ares.c
+++ b/lib/asyn-ares.c
@@ -109,7 +109,9 @@ struct thread_data {
   struct Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares
                                     parts */
   int last_status;
+#ifndef HAVE_CARES_GETADDRINFO
   struct curltime happy_eyeballs_dns_time; /* when this timer started, or 0 */
+#endif
 };
 
 /* How long we are willing to wait for additional parallel responses after
@@ -341,7 +343,7 @@ static int waitperform(struct Curl_easy *data, timediff_t timeout_ms)
     nfds = 0;
 
   if(!nfds)
-    /* Call ares_process() unconditonally here, even if we simply timed out
+    /* Call ares_process() unconditionally here, even if we simply timed out
        above, as otherwise the ares name resolve won't timeout! */
     ares_process_fd((ares_channel)data->state.async.resolver, ARES_SOCKET_BAD,
                     ARES_SOCKET_BAD);
@@ -375,6 +377,7 @@ CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
 
   waitperform(data, 0);
 
+#ifndef HAVE_CARES_GETADDRINFO
   /* Now that we've checked for any last minute results above, see if there are
      any responses still pending when the EXPIRE_HAPPY_EYEBALLS_DNS timer
      expires. */
@@ -397,6 +400,7 @@ CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
     ares_cancel((ares_channel)data->state.async.resolver);
     DEBUGASSERT(res->num_pending == 0);
   }
+#endif
 
   if(res && !res->num_pending) {
     (void)Curl_addrinfo_callback(data, res->last_status, res->temp_ai);
diff --git a/lib/c-hyper.c b/lib/c-hyper.c
index 26635cd..c253cd3 100644
--- a/lib/c-hyper.c
+++ b/lib/c-hyper.c
@@ -156,13 +156,15 @@ static int hyper_each_header(void *userdata,
 
   Curl_debug(data, CURLINFO_HEADER_IN, headp, len);
 
-  writetype = CLIENTWRITE_HEADER;
-  if(data->set.include_header)
-    writetype |= CLIENTWRITE_BODY;
-  result = Curl_client_write(data, writetype, headp, len);
-  if(result) {
-    data->state.hresult = CURLE_ABORTED_BY_CALLBACK;
-    return HYPER_ITER_BREAK;
+  if(!data->state.hconnect || !data->set.suppress_connect_headers) {
+    writetype = CLIENTWRITE_HEADER;
+    if(data->set.include_header)
+      writetype |= CLIENTWRITE_BODY;
+    result = Curl_client_write(data, writetype, headp, len);
+    if(result) {
+      data->state.hresult = CURLE_ABORTED_BY_CALLBACK;
+      return HYPER_ITER_BREAK;
+    }
   }
 
   data->info.header_size += (long)len;
@@ -205,7 +207,8 @@ static int hyper_body_chunk(void *userdata, const hyper_buf *chunk)
         k->exp100 = EXP100_FAILED;
       }
     }
-    if(data->state.hconnect && (data->req.httpcode/100 != 2)) {
+    if(data->state.hconnect && (data->req.httpcode/100 != 2) &&
+       data->state.authproxy.done) {
       done = TRUE;
       result = CURLE_OK;
     }
@@ -260,6 +263,12 @@ static CURLcode status_line(struct Curl_easy *data,
   if(http_version == HYPER_HTTP_VERSION_1_0)
     data->state.httpwant = CURL_HTTP_VERSION_1_0;
 
+  if(data->state.hconnect)
+    /* CONNECT */
+    data->info.httpproxycode = http_status;
+
+  /* We need to set 'httpcodeq' for functions that check the response code in
+     a single place. */
   data->req.httpcode = http_status;
 
   result = Curl_http_statusline(data, conn);
@@ -277,16 +286,18 @@ static CURLcode status_line(struct Curl_easy *data,
   len = Curl_dyn_len(&data->state.headerb);
   Curl_debug(data, CURLINFO_HEADER_IN, Curl_dyn_ptr(&data->state.headerb),
              len);
-  writetype = CLIENTWRITE_HEADER;
-  if(data->set.include_header)
-    writetype |= CLIENTWRITE_BODY;
-  result = Curl_client_write(data, writetype,
-                             Curl_dyn_ptr(&data->state.headerb), len);
-  if(result) {
-    data->state.hresult = CURLE_ABORTED_BY_CALLBACK;
-    return HYPER_ITER_BREAK;
-  }
 
+  if(!data->state.hconnect || !data->set.suppress_connect_headers) {
+    writetype = CLIENTWRITE_HEADER;
+    if(data->set.include_header)
+      writetype |= CLIENTWRITE_BODY;
+    result = Curl_client_write(data, writetype,
+                               Curl_dyn_ptr(&data->state.headerb), len);
+    if(result) {
+      data->state.hresult = CURLE_ABORTED_BY_CALLBACK;
+      return HYPER_ITER_BREAK;
+    }
+  }
   data->info.header_size += (long)len;
   data->req.headerbytecount += (long)len;
   data->req.httpcode = http_status;
@@ -299,8 +310,14 @@ static CURLcode status_line(struct Curl_easy *data,
  */
 static CURLcode empty_header(struct Curl_easy *data)
 {
-  return hyper_each_header(data, NULL, 0, NULL, 0) ?
-    CURLE_WRITE_ERROR : CURLE_OK;
+  CURLcode result = Curl_http_size(data);
+  if(!result) {
+    result = hyper_each_header(data, NULL, 0, NULL, 0) ?
+      CURLE_WRITE_ERROR : CURLE_OK;
+    if(result)
+      failf(data, "hyperstream: couldn't pass blank header");
+  }
+  return result;
 }
 
 CURLcode Curl_hyper_stream(struct Curl_easy *data,
@@ -443,11 +460,9 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data,
       break;
     }
 
-    if(empty_header(data)) {
-      failf(data, "hyperstream: couldn't pass blank header");
-      result = CURLE_OUT_OF_MEMORY;
+    result = empty_header(data);
+    if(result)
       break;
-    }
 
     /* Curl_http_auth_act() checks what authentication methods that are
      * available and decides which one (if any) to use. It will set 'newurl'
@@ -584,9 +599,22 @@ static CURLcode request_target(struct Curl_easy *data,
   if(result)
     return result;
 
-  if(hyper_request_set_uri(req, (uint8_t *)Curl_dyn_uptr(&r),
-                           Curl_dyn_len(&r))) {
-    failf(data, "error setting path");
+  if(h2 && hyper_request_set_uri_parts(req,
+                                       /* scheme */
+                                       (uint8_t *)data->state.up.scheme,
+                                       strlen(data->state.up.scheme),
+                                       /* authority */
+                                       (uint8_t *)conn->host.name,
+                                       strlen(conn->host.name),
+                                       /* path_and_query */
+                                       (uint8_t *)Curl_dyn_uptr(&r),
+                                       Curl_dyn_len(&r))) {
+    failf(data, "error setting uri parts to hyper");
+    result = CURLE_OUT_OF_MEMORY;
+  }
+  else if(!h2 && hyper_request_set_uri(req, (uint8_t *)Curl_dyn_uptr(&r),
+                                       Curl_dyn_len(&r))) {
+    failf(data, "error setting uri to hyper");
     result = CURLE_OUT_OF_MEMORY;
   }
   else
@@ -850,6 +878,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
   io = hyper_io_new();
   if(!io) {
     failf(data, "Couldn't create hyper IO");
+    result = CURLE_OUT_OF_MEMORY;
     goto error;
   }
   /* tell Hyper how to read/write network data */
@@ -862,6 +891,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
     h->exec = hyper_executor_new();
     if(!h->exec) {
       failf(data, "Couldn't create hyper executor");
+      result = CURLE_OUT_OF_MEMORY;
       goto error;
     }
   }
@@ -869,6 +899,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
   options = hyper_clientconn_options_new();
   if(!options) {
     failf(data, "Couldn't create hyper client options");
+    result = CURLE_OUT_OF_MEMORY;
     goto error;
   }
   if(conn->negnpn == CURL_HTTP_VERSION_2) {
@@ -882,6 +913,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
   handshake = hyper_clientconn_handshake(io, options);
   if(!handshake) {
     failf(data, "Couldn't create hyper client handshake");
+    result = CURLE_OUT_OF_MEMORY;
     goto error;
   }
   io = NULL;
@@ -889,6 +921,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
 
   if(HYPERE_OK != hyper_executor_push(h->exec, handshake)) {
     failf(data, "Couldn't hyper_executor_push the handshake");
+    result = CURLE_OUT_OF_MEMORY;
     goto error;
   }
   handshake = NULL; /* ownership passed on */
@@ -896,6 +929,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
   task = hyper_executor_poll(h->exec);
   if(!task) {
     failf(data, "Couldn't hyper_executor_poll the handshake");
+    result = CURLE_OUT_OF_MEMORY;
     goto error;
   }
 
@@ -905,6 +939,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
   req = hyper_request_new();
   if(!req) {
     failf(data, "Couldn't hyper_request_new");
+    result = CURLE_OUT_OF_MEMORY;
     goto error;
   }
 
@@ -912,12 +947,14 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
     if(HYPERE_OK != hyper_request_set_version(req,
                                               HYPER_HTTP_VERSION_1_0)) {
       failf(data, "error setting HTTP version");
+      result = CURLE_OUT_OF_MEMORY;
       goto error;
     }
   }
 
   if(hyper_request_set_method(req, (uint8_t *)method, strlen(method))) {
     failf(data, "error setting method");
+    result = CURLE_OUT_OF_MEMORY;
     goto error;
   }
 
@@ -928,51 +965,81 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
   headers = hyper_request_headers(req);
   if(!headers) {
     failf(data, "hyper_request_headers");
+    result = CURLE_OUT_OF_MEMORY;
     goto error;
   }
 
   rc = hyper_request_on_informational(req, http1xx_cb, data);
-  if(rc)
-    return CURLE_OUT_OF_MEMORY;
+  if(rc) {
+    result = CURLE_OUT_OF_MEMORY;
+    goto error;
+  }
 
   result = Curl_http_body(data, conn, httpreq, &te);
   if(result)
-    return result;
-
-  if(data->state.aptr.host &&
-     Curl_hyper_header(data, headers, data->state.aptr.host))
     goto error;
 
-  if(data->state.aptr.proxyuserpwd &&
-     Curl_hyper_header(data, headers, data->state.aptr.proxyuserpwd))
-    goto error;
+  if(!h2) {
+    if(data->state.aptr.host) {
+      result = Curl_hyper_header(data, headers, data->state.aptr.host);
+      if(result)
+        goto error;
+    }
+  }
+  else {
+    /* For HTTP/2, we show the Host: header as if we sent it, to make it look
+       like for HTTP/1 but it isn't actually sent since :authority is then
+       used. */
+    result = Curl_debug(data, CURLINFO_HEADER_OUT, data->state.aptr.host,
+                        strlen(data->state.aptr.host));
+    if(result)
+      goto error;
+  }
 
-  if(data->state.aptr.userpwd &&
-     Curl_hyper_header(data, headers, data->state.aptr.userpwd))
-    goto error;
+  if(data->state.aptr.proxyuserpwd) {
+    result = Curl_hyper_header(data, headers, data->state.aptr.proxyuserpwd);
+    if(result)
+      goto error;
+  }
 
-  if((data->state.use_range && data->state.aptr.rangeline) &&
-     Curl_hyper_header(data, headers, data->state.aptr.rangeline))
-    goto error;
+  if(data->state.aptr.userpwd) {
+    result = Curl_hyper_header(data, headers, data->state.aptr.userpwd);
+    if(result)
+      goto error;
+  }
+
+  if((data->state.use_range && data->state.aptr.rangeline)) {
+    result = Curl_hyper_header(data, headers, data->state.aptr.rangeline);
+    if(result)
+      goto error;
+  }
 
   if(data->set.str[STRING_USERAGENT] &&
      *data->set.str[STRING_USERAGENT] &&
-     data->state.aptr.uagent &&
-     Curl_hyper_header(data, headers, data->state.aptr.uagent))
-    goto error;
+     data->state.aptr.uagent) {
+    result = Curl_hyper_header(data, headers, data->state.aptr.uagent);
+    if(result)
+      goto error;
+  }
 
   p_accept = Curl_checkheaders(data, "Accept")?NULL:"Accept: */*\r\n";
-  if(p_accept && Curl_hyper_header(data, headers, p_accept))
-    goto error;
-
-  if(te && Curl_hyper_header(data, headers, te))
-    goto error;
+  if(p_accept) {
+    result = Curl_hyper_header(data, headers, p_accept);
+    if(result)
+      goto error;
+  }
+  if(te) {
+    result = Curl_hyper_header(data, headers, te);
+    if(result)
+      goto error;
+  }
 
 #ifndef CURL_DISABLE_PROXY
   if(conn->bits.httpproxy && !conn->bits.tunnel_proxy &&
      !Curl_checkheaders(data, "Proxy-Connection") &&
      !Curl_checkProxyheaders(data, conn, "Proxy-Connection")) {
-    if(Curl_hyper_header(data, headers, "Proxy-Connection: Keep-Alive"))
+    result = Curl_hyper_header(data, headers, "Proxy-Connection: Keep-Alive");
+    if(result)
       goto error;
   }
 #endif
@@ -981,8 +1048,10 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
   if(data->state.referer && !Curl_checkheaders(data, "Referer")) {
     data->state.aptr.ref = aprintf("Referer: %s\r\n", data->state.referer);
     if(!data->state.aptr.ref)
-      return CURLE_OUT_OF_MEMORY;
-    if(Curl_hyper_header(data, headers, data->state.aptr.ref))
+      result = CURLE_OUT_OF_MEMORY;
+    else
+      result = Curl_hyper_header(data, headers, data->state.aptr.ref);
+    if(result)
       goto error;
   }
 
@@ -992,8 +1061,11 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
     data->state.aptr.accept_encoding =
       aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]);
     if(!data->state.aptr.accept_encoding)
-      return CURLE_OUT_OF_MEMORY;
-    if(Curl_hyper_header(data, headers, data->state.aptr.accept_encoding))
+      result = CURLE_OUT_OF_MEMORY;
+    else
+      result = Curl_hyper_header(data, headers,
+                                 data->state.aptr.accept_encoding);
+    if(result)
       goto error;
   }
   else
@@ -1003,38 +1075,43 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
   /* we only consider transfer-encoding magic if libz support is built-in */
   result = Curl_transferencode(data);
   if(result)
-    return result;
-  if(Curl_hyper_header(data, headers, data->state.aptr.te))
+    goto error;
+  result = Curl_hyper_header(data, headers, data->state.aptr.te);
+  if(result)
     goto error;
 #endif
 
   result = cookies(data, conn, headers);
   if(result)
-    return result;
+    goto error;
 
   result = Curl_add_timecondition(data, headers);
   if(result)
-    return result;
+    goto error;
 
   result = Curl_add_custom_headers(data, FALSE, headers);
   if(result)
-    return result;
+    goto error;
 
   result = bodysend(data, conn, headers, req, httpreq);
   if(result)
-    return result;
+    goto error;
 
-  Curl_debug(data, CURLINFO_HEADER_OUT, (char *)"\r\n", 2);
+  result = Curl_debug(data, CURLINFO_HEADER_OUT, (char *)"\r\n", 2);
+  if(result)
+    goto error;
 
   data->req.upload_chunky = FALSE;
   sendtask = hyper_clientconn_send(client, req);
   if(!sendtask) {
     failf(data, "hyper_clientconn_send");
+    result = CURLE_OUT_OF_MEMORY;
     goto error;
   }
 
   if(HYPERE_OK != hyper_executor_push(h->exec, sendtask)) {
     failf(data, "Couldn't hyper_executor_push the send");
+    result = CURLE_OUT_OF_MEMORY;
     goto error;
   }
 
@@ -1057,7 +1134,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
   Curl_safefree(data->state.aptr.proxyuserpwd);
   return CURLE_OK;
   error:
-
+  DEBUGASSERT(result);
   if(io)
     hyper_io_free(io);
 
@@ -1067,7 +1144,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
   if(handshake)
     hyper_task_free(handshake);
 
-  return CURLE_OUT_OF_MEMORY;
+  return result;
 }
 
 void Curl_hyper_done(struct Curl_easy *data)
diff --git a/lib/conncache.c b/lib/conncache.c
index f5ba8ff..fec1937 100644
--- a/lib/conncache.c
+++ b/lib/conncache.c
@@ -113,21 +113,16 @@ static void free_bundle_hash_entry(void *freethis)
 
 int Curl_conncache_init(struct conncache *connc, int size)
 {
-  int rc;
-
   /* allocate a new easy handle to use when closing cached connections */
   connc->closure_handle = curl_easy_init();
   if(!connc->closure_handle)
     return 1; /* bad */
 
-  rc = Curl_hash_init(&connc->hash, size, Curl_hash_str,
-                      Curl_str_key_compare, free_bundle_hash_entry);
-  if(rc)
-    Curl_close(&connc->closure_handle);
-  else
-    connc->closure_handle->state.conn_cache = connc;
+  Curl_hash_init(&connc->hash, size, Curl_hash_str,
+                 Curl_str_key_compare, free_bundle_hash_entry);
+  connc->closure_handle->state.conn_cache = connc;
 
-  return rc;
+  return 0; /* good */
 }
 
 void Curl_conncache_destroy(struct conncache *connc)
diff --git a/lib/connect.c b/lib/connect.c
index d61b037..5252f97 100644
--- a/lib/connect.c
+++ b/lib/connect.c
@@ -85,7 +85,7 @@
 
 static bool verifyconnect(curl_socket_t sockfd, int *error);
 
-#if defined(__DragonFly__) || defined(HAVE_WINSOCK_H)
+#if defined(__DragonFly__) || defined(HAVE_WINSOCK2_H)
 /* DragonFlyBSD and Windows use millisecond units */
 #define KEEPALIVE_FACTOR(x) (x *= 1000)
 #else
@@ -629,7 +629,7 @@ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
 #ifdef ENABLE_IPV6
   struct sockaddr_in6 *si6 = NULL;
 #endif
-#if defined(HAVE_SYS_UN_H) && defined(AF_UNIX)
+#if (defined(HAVE_SYS_UN_H) || defined(WIN32_SOCKADDR_UN)) && defined(AF_UNIX)
   struct sockaddr_un *su = NULL;
 #else
   (void)salen;
@@ -656,7 +656,7 @@ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
       }
       break;
 #endif
-#if defined(HAVE_SYS_UN_H) && defined(AF_UNIX)
+#if (defined(HAVE_SYS_UN_H) || defined(WIN32_SOCKADDR_UN)) && defined(AF_UNIX)
     case AF_UNIX:
       if(salen > (curl_socklen_t)sizeof(CURL_SA_FAMILY_T)) {
         su = (struct sockaddr_un*)sa;
@@ -894,6 +894,8 @@ CURLcode Curl_is_connected(struct Curl_easy *data,
         connkeep(conn, "HTTP/3 default");
         return CURLE_OK;
       }
+      /* When a QUIC connect attempt fails, the better error explanation is in
+         'result' and not in errno */
       if(result) {
         conn->tempsock[i] = CURL_SOCKET_BAD;
         error = SOCKERRNO;
@@ -977,6 +979,13 @@ CURLcode Curl_is_connected(struct Curl_easy *data,
         char buffer[STRERROR_LEN];
         Curl_printable_address(conn->tempaddr[i], ipaddress,
                                sizeof(ipaddress));
+#ifdef ENABLE_QUIC
+        if(conn->transport == TRNSPRT_QUIC) {
+          infof(data, "connect to %s port %u failed: %s",
+                ipaddress, conn->port, curl_easy_strerror(result));
+        }
+        else
+#endif
         infof(data, "connect to %s port %u failed: %s",
               ipaddress, conn->port,
               Curl_strerror(error, buffer, sizeof(buffer)));
@@ -988,9 +997,11 @@ CURLcode Curl_is_connected(struct Curl_easy *data,
         ainext(conn, i, TRUE);
         status = trynextip(data, conn, sockindex, i);
         if((status != CURLE_COULDNT_CONNECT) ||
-           conn->tempsock[other] == CURL_SOCKET_BAD)
+           conn->tempsock[other] == CURL_SOCKET_BAD) {
           /* the last attempt failed and no other sockets remain open */
-          result = status;
+          if(!result)
+            result = status;
+        }
       }
     }
   }
@@ -1016,6 +1027,7 @@ CURLcode Curl_is_connected(struct Curl_easy *data,
     /* no more addresses to try */
     const char *hostname;
     char buffer[STRERROR_LEN];
+    CURLcode failreason = result;
 
     /* if the first address family runs out of addresses to try before the
        happy eyeball timeout, go ahead and try the next family now */
@@ -1023,6 +1035,8 @@ CURLcode Curl_is_connected(struct Curl_easy *data,
     if(!result)
       return result;
 
+    result = failreason;
+
 #ifndef CURL_DISABLE_PROXY
     if(conn->bits.socksproxy)
       hostname = conn->socks_proxy.host.name;
@@ -1036,10 +1050,14 @@ CURLcode Curl_is_connected(struct Curl_easy *data,
       hostname = conn->host.name;
 
     failf(data, "Failed to connect to %s port %u after "
-                "%" CURL_FORMAT_TIMEDIFF_T " ms: %s",
-        hostname, conn->port,
-        Curl_timediff(now, data->progress.t_startsingle),
-        Curl_strerror(error, buffer, sizeof(buffer)));
+          "%" CURL_FORMAT_TIMEDIFF_T " ms: %s",
+          hostname, conn->port,
+          Curl_timediff(now, data->progress.t_startsingle),
+#ifdef ENABLE_QUIC
+          (conn->transport == TRNSPRT_QUIC) ?
+          curl_easy_strerror(result) :
+#endif
+          Curl_strerror(error, buffer, sizeof(buffer)));
 
     Curl_quic_disconnect(data, conn, 0);
     Curl_quic_disconnect(data, conn, 1);
@@ -1127,7 +1145,7 @@ void Curl_sndbufset(curl_socket_t sockfd)
   static int detectOsState = DETECT_OS_NONE;
 
   if(detectOsState == DETECT_OS_NONE) {
-    if(curlx_verify_windows_version(6, 0, PLATFORM_WINNT,
+    if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT,
                                     VERSION_GREATER_THAN_EQUAL))
       detectOsState = DETECT_OS_VISTA_OR_LATER;
     else
diff --git a/lib/content_encoding.c b/lib/content_encoding.c
index a84ff54..c03637a 100644
--- a/lib/content_encoding.c
+++ b/lib/content_encoding.c
@@ -240,7 +240,8 @@ static CURLcode inflate_stream(struct Curl_easy *data,
         }
         zp->zlib_init = ZLIB_UNINIT;    /* inflateEnd() already called. */
       }
-      /* FALLTHROUGH */
+      result = exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z));
+      break;
     default:
       result = exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z));
       break;
diff --git a/lib/cookie.c b/lib/cookie.c
index b7531f7..d418efa 100644
--- a/lib/cookie.c
+++ b/lib/cookie.c
@@ -1164,7 +1164,7 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
   bool fromfile = TRUE;
   char *line = NULL;
 
-  if(NULL == inc) {
+  if(!inc) {
     /* we didn't get a struct, create one */
     c = calloc(1, sizeof(struct CookieInfo));
     if(!c)
diff --git a/lib/curl_config.h.cmake b/lib/curl_config.h.cmake
index 4ef4883..d2a0f43 100644
--- a/lib/curl_config.h.cmake
+++ b/lib/curl_config.h.cmake
@@ -710,9 +710,6 @@
 /* Define to 1 if you have the winsock2.h header file. */
 #cmakedefine HAVE_WINSOCK2_H 1
 
-/* Define to 1 if you have the winsock.h header file. */
-#cmakedefine HAVE_WINSOCK_H 1
-
 /* Define this symbol if your OS supports changing the contents of argv */
 #cmakedefine HAVE_WRITABLE_ARGV 1
 
diff --git a/lib/curl_des.c b/lib/curl_des.c
index 8c5af19..76185cb 100644
--- a/lib/curl_des.c
+++ b/lib/curl_des.c
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2015 - 2020, Steve Holme, <steve_holme@hotmail.com>.
+ * Copyright (C) 2015 - 2021, Steve Holme, <steve_holme@hotmail.com>.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -22,7 +22,7 @@
 
 #include "curl_setup.h"
 
-#if defined(USE_NTLM) && !defined(USE_OPENSSL)
+#if defined(USE_NTLM) && !defined(USE_OPENSSL) && !defined(USE_WOLFSSL)
 
 #include "curl_des.h"
 
diff --git a/lib/curl_gssapi.c b/lib/curl_gssapi.c
index 5810dad..8f34056 100644
--- a/lib/curl_gssapi.c
+++ b/lib/curl_gssapi.c
@@ -32,10 +32,12 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
-static char spnego_oid_bytes[] = "\x2b\x06\x01\x05\x05\x02";
-gss_OID_desc Curl_spnego_mech_oid = { 6, &spnego_oid_bytes };
-static char krb5_oid_bytes[] = "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02";
-gss_OID_desc Curl_krb5_mech_oid = { 9, &krb5_oid_bytes };
+gss_OID_desc Curl_spnego_mech_oid = {
+  6, (char *)"\x2b\x06\x01\x05\x05\x02"
+};
+gss_OID_desc Curl_krb5_mech_oid = {
+  9, (char *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"
+};
 
 OM_uint32 Curl_gss_init_sec_context(
     struct Curl_easy *data,
diff --git a/lib/curl_hmac.h b/lib/curl_hmac.h
index 84c7312..5755655 100644
--- a/lib/curl_hmac.h
+++ b/lib/curl_hmac.h
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -26,7 +26,7 @@
 
 #define HMAC_MD5_LENGTH 16
 
-typedef void    (* HMAC_hinit_func)(void *context);
+typedef CURLcode (* HMAC_hinit_func)(void *context);
 typedef void    (* HMAC_hupdate_func)(void *context,
                                       const unsigned char *data,
                                       unsigned int len);
diff --git a/lib/curl_md5.h b/lib/curl_md5.h
index 5739c89..b7d7c1f 100644
--- a/lib/curl_md5.h
+++ b/lib/curl_md5.h
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -27,7 +27,7 @@
 
 #define MD5_DIGEST_LEN  16
 
-typedef void (* Curl_MD5_init_func)(void *context);
+typedef CURLcode (* Curl_MD5_init_func)(void *context);
 typedef void (* Curl_MD5_update_func)(void *context,
                                       const unsigned char *data,
                                       unsigned int len);
@@ -49,8 +49,8 @@ struct MD5_context {
 extern const struct MD5_params Curl_DIGEST_MD5[1];
 extern const struct HMAC_params Curl_HMAC_MD5[1];
 
-void Curl_md5it(unsigned char *output, const unsigned char *input,
-                const size_t len);
+CURLcode Curl_md5it(unsigned char *output, const unsigned char *input,
+                    const size_t len);
 
 struct MD5_context *Curl_MD5_init(const struct MD5_params *md5params);
 CURLcode Curl_MD5_update(struct MD5_context *context,
diff --git a/lib/curl_ntlm_core.c b/lib/curl_ntlm_core.c
index 749b44e..ed123d0 100644
--- a/lib/curl_ntlm_core.c
+++ b/lib/curl_ntlm_core.c
@@ -49,7 +49,14 @@
      in NTLM type-3 messages.
  */
 
-#if defined(USE_OPENSSL) || defined(USE_WOLFSSL)
+#if defined(USE_OPENSSL)
+  #include <openssl/opensslconf.h>
+  #if !defined(OPENSSL_NO_DES) && !defined(OPENSSL_NO_DEPRECATED_3_0)
+    #define USE_OPENSSL_DES
+  #endif
+#endif
+
+#if defined(USE_OPENSSL_DES) || defined(USE_WOLFSSL)
 
 #ifdef USE_WOLFSSL
 #include <wolfssl/options.h>
@@ -97,7 +104,7 @@
 #elif defined(USE_WIN32_CRYPTO)
 #  include <wincrypt.h>
 #else
-#  error "Can't compile NTLM support without a crypto library."
+#  error "Can't compile NTLM support without a crypto library with DES."
 #endif
 
 #include "urldata.h"
@@ -133,7 +140,7 @@ static void extend_key_56_to_64(const unsigned char *key_56, char *key)
   key[7] = (unsigned char) ((key_56[6] << 1) & 0xFF);
 }
 
-#if defined(USE_OPENSSL) || defined(USE_WOLFSSL)
+#if defined(USE_OPENSSL_DES) || defined(USE_WOLFSSL)
 /*
  * Turns a 56 bit key into the 64 bit, odd parity key and sets the key.  The
  * key schedule ks is also set.
@@ -150,7 +157,7 @@ static void setup_des_key(const unsigned char *key_56,
   DES_set_odd_parity(&key);
 
   /* Set the key */
-  DES_set_key(&key, ks);
+  DES_set_key_unchecked(&key, ks);
 }
 
 #elif defined(USE_GNUTLS)
@@ -362,7 +369,7 @@ void Curl_ntlm_core_lm_resp(const unsigned char *keys,
                             const unsigned char *plaintext,
                             unsigned char *results)
 {
-#if defined(USE_OPENSSL) || defined(USE_WOLFSSL)
+#if defined(USE_OPENSSL_DES) || defined(USE_WOLFSSL)
   DES_key_schedule ks;
 
   setup_des_key(keys, DESKEY(ks));
@@ -420,7 +427,7 @@ CURLcode Curl_ntlm_core_mk_lm_hash(struct Curl_easy *data,
   {
     /* Create LanManager hashed password. */
 
-#if defined(USE_OPENSSL) || defined(USE_WOLFSSL)
+#if defined(USE_OPENSSL_DES) || defined(USE_WOLFSSL)
     DES_key_schedule ks;
 
     setup_des_key(pw, DESKEY(ks));
diff --git a/lib/curl_sasl.c b/lib/curl_sasl.c
index 4a24887..8d39e4f 100644
--- a/lib/curl_sasl.c
+++ b/lib/curl_sasl.c
@@ -56,8 +56,8 @@
 
 /* Supported mechanisms */
 static const struct {
-  const char   *name;  /* Name */
-  size_t        len;   /* Name length */
+  const char    *name;  /* Name */
+  size_t         len;   /* Name length */
   unsigned short bit;   /* Flag bit */
 } mechtable[] = {
   { "LOGIN",        5,  SASL_MECH_LOGIN },
@@ -85,8 +85,11 @@ static const struct {
  * conn     [in]     - The connection data.
  * authused [in]     - The authentication mechanism used.
  */
-void Curl_sasl_cleanup(struct connectdata *conn, unsigned int authused)
+void Curl_sasl_cleanup(struct connectdata *conn, unsigned short authused)
 {
+  (void)conn;
+  (void)authused;
+
 #if defined(USE_KERBEROS5)
   /* Cleanup the gssapi structure */
   if(authused == SASL_MECH_GSSAPI) {
@@ -107,12 +110,6 @@ void Curl_sasl_cleanup(struct connectdata *conn, unsigned int authused)
     Curl_auth_cleanup_ntlm(&conn->ntlm);
   }
 #endif
-
-#if !defined(USE_KERBEROS5) && !defined(USE_NTLM)
-  /* Reserved for future use */
-  (void)conn;
-  (void)authused;
-#endif
 }
 
 /*
@@ -189,16 +186,35 @@ CURLcode Curl_sasl_parse_url_auth_option(struct SASL *sasl,
  *
  * Initializes the SASL structure.
  */
-void Curl_sasl_init(struct SASL *sasl, const struct SASLproto *params)
+void Curl_sasl_init(struct SASL *sasl, struct Curl_easy *data,
+                    const struct SASLproto *params)
 {
+  unsigned long auth = data->set.httpauth;
+
   sasl->params = params;           /* Set protocol dependent parameters */
   sasl->state = SASL_STOP;         /* Not yet running */
+  sasl->curmech = NULL;            /* No mechanism yet. */
   sasl->authmechs = SASL_AUTH_NONE; /* No known authentication mechanism yet */
-  sasl->prefmech = SASL_AUTH_DEFAULT; /* Prefer all mechanisms */
-  sasl->authused = SASL_AUTH_NONE; /* No the authentication mechanism used */
+  sasl->prefmech = params->defmechs; /* Default preferred mechanisms */
+  sasl->authused = SASL_AUTH_NONE; /* The authentication mechanism used */
   sasl->resetprefs = TRUE;         /* Reset prefmech upon AUTH parsing. */
   sasl->mutual_auth = FALSE;       /* No mutual authentication (GSSAPI only) */
   sasl->force_ir = FALSE;          /* Respect external option */
+
+  if(auth != CURLAUTH_BASIC) {
+    sasl->resetprefs = FALSE;
+    sasl->prefmech = SASL_AUTH_NONE;
+    if(auth & CURLAUTH_BASIC)
+      sasl->prefmech |= SASL_MECH_PLAIN | SASL_MECH_LOGIN;
+    if(auth & CURLAUTH_DIGEST)
+      sasl->prefmech |= SASL_MECH_DIGEST_MD5;
+    if(auth & CURLAUTH_NTLM)
+      sasl->prefmech |= SASL_MECH_NTLM;
+    if(auth & CURLAUTH_BEARER)
+      sasl->prefmech |= SASL_MECH_OAUTHBEARER | SASL_MECH_XOAUTH2;
+    if(auth & CURLAUTH_GSSAPI)
+      sasl->prefmech |= SASL_MECH_GSSAPI;
+  }
 }
 
 /*
@@ -247,40 +263,45 @@ static void state(struct SASL *sasl, struct Curl_easy *data,
 static CURLcode get_server_message(struct SASL *sasl, struct Curl_easy *data,
                                    struct bufref *out)
 {
-  unsigned char *msg;
-  size_t msglen;
-  char *serverdata = NULL;
   CURLcode result = CURLE_OK;
 
-  sasl->params->getmessage(data->state.buffer, &serverdata);
-  if(!serverdata)
-    result = CURLE_BAD_CONTENT_ENCODING;
-  else if(!*serverdata || *serverdata == '=')
-    Curl_bufref_set(out, NULL, 0, NULL);
-  else {
-    result = Curl_base64_decode(serverdata, &msg, &msglen);
-    if(!result)
-      Curl_bufref_set(out, msg, msglen, curl_free);
+  result = sasl->params->getmessage(data, out);
+  if(!result && (sasl->params->flags & SASL_FLAG_BASE64)) {
+    unsigned char *msg;
+    size_t msglen;
+    const char *serverdata = (const char *) Curl_bufref_ptr(out);
+
+    if(!*serverdata || *serverdata == '=')
+      Curl_bufref_set(out, NULL, 0, NULL);
+    else {
+      result = Curl_base64_decode(serverdata, &msg, &msglen);
+      if(!result)
+        Curl_bufref_set(out, msg, msglen, curl_free);
+    }
   }
   return result;
 }
 
 /* Encode the outgoing SASL message. */
-static CURLcode build_message(struct Curl_easy *data, struct bufref *msg)
+static CURLcode build_message(struct SASL *sasl, struct Curl_easy *data,
+                              struct bufref *msg)
 {
   CURLcode result = CURLE_OK;
-  char *base64;
-  size_t base64len;
 
-  if(!Curl_bufref_ptr(msg))             /* Empty mesage. */
-    Curl_bufref_set(msg, "", 0, NULL);
-  else if(!Curl_bufref_len(msg))        /* Explicit empty response. */
-    Curl_bufref_set(msg, "=", 1, NULL);
-  else {
-    result = Curl_base64_encode(data, (const char *) Curl_bufref_ptr(msg),
-                                Curl_bufref_len(msg), &base64, &base64len);
-    if(!result)
-      Curl_bufref_set(msg, base64, base64len, curl_free);
+  if(sasl->params->flags & SASL_FLAG_BASE64) {
+    if(!Curl_bufref_ptr(msg))                   /* Empty message. */
+      Curl_bufref_set(msg, "", 0, NULL);
+    else if(!Curl_bufref_len(msg))              /* Explicit empty response. */
+      Curl_bufref_set(msg, "=", 1, NULL);
+    else {
+      char *base64;
+      size_t base64len;
+
+      result = Curl_base64_encode(data, (const char *) Curl_bufref_ptr(msg),
+                                  Curl_bufref_len(msg), &base64, &base64len);
+      if(!result)
+        Curl_bufref_set(msg, base64, base64len, curl_free);
+    }
   }
 
   return result;
@@ -310,11 +331,11 @@ bool Curl_sasl_can_authenticate(struct SASL *sasl, struct connectdata *conn)
  * Calculate the required login details for SASL authentication.
  */
 CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data,
-                         struct connectdata *conn,
                          bool force_ir, saslprogress *progress)
 {
   CURLcode result = CURLE_OK;
-  unsigned int enabledmechs;
+  struct connectdata *conn = data->conn;
+  unsigned short enabledmechs;
   const char *mech = NULL;
   struct bufref resp;
   saslstate state1 = SASL_STOP;
@@ -471,16 +492,16 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data,
   }
 
   if(!result && mech) {
+    sasl->curmech = mech;
     if(Curl_bufref_ptr(&resp))
-      result = build_message(data, &resp);
+      result = build_message(sasl, data, &resp);
 
     if(sasl->params->maxirlen &&
        strlen(mech) + Curl_bufref_len(&resp) > sasl->params->maxirlen)
       Curl_bufref_free(&resp);
 
     if(!result)
-      result = sasl->params->sendauth(data, conn, mech,
-                                      (const char *) Curl_bufref_ptr(&resp));
+      result = sasl->params->sendauth(data, mech, &resp);
 
     if(!result) {
       *progress = SASL_INPROGRESS;
@@ -498,10 +519,10 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data,
  * Continue the authentication.
  */
 CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
-                            struct connectdata *conn,
                             int code, saslprogress *progress)
 {
   CURLcode result = CURLE_OK;
+  struct connectdata *conn = data->conn;
   saslstate newstate = SASL_FINAL;
   struct bufref resp;
   const char * const hostname = SSL_HOST_NAME();
@@ -574,7 +595,8 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
       result = Curl_auth_create_digest_md5_message(data, &serverdata,
                                                    conn->user, conn->passwd,
                                                    service, &resp);
-    newstate = SASL_DIGESTMD5_RESP;
+    if(!result && (sasl->params->flags & SASL_FLAG_BASE64))
+      newstate = SASL_DIGESTMD5_RESP;
     break;
   case SASL_DIGESTMD5_RESP:
     /* Keep response NULL to output an empty line. */
@@ -691,7 +713,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
     sasl->authmechs ^= sasl->authused;
 
     /* Start an alternative SASL authentication */
-    return Curl_sasl_start(sasl, data, conn, sasl->force_ir, progress);
+    return Curl_sasl_start(sasl, data, sasl->force_ir, progress);
   default:
     failf(data, "Unsupported SASL authentication mechanism");
     result = CURLE_UNSUPPORTED_PROTOCOL;  /* Should not happen */
@@ -703,14 +725,13 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
   switch(result) {
   case CURLE_BAD_CONTENT_ENCODING:
     /* Cancel dialog */
-    result = sasl->params->sendcont(data, conn, "*");
+    result = sasl->params->cancelauth(data, sasl->curmech);
     newstate = SASL_CANCEL;
     break;
   case CURLE_OK:
-    result = build_message(data, &resp);
+    result = build_message(sasl, data, &resp);
     if(!result)
-      result = sasl->params->sendcont(data, conn,
-                                      (const char *) Curl_bufref_ptr(&resp));
+      result = sasl->params->contauth(data, sasl->curmech, &resp);
     break;
   default:
     newstate = SASL_STOP;    /* Stop on error */
diff --git a/lib/curl_sasl.h b/lib/curl_sasl.h
index e17d323..91458c7 100644
--- a/lib/curl_sasl.h
+++ b/lib/curl_sasl.h
@@ -24,6 +24,8 @@
 
 #include <curl/curl.h>
 
+#include "bufref.h"
+
 struct Curl_easy;
 struct connectdata;
 
@@ -46,17 +48,20 @@ struct connectdata;
 #define SASL_AUTH_DEFAULT       (SASL_AUTH_ANY & ~SASL_MECH_EXTERNAL)
 
 /* Authentication mechanism strings */
-#define SASL_MECH_STRING_LOGIN        "LOGIN"
-#define SASL_MECH_STRING_PLAIN        "PLAIN"
-#define SASL_MECH_STRING_CRAM_MD5     "CRAM-MD5"
-#define SASL_MECH_STRING_DIGEST_MD5   "DIGEST-MD5"
-#define SASL_MECH_STRING_GSSAPI       "GSSAPI"
-#define SASL_MECH_STRING_EXTERNAL     "EXTERNAL"
-#define SASL_MECH_STRING_NTLM         "NTLM"
-#define SASL_MECH_STRING_XOAUTH2      "XOAUTH2"
-#define SASL_MECH_STRING_OAUTHBEARER  "OAUTHBEARER"
-#define SASL_MECH_STRING_SCRAM_SHA_1  "SCRAM-SHA-1"
-#define SASL_MECH_STRING_SCRAM_SHA_256 "SCRAM-SHA-256"
+#define SASL_MECH_STRING_LOGIN          "LOGIN"
+#define SASL_MECH_STRING_PLAIN          "PLAIN"
+#define SASL_MECH_STRING_CRAM_MD5       "CRAM-MD5"
+#define SASL_MECH_STRING_DIGEST_MD5     "DIGEST-MD5"
+#define SASL_MECH_STRING_GSSAPI         "GSSAPI"
+#define SASL_MECH_STRING_EXTERNAL       "EXTERNAL"
+#define SASL_MECH_STRING_NTLM           "NTLM"
+#define SASL_MECH_STRING_XOAUTH2        "XOAUTH2"
+#define SASL_MECH_STRING_OAUTHBEARER    "OAUTHBEARER"
+#define SASL_MECH_STRING_SCRAM_SHA_1    "SCRAM-SHA-1"
+#define SASL_MECH_STRING_SCRAM_SHA_256  "SCRAM-SHA-256"
+
+/* SASL flags */
+#define SASL_FLAG_BASE64        0x0001  /* Messages are base64-encoded */
 
 /* SASL machine states */
 typedef enum {
@@ -90,30 +95,37 @@ typedef enum {
 /* Protocol dependent SASL parameters */
 struct SASLproto {
   const char *service;     /* The service name */
-  int contcode;            /* Code to receive when continuation is expected */
-  int finalcode;           /* Code to receive upon authentication success */
-  size_t maxirlen;         /* Maximum initial response length */
-  CURLcode (*sendauth)(struct Curl_easy *data,
-                       struct connectdata *conn,
-                       const char *mech, const char *ir);
+  CURLcode (*sendauth)(struct Curl_easy *data, const char *mech,
+                       const struct bufref *ir);
                            /* Send authentication command */
-  CURLcode (*sendcont)(struct Curl_easy *data,
-                       struct connectdata *conn, const char *contauth);
+  CURLcode (*contauth)(struct Curl_easy *data, const char *mech,
+                       const struct bufref *contauth);
                            /* Send authentication continuation */
-  void (*getmessage)(char *buffer, char **outptr);
+  CURLcode (*cancelauth)(struct Curl_easy *data, const char *mech);
+                           /* Cancel authentication. */
+  CURLcode (*getmessage)(struct Curl_easy *data, struct bufref *out);
                            /* Get SASL response message */
+  size_t maxirlen;         /* Maximum initial response + mechanism length,
+                              or zero if no max. This is normally the max
+                              command length - other characters count.
+                              This has to be zero for non-base64 protocols. */
+  int contcode;            /* Code to receive when continuation is expected */
+  int finalcode;           /* Code to receive upon authentication success */
+  unsigned short defmechs; /* Mechanisms enabled by default */
+  unsigned short flags;    /* Configuration flags. */
 };
 
 /* Per-connection parameters */
 struct SASL {
   const struct SASLproto *params; /* Protocol dependent parameters */
-  saslstate state;         /* Current machine state */
+  saslstate state;           /* Current machine state */
+  const char *curmech;       /* Current mechanism id. */
   unsigned short authmechs;  /* Accepted authentication mechanisms */
   unsigned short prefmech;   /* Preferred authentication mechanism */
   unsigned short authused;   /* Auth mechanism used for the connection */
-  bool resetprefs;         /* For URL auth option parsing. */
-  bool mutual_auth;        /* Mutual authentication enabled (GSSAPI only) */
-  bool force_ir;           /* Protocol always supports initial response */
+  bool resetprefs;           /* For URL auth option parsing. */
+  bool mutual_auth;          /* Mutual authentication enabled (GSSAPI only) */
+  bool force_ir;             /* Protocol always supports initial response */
 };
 
 /* This is used to test whether the line starts with the given mechanism */
@@ -123,7 +135,7 @@ struct SASL {
 
 /* This is used to cleanup any libraries or curl modules used by the sasl
    functions */
-void Curl_sasl_cleanup(struct connectdata *conn, unsigned int authused);
+void Curl_sasl_cleanup(struct connectdata *conn, unsigned short authused);
 
 /* Convert a mechanism name to a token */
 unsigned short Curl_sasl_decode_mech(const char *ptr,
@@ -134,19 +146,18 @@ CURLcode Curl_sasl_parse_url_auth_option(struct SASL *sasl,
                                          const char *value, size_t len);
 
 /* Initializes an SASL structure */
-void Curl_sasl_init(struct SASL *sasl, const struct SASLproto *params);
+void Curl_sasl_init(struct SASL *sasl, struct Curl_easy *data,
+                    const struct SASLproto *params);
 
 /* Check if we have enough auth data and capabilities to authenticate */
 bool Curl_sasl_can_authenticate(struct SASL *sasl, struct connectdata *conn);
 
 /* Calculate the required login details for SASL authentication  */
 CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data,
-                         struct connectdata *conn,
                          bool force_ir, saslprogress *progress);
 
 /* Continue an SASL authentication  */
 CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
-                            struct connectdata *conn,
                             int code, saslprogress *progress);
 
 #endif /* HEADER_CURL_SASL_H */
diff --git a/lib/curl_setup.h b/lib/curl_setup.h
index 99048c4..61384cd 100644
--- a/lib/curl_setup.h
+++ b/lib/curl_setup.h
@@ -717,7 +717,6 @@ int netware_init(void);
 #if defined(__LWIP_OPT_H__) || defined(LWIP_HDR_OPT_H)
 #  if defined(SOCKET) || \
      defined(USE_WINSOCK) || \
-     defined(HAVE_WINSOCK_H) || \
      defined(HAVE_WINSOCK2_H) || \
      defined(HAVE_WS2TCPIP_H)
 #    error "WinSock and lwIP TCP/IP stack definitions shall not coexist!"
@@ -839,6 +838,7 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf,
        ADDRESS_FAMILY sun_family;
        char sun_path[UNIX_PATH_MAX];
      } SOCKADDR_UN, *PSOCKADDR_UN;
+#    define WIN32_SOCKADDR_UN
 #  endif
 #endif
 
diff --git a/lib/curl_sha256.h b/lib/curl_sha256.h
index b4579d7..55dc30a 100644
--- a/lib/curl_sha256.h
+++ b/lib/curl_sha256.h
@@ -8,7 +8,7 @@
  *                             \___|\___/|_| \_\_____|
  *
  * Copyright (C) 2017, Florin Petriuc, <petriuc.florin@gmail.com>
- * Copyright (C) 2018 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2018 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -28,10 +28,17 @@
 
 extern const struct HMAC_params Curl_HMAC_SHA256[1];
 
+#ifdef USE_WOLFSSL
+/* SHA256_DIGEST_LENGTH is an enum value in wolfSSL. Need to import it from
+ * sha.h*/
+#include <wolfssl/options.h>
+#include <openssl/sha.h>
+#else
 #define SHA256_DIGEST_LENGTH 32
+#endif
 
-void Curl_sha256it(unsigned char *outbuffer, const unsigned char *input,
-                   const size_t len);
+CURLcode Curl_sha256it(unsigned char *outbuffer, const unsigned char *input,
+                       const size_t len);
 
 #endif
 
diff --git a/lib/curl_sspi.c b/lib/curl_sspi.c
index 06841dd..339bf54 100644
--- a/lib/curl_sspi.c
+++ b/lib/curl_sspi.c
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -83,7 +83,7 @@ CURLcode Curl_sspi_global_init(void)
      * have both these DLLs (security.dll forwards calls to secur32.dll) */
 
     /* Load SSPI dll into the address space of the calling process */
-    if(curlx_verify_windows_version(4, 0, PLATFORM_WINNT, VERSION_EQUAL))
+    if(curlx_verify_windows_version(4, 0, 0, PLATFORM_WINNT, VERSION_EQUAL))
       s_hSecDll = Curl_load_library(TEXT("security.dll"));
     else
       s_hSecDll = Curl_load_library(TEXT("secur32.dll"));
diff --git a/lib/doh.c b/lib/doh.c
index de0c902..d6a2167 100644
--- a/lib/doh.c
+++ b/lib/doh.c
@@ -235,25 +235,6 @@ static CURLcode dohprobe(struct Curl_easy *data,
   p->dnstype = dnstype;
   Curl_dyn_init(&p->serverdoh, DYN_DOH_RESPONSE);
 
-  /* Note: this is code for sending the DoH request with GET but there's still
-     no logic that actually enables this. We should either add that ability or
-     yank out the GET code. Discuss! */
-  if(data->set.doh_get) {
-    char *b64;
-    size_t b64len;
-    result = Curl_base64url_encode(data, (char *)p->dohbuffer, p->dohlen,
-                                   &b64, &b64len);
-    if(result)
-      goto error;
-    nurl = aprintf("%s?dns=%s", url, b64);
-    free(b64);
-    if(!nurl) {
-      result = CURLE_OUT_OF_MEMORY;
-      goto error;
-    }
-    url = nurl;
-  }
-
   timeout_ms = Curl_timeleft(data, NULL, TRUE);
   if(timeout_ms <= 0) {
     result = CURLE_OPERATION_TIMEDOUT;
@@ -268,10 +249,8 @@ static CURLcode dohprobe(struct Curl_easy *data,
     ERROR_CHECK_SETOPT(CURLOPT_URL, url);
     ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_write_cb);
     ERROR_CHECK_SETOPT(CURLOPT_WRITEDATA, resp);
-    if(!data->set.doh_get) {
-      ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDS, p->dohbuffer);
-      ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDSIZE, (long)p->dohlen);
-    }
+    ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDS, p->dohbuffer);
+    ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDSIZE, (long)p->dohlen);
     ERROR_CHECK_SETOPT(CURLOPT_HTTPHEADER, headers);
 #ifdef USE_NGHTTP2
     ERROR_CHECK_SETOPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
diff --git a/lib/easy.c b/lib/easy.c
index 2aca938..20293a7 100644
--- a/lib/easy.c
+++ b/lib/easy.c
@@ -822,7 +822,7 @@ static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src)
 struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data)
 {
   struct Curl_easy *outcurl = calloc(1, sizeof(struct Curl_easy));
-  if(NULL == outcurl)
+  if(!outcurl)
     goto fail;
 
   /*
@@ -1087,14 +1087,16 @@ CURLcode curl_easy_pause(struct Curl_easy *data, int action)
       /* if not pausing again, force a recv/send check of this connection as
          the data might've been read off the socket already */
       data->conn->cselect_bits = CURL_CSELECT_IN | CURL_CSELECT_OUT;
-    if(data->multi)
-      Curl_update_timer(data->multi);
+    if(data->multi) {
+      if(Curl_update_timer(data->multi))
+        return CURLE_ABORTED_BY_CALLBACK;
+    }
   }
 
   if(!data->state.done)
     /* This transfer may have been moved in or out of the bundle, update the
        corresponding socket callback, if used */
-    Curl_updatesocket(data);
+    result = Curl_updatesocket(data);
 
   return result;
 }
diff --git a/lib/easyoptions.c b/lib/easyoptions.c
index 4e65e35..04871ad 100644
--- a/lib/easyoptions.c
+++ b/lib/easyoptions.c
@@ -165,10 +165,12 @@ struct curl_easyoption Curl_easyopts[] = {
   {"MAXCONNECTS", CURLOPT_MAXCONNECTS, CURLOT_LONG, 0},
   {"MAXFILESIZE", CURLOPT_MAXFILESIZE, CURLOT_LONG, 0},
   {"MAXFILESIZE_LARGE", CURLOPT_MAXFILESIZE_LARGE, CURLOT_OFF_T, 0},
+  {"MAXLIFETIME_CONN", CURLOPT_MAXLIFETIME_CONN, CURLOT_LONG, 0},
   {"MAXREDIRS", CURLOPT_MAXREDIRS, CURLOT_LONG, 0},
   {"MAX_RECV_SPEED_LARGE", CURLOPT_MAX_RECV_SPEED_LARGE, CURLOT_OFF_T, 0},
   {"MAX_SEND_SPEED_LARGE", CURLOPT_MAX_SEND_SPEED_LARGE, CURLOT_OFF_T, 0},
   {"MIMEPOST", CURLOPT_MIMEPOST, CURLOT_OBJECT, 0},
+  {"MIME_OPTIONS", CURLOPT_MIME_OPTIONS, CURLOT_LONG, 0},
   {"NETRC", CURLOPT_NETRC, CURLOT_VALUES, 0},
   {"NETRC_FILE", CURLOPT_NETRC_FILE, CURLOT_STRING, 0},
   {"NEW_DIRECTORY_PERMS", CURLOPT_NEW_DIRECTORY_PERMS, CURLOT_LONG, 0},
@@ -192,6 +194,8 @@ struct curl_easyoption Curl_easyopts[] = {
   {"POSTQUOTE", CURLOPT_POSTQUOTE, CURLOT_SLIST, 0},
   {"POSTREDIR", CURLOPT_POSTREDIR, CURLOT_VALUES, 0},
   {"PREQUOTE", CURLOPT_PREQUOTE, CURLOT_SLIST, 0},
+  {"PREREQDATA", CURLOPT_PREREQDATA, CURLOT_CBPTR, 0},
+  {"PREREQFUNCTION", CURLOPT_PREREQFUNCTION, CURLOT_FUNCTION, 0},
   {"PRE_PROXY", CURLOPT_PRE_PROXY, CURLOT_STRING, 0},
   {"PRIVATE", CURLOPT_PRIVATE, CURLOT_OBJECT, 0},
   {"PROGRESSDATA", CURLOPT_XFERINFODATA, CURLOT_CBPTR, CURLOT_FLAG_ALIAS},
@@ -271,6 +275,8 @@ struct curl_easyoption Curl_easyopts[] = {
   {"SSH_COMPRESSION", CURLOPT_SSH_COMPRESSION, CURLOT_LONG, 0},
   {"SSH_HOST_PUBLIC_KEY_MD5", CURLOPT_SSH_HOST_PUBLIC_KEY_MD5,
    CURLOT_STRING, 0},
+  {"SSH_HOST_PUBLIC_KEY_SHA256", CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256,
+   CURLOT_STRING, 0},
   {"SSH_KEYDATA", CURLOPT_SSH_KEYDATA, CURLOT_CBPTR, 0},
   {"SSH_KEYFUNCTION", CURLOPT_SSH_KEYFUNCTION, CURLOT_FUNCTION, 0},
   {"SSH_KNOWNHOSTS", CURLOPT_SSH_KNOWNHOSTS, CURLOT_STRING, 0},
@@ -354,6 +360,6 @@ struct curl_easyoption Curl_easyopts[] = {
  */
 int Curl_easyopts_check(void)
 {
-  return ((CURLOPT_LASTENTRY%10000) != (310 + 1));
+  return ((CURLOPT_LASTENTRY%10000) != (315 + 1));
 }
 #endif
diff --git a/lib/ftp.c b/lib/ftp.c
index 0b9c9b7..f6921e4 100644
--- a/lib/ftp.c
+++ b/lib/ftp.c
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -876,11 +876,6 @@ static CURLcode ftp_state_cwd(struct Curl_easy *data,
 
     ftpc->count2 = 0; /* count2 counts failed CWDs */
 
-    /* count3 is set to allow a MKD to fail once. In the case when first CWD
-       fails and then MKD fails (due to another session raced it to create the
-       dir) this then allows for a second try to CWD to it */
-    ftpc->count3 = (data->set.ftp_create_missing_dirs == 2)?1:0;
-
     if(conn->bits.reuse && ftpc->entrypath &&
        /* no need to go to entrypath when we have an absolute path */
        !(ftpc->dirdepth && ftpc->dirs[0][0] == '/')) {
@@ -1009,7 +1004,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
       }
 
     /* parse the port */
-    if(ip_end != NULL) {
+    if(ip_end) {
       port_start = strchr(ip_end, ':');
       if(port_start) {
         port_min = curlx_ultous(strtoul(port_start + 1, NULL, 10));
@@ -3002,6 +2997,12 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
            ftpc->cwdcount && !ftpc->count2) {
           /* try making it */
           ftpc->count2++; /* counter to prevent CWD-MKD loops */
+
+          /* count3 is set to allow MKD to fail once per dir. In the case when
+          CWD fails and then MKD fails (due to another session raced it to
+          create the dir) this then allows for a second try to CWD to it. */
+          ftpc->count3 = (data->set.ftp_create_missing_dirs == 2) ? 1 : 0;
+
           result = Curl_pp_sendf(data, &ftpc->pp, "MKD %s",
                                  ftpc->dirs[ftpc->cwdcount - 1]);
           if(!result)
@@ -4101,6 +4102,11 @@ static CURLcode ftp_disconnect(struct Curl_easy *data,
   return CURLE_OK;
 }
 
+#ifdef _MSC_VER
+/* warning C4706: assignment within conditional expression */
+#pragma warning(disable:4706)
+#endif
+
 /***********************************************************************
  *
  * ftp_parse_url_path()
@@ -4189,7 +4195,7 @@ CURLcode ftp_parse_url_path(struct Curl_easy *data)
         }
 
         /* parse the URL path into separate path components */
-        while((slashPos = strchr(curPos, '/')) != NULL) {
+        while((slashPos = strchr(curPos, '/'))) {
           size_t compLen = slashPos - curPos;
 
           /* path starts with a slash: add that as a directory */
@@ -4356,7 +4362,7 @@ static CURLcode ftp_setup_connection(struct Curl_easy *data,
   struct FTP *ftp;
 
   data->req.p.ftp = ftp = calloc(sizeof(struct FTP), 1);
-  if(NULL == ftp)
+  if(!ftp)
     return CURLE_OUT_OF_MEMORY;
 
   ftp->path = &data->state.up.path[1]; /* don't include the initial slash */
diff --git a/lib/hash.c b/lib/hash.c
index 12e7aa5..8848906 100644
--- a/lib/hash.c
+++ b/lib/hash.c
@@ -53,32 +53,25 @@ hash_element_dtor(void *user, void *element)
  * @unittest: 1602
  * @unittest: 1603
  */
-int
+void
 Curl_hash_init(struct Curl_hash *h,
                int slots,
                hash_function hfunc,
                comp_function comparator,
                Curl_hash_dtor dtor)
 {
-  if(!slots || !hfunc || !comparator ||!dtor) {
-    return 1; /* failure */
-  }
+  DEBUGASSERT(h);
+  DEBUGASSERT(slots);
+  DEBUGASSERT(hfunc);
+  DEBUGASSERT(comparator);
+  DEBUGASSERT(dtor);
 
+  h->table = NULL;
   h->hash_func = hfunc;
   h->comp_func = comparator;
   h->dtor = dtor;
   h->size = 0;
   h->slots = slots;
-
-  h->table = malloc(slots * sizeof(struct Curl_llist));
-  if(h->table) {
-    int i;
-    for(i = 0; i < slots; ++i)
-      Curl_llist_init(&h->table[i], (Curl_llist_dtor) hash_element_dtor);
-    return 0; /* fine */
-  }
-  h->slots = 0;
-  return 1; /* failure */
 }
 
 static struct Curl_hash_element *
@@ -98,8 +91,9 @@ mk_hash_element(const void *key, size_t key_len, const void *p)
 
 #define FETCH_LIST(x,y,z) &x->table[x->hash_func(y, z, x->slots)]
 
-/* Insert the data in the hash. If there already was a match in the hash,
- * that data is replaced.
+/* Insert the data in the hash. If there already was a match in the hash, that
+ * data is replaced. This function also "lazily" allocates the table if
+ * needed, as it isn't done in the _init function (anymore).
  *
  * @unittest: 1305
  * @unittest: 1602
@@ -110,7 +104,20 @@ Curl_hash_add(struct Curl_hash *h, void *key, size_t key_len, void *p)
 {
   struct Curl_hash_element  *he;
   struct Curl_llist_element *le;
-  struct Curl_llist *l = FETCH_LIST(h, key, key_len);
+  struct Curl_llist *l;
+
+  DEBUGASSERT(h);
+  DEBUGASSERT(h->slots);
+  if(!h->table) {
+    int i;
+    h->table = malloc(h->slots * sizeof(struct Curl_llist));
+    if(!h->table)
+      return NULL; /* OOM */
+    for(i = 0; i < h->slots; ++i)
+      Curl_llist_init(&h->table[i], hash_element_dtor);
+  }
+
+  l = FETCH_LIST(h, key, key_len);
 
   for(le = l->head; le; le = le->next) {
     he = (struct Curl_hash_element *) le->ptr;
@@ -139,14 +146,20 @@ Curl_hash_add(struct Curl_hash *h, void *key, size_t key_len, void *p)
 int Curl_hash_delete(struct Curl_hash *h, void *key, size_t key_len)
 {
   struct Curl_llist_element *le;
-  struct Curl_llist *l = FETCH_LIST(h, key, key_len);
+  struct Curl_llist *l;
 
-  for(le = l->head; le; le = le->next) {
-    struct Curl_hash_element *he = le->ptr;
-    if(h->comp_func(he->key, he->key_len, key, key_len)) {
-      Curl_llist_remove(l, le, (void *) h);
-      --h->size;
-      return 0;
+  DEBUGASSERT(h);
+  DEBUGASSERT(h->slots);
+  if(h->table) {
+    l = FETCH_LIST(h, key, key_len);
+
+    for(le = l->head; le; le = le->next) {
+      struct Curl_hash_element *he = le->ptr;
+      if(h->comp_func(he->key, he->key_len, key, key_len)) {
+        Curl_llist_remove(l, le, (void *) h);
+        --h->size;
+        return 0;
+      }
     }
   }
   return 1;
@@ -162,7 +175,9 @@ Curl_hash_pick(struct Curl_hash *h, void *key, size_t key_len)
   struct Curl_llist_element *le;
   struct Curl_llist *l;
 
-  if(h) {
+  DEBUGASSERT(h);
+  if(h->table) {
+    DEBUGASSERT(h->slots);
     l = FETCH_LIST(h, key, key_len);
     for(le = l->head; le; le = le->next) {
       struct Curl_hash_element *he = le->ptr;
@@ -204,13 +219,13 @@ Curl_hash_apply(Curl_hash *h, void *user,
 void
 Curl_hash_destroy(struct Curl_hash *h)
 {
-  int i;
-
-  for(i = 0; i < h->slots; ++i) {
-    Curl_llist_destroy(&h->table[i], (void *) h);
+  if(h->table) {
+    int i;
+    for(i = 0; i < h->slots; ++i) {
+      Curl_llist_destroy(&h->table[i], (void *) h);
+    }
+    Curl_safefree(h->table);
   }
-
-  Curl_safefree(h->table);
   h->size = 0;
   h->slots = 0;
 }
@@ -235,7 +250,7 @@ Curl_hash_clean_with_criterium(struct Curl_hash *h, void *user,
   struct Curl_llist *list;
   int i;
 
-  if(!h)
+  if(!h || !h->table)
     return;
 
   for(i = 0; i < h->slots; ++i) {
@@ -290,6 +305,9 @@ Curl_hash_next_element(struct Curl_hash_iterator *iter)
 {
   struct Curl_hash *h = iter->hash;
 
+  if(!h->table)
+    return NULL; /* empty hash, nothing to return */
+
   /* Get the next element in the current list, if any */
   if(iter->current_element)
     iter->current_element = iter->current_element->next;
diff --git a/lib/hash.h b/lib/hash.h
index b7f828e..e166916 100644
--- a/lib/hash.h
+++ b/lib/hash.h
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -69,11 +69,11 @@ struct Curl_hash_iterator {
   struct Curl_llist_element *current_element;
 };
 
-int Curl_hash_init(struct Curl_hash *h,
-                   int slots,
-                   hash_function hfunc,
-                   comp_function comparator,
-                   Curl_hash_dtor dtor);
+void Curl_hash_init(struct Curl_hash *h,
+                    int slots,
+                    hash_function hfunc,
+                    comp_function comparator,
+                    Curl_hash_dtor dtor);
 
 void *Curl_hash_add(struct Curl_hash *h, void *key, size_t key_len, void *p);
 int Curl_hash_delete(struct Curl_hash *h, void *key, size_t key_len);
diff --git a/lib/hostcheck.c b/lib/hostcheck.c
index cf267a7..cd45bd0 100644
--- a/lib/hostcheck.c
+++ b/lib/hostcheck.c
@@ -89,7 +89,7 @@ static int hostmatch(char *hostname, char *pattern)
      match. */
   wildcard_enabled = 1;
   pattern_label_end = strchr(pattern, '.');
-  if(!pattern_label_end || strchr(pattern_label_end + 1, '.') == NULL ||
+  if(!pattern_label_end || !strchr(pattern_label_end + 1, '.') ||
      pattern_wildcard > pattern_label_end ||
      strncasecompare(pattern, "xn--", 4)) {
     wildcard_enabled = 0;
diff --git a/lib/hostip.c b/lib/hostip.c
index 117caa2..911d5ed 100644
--- a/lib/hostip.c
+++ b/lib/hostip.c
@@ -507,9 +507,6 @@ static struct Curl_addrinfo *get_localhost(int port)
   struct sockaddr_in sa;
   unsigned int ipv4;
   unsigned short port16 = (unsigned short)(port & 0xffff);
-  ca = calloc(sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1, 1);
-  if(!ca)
-    return NULL;
 
   /* memset to clear the sa.sin_zero field */
   memset(&sa, 0, sizeof(sa));
@@ -519,6 +516,9 @@ static struct Curl_addrinfo *get_localhost(int port)
     return NULL;
   memcpy(&sa.sin_addr, &ipv4, sizeof(ipv4));
 
+  ca = calloc(sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1, 1);
+  if(!ca)
+    return NULL;
   ca->ai_flags     = 0;
   ca->ai_family    = AF_INET;
   ca->ai_socktype  = SOCK_STREAM;
@@ -609,7 +609,11 @@ enum resolve_t Curl_resolv(struct Curl_easy *data,
   enum resolve_t rc = CURLRESOLV_ERROR; /* default to failure */
   struct connectdata *conn = data->conn;
   *entry = NULL;
+#ifndef CURL_DISABLE_DOH
   conn->bits.doh = FALSE; /* default is not */
+#else
+  (void)allowDOH;
+#endif
 
   if(data->share)
     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
@@ -630,11 +634,15 @@ enum resolve_t Curl_resolv(struct Curl_easy *data,
 
     struct Curl_addrinfo *addr = NULL;
     int respwait = 0;
+#if !defined(CURL_DISABLE_DOH) || !defined(USE_RESOLVE_ON_IPS)
     struct in_addr in;
+#endif
+#ifndef CURL_DISABLE_DOH
 #ifndef USE_RESOLVE_ON_IPS
     const
 #endif
       bool ipnum = FALSE;
+#endif
 
     /* notify the resolver start callback */
     if(data->set.resolver_start) {
@@ -686,6 +694,7 @@ enum resolve_t Curl_resolv(struct Curl_easy *data,
 #endif /* ENABLE_IPV6 */
 
 #else /* if USE_RESOLVE_ON_IPS */
+#ifndef CURL_DISABLE_DOH
     /* First check if this is an IPv4 address string */
     if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
       /* This is a dotted IP address 123.123.123.123-style */
@@ -699,6 +708,7 @@ enum resolve_t Curl_resolv(struct Curl_easy *data,
         ipnum = TRUE;
     }
 #endif /* ENABLE_IPV6 */
+#endif /* CURL_DISABLE_DOH */
 
 #endif /* !USE_RESOLVE_ON_IPS */
 
@@ -708,8 +718,10 @@ enum resolve_t Curl_resolv(struct Curl_easy *data,
 
       if(strcasecompare(hostname, "localhost"))
         addr = get_localhost(port);
+#ifndef CURL_DISABLE_DOH
       else if(allowDOH && data->set.doh && !ipnum)
         addr = Curl_doh(data, hostname, port, &respwait);
+#endif
       else {
         /* Check what IP specifics the app has requested and if we can provide
          * it. If not, bail out. */
@@ -977,12 +989,12 @@ static void freednsentry(void *freethis)
 }
 
 /*
- * Curl_mk_dnscache() inits a new DNS cache and returns success/failure.
+ * Curl_init_dnscache() inits a new DNS cache.
  */
-int Curl_mk_dnscache(struct Curl_hash *hash)
+void Curl_init_dnscache(struct Curl_hash *hash)
 {
-  return Curl_hash_init(hash, 7, Curl_hash_str, Curl_str_key_compare,
-                        freednsentry);
+  Curl_hash_init(hash, 7, Curl_hash_str, Curl_str_key_compare,
+                 freednsentry);
 }
 
 /*
@@ -1210,9 +1222,10 @@ CURLcode Curl_resolv_check(struct Curl_easy *data,
 #if defined(CURL_DISABLE_DOH) && !defined(CURLRES_ASYNCH)
   (void)dns;
 #endif
-
+#ifndef CURL_DISABLE_DOH
   if(data->conn->bits.doh)
     return Curl_doh_is_resolved(data, dns);
+#endif
   return Curl_resolver_is_resolved(data, dns);
 }
 
@@ -1220,10 +1233,12 @@ int Curl_resolv_getsock(struct Curl_easy *data,
                         curl_socket_t *socks)
 {
 #ifdef CURLRES_ASYNCH
+#ifndef CURL_DISABLE_DOH
   if(data->conn->bits.doh)
     /* nothing to wait for during DoH resolve, those handles have their own
        sockets */
     return GETSOCK_BLANK;
+#endif
   return Curl_resolver_getsock(data, socks);
 #else
   (void)data;
diff --git a/lib/hostip.h b/lib/hostip.h
index 67a688a..1db5981 100644
--- a/lib/hostip.h
+++ b/lib/hostip.h
@@ -129,8 +129,8 @@ struct Curl_addrinfo *Curl_getaddrinfo(struct Curl_easy *data,
 void Curl_resolv_unlock(struct Curl_easy *data,
                         struct Curl_dns_entry *dns);
 
-/* init a new dns cache and return success */
-int Curl_mk_dnscache(struct Curl_hash *hash);
+/* init a new dns cache */
+void Curl_init_dnscache(struct Curl_hash *hash);
 
 /* prune old entries from the DNS cache */
 void Curl_hostcache_prune(struct Curl_easy *data);
diff --git a/lib/http.c b/lib/http.c
index 648583c..f08a343 100644
--- a/lib/http.c
+++ b/lib/http.c
@@ -323,7 +323,7 @@ static CURLcode http_output_basic(struct Curl_easy *data, bool proxy)
     pwd = data->state.aptr.passwd;
   }
 
-  out = aprintf("%s:%s", user, pwd ? pwd : "");
+  out = aprintf("%s:%s", user ? user : "", pwd ? pwd : "");
   if(!out)
     return CURLE_OUT_OF_MEMORY;
 
@@ -1153,7 +1153,6 @@ static bool http_should_fail(struct Curl_easy *data)
   return data->state.authproblem;
 }
 
-#ifndef USE_HYPER
 /*
  * readmoredata() is a "fread() emulation" to provide POST and/or request
  * data. It is used when a huge POST is to be made and the entire chunk wasn't
@@ -1412,8 +1411,6 @@ CURLcode Curl_buffer_send(struct dynbuf *in,
   return result;
 }
 
-#endif
-
 /* end of the add_buffer functions */
 /* ------------------------------------------------------------------------- */
 
@@ -2375,6 +2372,9 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
 #ifndef USE_HYPER
   /* Hyper always handles the body separately */
   curl_off_t included_body = 0;
+#else
+  /* from this point down, this function should not be used */
+#define Curl_buffer_send(a,b,c,d,e) CURLE_OK
 #endif
   CURLcode result = CURLE_OK;
   struct HTTP *http = data->req.p.http;
@@ -2685,7 +2685,6 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
     /* issue the request */
     result = Curl_buffer_send(r, data, &data->info.request_size, 0,
                               FIRSTSOCKET);
-
     if(result)
       failf(data, "Failed sending HTTP request");
     else
@@ -2902,20 +2901,6 @@ CURLcode Curl_http_firstwrite(struct Curl_easy *data,
                               bool *done)
 {
   struct SingleRequest *k = &data->req;
-  DEBUGASSERT(conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_RTSP));
-  if(data->req.ignore_cl) {
-    k->size = k->maxdownload = -1;
-  }
-  else if(k->size != -1) {
-    /* We wait until after all headers have been received to set this so that
-       we know for sure Content-Length is valid. */
-    if(data->set.max_filesize &&
-       k->size > data->set.max_filesize) {
-      failf(data, "Maximum file size exceeded");
-      return CURLE_FILESIZE_EXCEEDED;
-    }
-    Curl_pgrsSetDownloadSize(data, k->size);
-  }
 
   if(data->req.newurl) {
     if(conn->bits.close) {
@@ -3326,7 +3311,7 @@ checkhttpprefix(struct Curl_easy *data,
 #ifdef CURL_DOES_CONVERSIONS
   /* convert from the network encoding using a scratch area */
   char *scratch = strdup(s);
-  if(NULL == scratch) {
+  if(!scratch) {
     failf(data, "Failed to allocate memory for conversion!");
     return FALSE; /* can't return CURLE_OUT_OF_MEMORY so return FALSE */
   }
@@ -3366,7 +3351,7 @@ checkrtspprefix(struct Curl_easy *data,
 #ifdef CURL_DOES_CONVERSIONS
   /* convert from the network encoding using a scratch area */
   char *scratch = strdup(s);
-  if(NULL == scratch) {
+  if(!scratch) {
     failf(data, "Failed to allocate memory for conversion!");
     return FALSE; /* can't return CURLE_OUT_OF_MEMORY so return FALSE */
   }
@@ -3787,6 +3772,29 @@ CURLcode Curl_http_statusline(struct Curl_easy *data,
   return CURLE_OK;
 }
 
+/* Content-Length must be ignored if any Transfer-Encoding is present in the
+   response. Refer to RFC 7230 section 3.3.3 and RFC2616 section 4.4.  This is
+   figured out here after all headers have been received but before the final
+   call to the user's header callback, so that a valid content length can be
+   retrieved by the user in the final call. */
+CURLcode Curl_http_size(struct Curl_easy *data)
+{
+  struct SingleRequest *k = &data->req;
+  if(data->req.ignore_cl || k->chunk) {
+    k->size = k->maxdownload = -1;
+  }
+  else if(k->size != -1) {
+    if(data->set.max_filesize &&
+       k->size > data->set.max_filesize) {
+      failf(data, "Maximum file size exceeded");
+      return CURLE_FILESIZE_EXCEEDED;
+    }
+    Curl_pgrsSetDownloadSize(data, k->size);
+    k->maxdownload = k->size;
+  }
+  return CURLE_OK;
+}
+
 /*
  * Read any HTTP header lines from the server and pass them to the client app.
  */
@@ -3981,6 +3989,12 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
         }
       }
 
+      if(!k->header) {
+        result = Curl_http_size(data);
+        if(result)
+          return result;
+      }
+
       /* At this point we have some idea about the fate of the connection.
          If we are closing the connection it may result auth failure. */
 #if defined(USE_NTLM)
@@ -4137,31 +4151,6 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
              reason */
           *stop_reading = TRUE;
 #endif
-        else {
-          /* If we know the expected size of this document, we set the
-             maximum download size to the size of the expected
-             document or else, we won't know when to stop reading!
-
-             Note that we set the download maximum even if we read a
-             "Connection: close" header, to make sure that
-             "Content-Length: 0" still prevents us from attempting to
-             read the (missing) response-body.
-          */
-          /* According to RFC2616 section 4.4, we MUST ignore
-             Content-Length: headers if we are now receiving data
-             using chunked Transfer-Encoding.
-          */
-          if(k->chunk)
-            k->maxdownload = k->size = -1;
-        }
-        if(-1 != k->size) {
-          /* We do this operation even if no_body is true, since this
-             data might be retrieved later with curl_easy_getinfo()
-             and its CURLINFO_CONTENT_LENGTH_DOWNLOAD option. */
-
-          Curl_pgrsSetDownloadSize(data, k->size);
-          k->maxdownload = k->size;
-        }
 
         /* If max download size is *zero* (nothing) we already have
            nothing and can safely return ok now!  But for HTTP/2, we'd
@@ -4250,8 +4239,12 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
 
         /* There can only be a 4th response code digit stored in 'digit4' if
            all the other fields were parsed and stored first, so nc is 5 when
-           digit4 a digit */
-        else if(ISDIGIT(digit4)) {
+           digit4 a digit.
+
+           The sscanf() line above will also allow zero-prefixed and negative
+           numbers, so we check for that too here.
+        */
+        else if(ISDIGIT(digit4) || (nc >= 4 && k->httpcode < 100)) {
           failf(data, "Unsupported response code in HTTP response");
           return CURLE_UNSUPPORTED_PROTOCOL;
         }
diff --git a/lib/http.h b/lib/http.h
index e4ab466..b4aaba2 100644
--- a/lib/http.h
+++ b/lib/http.h
@@ -54,15 +54,11 @@ char *Curl_copy_header_value(const char *header);
 char *Curl_checkProxyheaders(struct Curl_easy *data,
                              const struct connectdata *conn,
                              const char *thisheader);
-#ifndef USE_HYPER
 CURLcode Curl_buffer_send(struct dynbuf *in,
                           struct Curl_easy *data,
                           curl_off_t *bytes_written,
                           curl_off_t included_body_bytes,
                           int socketindex);
-#else
-#define Curl_buffer_send(a,b,c,d,e) CURLE_OK
-#endif
 
 CURLcode Curl_add_timecondition(struct Curl_easy *data,
 #ifndef USE_HYPER
@@ -289,6 +285,8 @@ struct http_conn {
 #endif
 };
 
+CURLcode Curl_http_size(struct Curl_easy *data);
+
 CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
                                      struct connectdata *conn,
                                      ssize_t *nread,
diff --git a/lib/http2.c b/lib/http2.c
index 6d63f43..e74400a 100644
--- a/lib/http2.c
+++ b/lib/http2.c
@@ -100,6 +100,7 @@ static int http2_getsock(struct Curl_easy *data,
   const struct http_conn *c = &conn->proto.httpc;
   struct SingleRequest *k = &data->req;
   int bitmap = GETSOCK_BLANK;
+  struct HTTP *stream = data->req.p.http;
 
   sock[0] = conn->sock[FIRSTSOCKET];
 
@@ -108,9 +109,13 @@ static int http2_getsock(struct Curl_easy *data,
        frame so we should always be ready for one */
     bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
 
-  /* we're still uploading or the HTTP/2 layer wants to send data */
-  if(((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND) ||
-     nghttp2_session_want_write(c->h2))
+  /* we're (still uploading OR the HTTP/2 layer wants to send data) AND
+     there's a window to send data in */
+  if((((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND) ||
+      nghttp2_session_want_write(c->h2)) &&
+     (nghttp2_session_get_remote_window_size(c->h2) &&
+      nghttp2_session_get_stream_remote_window_size(c->h2,
+                                                    stream->stream_id)))
     bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
 
   return bitmap;
@@ -500,10 +505,13 @@ static int set_transfer_url(struct Curl_easy *data,
                             struct curl_pushheaders *hp)
 {
   const char *v;
-  CURLU *u = curl_url();
   CURLUcode uc;
   char *url = NULL;
   int rc = 0;
+  CURLU *u = curl_url();
+
+  if(!u)
+    return 5;
 
   v = curl_pushheader_byname(hp, ":scheme");
   if(v) {
@@ -1953,8 +1961,19 @@ static ssize_t http2_send(struct Curl_easy *data, int sockindex,
       nghttp2_session_resume_data(h2, stream->stream_id);
     }
 
-    H2BUGF(infof(data, "http2_send returns %zu for stream %u", len,
-                 stream->stream_id));
+#ifdef DEBUG_HTTP2
+    if(!len) {
+      infof(data, "http2_send: easy %p (stream %u) win %u/%u",
+            data, stream->stream_id,
+            nghttp2_session_get_remote_window_size(httpc->h2),
+            nghttp2_session_get_stream_remote_window_size(httpc->h2,
+                                                          stream->stream_id)
+        );
+
+    }
+    infof(data, "http2_send returns %zu for stream %u", len,
+          stream->stream_id);
+#endif
     return len;
   }
 
diff --git a/lib/http_aws_sigv4.c b/lib/http_aws_sigv4.c
index 02663ab..751e5af 100644
--- a/lib/http_aws_sigv4.c
+++ b/lib/http_aws_sigv4.c
@@ -92,6 +92,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
   char *signed_headers = NULL;
   Curl_HttpReq httpreq;
   const char *method;
+  size_t post_data_len;
   const char *post_data = data->set.postfields ? data->set.postfields : "";
   unsigned char sha_hash[32];
   char sha_hex[65];
@@ -281,8 +282,15 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
     goto fail;
   }
 
-  Curl_sha256it(sha_hash,
-                (const unsigned char *) post_data, strlen(post_data));
+  if(data->set.postfieldsize < 0)
+    post_data_len = strlen(post_data);
+  else
+    post_data_len = (size_t)data->set.postfieldsize;
+  if(Curl_sha256it(sha_hash, (const unsigned char *) post_data,
+                   post_data_len)) {
+    goto fail;
+  }
+
   sha256_to_hex(sha_hex, sha_hash, sizeof(sha_hex));
 
   Curl_http_method(data, conn, &method, &httpreq);
@@ -315,13 +323,16 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
     goto fail;
   }
 
-  Curl_sha256it(sha_hash, (unsigned char *) canonical_request,
-                strlen(canonical_request));
+  if(Curl_sha256it(sha_hash, (unsigned char *) canonical_request,
+                   strlen(canonical_request))) {
+    goto fail;
+  }
+
   sha256_to_hex(sha_hex, sha_hash, sizeof(sha_hex));
 
   /*
    * Google allow to use rsa key instead of HMAC, so this code might change
-   * In the furure, but for now we support only HMAC version
+   * In the future, but for now we support only HMAC version
    */
   str_to_sign = curl_maprintf("%s4-HMAC-SHA256\n" /* Algorithm */
                               "%s\n" /* RequestDateTime */
diff --git a/lib/http_ntlm.c b/lib/http_ntlm.c
index 627a11c..a6526db 100644
--- a/lib/http_ntlm.c
+++ b/lib/http_ntlm.c
@@ -198,6 +198,12 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy)
 #endif
 
   Curl_bufref_init(&ntlmmsg);
+
+  /* connection is already authenticated, don't send a header in future
+   * requests so go directly to NTLMSTATE_LAST */
+  if(*state == NTLMSTATE_TYPE3)
+    *state = NTLMSTATE_LAST;
+
   switch(*state) {
   case NTLMSTATE_TYPE1:
   default: /* for the weird cases we (re)start here */
@@ -246,11 +252,6 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy)
     }
     break;
 
-  case NTLMSTATE_TYPE3:
-    /* connection is already authenticated,
-     * don't send a header in future requests */
-    *state = NTLMSTATE_LAST;
-    /* FALLTHROUGH */
   case NTLMSTATE_LAST:
     Curl_safefree(*allocuserpwd);
     authp->done = TRUE;
diff --git a/lib/http_proxy.c b/lib/http_proxy.c
index 58489ab..e13f485 100644
--- a/lib/http_proxy.c
+++ b/lib/http_proxy.c
@@ -158,6 +158,10 @@ static CURLcode connect_init(struct Curl_easy *data, bool reinit)
 {
   struct http_connect_state *s;
   struct connectdata *conn = data->conn;
+  if(conn->handler->flags & PROTOPT_NOTCPPROXY) {
+    failf(data, "%s cannot be done over CONNECT", conn->handler->scheme);
+    return CURLE_UNSUPPORTED_PROTOCOL;
+  }
   if(!reinit) {
     CURLcode result;
     DEBUGASSERT(!conn->connect_state);
@@ -198,18 +202,25 @@ static CURLcode connect_init(struct Curl_easy *data, bool reinit)
   return CURLE_OK;
 }
 
-static void connect_done(struct Curl_easy *data)
+void Curl_connect_done(struct Curl_easy *data)
 {
   struct connectdata *conn = data->conn;
   struct http_connect_state *s = conn->connect_state;
-  if(s->tunnel_state != TUNNEL_EXIT) {
+  if(s && (s->tunnel_state != TUNNEL_EXIT)) {
     s->tunnel_state = TUNNEL_EXIT;
     Curl_dyn_free(&s->rcvbuf);
     Curl_dyn_free(&s->req);
 
-    /* retore the protocol pointer */
-    data->req.p.http = s->prot_save;
+    /* restore the protocol pointer, if not already done */
+    if(s->prot_save)
+      data->req.p.http = s->prot_save;
     s->prot_save = NULL;
+    data->info.httpcode = 0; /* clear it as it might've been used for the
+                                proxy */
+    data->req.ignorebody = FALSE;
+#ifdef USE_HYPER
+    data->state.hconnect = FALSE;
+#endif
     infof(data, "CONNECT phase completed!");
   }
 }
@@ -284,8 +295,7 @@ static CURLcode CONNECT(struct Curl_easy *data,
         /* This only happens if we've looped here due to authentication
            reasons, and we don't really use the newly cloned URL here
            then. Just free() it. */
-      free(data->req.newurl);
-      data->req.newurl = NULL;
+      Curl_safefree(data->req.newurl);
 
       /* initialize send-buffer */
       Curl_dyn_init(req, DYN_HTTP_REQUEST);
@@ -657,15 +667,13 @@ static CURLcode CONNECT(struct Curl_easy *data,
     if(s->close_connection && data->req.newurl) {
       conn->bits.proxy_connect_closed = TRUE;
       infof(data, "Connect me again please");
-      connect_done(data);
+      Curl_connect_done(data);
     }
     else {
       free(data->req.newurl);
       data->req.newurl = NULL;
       /* failure, close this connection to avoid re-use */
       streamclose(conn, "proxy CONNECT failure");
-      Curl_closesocket(data, conn, conn->sock[sockindex]);
-      conn->sock[sockindex] = CURL_SOCKET_BAD;
     }
 
     /* to back to init state */
@@ -735,6 +743,7 @@ static CURLcode CONNECT(struct Curl_easy *data,
       io = hyper_io_new();
       if(!io) {
         failf(data, "Couldn't create hyper IO");
+        result = CURLE_OUT_OF_MEMORY;
         goto error;
       }
       /* tell Hyper how to read/write network data */
@@ -750,6 +759,7 @@ static CURLcode CONNECT(struct Curl_easy *data,
         h->exec = hyper_executor_new();
         if(!h->exec) {
           failf(data, "Couldn't create hyper executor");
+          result = CURLE_OUT_OF_MEMORY;
           goto error;
         }
       }
@@ -757,6 +767,7 @@ static CURLcode CONNECT(struct Curl_easy *data,
       options = hyper_clientconn_options_new();
       if(!options) {
         failf(data, "Couldn't create hyper client options");
+        result = CURLE_OUT_OF_MEMORY;
         goto error;
       }
 
@@ -767,6 +778,7 @@ static CURLcode CONNECT(struct Curl_easy *data,
       handshake = hyper_clientconn_handshake(io, options);
       if(!handshake) {
         failf(data, "Couldn't create hyper client handshake");
+        result = CURLE_OUT_OF_MEMORY;
         goto error;
       }
       io = NULL;
@@ -774,6 +786,7 @@ static CURLcode CONNECT(struct Curl_easy *data,
 
       if(HYPERE_OK != hyper_executor_push(h->exec, handshake)) {
         failf(data, "Couldn't hyper_executor_push the handshake");
+        result = CURLE_OUT_OF_MEMORY;
         goto error;
       }
       handshake = NULL; /* ownership passed on */
@@ -781,6 +794,7 @@ static CURLcode CONNECT(struct Curl_easy *data,
       task = hyper_executor_poll(h->exec);
       if(!task) {
         failf(data, "Couldn't hyper_executor_poll the handshake");
+        result = CURLE_OUT_OF_MEMORY;
         goto error;
       }
 
@@ -789,14 +803,24 @@ static CURLcode CONNECT(struct Curl_easy *data,
       req = hyper_request_new();
       if(!req) {
         failf(data, "Couldn't hyper_request_new");
+        result = CURLE_OUT_OF_MEMORY;
         goto error;
       }
       if(hyper_request_set_method(req, (uint8_t *)"CONNECT",
                                   strlen("CONNECT"))) {
         failf(data, "error setting method");
+        result = CURLE_OUT_OF_MEMORY;
         goto error;
       }
 
+      infof(data, "Establish HTTP proxy tunnel to %s:%d",
+            hostname, remote_port);
+
+        /* This only happens if we've looped here due to authentication
+           reasons, and we don't really use the newly cloned URL here
+           then. Just free() it. */
+      Curl_safefree(data->req.newurl);
+
       result = CONNECT_host(data, conn, hostname, remote_port,
                             &hostheader, &host);
       if(result)
@@ -806,6 +830,16 @@ static CURLcode CONNECT(struct Curl_easy *data,
                                strlen(hostheader))) {
         failf(data, "error setting path");
         result = CURLE_OUT_OF_MEMORY;
+        goto error;
+      }
+      if(data->set.verbose) {
+        char *se = aprintf("CONNECT %s HTTP/1.1\r\n", hostheader);
+        if(!se) {
+          result = CURLE_OUT_OF_MEMORY;
+          goto error;
+        }
+        Curl_debug(data, CURLINFO_HEADER_OUT, se, strlen(se));
+        free(se);
       }
       /* Setup the proxy-authorization header, if any */
       result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET,
@@ -819,21 +853,29 @@ static CURLcode CONNECT(struct Curl_easy *data,
          (HYPERE_OK != hyper_request_set_version(req,
                                                  HYPER_HTTP_VERSION_1_0))) {
         failf(data, "error setting HTTP version");
+        result = CURLE_OUT_OF_MEMORY;
         goto error;
       }
 
       headers = hyper_request_headers(req);
       if(!headers) {
         failf(data, "hyper_request_headers");
+        result = CURLE_OUT_OF_MEMORY;
         goto error;
       }
-      if(host && Curl_hyper_header(data, headers, host))
-        goto error;
-      Curl_safefree(host);
+      if(host) {
+        result = Curl_hyper_header(data, headers, host);
+        if(result)
+          goto error;
+        Curl_safefree(host);
+      }
 
-      if(data->state.aptr.proxyuserpwd &&
-         Curl_hyper_header(data, headers, data->state.aptr.proxyuserpwd))
-        goto error;
+      if(data->state.aptr.proxyuserpwd) {
+        result = Curl_hyper_header(data, headers,
+                                   data->state.aptr.proxyuserpwd);
+        if(result)
+          goto error;
+      }
 
       if(!Curl_checkProxyheaders(data, conn, "User-Agent") &&
          data->set.str[STRING_USERAGENT]) {
@@ -843,26 +885,33 @@ static CURLcode CONNECT(struct Curl_easy *data,
                                data->set.str[STRING_USERAGENT]);
         if(result)
           goto error;
-        if(Curl_hyper_header(data, headers, Curl_dyn_ptr(&ua)))
+        result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&ua));
+        if(result)
           goto error;
         Curl_dyn_free(&ua);
       }
 
-      if(!Curl_checkProxyheaders(data, conn, "Proxy-Connection") &&
-         Curl_hyper_header(data, headers, "Proxy-Connection: Keep-Alive"))
-        goto error;
+      if(!Curl_checkProxyheaders(data, conn, "Proxy-Connection")) {
+        result = Curl_hyper_header(data, headers,
+                                   "Proxy-Connection: Keep-Alive");
+        if(result)
+          goto error;
+      }
 
-      if(Curl_add_custom_headers(data, TRUE, headers))
+      result = Curl_add_custom_headers(data, TRUE, headers);
+      if(result)
         goto error;
 
       sendtask = hyper_clientconn_send(client, req);
       if(!sendtask) {
         failf(data, "hyper_clientconn_send");
+        result = CURLE_OUT_OF_MEMORY;
         goto error;
       }
 
       if(HYPERE_OK != hyper_executor_push(h->exec, sendtask)) {
         failf(data, "Couldn't hyper_executor_push the send");
+        result = CURLE_OUT_OF_MEMORY;
         goto error;
       }
 
@@ -875,8 +924,11 @@ static CURLcode CONNECT(struct Curl_easy *data,
           if(error)
             hypererr = hyper_task_value(task);
           hyper_task_free(task);
-          if(error)
+          if(error) {
+            /* this could probably use a better error code? */
+            result = CURLE_OUT_OF_MEMORY;
             goto error;
+          }
         }
       } while(task);
       s->tunnel_state = TUNNEL_CONNECT;
@@ -904,20 +956,28 @@ static CURLcode CONNECT(struct Curl_easy *data,
         h->write_waker = NULL;
       }
     }
-      /* FALLTHROUGH */
+    break;
+
     default:
       break;
     }
+
+    /* If we are supposed to continue and request a new URL, which basically
+     * means the HTTP authentication is still going on so if the tunnel
+     * is complete we start over in INIT state */
+    if(data->req.newurl && (TUNNEL_COMPLETE == s->tunnel_state)) {
+      infof(data, "CONNECT request done, loop to make another");
+      connect_init(data, TRUE); /* reinit */
+    }
   } while(data->req.newurl);
 
   result = CURLE_OK;
   if(s->tunnel_state == TUNNEL_COMPLETE) {
-    data->info.httpproxycode = data->req.httpcode;
     if(data->info.httpproxycode/100 != 2) {
       if(conn->bits.close && data->req.newurl) {
         conn->bits.proxy_connect_closed = TRUE;
         infof(data, "Connect me again please");
-        connect_done(data);
+        Curl_connect_done(data);
       }
       else {
         free(data->req.newurl);
@@ -991,7 +1051,7 @@ CURLcode Curl_proxyCONNECT(struct Curl_easy *data,
   result = CONNECT(data, sockindex, hostname, remote_port);
 
   if(result || Curl_connect_complete(conn))
-    connect_done(data);
+    Curl_connect_done(data);
 
   return result;
 }
diff --git a/lib/http_proxy.h b/lib/http_proxy.h
index cdf8de4..2820e11 100644
--- a/lib/http_proxy.h
+++ b/lib/http_proxy.h
@@ -39,6 +39,7 @@ CURLcode Curl_proxy_connect(struct Curl_easy *data, int sockindex);
 bool Curl_connect_complete(struct connectdata *conn);
 bool Curl_connect_ongoing(struct connectdata *conn);
 int Curl_connect_getsock(struct connectdata *conn);
+void Curl_connect_done(struct Curl_easy *data);
 
 #else
 #define Curl_proxyCONNECT(x,y,z,w) CURLE_NOT_BUILT_IN
@@ -46,10 +47,10 @@ int Curl_connect_getsock(struct connectdata *conn);
 #define Curl_connect_complete(x) CURLE_OK
 #define Curl_connect_ongoing(x) FALSE
 #define Curl_connect_getsock(x) 0
+#define Curl_connect_done(x)
 #endif
 
 void Curl_connect_free(struct Curl_easy *data);
-void Curl_connect_done(struct Curl_easy *data);
 
 /* struct for HTTP CONNECT state data */
 struct http_connect_state {
diff --git a/lib/if2ip.c b/lib/if2ip.c
index 21e00b1..132b3ee 100644
--- a/lib/if2ip.c
+++ b/lib/if2ip.c
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -114,7 +114,7 @@ if2ip_result_t Curl_if2ip(int af, unsigned int remote_scope,
 
   if(getifaddrs(&head) >= 0) {
     for(iface = head; iface != NULL; iface = iface->ifa_next) {
-      if(iface->ifa_addr != NULL) {
+      if(iface->ifa_addr) {
         if(iface->ifa_addr->sa_family == af) {
           if(strcasecompare(iface->ifa_name, interf)) {
             void *addr;
diff --git a/lib/imap.c b/lib/imap.c
index 6163899..958ad14 100644
--- a/lib/imap.c
+++ b/lib/imap.c
@@ -78,6 +78,7 @@
 #include "multiif.h"
 #include "url.h"
 #include "strcase.h"
+#include "bufref.h"
 #include "curl_sasl.h"
 #include "warnless.h"
 
@@ -101,19 +102,19 @@ static CURLcode imap_doing(struct Curl_easy *data, bool *dophase_done);
 static CURLcode imap_setup_connection(struct Curl_easy *data,
                                       struct connectdata *conn);
 static char *imap_atom(const char *str, bool escape_only);
-static CURLcode imap_sendf(struct Curl_easy *data,
-                           struct connectdata *conn, const char *fmt, ...);
+static CURLcode imap_sendf(struct Curl_easy *data, const char *fmt, ...);
 static CURLcode imap_parse_url_options(struct connectdata *conn);
 static CURLcode imap_parse_url_path(struct Curl_easy *data);
 static CURLcode imap_parse_custom_request(struct Curl_easy *data);
 static CURLcode imap_perform_authenticate(struct Curl_easy *data,
-                                          struct connectdata *conn,
                                           const char *mech,
-                                          const char *initresp);
+                                          const struct bufref *initresp);
 static CURLcode imap_continue_authenticate(struct Curl_easy *data,
-                                           struct connectdata *conn,
-                                           const char *resp);
-static void imap_get_message(char *buffer, char **outptr);
+                                           const char *mech,
+                                           const struct bufref *resp);
+static CURLcode imap_cancel_authenticate(struct Curl_easy *data,
+                                         const char *mech);
+static CURLcode imap_get_message(struct Curl_easy *data, struct bufref *out);
 
 /*
  * IMAP protocol handler.
@@ -180,12 +181,15 @@ const struct Curl_handler Curl_handler_imaps = {
 /* SASL parameters for the imap protocol */
 static const struct SASLproto saslimap = {
   "imap",                     /* The service name */
-  '+',                        /* Code received when continuation is expected */
-  IMAP_RESP_OK,               /* Code to receive upon authentication success */
-  0,                          /* Maximum initial response length (no max) */
   imap_perform_authenticate,  /* Send authentication command */
   imap_continue_authenticate, /* Send authentication continuation */
-  imap_get_message            /* Get SASL response message */
+  imap_cancel_authenticate,   /* Send authentication cancellation */
+  imap_get_message,           /* Get SASL response message */
+  0,                          /* No maximum initial response length */
+  '+',                        /* Code received when continuation is expected */
+  IMAP_RESP_OK,               /* Code to receive upon authentication success */
+  SASL_AUTH_DEFAULT,          /* Default mechanisms */
+  SASL_FLAG_BASE64            /* Configuration flags */
 };
 
 
@@ -293,6 +297,7 @@ static bool imap_endofresp(struct Curl_easy *data, struct connectdata *conn,
            !strcasecompare(imap->custom, "EXPUNGE") &&
            !strcasecompare(imap->custom, "LSUB") &&
            !strcasecompare(imap->custom, "UID") &&
+           !strcasecompare(imap->custom, "GETQUOTAROOT") &&
            !strcasecompare(imap->custom, "NOOP")))
           return FALSE;
         break;
@@ -324,7 +329,7 @@ static bool imap_endofresp(struct Curl_easy *data, struct connectdata *conn,
   /* Do we have a continuation response? This should be a + symbol followed by
      a space and optionally some text as per RFC-3501 for the AUTHENTICATE and
      APPEND commands and as outlined in Section 4. Examples of RFC-4959 but
-     some e-mail servers ignore this and only send a single + instead. */
+     some email servers ignore this and only send a single + instead. */
   if(imap && !imap->custom && ((len == 3 && line[0] == '+') ||
      (len >= 2 && !memcmp("+ ", line, 2)))) {
     switch(imapc->state) {
@@ -352,34 +357,32 @@ static bool imap_endofresp(struct Curl_easy *data, struct connectdata *conn,
  *
  * Gets the authentication message from the response buffer.
  */
-static void imap_get_message(char *buffer, char **outptr)
+static CURLcode imap_get_message(struct Curl_easy *data, struct bufref *out)
 {
-  size_t len = strlen(buffer);
-  char *message = NULL;
+  char *message = data->state.buffer;
+  size_t len = strlen(message);
 
   if(len > 2) {
     /* Find the start of the message */
     len -= 2;
-    for(message = buffer + 2; *message == ' ' || *message == '\t';
-        message++, len--)
+    for(message += 2; *message == ' ' || *message == '\t'; message++, len--)
       ;
 
     /* Find the end of the message */
-    for(; len--;)
+    while(len--)
       if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
          message[len] != '\t')
         break;
 
     /* Terminate the message */
-    if(++len) {
-      message[len] = '\0';
-    }
+    message[++len] = '\0';
+    Curl_bufref_set(out, message, len, NULL);
   }
   else
     /* junk input => zero length output */
-    message = &buffer[len];
+    Curl_bufref_set(out, "", 0, NULL);
 
-  *outptr = message;
+  return CURLE_OK;
 }
 
 /***********************************************************************
@@ -437,7 +440,7 @@ static CURLcode imap_perform_capability(struct Curl_easy *data,
   imapc->tls_supported = FALSE;           /* Clear the TLS capability */
 
   /* Send the CAPABILITY command */
-  result = imap_sendf(data, conn, "CAPABILITY");
+  result = imap_sendf(data, "CAPABILITY");
 
   if(!result)
     state(data, IMAP_CAPABILITY);
@@ -451,11 +454,10 @@ static CURLcode imap_perform_capability(struct Curl_easy *data,
  *
  * Sends the STARTTLS command to start the upgrade to TLS.
  */
-static CURLcode imap_perform_starttls(struct Curl_easy *data,
-                                      struct connectdata *conn)
+static CURLcode imap_perform_starttls(struct Curl_easy *data)
 {
   /* Send the STARTTLS command */
-  CURLcode result = imap_sendf(data, conn, "STARTTLS");
+  CURLcode result = imap_sendf(data, "STARTTLS");
 
   if(!result)
     state(data, IMAP_STARTTLS);
@@ -516,7 +518,7 @@ static CURLcode imap_perform_login(struct Curl_easy *data,
   passwd = imap_atom(conn->passwd, false);
 
   /* Send the LOGIN command */
-  result = imap_sendf(data, conn, "LOGIN %s %s", user ? user : "",
+  result = imap_sendf(data, "LOGIN %s %s", user ? user : "",
                       passwd ? passwd : "");
 
   free(user);
@@ -536,20 +538,19 @@ static CURLcode imap_perform_login(struct Curl_easy *data,
  * SASL authentication mechanism.
  */
 static CURLcode imap_perform_authenticate(struct Curl_easy *data,
-                                          struct connectdata *conn,
                                           const char *mech,
-                                          const char *initresp)
+                                          const struct bufref *initresp)
 {
   CURLcode result = CURLE_OK;
-  (void)data;
+  const char *ir = (const char *) Curl_bufref_ptr(initresp);
 
-  if(initresp) {
+  if(ir) {
     /* Send the AUTHENTICATE command with the initial response */
-    result = imap_sendf(data, conn, "AUTHENTICATE %s %s", mech, initresp);
+    result = imap_sendf(data, "AUTHENTICATE %s %s", mech, ir);
   }
   else {
     /* Send the AUTHENTICATE command */
-    result = imap_sendf(data, conn, "AUTHENTICATE %s", mech);
+    result = imap_sendf(data, "AUTHENTICATE %s", mech);
   }
 
   return result;
@@ -559,15 +560,34 @@ static CURLcode imap_perform_authenticate(struct Curl_easy *data,
  *
  * imap_continue_authenticate()
  *
- * Sends SASL continuation data or cancellation.
+ * Sends SASL continuation data.
  */
 static CURLcode imap_continue_authenticate(struct Curl_easy *data,
-                                           struct connectdata *conn,
-                                           const char *resp)
+                                           const char *mech,
+                                           const struct bufref *resp)
 {
-  struct imap_conn *imapc = &conn->proto.imapc;
+  struct imap_conn *imapc = &data->conn->proto.imapc;
+
+  (void)mech;
 
-  return Curl_pp_sendf(data, &imapc->pp, "%s", resp);
+  return Curl_pp_sendf(data, &imapc->pp,
+                       "%s", (const char *) Curl_bufref_ptr(resp));
+}
+
+/***********************************************************************
+ *
+ * imap_cancel_authenticate()
+ *
+ * Sends SASL cancellation.
+ */
+static CURLcode imap_cancel_authenticate(struct Curl_easy *data,
+                                         const char *mech)
+{
+  struct imap_conn *imapc = &data->conn->proto.imapc;
+
+  (void)mech;
+
+  return Curl_pp_sendf(data, &imapc->pp, "*");
 }
 
 /***********************************************************************
@@ -594,8 +614,7 @@ static CURLcode imap_perform_authentication(struct Curl_easy *data,
   }
 
   /* Calculate the SASL login details */
-  result = Curl_sasl_start(&imapc->sasl, data, conn,
-                           imapc->ir_supported, &progress);
+  result = Curl_sasl_start(&imapc->sasl, data, imapc->ir_supported, &progress);
 
   if(!result) {
     if(progress == SASL_INPROGRESS)
@@ -622,12 +641,11 @@ static CURLcode imap_perform_authentication(struct Curl_easy *data,
 static CURLcode imap_perform_list(struct Curl_easy *data)
 {
   CURLcode result = CURLE_OK;
-  struct connectdata *conn = data->conn;
   struct IMAP *imap = data->req.p.imap;
 
   if(imap->custom)
     /* Send the custom request */
-    result = imap_sendf(data, conn, "%s%s", imap->custom,
+    result = imap_sendf(data, "%s%s", imap->custom,
                         imap->custom_params ? imap->custom_params : "");
   else {
     /* Make sure the mailbox is in the correct atom format if necessary */
@@ -637,7 +655,7 @@ static CURLcode imap_perform_list(struct Curl_easy *data)
       return CURLE_OUT_OF_MEMORY;
 
     /* Send the LIST command */
-    result = imap_sendf(data, conn, "LIST \"%s\" *", mailbox);
+    result = imap_sendf(data, "LIST \"%s\" *", mailbox);
 
     free(mailbox);
   }
@@ -678,7 +696,7 @@ static CURLcode imap_perform_select(struct Curl_easy *data)
     return CURLE_OUT_OF_MEMORY;
 
   /* Send the SELECT command */
-  result = imap_sendf(data, conn, "SELECT %s", mailbox);
+  result = imap_sendf(data, "SELECT %s", mailbox);
 
   free(mailbox);
 
@@ -694,8 +712,7 @@ static CURLcode imap_perform_select(struct Curl_easy *data)
  *
  * Sends a FETCH command to initiate the download of a message.
  */
-static CURLcode imap_perform_fetch(struct Curl_easy *data,
-                                   struct connectdata *conn)
+static CURLcode imap_perform_fetch(struct Curl_easy *data)
 {
   CURLcode result = CURLE_OK;
   struct IMAP *imap = data->req.p.imap;
@@ -704,21 +721,21 @@ static CURLcode imap_perform_fetch(struct Curl_easy *data,
 
     /* Send the FETCH command */
     if(imap->partial)
-      result = imap_sendf(data, conn, "UID FETCH %s BODY[%s]<%s>",
+      result = imap_sendf(data, "UID FETCH %s BODY[%s]<%s>",
                           imap->uid, imap->section ? imap->section : "",
                           imap->partial);
     else
-      result = imap_sendf(data, conn, "UID FETCH %s BODY[%s]",
+      result = imap_sendf(data, "UID FETCH %s BODY[%s]",
                           imap->uid, imap->section ? imap->section : "");
   }
   else if(imap->mindex) {
     /* Send the FETCH command */
     if(imap->partial)
-      result = imap_sendf(data, conn, "FETCH %s BODY[%s]<%s>",
+      result = imap_sendf(data, "FETCH %s BODY[%s]<%s>",
                           imap->mindex, imap->section ? imap->section : "",
                           imap->partial);
     else
-      result = imap_sendf(data, conn, "FETCH %s BODY[%s]",
+      result = imap_sendf(data, "FETCH %s BODY[%s]",
                           imap->mindex, imap->section ? imap->section : "");
   }
   else {
@@ -740,7 +757,6 @@ static CURLcode imap_perform_fetch(struct Curl_easy *data,
 static CURLcode imap_perform_append(struct Curl_easy *data)
 {
   CURLcode result = CURLE_OK;
-  struct connectdata *conn = data->conn;
   struct IMAP *imap = data->req.p.imap;
   char *mailbox;
 
@@ -791,7 +807,7 @@ static CURLcode imap_perform_append(struct Curl_easy *data)
     return CURLE_OUT_OF_MEMORY;
 
   /* Send the APPEND command */
-  result = imap_sendf(data, conn,
+  result = imap_sendf(data,
                       "APPEND %s (\\Seen) {%" CURL_FORMAT_CURL_OFF_T "}",
                       mailbox, data->state.infilesize);
 
@@ -809,8 +825,7 @@ static CURLcode imap_perform_append(struct Curl_easy *data)
  *
  * Sends a SEARCH command.
  */
-static CURLcode imap_perform_search(struct Curl_easy *data,
-                                    struct connectdata *conn)
+static CURLcode imap_perform_search(struct Curl_easy *data)
 {
   CURLcode result = CURLE_OK;
   struct IMAP *imap = data->req.p.imap;
@@ -822,7 +837,7 @@ static CURLcode imap_perform_search(struct Curl_easy *data,
   }
 
   /* Send the SEARCH command */
-  result = imap_sendf(data, conn, "SEARCH %s", imap->query);
+  result = imap_sendf(data, "SEARCH %s", imap->query);
 
   if(!result)
     state(data, IMAP_SEARCH);
@@ -836,11 +851,10 @@ static CURLcode imap_perform_search(struct Curl_easy *data,
  *
  * Performs the logout action prior to sclose() being called.
  */
-static CURLcode imap_perform_logout(struct Curl_easy *data,
-                                    struct connectdata *conn)
+static CURLcode imap_perform_logout(struct Curl_easy *data)
 {
   /* Send the LOGOUT command */
-  CURLcode result = imap_sendf(data, conn, "LOGOUT");
+  CURLcode result = imap_sendf(data, "LOGOUT");
 
   if(!result)
     state(data, IMAP_LOGOUT);
@@ -938,7 +952,7 @@ static CURLcode imap_state_capability_resp(struct Curl_easy *data,
     /* PREAUTH is not compatible with STARTTLS. */
     if(imapcode == IMAP_RESP_OK && imapc->tls_supported && !imapc->preauth) {
       /* Switch to TLS connection now */
-      result = imap_perform_starttls(data, conn);
+      result = imap_perform_starttls(data);
     }
     else if(data->set.use_ssl <= CURLUSESSL_TRY)
       result = imap_perform_authentication(data, conn);
@@ -993,7 +1007,7 @@ static CURLcode imap_state_auth_resp(struct Curl_easy *data,
 
   (void)instate; /* no use for this yet */
 
-  result = Curl_sasl_continue(&imapc->sasl, data, conn, imapcode, &progress);
+  result = Curl_sasl_continue(&imapc->sasl, data, imapcode, &progress);
   if(!result)
     switch(progress) {
     case SASL_DONE:
@@ -1094,9 +1108,9 @@ static CURLcode imap_state_select_resp(struct Curl_easy *data, int imapcode,
       if(imap->custom)
         result = imap_perform_list(data);
       else if(imap->query)
-        result = imap_perform_search(data, conn);
+        result = imap_perform_search(data);
       else
-        result = imap_perform_fetch(data, conn);
+        result = imap_perform_fetch(data);
     }
   }
   else {
@@ -1441,7 +1455,7 @@ static CURLcode imap_connect(struct Curl_easy *data, bool *done)
 
   /* Set the default preferred authentication type and mechanism */
   imapc->preftype = IMAP_TYPE_ANY;
-  Curl_sasl_init(&imapc->sasl, &saslimap);
+  Curl_sasl_init(&imapc->sasl, data, &saslimap);
 
   Curl_dyn_init(&imapc->dyn, DYN_IMAP_CMD);
   /* Initialise the pingpong layer */
@@ -1568,10 +1582,10 @@ static CURLcode imap_perform(struct Curl_easy *data, bool *connected,
     result = imap_perform_list(data);
   else if(!imap->custom && selected && (imap->uid || imap->mindex))
     /* FETCH from the same mailbox */
-    result = imap_perform_fetch(data, conn);
+    result = imap_perform_fetch(data);
   else if(!imap->custom && selected && imap->query)
     /* SEARCH the current mailbox */
-    result = imap_perform_search(data, conn);
+    result = imap_perform_search(data);
   else if(imap->mailbox && !selected &&
          (imap->custom || imap->uid || imap->mindex || imap->query))
     /* SELECT the mailbox */
@@ -1643,7 +1657,7 @@ static CURLcode imap_disconnect(struct Curl_easy *data,
   /* The IMAP session may or may not have been allocated/setup at this
      point! */
   if(!dead_connection && conn->bits.protoconnstart) {
-    if(!imap_perform_logout(data, conn))
+    if(!imap_perform_logout(data))
       (void)imap_block_statemach(data, conn, TRUE); /* ignore errors */
   }
 
@@ -1747,17 +1761,16 @@ static CURLcode imap_setup_connection(struct Curl_easy *data,
  *
  * Designed to never block.
  */
-static CURLcode imap_sendf(struct Curl_easy *data,
-                           struct connectdata *conn, const char *fmt, ...)
+static CURLcode imap_sendf(struct Curl_easy *data, const char *fmt, ...)
 {
   CURLcode result = CURLE_OK;
-  struct imap_conn *imapc = &conn->proto.imapc;
+  struct imap_conn *imapc = &data->conn->proto.imapc;
 
   DEBUGASSERT(fmt);
 
   /* Calculate the tag based on the connection ID and command ID */
   msnprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d",
-            'A' + curlx_sltosi(conn->connection_id % 26),
+            'A' + curlx_sltosi(data->conn->connection_id % 26),
             (++imapc->cmdid)%1000);
 
   /* start with a blank buffer */
@@ -1911,8 +1924,6 @@ static CURLcode imap_parse_url_options(struct connectdata *conn)
   struct imap_conn *imapc = &conn->proto.imapc;
   const char *ptr = conn->options;
 
-  imapc->sasl.resetprefs = TRUE;
-
   while(!result && ptr && *ptr) {
     const char *key = ptr;
     const char *value;
diff --git a/lib/inet_pton.c b/lib/inet_pton.c
index 4923cae..ada57af 100644
--- a/lib/inet_pton.c
+++ b/lib/inet_pton.c
@@ -1,6 +1,6 @@
 /* This is from the BIND 4.9.4 release, modified to compile by itself */
 
-/* Copyright (c) 1996 - 2020 by Internet Software Consortium.
+/* Copyright (c) 1996 - 2021 by Internet Software Consortium.
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -174,7 +174,7 @@ inet_pton6(const char *src, unsigned char *dst)
     pch = strchr((xdigits = xdigits_l), ch);
     if(!pch)
       pch = strchr((xdigits = xdigits_u), ch);
-    if(pch != NULL) {
+    if(pch) {
       val <<= 4;
       val |= (pch - xdigits);
       if(++saw_xdigit > 4)
@@ -211,7 +211,7 @@ inet_pton6(const char *src, unsigned char *dst)
     *tp++ = (unsigned char) ((val >> 8) & 0xff);
     *tp++ = (unsigned char) (val & 0xff);
   }
-  if(colonp != NULL) {
+  if(colonp) {
     /*
      * Since some memmove()'s erroneously fail to handle
      * overlapping regions, we'll do the shift by hand.
diff --git a/lib/krb5.c b/lib/krb5.c
index 659947d..54edb47 100644
--- a/lib/krb5.c
+++ b/lib/krb5.c
@@ -374,7 +374,7 @@ static void krb5_end(void *app_data)
     }
 }
 
-static struct Curl_sec_client_mech Curl_krb5_client_mech = {
+static const struct Curl_sec_client_mech Curl_krb5_client_mech = {
   "GSSAPI",
   sizeof(gss_ctx_id_t),
   krb5_init,
@@ -684,7 +684,7 @@ int Curl_sec_read_msg(struct Curl_easy *data, struct connectdata *conn,
   (void) data;
 
   if(!conn->mech)
-    /* not inititalized, return error */
+    /* not initialized, return error */
     return -1;
 
   DEBUGASSERT(level > PROT_NONE && level < PROT_LAST);
@@ -768,7 +768,7 @@ static int sec_set_protection_level(struct Curl_easy *data)
     }
   }
 
-  /* Now try to negiociate the protection level. */
+  /* Now try to negotiate the protection level. */
   code = ftp_send_command(data, "PROT %c", level_to_char(level));
 
   if(code < 0)
@@ -880,7 +880,7 @@ Curl_sec_login(struct Curl_easy *data, struct connectdata *conn)
 void
 Curl_sec_end(struct connectdata *conn)
 {
-  if(conn->mech != NULL && conn->mech->end)
+  if(conn->mech && conn->mech->end)
     conn->mech->end(conn->app_data);
   free(conn->app_data);
   conn->app_data = NULL;
diff --git a/lib/ldap.c b/lib/ldap.c
index 1d9e44c..3154db5 100644
--- a/lib/ldap.c
+++ b/lib/ldap.c
@@ -464,6 +464,11 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
 #endif
 #endif /* CURL_LDAP_USE_SSL */
   }
+  else if(data->set.use_ssl > CURLUSESSL_TRY) {
+    failf(data, "LDAP local: explicit TLS not supported");
+    result = CURLE_NOT_BUILT_IN;
+    goto quit;
+  }
   else {
     server = ldap_init(host, (int)conn->port);
     if(!server) {
@@ -590,7 +595,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
       attr_len = strlen(attr);
 
       vals = ldap_get_values_len(server, entryIterator, attribute);
-      if(vals != NULL) {
+      if(vals) {
         for(i = 0; (vals[i] != NULL); i++) {
           result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\t", 1);
           if(result) {
diff --git a/lib/libcurl.rc b/lib/libcurl.rc
index 3f7ae16..fde6c8c 100644
--- a/lib/libcurl.rc
+++ b/lib/libcurl.rc
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -51,7 +51,7 @@ BEGIN
       VALUE "OriginalFilename", "libcurl.dll\0"
       VALUE "ProductName",      "The curl library\0"
       VALUE "ProductVersion",   LIBCURL_VERSION "\0"
-      VALUE "LegalCopyright",   "\xa9 " LIBCURL_COPYRIGHT "\0"  /* a9: Copyright symbol */
+      VALUE "LegalCopyright",   "Copyright (C) " LIBCURL_COPYRIGHT "\0"
       VALUE "License",          "https://curl.se/docs/copyright.html\0"
     END
   END
diff --git a/lib/llist.c b/lib/llist.c
index e0ec739..e78da7d 100644
--- a/lib/llist.c
+++ b/lib/llist.c
@@ -106,9 +106,7 @@ Curl_llist_remove(struct Curl_llist *list, struct Curl_llist_element *e,
       e->next->prev = NULL;
   }
   else {
-    if(!e->prev)
-      list->head = e->next;
-    else
+    if(e->prev)
       e->prev->next = e->next;
 
     if(!e->next)
diff --git a/lib/md4.c b/lib/md4.c
index e7a428f..117cce4 100644
--- a/lib/md4.c
+++ b/lib/md4.c
@@ -27,6 +27,7 @@
 #include "curl_md4.h"
 #include "warnless.h"
 
+
 #ifdef USE_OPENSSL
 #include <openssl/opensslconf.h>
 #if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
@@ -35,6 +36,13 @@
 #endif
 #endif /* USE_OPENSSL */
 
+#ifdef USE_WOLFSSL
+#include <wolfssl/options.h>
+#ifdef NO_MD4
+#define OPENSSL_NO_MD4
+#endif
+#endif
+
 #ifdef USE_MBEDTLS
 #include <mbedtls/version.h>
 #if MBEDTLS_VERSION_NUMBER >= 0x03000000
@@ -74,8 +82,9 @@ static void MD4_Final(unsigned char *result, MD4_CTX *ctx)
   md4_digest(ctx, MD4_DIGEST_SIZE, result);
 }
 
-#elif defined(USE_OPENSSL) && !defined(OPENSSL_NO_MD4)
-/* When OpenSSL is available we use the MD4-functions from OpenSSL */
+#elif (defined(USE_OPENSSL) || defined(USE_WOLFSSL)) && \
+      !defined(OPENSSL_NO_MD4)
+/* When OpenSSL or wolfSSL is available, we use their MD4 functions. */
 #include <openssl/md4.h>
 
 #elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \
@@ -180,7 +189,7 @@ static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size)
 {
   if(!ctx->data) {
     ctx->data = malloc(size);
-    if(ctx->data != NULL) {
+    if(ctx->data) {
       memcpy(ctx->data, data, size);
       ctx->size = size;
     }
@@ -189,7 +198,7 @@ static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size)
 
 static void MD4_Final(unsigned char *result, MD4_CTX *ctx)
 {
-  if(ctx->data != NULL) {
+  if(ctx->data) {
 #if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS)
     mbedtls_md4(ctx->data, ctx->size, result);
 #else
diff --git a/lib/md5.c b/lib/md5.c
index 983ed97..c6923e0 100644
--- a/lib/md5.c
+++ b/lib/md5.c
@@ -39,6 +39,20 @@
 #endif
 #endif /* USE_MBEDTLS */
 
+#if defined(USE_OPENSSL) && !defined(USE_AMISSL)
+  #include <openssl/opensslconf.h>
+  #if !defined(OPENSSL_NO_MD5) && !defined(OPENSSL_NO_DEPRECATED_3_0)
+    #define USE_OPENSSL_MD5
+  #endif
+#endif
+
+#ifdef USE_WOLFSSL
+  #include <wolfssl/options.h>
+  #ifndef NO_MD5
+    #define USE_WOLFSSL_MD5
+  #endif
+#endif
+
 #if defined(USE_GNUTLS)
 
 #include <nettle/md5.h>
@@ -48,9 +62,10 @@
 
 typedef struct md5_ctx MD5_CTX;
 
-static void MD5_Init(MD5_CTX *ctx)
+static CURLcode MD5_Init(MD5_CTX *ctx)
 {
   md5_init(ctx);
+  return CURLE_OK;
 }
 
 static void MD5_Update(MD5_CTX *ctx,
@@ -65,8 +80,9 @@ static void MD5_Final(unsigned char *digest, MD5_CTX *ctx)
   md5_digest(ctx, 16, digest);
 }
 
-#elif defined(USE_OPENSSL) && !defined(USE_AMISSL)
-/* When OpenSSL is available we use the MD5-function from OpenSSL */
+#elif defined(USE_OPENSSL_MD5) || defined(USE_WOLFSSL_MD5)
+
+/* When OpenSSL or wolfSSL is available, we use their MD5 functions. */
 #include <openssl/md5.h>
 #include "curl_memory.h"
 /* The last #include file should be: */
@@ -83,13 +99,14 @@ static void MD5_Final(unsigned char *digest, MD5_CTX *ctx)
 
 typedef mbedtls_md5_context MD5_CTX;
 
-static void MD5_Init(MD5_CTX *ctx)
+static CURLcode MD5_Init(MD5_CTX *ctx)
 {
 #if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS)
   (void) mbedtls_md5_starts(ctx);
 #else
   (void) mbedtls_md5_starts_ret(ctx);
 #endif
+  return CURLE_OK;
 }
 
 static void MD5_Update(MD5_CTX *ctx,
@@ -131,9 +148,10 @@ static void MD5_Final(unsigned char *digest, MD5_CTX *ctx)
 /* The last #include file should be: */
 #include "memdebug.h"
 
-static void MD5_Init(MD5_CTX *ctx)
+static CURLcode MD5_Init(MD5_CTX *ctx)
 {
   CC_MD5_Init(ctx);
+  return CURLE_OK;
 }
 
 static void MD5_Update(MD5_CTX *ctx,
@@ -161,12 +179,13 @@ struct md5_ctx {
 };
 typedef struct md5_ctx MD5_CTX;
 
-static void MD5_Init(MD5_CTX *ctx)
+static CURLcode MD5_Init(MD5_CTX *ctx)
 {
   if(CryptAcquireContext(&ctx->hCryptProv, NULL, NULL, PROV_RSA_FULL,
                          CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) {
     CryptCreateHash(ctx->hCryptProv, CALG_MD5, 0, 0, &ctx->hHash);
   }
+  return CURLE_OK;
 }
 
 static void MD5_Update(MD5_CTX *ctx,
@@ -246,7 +265,7 @@ struct md5_ctx {
 };
 typedef struct md5_ctx MD5_CTX;
 
-static void MD5_Init(MD5_CTX *ctx);
+static CURLcode MD5_Init(MD5_CTX *ctx);
 static void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size);
 static void MD5_Final(unsigned char *result, MD5_CTX *ctx);
 
@@ -407,7 +426,7 @@ static const void *body(MD5_CTX *ctx, const void *data, unsigned long size)
   return ptr;
 }
 
-static void MD5_Init(MD5_CTX *ctx)
+static CURLcode MD5_Init(MD5_CTX *ctx)
 {
   ctx->a = 0x67452301;
   ctx->b = 0xefcdab89;
@@ -416,6 +435,8 @@ static void MD5_Init(MD5_CTX *ctx)
 
   ctx->lo = 0;
   ctx->hi = 0;
+
+  return CURLE_OK;
 }
 
 static void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size)
@@ -540,8 +561,9 @@ const struct MD5_params Curl_DIGEST_MD5[] = {
 
 /*
  * @unittest: 1601
+ * Returns CURLE_OK on success.
  */
-void Curl_md5it(unsigned char *outbuffer, const unsigned char *input,
+CURLcode Curl_md5it(unsigned char *outbuffer, const unsigned char *input,
                 const size_t len)
 {
   MD5_CTX ctx;
@@ -549,6 +571,8 @@ void Curl_md5it(unsigned char *outbuffer, const unsigned char *input,
   MD5_Init(&ctx);
   MD5_Update(&ctx, input, curlx_uztoui(len));
   MD5_Final(outbuffer, &ctx);
+
+  return CURLE_OK;
 }
 
 struct MD5_context *Curl_MD5_init(const struct MD5_params *md5params)
diff --git a/lib/mime.c b/lib/mime.c
index 0bf1b46..7783b89 100644
--- a/lib/mime.c
+++ b/lib/mime.c
@@ -40,6 +40,7 @@
 #include "rand.h"
 #include "slist.h"
 #include "strcase.h"
+#include "dynbuf.h"
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
@@ -279,29 +280,52 @@ static void mimesetstate(struct mime_state *state,
 
 
 /* Escape header string into allocated memory. */
-static char *escape_string(const char *src)
-{
-  size_t bytecount = 0;
-  size_t i;
-  char *dst;
+static char *escape_string(struct Curl_easy *data,
+                           const char *src, enum mimestrategy strategy)
+{
+  CURLcode result;
+  struct dynbuf db;
+  const char * const *table;
+  const char * const *p;
+  /* replace first character by rest of string. */
+  static const char * const mimetable[] = {
+    "\\\\\\",
+    "\"\\\"",
+    NULL
+  };
+  /* WHATWG HTML living standard 4.10.21.8 2 specifies:
+     For field names and filenames for file fields, the result of the
+     encoding in the previous bullet point must be escaped by replacing
+     any 0x0A (LF) bytes with the byte sequence `%0A`, 0x0D (CR) with `%0D`
+     and 0x22 (") with `%22`.
+     The user agent must not perform any other escapes. */
+  static const char * const formtable[] = {
+    "\"%22",
+    "\r%0D",
+    "\n%0A",
+    NULL
+  };
 
-  for(i = 0; src[i]; i++)
-    if(src[i] == '"' || src[i] == '\\')
-      bytecount++;
+  table = formtable;
+  /* data can be NULL when this function is called indirectly from
+     curl_formget(). */
+  if(strategy == MIMESTRATEGY_MAIL ||
+     (data && (data->set.mime_options & CURLMIMEOPT_FORMESCAPE)))
+    table = mimetable;
 
-  bytecount += i;
-  dst = malloc(bytecount + 1);
-  if(!dst)
-    return NULL;
+  Curl_dyn_init(&db, CURL_MAX_INPUT_LENGTH);
+
+  for(result = Curl_dyn_add(&db, ""); !result && *src; src++) {
+    for(p = table; *p && **p != *src; p++)
+      ;
 
-  for(i = 0; *src; src++) {
-    if(*src == '"' || *src == '\\')
-      dst[i++] = '\\';
-    dst[i++] = *src;
+    if(*p)
+      result = Curl_dyn_add(&db, *p + 1);
+    else
+      result = Curl_dyn_addn(&db, src, 1);
   }
 
-  dst[i] = '\0';
-  return dst;
+  return Curl_dyn_ptr(&db);
 }
 
 /* Check if header matches. */
@@ -462,11 +486,13 @@ static size_t encoder_base64_read(char *buffer, size_t size, bool ateof,
       /* Buffered data size can only be 0, 1 or 2. */
       ptr[2] = ptr[3] = '=';
       i = 0;
-      switch(st->bufend - st->bufbeg) {
-      case 2:
-        i = (st->buf[st->bufbeg + 1] & 0xFF) << 8;
-        /* FALLTHROUGH */
-      case 1:
+
+      /* If there is buffered data */
+      if(st->bufend != st->bufbeg) {
+
+        if(st->bufend - st->bufbeg == 2)
+          i = (st->buf[st->bufbeg + 1] & 0xFF) << 8;
+
         i |= (st->buf[st->bufbeg] & 0xFF) << 16;
         ptr[0] = base64[(i >> 18) & 0x3F];
         ptr[1] = base64[(i >> 12) & 0x3F];
@@ -476,7 +502,6 @@ static size_t encoder_base64_read(char *buffer, size_t size, bool ateof,
         }
         cursize += 4;
         st->pos += 4;
-        break;
       }
     }
   }
@@ -1865,12 +1890,12 @@ CURLcode Curl_mime_prepare_headers(curl_mimepart *part,
       char *filename = NULL;
 
       if(part->name) {
-        name = escape_string(part->name);
+        name = escape_string(part->easy, part->name, strategy);
         if(!name)
           ret = CURLE_OUT_OF_MEMORY;
       }
       if(!ret && part->filename) {
-        filename = escape_string(part->filename);
+        filename = escape_string(part->easy, part->filename, strategy);
         if(!filename)
           ret = CURLE_OUT_OF_MEMORY;
       }
@@ -1954,7 +1979,8 @@ void Curl_mime_unpause(curl_mimepart *part)
 }
 
 
-#else /* !CURL_DISABLE_HTTP || !CURL_DISABLE_SMTP || !CURL_DISABLE_IMAP */
+#else /* !CURL_DISABLE_HTTP && !CURL_DISABLE_MIME ||
+         !CURL_DISABLE_SMTP || !CURL_DISABLE_IMAP */
 
 /* Mime not compiled in: define stubs for externally-referenced functions. */
 curl_mime *curl_mime_init(CURL *easy)
diff --git a/lib/mprintf.c b/lib/mprintf.c
index 7a1aec5..0fd3afc 100644
--- a/lib/mprintf.c
+++ b/lib/mprintf.c
@@ -858,7 +858,7 @@ static int dprintf_formatf(
       {
         void *ptr;
         ptr = (void *) p->data.ptr;
-        if(ptr != NULL) {
+        if(ptr) {
           /* If the pointer is not NULL, write it as a %#x spec.  */
           base = 16;
           digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits;
diff --git a/lib/multi.c b/lib/multi.c
index 518ceb5..f8dcc63 100644
--- a/lib/multi.c
+++ b/lib/multi.c
@@ -243,6 +243,26 @@ static void trhash_dtor(void *nada)
   (void)nada;
 }
 
+/*
+ * The sockhash has its own separate subhash in each entry that need to be
+ * safely destroyed first.
+ */
+static void sockhash_destroy(struct Curl_hash *h)
+{
+  struct Curl_hash_iterator iter;
+  struct Curl_hash_element *he;
+
+  DEBUGASSERT(h);
+  Curl_hash_start_iterate(h, &iter);
+  he = Curl_hash_next_element(&iter);
+  while(he) {
+    struct Curl_sh_entry *sh = (struct Curl_sh_entry *)he->ptr;
+    Curl_hash_destroy(&sh->transfers);
+    he = Curl_hash_next_element(&iter);
+  }
+  Curl_hash_destroy(h);
+}
+
 
 /* make sure this socket is present in the hash for this handle */
 static struct Curl_sh_entry *sh_addentry(struct Curl_hash *sh,
@@ -261,11 +281,8 @@ static struct Curl_sh_entry *sh_addentry(struct Curl_hash *sh,
   if(!check)
     return NULL; /* major failure */
 
-  if(Curl_hash_init(&check->transfers, TRHASH_SIZE, trhash,
-                    trhash_compare, trhash_dtor)) {
-    free(check);
-    return NULL;
-  }
+  Curl_hash_init(&check->transfers, TRHASH_SIZE, trhash, trhash_compare,
+                 trhash_dtor);
 
   /* make/add new hash entry */
   if(!Curl_hash_add(sh, (char *)&s, sizeof(curl_socket_t), check)) {
@@ -332,10 +349,10 @@ static size_t hash_fd(void *key, size_t key_length, size_t slots_num)
  * per call."
  *
  */
-static int sh_init(struct Curl_hash *hash, int hashsize)
+static void sh_init(struct Curl_hash *hash, int hashsize)
 {
-  return Curl_hash_init(hash, hashsize, hash_fd, fd_key_compare,
-                        sh_freeentry);
+  Curl_hash_init(hash, hashsize, hash_fd, fd_key_compare,
+                 sh_freeentry);
 }
 
 /*
@@ -362,11 +379,9 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */
 
   multi->magic = CURL_MULTI_HANDLE;
 
-  if(Curl_mk_dnscache(&multi->hostcache))
-    goto error;
+  Curl_init_dnscache(&multi->hostcache);
 
-  if(sh_init(&multi->sockhash, hashsize))
-    goto error;
+  sh_init(&multi->sockhash, hashsize);
 
   if(Curl_conncache_init(&multi->conn_cache, chashsize))
     goto error;
@@ -405,7 +420,7 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */
 
   error:
 
-  Curl_hash_destroy(&multi->sockhash);
+  sockhash_destroy(&multi->sockhash);
   Curl_hash_destroy(&multi->hostcache);
   Curl_conncache_destroy(&multi->conn_cache);
   Curl_llist_destroy(&multi->msglist, NULL);
@@ -424,6 +439,7 @@ struct Curl_multi *curl_multi_init(void)
 CURLMcode curl_multi_add_handle(struct Curl_multi *multi,
                                 struct Curl_easy *data)
 {
+  CURLMcode rc;
   /* First, make some basic checks that the CURLM handle is a good handle */
   if(!GOOD_MULTI_HANDLE(multi))
     return CURLM_BAD_HANDLE;
@@ -440,6 +456,15 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi,
   if(multi->in_callback)
     return CURLM_RECURSIVE_API_CALL;
 
+  if(multi->dead) {
+    /* a "dead" handle cannot get added transfers while any existing easy
+       handles are still alive - but if there are none alive anymore, it is
+       fine to start over and unmark the "deadness" of this handle */
+    if(multi->num_alive)
+      return CURLM_ABORTED_BY_CALLBACK;
+    multi->dead = FALSE;
+  }
+
   /* Initialize timeout list for this handle */
   Curl_llist_init(&data->state.timeoutlist, NULL);
 
@@ -452,6 +477,34 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi,
   if(data->set.errorbuffer)
     data->set.errorbuffer[0] = 0;
 
+  /* make the Curl_easy refer back to this multi handle - before Curl_expire()
+     is called. */
+  data->multi = multi;
+
+  /* Set the timeout for this handle to expire really soon so that it will
+     be taken care of even when this handle is added in the midst of operation
+     when only the curl_multi_socket() API is used. During that flow, only
+     sockets that time-out or have actions will be dealt with. Since this
+     handle has no action yet, we make sure it times out to get things to
+     happen. */
+  Curl_expire(data, 0, EXPIRE_RUN_NOW);
+
+  /* A somewhat crude work-around for a little glitch in Curl_update_timer()
+     that happens if the lastcall time is set to the same time when the handle
+     is removed as when the next handle is added, as then the check in
+     Curl_update_timer() that prevents calling the application multiple times
+     with the same timer info will not trigger and then the new handle's
+     timeout will not be notified to the app.
+
+     The work-around is thus simply to clear the 'lastcall' variable to force
+     Curl_update_timer() to always trigger a callback to the app when a new
+     easy handle is added */
+  memset(&multi->timer_lastcall, 0, sizeof(multi->timer_lastcall));
+
+  rc = Curl_update_timer(multi);
+  if(rc)
+    return rc;
+
   /* set the easy handle */
   multistate(data, MSTATE_INIT);
 
@@ -492,35 +545,12 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi,
     multi->easylp = multi->easyp = data; /* both first and last */
   }
 
-  /* make the Curl_easy refer back to this multi handle */
-  data->multi = multi;
-
-  /* Set the timeout for this handle to expire really soon so that it will
-     be taken care of even when this handle is added in the midst of operation
-     when only the curl_multi_socket() API is used. During that flow, only
-     sockets that time-out or have actions will be dealt with. Since this
-     handle has no action yet, we make sure it times out to get things to
-     happen. */
-  Curl_expire(data, 0, EXPIRE_RUN_NOW);
-
   /* increase the node-counter */
   multi->num_easy++;
 
   /* increase the alive-counter */
   multi->num_alive++;
 
-  /* A somewhat crude work-around for a little glitch in Curl_update_timer()
-     that happens if the lastcall time is set to the same time when the handle
-     is removed as when the next handle is added, as then the check in
-     Curl_update_timer() that prevents calling the application multiple times
-     with the same timer info will not trigger and then the new handle's
-     timeout will not be notified to the app.
-
-     The work-around is thus simply to clear the 'lastcall' variable to force
-     Curl_update_timer() to always trigger a callback to the app when a new
-     easy handle is added */
-  memset(&multi->timer_lastcall, 0, sizeof(multi->timer_lastcall));
-
   CONNCACHE_LOCK(data);
   /* The closure handle only ever has default timeouts set. To improve the
      state somewhat we clone the timeouts from each added handle so that the
@@ -533,14 +563,13 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi,
     data->set.no_signal;
   CONNCACHE_UNLOCK(data);
 
-  Curl_update_timer(multi);
   return CURLM_OK;
 }
 
 #if 0
 /* Debug-function, used like this:
  *
- * Curl_hash_print(multi->sockhash, debug_print_sock_hash);
+ * Curl_hash_print(&multi->sockhash, debug_print_sock_hash);
  *
  * Enable the hash print function first by editing hash.c
  */
@@ -548,8 +577,8 @@ static void debug_print_sock_hash(void *p)
 {
   struct Curl_sh_entry *sh = (struct Curl_sh_entry *)p;
 
-  fprintf(stderr, " [easy %p/magic %x/socket %d]",
-          (void *)sh->data, sh->data->magic, (int)sh->socket);
+  fprintf(stderr, " [readers %u][writers %u]",
+          sh->readers, sh->writers);
 }
 #endif
 
@@ -562,7 +591,8 @@ static CURLcode multi_done(struct Curl_easy *data,
   struct connectdata *conn = data->conn;
   unsigned int i;
 
-  DEBUGF(infof(data, "multi_done"));
+  DEBUGF(infof(data, "multi_done: status: %d prem: %d done: %d",
+               (int)status, (int)premature, data->state.done));
 
   if(data->state.done)
     /* Stop if multi_done() has already been called */
@@ -719,6 +749,7 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi,
   struct Curl_easy *easy = data;
   bool premature;
   struct Curl_llist_element *e;
+  CURLMcode rc;
 
   /* First, make some basic checks that the CURLM handle is a good handle */
   if(!GOOD_MULTI_HANDLE(multi))
@@ -792,8 +823,11 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi,
   /* change state without using multistate(), only to make singlesocket() do
      what we want */
   data->mstate = MSTATE_COMPLETED;
-  singlesocket(multi, easy); /* to let the application know what sockets that
-                                vanish with this handle */
+
+  /* This ignores the return code even in case of problems because there's
+     nothing more to do about that, here */
+  (void)singlesocket(multi, easy); /* to let the application know what sockets
+                                      that vanish with this handle */
 
   /* Remove the association between the connection and the handle */
   Curl_detach_connnection(data);
@@ -858,7 +892,9 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi,
 
   process_pending_handles(multi);
 
-  Curl_update_timer(multi);
+  rc = Curl_update_timer(multi);
+  if(rc)
+    return rc;
   return CURLM_OK;
 }
 
@@ -878,6 +914,7 @@ void Curl_detach_connnection(struct Curl_easy *data)
 {
   struct connectdata *conn = data->conn;
   if(conn) {
+    Curl_connect_done(data); /* if mid-CONNECT, shut it down */
     Curl_llist_remove(&conn->easyq, &data->conn_queue, NULL);
     Curl_ssl_detach_conn(data, conn);
   }
@@ -1742,6 +1779,15 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
   if(!GOOD_EASY_HANDLE(data))
     return CURLM_BAD_EASY_HANDLE;
 
+  if(multi->dead) {
+    /* a multi-level callback returned error before, meaning every individual
+     transfer now has failed */
+    result = CURLE_ABORTED_BY_CALLBACK;
+    Curl_posttransfer(data);
+    multi_done(data, result, FALSE);
+    multistate(data, MSTATE_COMPLETED);
+  }
+
   do {
     /* A "stream" here is a logical stream if the protocol can handle that
        (HTTP/2), or the full connection for older protocols */
@@ -1892,7 +1938,9 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
          down.  If the name has not yet been resolved, it is likely
          that new sockets have been opened in an attempt to contact
          another resolver. */
-      singlesocket(multi, data);
+      rc = singlesocket(multi, data);
+      if(rc)
+        return rc;
 
       if(dns) {
         /* Perform the next step in the connection phase, and then move on
@@ -2028,6 +2076,28 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
       break;
 
     case MSTATE_DO:
+      if(data->set.fprereq) {
+        int prereq_rc;
+
+        /* call the prerequest callback function */
+        Curl_set_in_callback(data, true);
+        prereq_rc = data->set.fprereq(data->set.prereq_userp,
+                                      data->info.conn_primary_ip,
+                                      data->info.conn_local_ip,
+                                      data->info.conn_primary_port,
+                                      data->info.conn_local_port);
+        Curl_set_in_callback(data, false);
+        if(prereq_rc != CURL_PREREQFUNC_OK) {
+          failf(data, "operation aborted by pre-request callback");
+          /* failure in pre-request callback - don't do any other processing */
+          result = CURLE_ABORTED_BY_CALLBACK;
+          Curl_posttransfer(data);
+          multi_done(data, result, FALSE);
+          stream_error = TRUE;
+          break;
+        }
+      }
+
       if(data->set.connect_only) {
         /* keep connection open for application to use the socket */
         connkeep(data->conn, "CONNECT_ONLY");
@@ -2595,7 +2665,7 @@ CURLMcode curl_multi_perform(struct Curl_multi *multi, int *running_handles)
   *running_handles = multi->num_alive;
 
   if(CURLM_OK >= returncode)
-    Curl_update_timer(multi);
+    returncode = Curl_update_timer(multi);
 
   return returncode;
 }
@@ -2611,7 +2681,7 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi)
 
     multi->magic = 0; /* not good anymore */
 
-    /* Firsrt remove all remaining easy handles */
+    /* First remove all remaining easy handles */
     data = multi->easyp;
     while(data) {
       nextdata = data->next;
@@ -2640,7 +2710,7 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi)
     /* Close all the connections in the connection cache */
     Curl_conncache_close_all_connections(&multi->conn_cache);
 
-    Curl_hash_destroy(&multi->sockhash);
+    sockhash_destroy(&multi->sockhash);
     Curl_conncache_destroy(&multi->conn_cache);
     Curl_llist_destroy(&multi->msglist, NULL);
     Curl_llist_destroy(&multi->pending, NULL);
@@ -2715,6 +2785,7 @@ static CURLMcode singlesocket(struct Curl_multi *multi,
   int num;
   unsigned int curraction;
   unsigned char actions[MAX_SOCKSPEREASYHANDLE];
+  int rc;
 
   for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++)
     socks[i] = CURL_SOCKET_BAD;
@@ -2786,8 +2857,10 @@ static CURLMcode singlesocket(struct Curl_multi *multi,
 
       /* add 'data' to the transfer hash on this socket! */
       if(!Curl_hash_add(&entry->transfers, (char *)&data, /* hash key */
-                        sizeof(struct Curl_easy *), data))
+                        sizeof(struct Curl_easy *), data)) {
+        Curl_hash_destroy(&entry->transfers);
         return CURLM_OUT_OF_MEMORY;
+      }
     }
 
     comboaction = (entry->writers? CURL_POLL_OUT : 0) |
@@ -2798,9 +2871,14 @@ static CURLMcode singlesocket(struct Curl_multi *multi,
       /* same, continue */
       continue;
 
-    if(multi->socket_cb)
-      multi->socket_cb(data, s, comboaction, multi->socket_userp,
-                       entry->socketp);
+    if(multi->socket_cb) {
+      rc = multi->socket_cb(data, s, comboaction, multi->socket_userp,
+                            entry->socketp);
+      if(rc == -1) {
+        multi->dead = TRUE;
+        return CURLM_ABORTED_BY_CALLBACK;
+      }
+    }
 
     entry->action = comboaction; /* store the current action state */
   }
@@ -2835,10 +2913,14 @@ static CURLMcode singlesocket(struct Curl_multi *multi,
       if(oldactions & CURL_POLL_IN)
         entry->readers--;
       if(!entry->users) {
-        if(multi->socket_cb)
-          multi->socket_cb(data, s, CURL_POLL_REMOVE,
-                           multi->socket_userp,
-                           entry->socketp);
+        if(multi->socket_cb) {
+          rc = multi->socket_cb(data, s, CURL_POLL_REMOVE,
+                                multi->socket_userp, entry->socketp);
+          if(rc == -1) {
+            multi->dead = TRUE;
+            return CURLM_ABORTED_BY_CALLBACK;
+          }
+        }
         sh_delentry(entry, &multi->sockhash, s);
       }
       else {
@@ -2857,9 +2939,11 @@ static CURLMcode singlesocket(struct Curl_multi *multi,
   return CURLM_OK;
 }
 
-void Curl_updatesocket(struct Curl_easy *data)
+CURLcode Curl_updatesocket(struct Curl_easy *data)
 {
-  singlesocket(data->multi, data);
+  if(singlesocket(data->multi, data))
+    return CURLE_ABORTED_BY_CALLBACK;
+  return CURLE_OK;
 }
 
 
@@ -2884,13 +2968,18 @@ void Curl_multi_closed(struct Curl_easy *data, curl_socket_t s)
       struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s);
 
       if(entry) {
+        int rc = 0;
         if(multi->socket_cb)
-          multi->socket_cb(data, s, CURL_POLL_REMOVE,
-                           multi->socket_userp,
-                           entry->socketp);
+          rc = multi->socket_cb(data, s, CURL_POLL_REMOVE,
+                                multi->socket_userp, entry->socketp);
 
         /* now remove it from the socket hash */
         sh_delentry(entry, &multi->sockhash, s);
+        if(rc == -1)
+          /* This just marks the multi handle as "dead" without returning an
+             error code primarily because this function is used from many
+             places where propagating an error back is tricky. */
+          multi->dead = TRUE;
       }
     }
   }
@@ -3150,7 +3239,7 @@ CURLMcode curl_multi_socket(struct Curl_multi *multi, curl_socket_t s,
     return CURLM_RECURSIVE_API_CALL;
   result = multi_socket(multi, FALSE, s, 0, running_handles);
   if(CURLM_OK >= result)
-    Curl_update_timer(multi);
+    result = Curl_update_timer(multi);
   return result;
 }
 
@@ -3162,7 +3251,7 @@ CURLMcode curl_multi_socket_action(struct Curl_multi *multi, curl_socket_t s,
     return CURLM_RECURSIVE_API_CALL;
   result = multi_socket(multi, FALSE, s, ev_bitmask, running_handles);
   if(CURLM_OK >= result)
-    Curl_update_timer(multi);
+    result = Curl_update_timer(multi);
   return result;
 }
 
@@ -3173,14 +3262,19 @@ CURLMcode curl_multi_socket_all(struct Curl_multi *multi, int *running_handles)
     return CURLM_RECURSIVE_API_CALL;
   result = multi_socket(multi, TRUE, CURL_SOCKET_BAD, 0, running_handles);
   if(CURLM_OK >= result)
-    Curl_update_timer(multi);
+    result = Curl_update_timer(multi);
   return result;
 }
 
 static CURLMcode multi_timeout(struct Curl_multi *multi,
                                long *timeout_ms)
 {
-  static struct curltime tv_zero = {0, 0};
+  static const struct curltime tv_zero = {0, 0};
+
+  if(multi->dead) {
+    *timeout_ms = 0;
+    return CURLM_OK;
+  }
 
   if(multi->timetree) {
     /* we have a tree of expire times */
@@ -3233,14 +3327,15 @@ CURLMcode curl_multi_timeout(struct Curl_multi *multi,
  * Tell the application it should update its timers, if it subscribes to the
  * update timer callback.
  */
-void Curl_update_timer(struct Curl_multi *multi)
+CURLMcode Curl_update_timer(struct Curl_multi *multi)
 {
   long timeout_ms;
+  int rc;
 
-  if(!multi->timer_cb)
-    return;
+  if(!multi->timer_cb || multi->dead)
+    return CURLM_OK;
   if(multi_timeout(multi, &timeout_ms)) {
-    return;
+    return CURLM_OK;
   }
   if(timeout_ms < 0) {
     static const struct curltime none = {0, 0};
@@ -3248,10 +3343,14 @@ void Curl_update_timer(struct Curl_multi *multi)
       multi->timer_lastcall = none;
       /* there's no timeout now but there was one previously, tell the app to
          disable it */
-      multi->timer_cb(multi, -1, multi->timer_userp);
-      return;
+      rc = multi->timer_cb(multi, -1, multi->timer_userp);
+      if(rc == -1) {
+        multi->dead = TRUE;
+        return CURLM_ABORTED_BY_CALLBACK;
+      }
+      return CURLM_OK;
     }
-    return;
+    return CURLM_OK;
   }
 
   /* When multi_timeout() is done, multi->timetree points to the node with the
@@ -3259,11 +3358,16 @@ void Curl_update_timer(struct Curl_multi *multi)
    * if this is the same (fixed) time as we got in a previous call and then
    * avoid calling the callback again. */
   if(Curl_splaycomparekeys(multi->timetree->key, multi->timer_lastcall) == 0)
-    return;
+    return CURLM_OK;
 
   multi->timer_lastcall = multi->timetree->key;
 
-  multi->timer_cb(multi, timeout_ms, multi->timer_userp);
+  rc = multi->timer_cb(multi, timeout_ms, multi->timer_userp);
+  if(rc == -1) {
+    multi->dead = TRUE;
+    return CURLM_ABORTED_BY_CALLBACK;
+  }
+  return CURLM_OK;
 }
 
 /*
diff --git a/lib/multihandle.h b/lib/multihandle.h
index 2e4a6ff..db7f130 100644
--- a/lib/multihandle.h
+++ b/lib/multihandle.h
@@ -156,6 +156,8 @@ struct Curl_multi {
 #ifdef USE_OPENSSL
   bool ssl_seeded;
 #endif
+  bool dead; /* a callback returned error, everything needs to crash and
+                burn */
 };
 
 #endif /* HEADER_CURL_MULTIHANDLE_H */
diff --git a/lib/multiif.h b/lib/multiif.h
index 2fbef53..f4d0ada 100644
--- a/lib/multiif.h
+++ b/lib/multiif.h
@@ -26,11 +26,11 @@
  * Prototypes for library-wide functions provided by multi.c
  */
 
-void Curl_updatesocket(struct Curl_easy *data);
+CURLcode Curl_updatesocket(struct Curl_easy *data);
 void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id);
 void Curl_expire_clear(struct Curl_easy *data);
 void Curl_expire_done(struct Curl_easy *data, expire_id id);
-void Curl_update_timer(struct Curl_multi *multi);
+CURLMcode Curl_update_timer(struct Curl_multi *multi) WARN_UNUSED_RESULT;
 void Curl_attach_connnection(struct Curl_easy *data,
                              struct connectdata *conn);
 void Curl_detach_connnection(struct Curl_easy *data);
diff --git a/lib/openldap.c b/lib/openldap.c
index fb5e743..0ffb6a3 100644
--- a/lib/openldap.c
+++ b/lib/openldap.c
@@ -70,6 +70,17 @@
  */
 /* #define CURL_OPENLDAP_DEBUG */
 
+/* Machine states. */
+typedef enum {
+  OLDAP_STOP,           /* Do nothing state, stops the state machine */
+  OLDAP_SSL,            /* Performing SSL handshake. */
+  OLDAP_STARTTLS,       /* STARTTLS request sent. */
+  OLDAP_TLS,            /* Performing TLS handshake. */
+  OLDAP_BIND,           /* Simple bind reply. */
+  OLDAP_BINDV2,         /* Simple bind reply in protocol version 2. */
+  OLDAP_LAST            /* Never used */
+} ldapstate;
+
 #ifndef _LDAP_PVT_H
 extern int ldap_pvt_url_scheme2proto(const char *);
 extern int ldap_init_fd(ber_socket_t fd, int proto, const char *url,
@@ -143,29 +154,13 @@ const struct Curl_handler Curl_handler_ldaps = {
 };
 #endif
 
-static const char *url_errs[] = {
-  "success",
-  "out of memory",
-  "bad parameter",
-  "unrecognized scheme",
-  "unbalanced delimiter",
-  "bad URL",
-  "bad host or port",
-  "bad or missing attributes",
-  "bad or missing scope",
-  "bad or missing filter",
-  "bad or missing extensions"
-};
-
 struct ldapconninfo {
-  LDAP *ld;
-  Curl_recv *recv;  /* for stacking SSL handler */
+  LDAP *ld;                  /* Openldap connection handle. */
+  Curl_recv *recv;           /* For stacking SSL handler */
   Curl_send *send;
-  int proto;
-  int msgid;
-  bool ssldone;
-  bool sslinst;
-  bool didbind;
+  ldapstate state;           /* Current machine state. */
+  int proto;                 /* LDAP_PROTO_TCP/LDAP_PROTO_UDP/LDAP_PROTO_IPC */
+  int msgid;                 /* Current message id. */
 };
 
 struct ldapreqinfo {
@@ -173,194 +168,379 @@ struct ldapreqinfo {
   int nument;
 };
 
-static CURLcode oldap_setup_connection(struct Curl_easy *data,
-                                       struct connectdata *conn)
+/*
+ * state()
+ *
+ * This is the ONLY way to change LDAP state!
+ */
+static void state(struct Curl_easy *data, ldapstate newstate)
 {
-  struct ldapconninfo *li;
-  LDAPURLDesc *lud;
-  int rc, proto;
-  CURLcode status;
+  struct ldapconninfo *ldapc = data->conn->proto.ldapc;
+
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+  /* for debug purposes */
+  static const char * const names[] = {
+    "STOP",
+    "SSL",
+    "STARTTLS",
+    "TLS",
+    "BIND",
+    "BINDV2",
+    /* LAST */
+  };
+
+  if(ldapc->state != newstate)
+    infof(data, "LDAP %p state change from %s to %s",
+          (void *)ldapc, names[ldapc->state], names[newstate]);
+#endif
+
+  ldapc->state = newstate;
+}
 
-  rc = ldap_url_parse(data->state.url, &lud);
+/* Map some particular LDAP error codes to CURLcode values. */
+static CURLcode oldap_map_error(int rc, CURLcode result)
+{
+  switch(rc) {
+  case LDAP_NO_MEMORY:
+    result = CURLE_OUT_OF_MEMORY;
+    break;
+  case LDAP_INVALID_CREDENTIALS:
+    result = CURLE_LOGIN_DENIED;
+    break;
+  case LDAP_PROTOCOL_ERROR:
+    result = CURLE_UNSUPPORTED_PROTOCOL;
+    break;
+  case LDAP_INSUFFICIENT_ACCESS:
+    result = CURLE_REMOTE_ACCESS_DENIED;
+    break;
+  }
+  return result;
+}
+
+static CURLcode oldap_url_parse(struct Curl_easy *data, LDAPURLDesc **ludp)
+{
+  CURLcode result = CURLE_OK;
+  int rc = LDAP_URL_ERR_BADURL;
+  static const char * const url_errs[] = {
+    "success",
+    "out of memory",
+    "bad parameter",
+    "unrecognized scheme",
+    "unbalanced delimiter",
+    "bad URL",
+    "bad host or port",
+    "bad or missing attributes",
+    "bad or missing scope",
+    "bad or missing filter",
+    "bad or missing extensions"
+  };
+
+  *ludp = NULL;
+  if(!data->state.up.user && !data->state.up.password &&
+     !data->state.up.options)
+    rc = ldap_url_parse(data->state.url, ludp);
   if(rc != LDAP_URL_SUCCESS) {
     const char *msg = "url parsing problem";
-    status = CURLE_URL_MALFORMAT;
-    if(rc > LDAP_URL_SUCCESS && rc <= LDAP_URL_ERR_BADEXTS) {
-      if(rc == LDAP_URL_ERR_MEM)
-        status = CURLE_OUT_OF_MEMORY;
+
+    result = rc == LDAP_URL_ERR_MEM? CURLE_OUT_OF_MEMORY: CURLE_URL_MALFORMAT;
+    rc -= LDAP_URL_SUCCESS;
+    if((size_t) rc < sizeof(url_errs) / sizeof(url_errs[0]))
       msg = url_errs[rc];
-    }
     failf(data, "LDAP local: %s", msg);
-    return status;
   }
-  proto = ldap_pvt_url_scheme2proto(lud->lud_scheme);
+  return result;
+}
+
+static CURLcode oldap_setup_connection(struct Curl_easy *data,
+                                       struct connectdata *conn)
+{
+  CURLcode result;
+  LDAPURLDesc *lud;
+  struct ldapconninfo *li;
+
+  /* Early URL syntax check. */
+  result = oldap_url_parse(data, &lud);
   ldap_free_urldesc(lud);
 
-  li = calloc(1, sizeof(struct ldapconninfo));
-  if(!li)
-    return CURLE_OUT_OF_MEMORY;
-  li->proto = proto;
-  conn->proto.ldapc = li;
-  connkeep(conn, "OpenLDAP default");
-  return CURLE_OK;
+  if(!result) {
+    li = calloc(1, sizeof(struct ldapconninfo));
+    if(!li)
+      result = CURLE_OUT_OF_MEMORY;
+    else {
+      li->proto = ldap_pvt_url_scheme2proto(data->state.up.scheme);
+      conn->proto.ldapc = li;
+      connkeep(conn, "OpenLDAP default");
+
+      /* Clear the TLS upgraded flag */
+      conn->bits.tls_upgraded = FALSE;
+    }
+  }
+
+  return result;
+}
+
+/* Starts LDAP simple bind. */
+static CURLcode oldap_perform_bind(struct Curl_easy *data, ldapstate newstate)
+{
+  CURLcode result = CURLE_OK;
+  struct connectdata *conn = data->conn;
+  struct ldapconninfo *li = conn->proto.ldapc;
+  char *binddn = NULL;
+  struct berval passwd;
+  int rc;
+
+  passwd.bv_val = NULL;
+  passwd.bv_len = 0;
+
+  if(conn->bits.user_passwd) {
+    binddn = conn->user;
+    passwd.bv_val = conn->passwd;
+    passwd.bv_len = strlen(passwd.bv_val);
+  }
+
+  rc = ldap_sasl_bind(li->ld, binddn, LDAP_SASL_SIMPLE, &passwd,
+                      NULL, NULL, &li->msgid);
+  if(rc == LDAP_SUCCESS)
+    state(data, newstate);
+  else
+    result = oldap_map_error(rc,
+                             conn->bits.user_passwd?
+                             CURLE_LOGIN_DENIED: CURLE_LDAP_CANNOT_BIND);
+  return result;
 }
 
 #ifdef USE_SSL
 static Sockbuf_IO ldapsb_tls;
-#endif
 
-static CURLcode oldap_connect(struct Curl_easy *data, bool *done)
+static bool ssl_installed(struct connectdata *conn)
+{
+  return conn->proto.ldapc->recv != NULL;
+}
+
+static CURLcode oldap_ssl_connect(struct Curl_easy *data, ldapstate newstate)
 {
+  CURLcode result = CURLE_OK;
   struct connectdata *conn = data->conn;
   struct ldapconninfo *li = conn->proto.ldapc;
-  int rc, proto = LDAP_VERSION3;
-  char hosturl[1024];
-  char *ptr;
+  bool ssldone = 0;
 
-  (void)done;
+  result = Curl_ssl_connect_nonblocking(data, conn, FALSE,
+                                        FIRSTSOCKET, &ssldone);
+  if(!result) {
+    state(data, newstate);
 
-  strcpy(hosturl, "ldap");
-  ptr = hosturl + 4;
-  if(conn->handler->flags & PROTOPT_SSL)
-    *ptr++ = 's';
-  msnprintf(ptr, sizeof(hosturl)-(ptr-hosturl), "://%s:%d",
-            conn->host.name, conn->remote_port);
+    if(ssldone) {
+      Sockbuf *sb;
 
-#ifdef CURL_OPENLDAP_DEBUG
-  static int do_trace = 0;
-  const char *env = getenv("CURL_OPENLDAP_TRACE");
-  do_trace = (env && strtol(env, NULL, 10) > 0);
-  if(do_trace) {
-    ldap_set_option(li->ld, LDAP_OPT_DEBUG_LEVEL, &do_trace);
+      /* Install the libcurl SSL handlers into the sockbuf. */
+      ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb);
+      ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data);
+      li->recv = conn->recv[FIRSTSOCKET];
+      li->send = conn->send[FIRSTSOCKET];
+    }
   }
+
+  return result;
+}
+
+/* Send the STARTTLS request */
+static CURLcode oldap_perform_starttls(struct Curl_easy *data)
+{
+  CURLcode result = CURLE_OK;
+  struct ldapconninfo *li = data->conn->proto.ldapc;
+  int rc = ldap_start_tls(li->ld, NULL, NULL, &li->msgid);
+
+  if(rc == LDAP_SUCCESS)
+    state(data, OLDAP_STARTTLS);
+  else
+    result = oldap_map_error(rc, CURLE_USE_SSL_FAILED);
+  return result;
+}
 #endif
 
+static CURLcode oldap_connect(struct Curl_easy *data, bool *done)
+{
+  struct connectdata *conn = data->conn;
+  struct ldapconninfo *li = conn->proto.ldapc;
+  static const int version = LDAP_VERSION3;
+  int rc;
+  char *hosturl;
+#ifdef CURL_OPENLDAP_DEBUG
+  static int do_trace = -1;
+#endif
+
+  (void)done;
+
+  hosturl = aprintf("ldap%s://%s:%d",
+                    conn->handler->flags & PROTOPT_SSL? "s": "",
+                    conn->host.name, conn->remote_port);
+  if(!hosturl)
+    return CURLE_OUT_OF_MEMORY;
+
   rc = ldap_init_fd(conn->sock[FIRSTSOCKET], li->proto, hosturl, &li->ld);
   if(rc) {
     failf(data, "LDAP local: Cannot connect to %s, %s",
           hosturl, ldap_err2string(rc));
+    free(hosturl);
     return CURLE_COULDNT_CONNECT;
   }
 
-  ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &proto);
+  free(hosturl);
+
+#ifdef CURL_OPENLDAP_DEBUG
+  if(do_trace < 0) {
+    const char *env = getenv("CURL_OPENLDAP_TRACE");
+    do_trace = (env && strtol(env, NULL, 10) > 0);
+  }
+  if(do_trace)
+    ldap_set_option(li->ld, LDAP_OPT_DEBUG_LEVEL, &do_trace);
+#endif
+
+  /* Try version 3 first. */
+  ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
+
+  /* Do not chase referrals. */
+  ldap_set_option(li->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
 
 #ifdef USE_SSL
-  if(conn->handler->flags & PROTOPT_SSL) {
-    CURLcode result;
-    result = Curl_ssl_connect_nonblocking(data, conn, FALSE,
-                                          FIRSTSOCKET, &li->ssldone);
-    if(result)
+  if(conn->handler->flags & PROTOPT_SSL)
+    return oldap_ssl_connect(data, OLDAP_SSL);
+
+  if(data->set.use_ssl) {
+    CURLcode result = oldap_perform_starttls(data);
+
+    if(!result || data->set.use_ssl != CURLUSESSL_TRY)
       return result;
   }
 #endif
 
-  return CURLE_OK;
+  /* Force bind even if anonymous bind is not needed in protocol version 3
+     to detect missing version 3 support. */
+  return oldap_perform_bind(data, OLDAP_BIND);
 }
 
-static CURLcode oldap_connecting(struct Curl_easy *data, bool *done)
+/* Handle a simple bind response. */
+static CURLcode oldap_state_bind_resp(struct Curl_easy *data, LDAPMessage *msg,
+                                      int code)
 {
   struct connectdata *conn = data->conn;
   struct ldapconninfo *li = conn->proto.ldapc;
-  LDAPMessage *msg = NULL;
-  struct timeval tv = {0, 1}, *tvp;
-  int rc, err;
-  char *info = NULL;
+  CURLcode result = CURLE_OK;
+  struct berval *bv = NULL;
+  int rc;
 
-#ifdef USE_SSL
-  if(conn->handler->flags & PROTOPT_SSL) {
-    /* Is the SSL handshake complete yet? */
-    if(!li->ssldone) {
-      CURLcode result = Curl_ssl_connect_nonblocking(data, conn, FALSE,
-                                                     FIRSTSOCKET,
-                                                     &li->ssldone);
-      if(result || !li->ssldone)
-        return result;
-    }
+  if(code != LDAP_SUCCESS)
+    return oldap_map_error(code, CURLE_LDAP_CANNOT_BIND);
 
-    /* Have we installed the libcurl SSL handlers into the sockbuf yet? */
-    if(!li->sslinst) {
-      Sockbuf *sb;
-      ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb);
-      ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data);
-      li->sslinst = TRUE;
-      li->recv = conn->recv[FIRSTSOCKET];
-      li->send = conn->send[FIRSTSOCKET];
-    }
+  rc = ldap_parse_sasl_bind_result(li->ld, msg, &bv, 0);
+  if(rc != LDAP_SUCCESS) {
+    failf(data, "LDAP local: bind ldap_parse_sasl_bind_result %s",
+          ldap_err2string(rc));
+    result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND);
   }
-#endif
+  else
+    state(data, OLDAP_STOP);
 
-  tvp = &tv;
-
-  retry:
-  if(!li->didbind) {
-    char *binddn;
-    struct berval passwd;
+  if(bv)
+    ber_bvfree(bv);
+  return result;
+}
 
-    if(conn->bits.user_passwd) {
-      binddn = conn->user;
-      passwd.bv_val = conn->passwd;
-      passwd.bv_len = strlen(passwd.bv_val);
+static CURLcode oldap_connecting(struct Curl_easy *data, bool *done)
+{
+  CURLcode result = CURLE_OK;
+  struct connectdata *conn = data->conn;
+  struct ldapconninfo *li = conn->proto.ldapc;
+  LDAPMessage *msg = NULL;
+  struct timeval tv = {0, 0};
+  int code = LDAP_SUCCESS;
+  int rc;
+
+  if(li->state != OLDAP_SSL && li->state != OLDAP_TLS) {
+    /* Get response to last command. */
+    rc = ldap_result(li->ld, li->msgid, LDAP_MSG_ONE, &tv, &msg);
+    if(!rc)
+      return CURLE_OK;                    /* Timed out. */
+    if(rc < 0) {
+      failf(data, "LDAP local: connecting ldap_result %s",
+            ldap_err2string(rc));
+      return oldap_map_error(rc, CURLE_COULDNT_CONNECT);
     }
+
+    /* Get error code from message. */
+    rc = ldap_parse_result(li->ld, msg, &code, NULL, NULL, NULL, NULL, 0);
+    if(rc)
+      code = rc;
     else {
-      binddn = NULL;
-      passwd.bv_val = NULL;
-      passwd.bv_len = 0;
+      /* store the latest code for later retrieval */
+      data->info.httpcode = code;
     }
-    rc = ldap_sasl_bind(li->ld, binddn, LDAP_SASL_SIMPLE, &passwd,
-                        NULL, NULL, &li->msgid);
-    if(rc)
-      return CURLE_LDAP_CANNOT_BIND;
-    li->didbind = TRUE;
-    if(tvp)
-      return CURLE_OK;
-  }
 
-  rc = ldap_result(li->ld, li->msgid, LDAP_MSG_ONE, tvp, &msg);
-  if(rc < 0) {
-    failf(data, "LDAP local: bind ldap_result %s", ldap_err2string(rc));
-    return CURLE_LDAP_CANNOT_BIND;
-  }
-  if(rc == 0) {
-    /* timed out */
-    return CURLE_OK;
-  }
+    /* If protocol version 3 is not supported, fallback to version 2. */
+    if(code == LDAP_PROTOCOL_ERROR && li->state != OLDAP_BINDV2
+#ifdef USE_SSL
+       && (ssl_installed(conn) || data->set.use_ssl <= CURLUSESSL_TRY)
+#endif
+       ) {
+      static const int version = LDAP_VERSION2;
 
-  rc = ldap_parse_result(li->ld, msg, &err, NULL, &info, NULL, NULL, 1);
-  if(rc) {
-    failf(data, "LDAP local: bind ldap_parse_result %s", ldap_err2string(rc));
-    return CURLE_LDAP_CANNOT_BIND;
+      ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &version);
+      ldap_msgfree(msg);
+      return oldap_perform_bind(data, OLDAP_BINDV2);
+    }
   }
 
-  /* Try to fallback to LDAPv2? */
-  if(err == LDAP_PROTOCOL_ERROR) {
-    int proto;
-    ldap_get_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &proto);
-    if(proto == LDAP_VERSION3) {
-      if(info) {
-        ldap_memfree(info);
-        info = NULL;
+  /* Handle response message according to current state. */
+  switch(li->state) {
+
+#ifdef USE_SSL
+  case OLDAP_SSL:
+    result = oldap_ssl_connect(data, OLDAP_SSL);
+    if(!result && ssl_installed(conn))
+      result = oldap_perform_bind(data, OLDAP_BIND);
+    break;
+  case OLDAP_STARTTLS:
+    if(code != LDAP_SUCCESS) {
+      if(data->set.use_ssl != CURLUSESSL_TRY)
+        result = oldap_map_error(code, CURLE_USE_SSL_FAILED);
+      else
+        result = oldap_perform_bind(data, OLDAP_BIND);
+      break;
+    }
+    /* FALLTHROUGH */
+  case OLDAP_TLS:
+    result = oldap_ssl_connect(data, OLDAP_TLS);
+    if(result && data->set.use_ssl != CURLUSESSL_TRY)
+      result = oldap_map_error(code, CURLE_USE_SSL_FAILED);
+    else if(ssl_installed(conn)) {
+      conn->bits.tls_upgraded = TRUE;
+      if(conn->bits.user_passwd)
+        result = oldap_perform_bind(data, OLDAP_BIND);
+      else {
+        state(data, OLDAP_STOP); /* Version 3 supported: no bind required */
+        result = CURLE_OK;
       }
-      proto = LDAP_VERSION2;
-      ldap_set_option(li->ld, LDAP_OPT_PROTOCOL_VERSION, &proto);
-      li->didbind = FALSE;
-      goto retry;
     }
-  }
+    break;
+#endif
 
-  if(err) {
-    failf(data, "LDAP remote: bind failed %s %s", ldap_err2string(rc),
-          info ? info : "");
-    if(info)
-      ldap_memfree(info);
-    return CURLE_LOGIN_DENIED;
+  case OLDAP_BIND:
+  case OLDAP_BINDV2:
+    result = oldap_state_bind_resp(data, msg, code);
+    break;
+  default:
+    /* internal error */
+    result = CURLE_COULDNT_CONNECT;
+    break;
   }
 
-  if(info)
-    ldap_memfree(info);
-  conn->recv[FIRSTSOCKET] = oldap_recv;
-  *done = TRUE;
+  ldap_msgfree(msg);
 
-  return CURLE_OK;
+  *done = li->state == OLDAP_STOP;
+  if(*done)
+    conn->recv[FIRSTSOCKET] = oldap_recv;
+
+  return result;
 }
 
 static CURLcode oldap_disconnect(struct Curl_easy *data,
@@ -373,7 +553,7 @@ static CURLcode oldap_disconnect(struct Curl_easy *data,
   if(li) {
     if(li->ld) {
 #ifdef USE_SSL
-      if(conn->ssl[FIRSTSOCKET].use) {
+      if(ssl_installed(conn)) {
         Sockbuf *sb;
         ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb);
         ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data);
@@ -393,44 +573,40 @@ static CURLcode oldap_do(struct Curl_easy *data, bool *done)
   struct connectdata *conn = data->conn;
   struct ldapconninfo *li = conn->proto.ldapc;
   struct ldapreqinfo *lr;
-  CURLcode status = CURLE_OK;
-  int rc = 0;
-  LDAPURLDesc *ludp = NULL;
+  CURLcode result;
+  int rc;
+  LDAPURLDesc *lud;
   int msgid;
 
   connkeep(conn, "OpenLDAP do");
 
   infof(data, "LDAP local: %s", data->state.url);
 
-  rc = ldap_url_parse(data->state.url, &ludp);
-  if(rc != LDAP_URL_SUCCESS) {
-    const char *msg = "url parsing problem";
-    status = CURLE_URL_MALFORMAT;
-    if(rc > LDAP_URL_SUCCESS && rc <= LDAP_URL_ERR_BADEXTS) {
-      if(rc == LDAP_URL_ERR_MEM)
-        status = CURLE_OUT_OF_MEMORY;
-      msg = url_errs[rc];
+  result = oldap_url_parse(data, &lud);
+  if(!result) {
+    rc = ldap_search_ext(li->ld, lud->lud_dn, lud->lud_scope,
+                         lud->lud_filter, lud->lud_attrs, 0,
+                         NULL, NULL, NULL, 0, &msgid);
+    ldap_free_urldesc(lud);
+    if(rc != LDAP_SUCCESS) {
+      failf(data, "LDAP local: ldap_search_ext %s", ldap_err2string(rc));
+      result = CURLE_LDAP_SEARCH_FAILED;
+    }
+    else {
+      lr = calloc(1, sizeof(struct ldapreqinfo));
+      if(!lr) {
+        ldap_abandon_ext(li->ld, msgid, NULL, NULL);
+        result = CURLE_OUT_OF_MEMORY;
+      }
+      else {
+        lr->msgid = msgid;
+        data->req.p.ldap = lr;
+        Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
+        *done = TRUE;
+      }
     }
-    failf(data, "LDAP local: %s", msg);
-    return status;
-  }
-
-  rc = ldap_search_ext(li->ld, ludp->lud_dn, ludp->lud_scope,
-                       ludp->lud_filter, ludp->lud_attrs, 0,
-                       NULL, NULL, NULL, 0, &msgid);
-  ldap_free_urldesc(ludp);
-  if(rc != LDAP_SUCCESS) {
-    failf(data, "LDAP local: ldap_search_ext %s", ldap_err2string(rc));
-    return CURLE_LDAP_SEARCH_FAILED;
   }
-  lr = calloc(1, sizeof(struct ldapreqinfo));
-  if(!lr)
-    return CURLE_OUT_OF_MEMORY;
-  lr->msgid = msgid;
-  data->req.p.ldap = lr;
-  Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
-  *done = TRUE;
-  return CURLE_OK;
+  return result;
 }
 
 static CURLcode oldap_done(struct Curl_easy *data, CURLcode res,
@@ -456,163 +632,146 @@ static CURLcode oldap_done(struct Curl_easy *data, CURLcode res,
   return CURLE_OK;
 }
 
+static CURLcode client_write(struct Curl_easy *data, const char *prefix,
+                             const char *value, size_t len, const char *suffix)
+{
+  CURLcode result = CURLE_OK;
+  size_t l;
+
+  if(prefix) {
+    l = strlen(prefix);
+    /* If we have a zero-length value and the prefix ends with a space
+       separator, drop the latter. */
+    if(!len && l && prefix[l - 1] == ' ')
+      l--;
+    result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) prefix, l);
+    if(!result)
+      data->req.bytecount += l;
+  }
+  if(!result && value) {
+    result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) value, len);
+    if(!result)
+      data->req.bytecount += len;
+  }
+  if(!result && suffix) {
+    l = strlen(suffix);
+    result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) suffix, l);
+    if(!result)
+      data->req.bytecount += l;
+  }
+  return result;
+}
+
 static ssize_t oldap_recv(struct Curl_easy *data, int sockindex, char *buf,
                           size_t len, CURLcode *err)
 {
   struct connectdata *conn = data->conn;
   struct ldapconninfo *li = conn->proto.ldapc;
   struct ldapreqinfo *lr = data->req.p.ldap;
-  int rc, ret;
+  int rc;
   LDAPMessage *msg = NULL;
-  LDAPMessage *ent;
   BerElement *ber = NULL;
-  struct timeval tv = {0, 1};
+  struct timeval tv = {0, 0};
+  struct berval bv, *bvals;
+  int binary = 0;
+  CURLcode result = CURLE_AGAIN;
+  int code;
+  char *info = NULL;
 
   (void)len;
   (void)buf;
   (void)sockindex;
 
-  rc = ldap_result(li->ld, lr->msgid, LDAP_MSG_RECEIVED, &tv, &msg);
+  rc = ldap_result(li->ld, lr->msgid, LDAP_MSG_ONE, &tv, &msg);
   if(rc < 0) {
     failf(data, "LDAP local: search ldap_result %s", ldap_err2string(rc));
-    *err = CURLE_RECV_ERROR;
-    return -1;
+    result = CURLE_RECV_ERROR;
   }
 
-  *err = CURLE_AGAIN;
-  ret = -1;
+  *err = result;
 
-  /* timed out */
+  /* error or timed out */
   if(!msg)
-    return ret;
-
-  for(ent = ldap_first_message(li->ld, msg); ent;
-      ent = ldap_next_message(li->ld, ent)) {
-    struct berval bv, *bvals;
-    int binary = 0, msgtype;
-    CURLcode writeerr;
-
-    msgtype = ldap_msgtype(ent);
-    if(msgtype == LDAP_RES_SEARCH_RESULT) {
-      int code;
-      char *info = NULL;
-      rc = ldap_parse_result(li->ld, ent, &code, NULL, &info, NULL, NULL, 0);
-      if(rc) {
-        failf(data, "LDAP local: search ldap_parse_result %s",
-              ldap_err2string(rc));
-        *err = CURLE_LDAP_SEARCH_FAILED;
-      }
-      else if(code && code != LDAP_SIZELIMIT_EXCEEDED) {
-        failf(data, "LDAP remote: search failed %s %s", ldap_err2string(rc),
-              info ? info : "");
-        *err = CURLE_LDAP_SEARCH_FAILED;
-      }
-      else {
-        /* successful */
-        if(code == LDAP_SIZELIMIT_EXCEEDED)
-          infof(data, "There are more than %d entries", lr->nument);
-        data->req.size = data->req.bytecount;
-        *err = CURLE_OK;
-        ret = 0;
-      }
-      lr->msgid = 0;
-      ldap_memfree(info);
+    return -1;
+
+  result = CURLE_OK;
+
+  switch(ldap_msgtype(msg)) {
+  case LDAP_RES_SEARCH_RESULT:
+    lr->msgid = 0;
+    rc = ldap_parse_result(li->ld, msg, &code, NULL, &info, NULL, NULL, 0);
+    if(rc) {
+      failf(data, "LDAP local: search ldap_parse_result %s",
+            ldap_err2string(rc));
+      result = CURLE_LDAP_SEARCH_FAILED;
       break;
     }
-    else if(msgtype != LDAP_RES_SEARCH_ENTRY)
-      continue;
 
+    /* store the latest code for later retrieval */
+    data->info.httpcode = code;
+
+    switch(code) {
+    case LDAP_SIZELIMIT_EXCEEDED:
+      infof(data, "There are more than %d entries", lr->nument);
+      /* FALLTHROUGH */
+    case LDAP_SUCCESS:
+      data->req.size = data->req.bytecount;
+      break;
+    default:
+      failf(data, "LDAP remote: search failed %s %s", ldap_err2string(code),
+            info ? info : "");
+      result = CURLE_LDAP_SEARCH_FAILED;
+      break;
+    }
+    if(info)
+      ldap_memfree(info);
+    break;
+  case LDAP_RES_SEARCH_ENTRY:
     lr->nument++;
-    rc = ldap_get_dn_ber(li->ld, ent, &ber, &bv);
+    rc = ldap_get_dn_ber(li->ld, msg, &ber, &bv);
     if(rc < 0) {
-      *err = CURLE_RECV_ERROR;
-      return -1;
-    }
-    writeerr = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"DN: ", 4);
-    if(writeerr) {
-      *err = writeerr;
-      return -1;
-    }
-
-    writeerr = Curl_client_write(data, CLIENTWRITE_BODY, (char *)bv.bv_val,
-                                 bv.bv_len);
-    if(writeerr) {
-      *err = writeerr;
-      return -1;
+      result = CURLE_RECV_ERROR;
+      break;
     }
 
-    writeerr = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 1);
-    if(writeerr) {
-      *err = writeerr;
-      return -1;
-    }
-    data->req.bytecount += bv.bv_len + 5;
+    result = client_write(data, "DN: ", bv.bv_val, bv.bv_len, "\n");
+    if(result)
+      break;
 
-    for(rc = ldap_get_attribute_ber(li->ld, ent, ber, &bv, &bvals);
+    for(rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals);
         rc == LDAP_SUCCESS;
-        rc = ldap_get_attribute_ber(li->ld, ent, ber, &bv, &bvals)) {
+        rc = ldap_get_attribute_ber(li->ld, msg, ber, &bv, &bvals)) {
       int i;
 
       if(!bv.bv_val)
         break;
 
-      if(bv.bv_len > 7 && !strncmp(bv.bv_val + bv.bv_len - 7, ";binary", 7))
-        binary = 1;
-      else
-        binary = 0;
-
       if(!bvals) {
-        writeerr = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\t", 1);
-        if(writeerr) {
-          *err = writeerr;
-          return -1;
-        }
-        writeerr = Curl_client_write(data, CLIENTWRITE_BODY, (char *)bv.bv_val,
-                                     bv.bv_len);
-        if(writeerr) {
-          *err = writeerr;
-          return -1;
-        }
-        writeerr = Curl_client_write(data, CLIENTWRITE_BODY, (char *)":\n", 2);
-        if(writeerr) {
-          *err = writeerr;
-          return -1;
-        }
-        data->req.bytecount += bv.bv_len + 3;
+        result = client_write(data, "\t", bv.bv_val, bv.bv_len, ":\n");
+        if(result)
+          break;
         continue;
       }
 
+      binary = bv.bv_len > 7 &&
+               !strncmp(bv.bv_val + bv.bv_len - 7, ";binary", 7);
+
       for(i = 0; bvals[i].bv_val != NULL; i++) {
         int binval = 0;
-        writeerr = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\t", 1);
-        if(writeerr) {
-          *err = writeerr;
-          return -1;
-        }
 
-        writeerr = Curl_client_write(data, CLIENTWRITE_BODY, (char *)bv.bv_val,
-                                     bv.bv_len);
-        if(writeerr) {
-          *err = writeerr;
-          return -1;
-        }
-
-        writeerr = Curl_client_write(data, CLIENTWRITE_BODY, (char *)":", 1);
-        if(writeerr) {
-          *err = writeerr;
-          return -1;
-        }
-        data->req.bytecount += bv.bv_len + 2;
+        result = client_write(data, "\t", bv.bv_val, bv.bv_len, ":");
+        if(result)
+          break;
 
         if(!binary) {
           /* check for leading or trailing whitespace */
           if(ISSPACE(bvals[i].bv_val[0]) ||
-             ISSPACE(bvals[i].bv_val[bvals[i].bv_len-1]))
+             ISSPACE(bvals[i].bv_val[bvals[i].bv_len - 1]))
             binval = 1;
           else {
             /* check for unprintable characters */
             unsigned int j;
-            for(j = 0; j<bvals[i].bv_len; j++)
+            for(j = 0; j < bvals[i].bv_len; j++)
               if(!ISPRINT(bvals[i].bv_val[j])) {
                 binval = 1;
                 break;
@@ -622,80 +781,42 @@ static ssize_t oldap_recv(struct Curl_easy *data, int sockindex, char *buf,
         if(binary || binval) {
           char *val_b64 = NULL;
           size_t val_b64_sz = 0;
-          /* Binary value, encode to base64. */
-          CURLcode error = Curl_base64_encode(data,
-                                              bvals[i].bv_val,
-                                              bvals[i].bv_len,
-                                              &val_b64,
-                                              &val_b64_sz);
-          if(error) {
-            ber_memfree(bvals);
-            ber_free(ber, 0);
-            ldap_msgfree(msg);
-            *err = error;
-            return -1;
-          }
-          writeerr = Curl_client_write(data, CLIENTWRITE_BODY,
-                                       (char *)": ", 2);
-          if(writeerr) {
-            *err = writeerr;
-            return -1;
-          }
-
-          data->req.bytecount += 2;
-          if(val_b64_sz > 0) {
-            writeerr = Curl_client_write(data, CLIENTWRITE_BODY, val_b64,
-                                         val_b64_sz);
-            if(writeerr) {
-              *err = writeerr;
-              return -1;
-            }
-            free(val_b64);
-            data->req.bytecount += val_b64_sz;
-          }
-        }
-        else {
-          writeerr = Curl_client_write(data, CLIENTWRITE_BODY, (char *)" ", 1);
-          if(writeerr) {
-            *err = writeerr;
-            return -1;
-          }
 
-          writeerr = Curl_client_write(data, CLIENTWRITE_BODY, bvals[i].bv_val,
-                                       bvals[i].bv_len);
-          if(writeerr) {
-            *err = writeerr;
-            return -1;
-          }
-
-          data->req.bytecount += bvals[i].bv_len + 1;
-        }
-        writeerr = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 1);
-        if(writeerr) {
-          *err = writeerr;
-          return -1;
+          /* Binary value, encode to base64. */
+          if(bvals[i].bv_len)
+            result = Curl_base64_encode(data, bvals[i].bv_val, bvals[i].bv_len,
+                                        &val_b64, &val_b64_sz);
+          if(!result)
+            result = client_write(data, ": ", val_b64, val_b64_sz, "\n");
+          free(val_b64);
         }
-
-        data->req.bytecount++;
+        else
+          result = client_write(data, " ",
+                                bvals[i].bv_val, bvals[i].bv_len, "\n");
+        if(result)
+          break;
       }
+
       ber_memfree(bvals);
-      writeerr = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 1);
-      if(writeerr) {
-        *err = writeerr;
-        return -1;
-      }
-      data->req.bytecount++;
-    }
-    writeerr = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 1);
-    if(writeerr) {
-      *err = writeerr;
-      return -1;
+      bvals = NULL;
+      if(!result)
+        result = client_write(data, "\n", NULL, 0, NULL);
+      if(result)
+        break;
     }
-    data->req.bytecount++;
+
     ber_free(ber, 0);
+
+    if(!result)
+      result = client_write(data, "\n", NULL, 0, NULL);
+    if(!result)
+      result = CURLE_AGAIN;
+    break;
   }
+
   ldap_msgfree(msg);
-  return ret;
+  *err = result;
+  return result? -1: 0;
 }
 
 #ifdef USE_SSL
diff --git a/lib/pop3.c b/lib/pop3.c
index d3f3de6..d4ca678 100644
--- a/lib/pop3.c
+++ b/lib/pop3.c
@@ -78,6 +78,7 @@
 #include "select.h"
 #include "multiif.h"
 #include "url.h"
+#include "bufref.h"
 #include "curl_sasl.h"
 #include "curl_md5.h"
 #include "warnless.h"
@@ -103,12 +104,12 @@ static CURLcode pop3_setup_connection(struct Curl_easy *data,
 static CURLcode pop3_parse_url_options(struct connectdata *conn);
 static CURLcode pop3_parse_url_path(struct Curl_easy *data);
 static CURLcode pop3_parse_custom_request(struct Curl_easy *data);
-static CURLcode pop3_perform_auth(struct Curl_easy *data,
-                                  struct connectdata *conn, const char *mech,
-                                  const char *initresp);
-static CURLcode pop3_continue_auth(struct Curl_easy *data,
-                                   struct connectdata *conn, const char *resp);
-static void pop3_get_message(char *buffer, char **outptr);
+static CURLcode pop3_perform_auth(struct Curl_easy *data, const char *mech,
+                                  const struct bufref *initresp);
+static CURLcode pop3_continue_auth(struct Curl_easy *data, const char *mech,
+                                   const struct bufref *resp);
+static CURLcode pop3_cancel_auth(struct Curl_easy *data, const char *mech);
+static CURLcode pop3_get_message(struct Curl_easy *data, struct bufref *out);
 
 /*
  * POP3 protocol handler.
@@ -170,13 +171,16 @@ const struct Curl_handler Curl_handler_pop3s = {
 
 /* SASL parameters for the pop3 protocol */
 static const struct SASLproto saslpop3 = {
-  "pop",                      /* The service name */
-  '*',                        /* Code received when continuation is expected */
-  '+',                        /* Code to receive upon authentication success */
-  255 - 8,                    /* Maximum initial response length (no max) */
-  pop3_perform_auth,          /* Send authentication command */
-  pop3_continue_auth,         /* Send authentication continuation */
-  pop3_get_message            /* Get SASL response message */
+  "pop",                /* The service name */
+  pop3_perform_auth,    /* Send authentication command */
+  pop3_continue_auth,   /* Send authentication continuation */
+  pop3_cancel_auth,     /* Send authentication cancellation */
+  pop3_get_message,     /* Get SASL response message */
+  255 - 8,              /* Max line len - strlen("AUTH ") - 1 space - crlf */
+  '*',                  /* Code received when continuation is expected */
+  '+',                  /* Code to receive upon authentication success */
+  SASL_AUTH_DEFAULT,    /* Default mechanisms */
+  SASL_FLAG_BASE64      /* Configuration flags */
 };
 
 #ifdef USE_SSL
@@ -250,34 +254,32 @@ static bool pop3_endofresp(struct Curl_easy *data, struct connectdata *conn,
  *
  * Gets the authentication message from the response buffer.
  */
-static void pop3_get_message(char *buffer, char **outptr)
+static CURLcode pop3_get_message(struct Curl_easy *data, struct bufref *out)
 {
-  size_t len = strlen(buffer);
-  char *message = NULL;
+  char *message = data->state.buffer;
+  size_t len = strlen(message);
 
   if(len > 2) {
     /* Find the start of the message */
     len -= 2;
-    for(message = buffer + 2; *message == ' ' || *message == '\t';
-        message++, len--)
+    for(message += 2; *message == ' ' || *message == '\t'; message++, len--)
       ;
 
     /* Find the end of the message */
-    for(; len--;)
+    while(len--)
       if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
          message[len] != '\t')
         break;
 
     /* Terminate the message */
-    if(++len) {
-      message[len] = '\0';
-    }
+    message[++len] = '\0';
+    Curl_bufref_set(out, message, len, NULL);
   }
   else
     /* junk input => zero length output */
-    message = &buffer[len];
+    Curl_bufref_set(out, "", 0, NULL);
 
-  *outptr = message;
+  return CURLE_OK;
 }
 
 /***********************************************************************
@@ -474,16 +476,16 @@ static CURLcode pop3_perform_apop(struct Curl_easy *data,
  * authentication mechanism.
  */
 static CURLcode pop3_perform_auth(struct Curl_easy *data,
-                                  struct connectdata *conn,
                                   const char *mech,
-                                  const char *initresp)
+                                  const struct bufref *initresp)
 {
   CURLcode result = CURLE_OK;
-  struct pop3_conn *pop3c = &conn->proto.pop3c;
+  struct pop3_conn *pop3c = &data->conn->proto.pop3c;
+  const char *ir = (const char *) Curl_bufref_ptr(initresp);
 
-  if(initresp) {                                  /* AUTH <mech> ...<crlf> */
+  if(ir) {                                  /* AUTH <mech> ...<crlf> */
     /* Send the AUTH command with the initial response */
-    result = Curl_pp_sendf(data, &pop3c->pp, "AUTH %s %s", mech, initresp);
+    result = Curl_pp_sendf(data, &pop3c->pp, "AUTH %s %s", mech, ir);
   }
   else {
     /* Send the AUTH command */
@@ -497,15 +499,33 @@ static CURLcode pop3_perform_auth(struct Curl_easy *data,
  *
  * pop3_continue_auth()
  *
- * Sends SASL continuation data or cancellation.
+ * Sends SASL continuation data.
  */
 static CURLcode pop3_continue_auth(struct Curl_easy *data,
-                                   struct connectdata *conn,
-                                   const char *resp)
+                                   const char *mech,
+                                   const struct bufref *resp)
 {
-  struct pop3_conn *pop3c = &conn->proto.pop3c;
+  struct pop3_conn *pop3c = &data->conn->proto.pop3c;
+
+  (void)mech;
 
-  return Curl_pp_sendf(data, &pop3c->pp, "%s", resp);
+  return Curl_pp_sendf(data, &pop3c->pp,
+                       "%s", (const char *) Curl_bufref_ptr(resp));
+}
+
+/***********************************************************************
+ *
+ * pop3_cancel_auth()
+ *
+ * Sends SASL cancellation.
+ */
+static CURLcode pop3_cancel_auth(struct Curl_easy *data, const char *mech)
+{
+  struct pop3_conn *pop3c = &data->conn->proto.pop3c;
+
+  (void)mech;
+
+  return Curl_pp_sendf(data, &pop3c->pp, "*");
 }
 
 /***********************************************************************
@@ -532,7 +552,7 @@ static CURLcode pop3_perform_authentication(struct Curl_easy *data,
 
   if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_SASL) {
     /* Calculate the SASL login details */
-    result = Curl_sasl_start(&pop3c->sasl, data, conn, FALSE, &progress);
+    result = Curl_sasl_start(&pop3c->sasl, data, FALSE, &progress);
 
     if(!result)
       if(progress == SASL_INPROGRESS)
@@ -801,7 +821,7 @@ static CURLcode pop3_state_auth_resp(struct Curl_easy *data,
 
   (void)instate; /* no use for this yet */
 
-  result = Curl_sasl_continue(&pop3c->sasl, data, conn, pop3code, &progress);
+  result = Curl_sasl_continue(&pop3c->sasl, data, pop3code, &progress);
   if(!result)
     switch(progress) {
     case SASL_DONE:
@@ -1011,7 +1031,9 @@ static CURLcode pop3_statemachine(struct Curl_easy *data,
       break;
 
     case POP3_QUIT:
-      /* fallthrough, just stop! */
+      state(data, POP3_STOP);
+      break;
+
     default:
       /* internal error */
       state(data, POP3_STOP);
@@ -1102,7 +1124,7 @@ static CURLcode pop3_connect(struct Curl_easy *data, bool *done)
 
   /* Set the default preferred authentication type and mechanism */
   pop3c->preftype = POP3_TYPE_ANY;
-  Curl_sasl_init(&pop3c->sasl, &saslpop3);
+  Curl_sasl_init(&pop3c->sasl, data, &saslpop3);
 
   /* Initialise the pingpong layer */
   Curl_pp_setup(pp);
@@ -1343,8 +1365,6 @@ static CURLcode pop3_parse_url_options(struct connectdata *conn)
   struct pop3_conn *pop3c = &conn->proto.pop3c;
   const char *ptr = conn->options;
 
-  pop3c->sasl.resetprefs = TRUE;
-
   while(!result && ptr && *ptr) {
     const char *key = ptr;
     const char *value;
diff --git a/lib/select.c b/lib/select.c
index 52dca5a..70d7ee5 100644
--- a/lib/select.c
+++ b/lib/select.c
@@ -64,7 +64,7 @@
  * Waiting indefinitely with this function is not allowed, a
  * zero or negative timeout value will return immediately.
  * Timeout resolution, accuracy, as well as maximum supported
- * value is system dependent, neither factor is a citical issue
+ * value is system dependent, neither factor is a critical issue
  * for the intended use of this function in the library.
  *
  * Return values:
diff --git a/lib/sendf.c b/lib/sendf.c
index 14ca84b..bcfa27a 100644
--- a/lib/sendf.c
+++ b/lib/sendf.c
@@ -608,7 +608,7 @@ static CURLcode chop_write(struct Curl_easy *data,
 /* Curl_client_write() sends data to the write callback(s)
 
    The bit pattern defines to what "streams" to write to. Body and/or header.
-   The defines are in sendf.h of course. "len" is not allowed to be 0.
+   The defines are in sendf.h of course.
 
    If CURL_DO_LINEEND_CONV is enabled, data is converted IN PLACE to the
    local character encoding.  This is a problem and should be changed in
@@ -621,8 +621,10 @@ CURLcode Curl_client_write(struct Curl_easy *data,
 {
   struct connectdata *conn = data->conn;
 
-  DEBUGASSERT(len);
-  DEBUGASSERT(type <= 3);
+  DEBUGASSERT(!(type & ~CLIENTWRITE_BOTH));
+
+  if(!len)
+    return CURLE_OK;
 
   /* FTP data may need conversion. */
   if((type & CLIENTWRITE_BODY) &&
diff --git a/lib/setopt.c b/lib/setopt.c
index 08827d1..599ed5d 100644
--- a/lib/setopt.c
+++ b/lib/setopt.c
@@ -1870,6 +1870,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
         data->set.ssl.primary.verifypeer;
     }
     break;
+#ifndef CURL_DISABLE_DOH
   case CURLOPT_DOH_SSL_VERIFYPEER:
     /*
      * Enable peer SSL verifying for DoH.
@@ -1877,6 +1878,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     data->set.doh_verifypeer = (0 != va_arg(param, long)) ?
       TRUE : FALSE;
     break;
+#endif
 #ifndef CURL_DISABLE_PROXY
   case CURLOPT_PROXY_SSL_VERIFYPEER:
     /*
@@ -1909,6 +1911,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
         data->set.ssl.primary.verifyhost;
     }
     break;
+#ifndef CURL_DISABLE_DOH
   case CURLOPT_DOH_SSL_VERIFYHOST:
     /*
      * Enable verification of the host name in the peer certificate for DoH
@@ -1918,6 +1921,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     /* Treat both 1 and 2 as TRUE */
     data->set.doh_verifyhost = (bool)((arg & 3) ? TRUE : FALSE);
     break;
+#endif
 #ifndef CURL_DISABLE_PROXY
   case CURLOPT_PROXY_SSL_VERIFYHOST:
     /*
@@ -1953,6 +1957,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
         data->set.ssl.primary.verifystatus;
     }
     break;
+#ifndef CURL_DISABLE_DOH
   case CURLOPT_DOH_SSL_VERIFYSTATUS:
     /*
      * Enable certificate status verifying for DoH.
@@ -1965,6 +1970,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     data->set.doh_verifystatus = (0 != va_arg(param, long)) ?
       TRUE : FALSE;
     break;
+#endif
   case CURLOPT_SSL_CTX_FUNCTION:
     /*
      * Set a SSL_CTX callback
@@ -2477,6 +2483,15 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
                             va_arg(param, char *));
     break;
 
+  case CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256:
+    /*
+     * Option to allow for the SHA256 of the host public key to be checked
+     * for validation purposes.
+     */
+    result = Curl_setstropt(&data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256],
+                            va_arg(param, char *));
+    break;
+
   case CURLOPT_SSH_KNOWNHOSTS:
     /*
      * Store the file name to read known hosts from.
@@ -2507,8 +2522,12 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     /*
      * disable libcurl transfer encoding is used
      */
+#ifndef USE_HYPER
     data->set.http_te_skip = (0 == va_arg(param, long)) ? TRUE : FALSE;
     break;
+#else
+    return CURLE_NOT_BUILT_IN; /* hyper doesn't support */
+#endif
 
   case CURLOPT_HTTP_CONTENT_DECODING:
     /*
@@ -2596,6 +2615,13 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     break;
 #endif
 
+#if (!defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_MIME)) || \
+  !defined(CURL_DISABLE_SMTP) || !defined(CURL_DISABLE_IMAP)
+  case CURLOPT_MIME_OPTIONS:
+    data->set.mime_options = va_arg(param, long);
+    break;
+#endif
+
   case CURLOPT_SASL_AUTHZID:
     /* Authorisation identity (identity to act as) */
     result = Curl_setstropt(&data->set.str[STRING_SASL_AUTHZID],
@@ -2929,6 +2955,12 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
       return CURLE_BAD_FUNCTION_ARGUMENT;
     data->set.maxage_conn = arg;
     break;
+  case CURLOPT_MAXLIFETIME_CONN:
+    arg = va_arg(param, long);
+    if(arg < 0)
+      return CURLE_BAD_FUNCTION_ARGUMENT;
+    data->set.maxlifetime_conn = arg;
+    break;
   case CURLOPT_TRAILERFUNCTION:
 #ifndef CURL_DISABLE_HTTP
     data->set.trailer_callback = va_arg(param, curl_trailer_callback);
@@ -3004,6 +3036,12 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
       return result;
     break;
 #endif
+  case CURLOPT_PREREQFUNCTION:
+    data->set.fprereq = va_arg(param, curl_prereq_callback);
+    break;
+  case CURLOPT_PREREQDATA:
+    data->set.prereq_userp = va_arg(param, void *);
+    break;
   default:
     /* unknown tag and its companion, just ignore: */
     result = CURLE_UNKNOWN_OPTION;
diff --git a/lib/setup-win32.h b/lib/setup-win32.h
index c35dec8..fa8742f 100644
--- a/lib/setup-win32.h
+++ b/lib/setup-win32.h
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -25,11 +25,11 @@
 /*
  * Include header files for windows builds before redefining anything.
  * Use this preprocessor block only to include or exclude windows.h,
- * winsock2.h, ws2tcpip.h or winsock.h. Any other windows thing belongs
+ * winsock2.h or ws2tcpip.h. Any other windows thing belongs
  * to any other further and independent block.  Under Cygwin things work
  * just as under linux (e.g. <sys/socket.h>) and the winsock headers should
  * never be included when __CYGWIN__ is defined.  configure script takes
- * care of this, not defining HAVE_WINDOWS_H, HAVE_WINSOCK_H, HAVE_WINSOCK2_H,
+ * care of this, not defining HAVE_WINDOWS_H, HAVE_WINSOCK2_H,
  * neither HAVE_WS2TCPIP_H when __CYGWIN__ is defined.
  */
 
@@ -47,10 +47,6 @@
 #    ifdef HAVE_WS2TCPIP_H
 #      include <ws2tcpip.h>
 #    endif
-#  else
-#    ifdef HAVE_WINSOCK_H
-#      include <winsock.h>
-#    endif
 #  endif
 #  include <tchar.h>
 #  ifdef UNICODE
@@ -67,10 +63,6 @@
 
 #ifdef HAVE_WINSOCK2_H
 #  define USE_WINSOCK 2
-#else
-#  ifdef HAVE_WINSOCK_H
-#    error "WinSock version 1 is no longer supported, version 2 is required!"
-#  endif
 #endif
 
 /*
diff --git a/lib/sha256.c b/lib/sha256.c
index a2e7e41..cf7ea4f 100644
--- a/lib/sha256.c
+++ b/lib/sha256.c
@@ -29,11 +29,18 @@
 #include "curl_sha256.h"
 #include "curl_hmac.h"
 
+#ifdef USE_WOLFSSL
+#include <wolfssl/options.h>
+#ifndef NO_SHA256
+#define USE_OPENSSL_SHA256
+#endif
+#endif
+
 #if defined(USE_OPENSSL)
 
 #include <openssl/opensslv.h>
 
-#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL)
+#if (OPENSSL_VERSION_NUMBER >= 0x0090700fL)
 #define USE_OPENSSL_SHA256
 #endif
 
@@ -63,7 +70,40 @@
 #if defined(USE_OPENSSL_SHA256)
 
 /* When OpenSSL is available we use the SHA256-function from OpenSSL */
-#include <openssl/sha.h>
+#include <openssl/evp.h>
+
+#include "curl_memory.h"
+
+/* The last #include file should be: */
+#include "memdebug.h"
+
+struct sha256_ctx {
+  EVP_MD_CTX *openssl_ctx;
+};
+typedef struct sha256_ctx my_sha256_ctx;
+
+static CURLcode my_sha256_init(my_sha256_ctx *ctx)
+{
+  ctx->openssl_ctx = EVP_MD_CTX_create();
+  if(!ctx->openssl_ctx)
+    return CURLE_OUT_OF_MEMORY;
+
+  EVP_DigestInit_ex(ctx->openssl_ctx, EVP_sha256(), NULL);
+  return CURLE_OK;
+}
+
+static void my_sha256_update(my_sha256_ctx *ctx,
+                             const unsigned char *data,
+                             unsigned int length)
+{
+  EVP_DigestUpdate(ctx->openssl_ctx, data, length);
+}
+
+static void my_sha256_final(unsigned char *digest, my_sha256_ctx *ctx)
+{
+  EVP_DigestFinal_ex(ctx->openssl_ctx, digest, NULL);
+  EVP_MD_CTX_destroy(ctx->openssl_ctx);
+}
 
 #elif defined(USE_GNUTLS)
 
@@ -74,21 +114,22 @@
 /* The last #include file should be: */
 #include "memdebug.h"
 
-typedef struct sha256_ctx SHA256_CTX;
+typedef struct sha256_ctx my_sha256_ctx;
 
-static void SHA256_Init(SHA256_CTX *ctx)
+static CURLcode my_sha256_init(my_sha256_ctx *ctx)
 {
   sha256_init(ctx);
+  return CURLE_OK;
 }
 
-static void SHA256_Update(SHA256_CTX *ctx,
-                          const unsigned char *data,
-                          unsigned int length)
+static void my_sha256_update(my_sha256_ctx *ctx,
+                             const unsigned char *data,
+                             unsigned int length)
 {
   sha256_update(ctx, length, data);
 }
 
-static void SHA256_Final(unsigned char *digest, SHA256_CTX *ctx)
+static void my_sha256_final(unsigned char *digest, my_sha256_ctx *ctx)
 {
   sha256_digest(ctx, SHA256_DIGEST_SIZE, digest);
 }
@@ -102,20 +143,21 @@ static void SHA256_Final(unsigned char *digest, SHA256_CTX *ctx)
 /* The last #include file should be: */
 #include "memdebug.h"
 
-typedef mbedtls_sha256_context SHA256_CTX;
+typedef mbedtls_sha256_context my_sha256_ctx;
 
-static void SHA256_Init(SHA256_CTX *ctx)
+static CURLcode my_sha256_init(my_sha256_ctx *ctx)
 {
 #if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS)
   (void) mbedtls_sha256_starts(ctx, 0);
 #else
   (void) mbedtls_sha256_starts_ret(ctx, 0);
 #endif
+  return CURLE_OK;
 }
 
-static void SHA256_Update(SHA256_CTX *ctx,
-                          const unsigned char *data,
-                          unsigned int length)
+static void my_sha256_update(my_sha256_ctx *ctx,
+                             const unsigned char *data,
+                             unsigned int length)
 {
 #if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS)
   (void) mbedtls_sha256_update(ctx, data, length);
@@ -124,7 +166,7 @@ static void SHA256_Update(SHA256_CTX *ctx,
 #endif
 }
 
-static void SHA256_Final(unsigned char *digest, SHA256_CTX *ctx)
+static void my_sha256_final(unsigned char *digest, my_sha256_ctx *ctx)
 {
 #if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS)
   (void) mbedtls_sha256_finish(ctx, digest);
@@ -145,21 +187,22 @@ static void SHA256_Final(unsigned char *digest, SHA256_CTX *ctx)
 /* The last #include file should be: */
 #include "memdebug.h"
 
-typedef CC_SHA256_CTX SHA256_CTX;
+typedef CC_SHA256_CTX my_sha256_ctx;
 
-static void SHA256_Init(SHA256_CTX *ctx)
+static CURLcode my_sha256_init(my_sha256_ctx *ctx)
 {
   (void) CC_SHA256_Init(ctx);
+  return CURLE_OK;
 }
 
-static void SHA256_Update(SHA256_CTX *ctx,
-                          const unsigned char *data,
-                          unsigned int length)
+static void my_sha256_update(my_sha256_ctx *ctx,
+                             const unsigned char *data,
+                             unsigned int length)
 {
   (void) CC_SHA256_Update(ctx, data, length);
 }
 
-static void SHA256_Final(unsigned char *digest, SHA256_CTX *ctx)
+static void my_sha256_final(unsigned char *digest, my_sha256_ctx *ctx)
 {
   (void) CC_SHA256_Final(digest, ctx);
 }
@@ -172,28 +215,30 @@ struct sha256_ctx {
   HCRYPTPROV hCryptProv;
   HCRYPTHASH hHash;
 };
-typedef struct sha256_ctx SHA256_CTX;
+typedef struct sha256_ctx my_sha256_ctx;
 
 #if !defined(CALG_SHA_256)
 #define CALG_SHA_256 0x0000800c
 #endif
 
-static void SHA256_Init(SHA256_CTX *ctx)
+static CURLcode my_sha256_init(my_sha256_ctx *ctx)
 {
   if(CryptAcquireContext(&ctx->hCryptProv, NULL, NULL, PROV_RSA_AES,
                          CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) {
     CryptCreateHash(ctx->hCryptProv, CALG_SHA_256, 0, 0, &ctx->hHash);
   }
+
+  return CURLE_OK;
 }
 
-static void SHA256_Update(SHA256_CTX *ctx,
-                          const unsigned char *data,
-                          unsigned int length)
+static void my_sha256_update(my_sha256_ctx *ctx,
+                             const unsigned char *data,
+                             unsigned int length)
 {
   CryptHashData(ctx->hHash, (unsigned char *) data, length, 0);
 }
 
-static void SHA256_Final(unsigned char *digest, SHA256_CTX *ctx)
+static void my_sha256_final(unsigned char *digest, my_sha256_ctx *ctx)
 {
   unsigned long length = 0;
 
@@ -262,7 +307,7 @@ struct sha256_state {
   unsigned long state[8], curlen;
   unsigned char buf[64];
 };
-typedef struct sha256_state SHA256_CTX;
+typedef struct sha256_state my_sha256_ctx;
 
 /* The K array */
 static const unsigned long K[64] = {
@@ -339,7 +384,7 @@ static int sha256_compress(struct sha256_state *md,
 }
 
 /* Initialize the hash state */
-static void SHA256_Init(struct sha256_state *md)
+static CURLcode my_sha256_init(struct sha256_state *md)
 {
   md->curlen = 0;
   md->length = 0;
@@ -351,6 +396,8 @@ static void SHA256_Init(struct sha256_state *md)
   md->state[5] = 0x9B05688CUL;
   md->state[6] = 0x1F83D9ABUL;
   md->state[7] = 0x5BE0CD19UL;
+
+  return CURLE_OK;
 }
 
 /*
@@ -358,11 +405,11 @@ static void SHA256_Init(struct sha256_state *md)
    @param md     The hash state
    @param in     The data to hash
    @param inlen  The length of the data (octets)
-   @return CRYPT_OK if successful
+   @return 0 if successful
 */
-static int SHA256_Update(struct sha256_state *md,
-                         const unsigned char *in,
-                         unsigned long inlen)
+static int my_sha256_update(struct sha256_state *md,
+                            const unsigned char *in,
+                            unsigned long inlen)
 {
   unsigned long n;
 
@@ -399,10 +446,10 @@ static int SHA256_Update(struct sha256_state *md,
    Terminate the hash to get the digest
    @param md  The hash state
    @param out [out] The destination of the hash (32 bytes)
-   @return CRYPT_OK if successful
+   @return 0 if successful
 */
-static int SHA256_Final(unsigned char *out,
-                        struct sha256_state *md)
+static int my_sha256_final(unsigned char *out,
+                           struct sha256_state *md)
 {
   int i;
 
@@ -455,28 +502,34 @@ static int SHA256_Final(unsigned char *out,
  * output [in/out] - The output buffer.
  * input  [in]     - The input data.
  * length [in]     - The input length.
+ *
+ * Returns CURLE_OK on success.
  */
-void Curl_sha256it(unsigned char *output, const unsigned char *input,
+CURLcode Curl_sha256it(unsigned char *output, const unsigned char *input,
                    const size_t length)
 {
-  SHA256_CTX ctx;
+  CURLcode result;
+  my_sha256_ctx ctx;
 
-  SHA256_Init(&ctx);
-  SHA256_Update(&ctx, input, curlx_uztoui(length));
-  SHA256_Final(output, &ctx);
+  result = my_sha256_init(&ctx);
+  if(!result) {
+    my_sha256_update(&ctx, input, curlx_uztoui(length));
+    my_sha256_final(output, &ctx);
+  }
+  return result;
 }
 
 
 const struct HMAC_params Curl_HMAC_SHA256[] = {
   {
     /* Hash initialization function. */
-    CURLX_FUNCTION_CAST(HMAC_hinit_func, SHA256_Init),
+    CURLX_FUNCTION_CAST(HMAC_hinit_func, my_sha256_init),
     /* Hash update function. */
-    CURLX_FUNCTION_CAST(HMAC_hupdate_func, SHA256_Update),
+    CURLX_FUNCTION_CAST(HMAC_hupdate_func, my_sha256_update),
     /* Hash computation end function. */
-    CURLX_FUNCTION_CAST(HMAC_hfinal_func, SHA256_Final),
+    CURLX_FUNCTION_CAST(HMAC_hfinal_func, my_sha256_final),
     /* Size of hash context structure. */
-    sizeof(SHA256_CTX),
+    sizeof(my_sha256_ctx),
     /* Maximum key length. */
     64,
     /* Result size. */
diff --git a/lib/share.c b/lib/share.c
index 9c43c8f..403563f 100644
--- a/lib/share.c
+++ b/lib/share.c
@@ -39,11 +39,7 @@ curl_share_init(void)
   if(share) {
     share->magic = CURL_GOOD_SHARE;
     share->specifier |= (1<<CURL_LOCK_DATA_SHARE);
-
-    if(Curl_mk_dnscache(&share->hostcache)) {
-      free(share);
-      return NULL;
-    }
+    Curl_init_dnscache(&share->hostcache);
   }
 
   return share;
diff --git a/lib/smtp.c b/lib/smtp.c
index 02ddaca..6c08293 100644
--- a/lib/smtp.c
+++ b/lib/smtp.c
@@ -82,6 +82,7 @@
 #include "multiif.h"
 #include "url.h"
 #include "curl_gethostname.h"
+#include "bufref.h"
 #include "curl_sasl.h"
 #include "warnless.h"
 /* The last 3 #include files should be in this order */
@@ -108,12 +109,12 @@ static CURLcode smtp_parse_url_path(struct Curl_easy *data);
 static CURLcode smtp_parse_custom_request(struct Curl_easy *data);
 static CURLcode smtp_parse_address(struct Curl_easy *data, const char *fqma,
                                    char **address, struct hostname *host);
-static CURLcode smtp_perform_auth(struct Curl_easy *data,
-                                  struct connectdata *conn, const char *mech,
-                                  const char *initresp);
-static CURLcode smtp_continue_auth(struct Curl_easy *data,
-                                   struct connectdata *conn, const char *resp);
-static void smtp_get_message(char *buffer, char **outptr);
+static CURLcode smtp_perform_auth(struct Curl_easy *data, const char *mech,
+                                  const struct bufref *initresp);
+static CURLcode smtp_continue_auth(struct Curl_easy *data, const char *mech,
+                                   const struct bufref *resp);
+static CURLcode smtp_cancel_auth(struct Curl_easy *data, const char *mech);
+static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out);
 
 /*
  * SMTP protocol handler.
@@ -175,13 +176,16 @@ const struct Curl_handler Curl_handler_smtps = {
 
 /* SASL parameters for the smtp protocol */
 static const struct SASLproto saslsmtp = {
-  "smtp",                     /* The service name */
-  334,                        /* Code received when continuation is expected */
-  235,                        /* Code to receive upon authentication success */
-  512 - 8,                    /* Maximum initial response length (no max) */
-  smtp_perform_auth,          /* Send authentication command */
-  smtp_continue_auth,         /* Send authentication continuation */
-  smtp_get_message            /* Get SASL response message */
+  "smtp",               /* The service name */
+  smtp_perform_auth,    /* Send authentication command */
+  smtp_continue_auth,   /* Send authentication continuation */
+  smtp_cancel_auth,     /* Cancel authentication */
+  smtp_get_message,     /* Get SASL response message */
+  512 - 8,              /* Max line len - strlen("AUTH ") - 1 space - crlf */
+  334,                  /* Code received when continuation is expected */
+  235,                  /* Code to receive upon authentication success */
+  SASL_AUTH_DEFAULT,    /* Default mechanisms */
+  SASL_FLAG_BASE64      /* Configuration flags */
 };
 
 #ifdef USE_SSL
@@ -218,7 +222,7 @@ static bool smtp_endofresp(struct Curl_easy *data, struct connectdata *conn,
 
   /* Do we have a command response? This should be the response code followed
      by a space and optionally some text as per RFC-5321 and as outlined in
-     Section 4. Examples of RFC-4954 but some e-mail servers ignore this and
+     Section 4. Examples of RFC-4954 but some email servers ignore this and
      only send the response code instead as per Section 4.2. */
   if(line[3] == ' ' || len == 5) {
     char tmpline[6];
@@ -248,34 +252,32 @@ static bool smtp_endofresp(struct Curl_easy *data, struct connectdata *conn,
  *
  * Gets the authentication message from the response buffer.
  */
-static void smtp_get_message(char *buffer, char **outptr)
+static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out)
 {
-  size_t len = strlen(buffer);
-  char *message = NULL;
+  char *message = data->state.buffer;
+  size_t len = strlen(message);
 
   if(len > 4) {
     /* Find the start of the message */
     len -= 4;
-    for(message = buffer + 4; *message == ' ' || *message == '\t';
-        message++, len--)
+    for(message += 4; *message == ' ' || *message == '\t'; message++, len--)
       ;
 
     /* Find the end of the message */
-    for(; len--;)
+    while(len--)
       if(message[len] != '\r' && message[len] != '\n' && message[len] != ' ' &&
          message[len] != '\t')
         break;
 
     /* Terminate the message */
-    if(++len) {
-      message[len] = '\0';
-    }
+    message[++len] = '\0';
+    Curl_bufref_set(out, message, len, NULL);
   }
   else
     /* junk input => zero length output */
-    message = &buffer[len];
+    Curl_bufref_set(out, "", 0, NULL);
 
-  *outptr = message;
+  return CURLE_OK;
 }
 
 /***********************************************************************
@@ -421,16 +423,16 @@ static CURLcode smtp_perform_upgrade_tls(struct Curl_easy *data)
  * authentication mechanism.
  */
 static CURLcode smtp_perform_auth(struct Curl_easy *data,
-                                  struct connectdata *conn,
                                   const char *mech,
-                                  const char *initresp)
+                                  const struct bufref *initresp)
 {
   CURLcode result = CURLE_OK;
-  struct smtp_conn *smtpc = &conn->proto.smtpc;
+  struct smtp_conn *smtpc = &data->conn->proto.smtpc;
+  const char *ir = (const char *) Curl_bufref_ptr(initresp);
 
-  if(initresp) {                                  /* AUTH <mech> ...<crlf> */
+  if(ir) {                                  /* AUTH <mech> ...<crlf> */
     /* Send the AUTH command with the initial response */
-    result = Curl_pp_sendf(data, &smtpc->pp, "AUTH %s %s", mech, initresp);
+    result = Curl_pp_sendf(data, &smtpc->pp, "AUTH %s %s", mech, ir);
   }
   else {
     /* Send the AUTH command */
@@ -444,14 +446,33 @@ static CURLcode smtp_perform_auth(struct Curl_easy *data,
  *
  * smtp_continue_auth()
  *
- * Sends SASL continuation data or cancellation.
+ * Sends SASL continuation data.
  */
 static CURLcode smtp_continue_auth(struct Curl_easy *data,
-                                   struct connectdata *conn, const char *resp)
+                                   const char *mech,
+                                   const struct bufref *resp)
 {
-  struct smtp_conn *smtpc = &conn->proto.smtpc;
+  struct smtp_conn *smtpc = &data->conn->proto.smtpc;
+
+  (void)mech;
+
+  return Curl_pp_sendf(data, &smtpc->pp,
+                       "%s", (const char *) Curl_bufref_ptr(resp));
+}
+
+/***********************************************************************
+ *
+ * smtp_cancel_auth()
+ *
+ * Sends SASL cancellation.
+ */
+static CURLcode smtp_cancel_auth(struct Curl_easy *data, const char *mech)
+{
+  struct smtp_conn *smtpc = &data->conn->proto.smtpc;
 
-  return Curl_pp_sendf(data, &smtpc->pp, "%s", resp);
+  (void)mech;
+
+  return Curl_pp_sendf(data, &smtpc->pp, "*");
 }
 
 /***********************************************************************
@@ -469,7 +490,7 @@ static CURLcode smtp_perform_authentication(struct Curl_easy *data)
   saslprogress progress;
 
   /* Check we have enough data to authenticate with, and the
-     server supports authentiation, and end the connect phase if not */
+     server supports authentication, and end the connect phase if not */
   if(!smtpc->auth_supported ||
      !Curl_sasl_can_authenticate(&smtpc->sasl, conn)) {
     state(data, SMTP_STOP);
@@ -477,7 +498,7 @@ static CURLcode smtp_perform_authentication(struct Curl_easy *data)
   }
 
   /* Calculate the SASL login details */
-  result = Curl_sasl_start(&smtpc->sasl, data, conn, FALSE, &progress);
+  result = Curl_sasl_start(&smtpc->sasl, data, FALSE, &progress);
 
   if(!result) {
     if(progress == SASL_INPROGRESS)
@@ -506,7 +527,7 @@ static CURLcode smtp_perform_command(struct Curl_easy *data)
 
   if(smtp->rcpt) {
     /* We notify the server we are sending UTF-8 data if a) it supports the
-       SMTPUTF8 extension and b) The mailbox contains UTF-8 charaacters, in
+       SMTPUTF8 extension and b) The mailbox contains UTF-8 characters, in
        either the local address or host name parts. This is regardless of
        whether the host name is encoded using IDN ACE */
     bool utf8 = FALSE;
@@ -579,7 +600,7 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
   struct connectdata *conn = data->conn;
 
   /* We notify the server we are sending UTF-8 data if a) it supports the
-     SMTPUTF8 extension and b) The mailbox contains UTF-8 charaacters, in
+     SMTPUTF8 extension and b) The mailbox contains UTF-8 characters, in
      either the local address or host name parts. This is regardless of
      whether the host name is encoded using IDN ACE */
   bool utf8 = FALSE;
@@ -985,7 +1006,7 @@ static CURLcode smtp_state_auth_resp(struct Curl_easy *data,
 
   (void)instate; /* no use for this yet */
 
-  result = Curl_sasl_continue(&smtpc->sasl, data, conn, smtpcode, &progress);
+  result = Curl_sasl_continue(&smtpc->sasl, data, smtpcode, &progress);
   if(!result)
     switch(progress) {
     case SASL_DONE:
@@ -1333,7 +1354,7 @@ static CURLcode smtp_connect(struct Curl_easy *data, bool *done)
   PINGPONG_SETUP(pp, smtp_statemachine, smtp_endofresp);
 
   /* Initialize the SASL storage */
-  Curl_sasl_init(&smtpc->sasl, &saslsmtp);
+  Curl_sasl_init(&smtpc->sasl, data, &saslsmtp);
 
   /* Initialise the pingpong layer */
   Curl_pp_setup(pp);
@@ -1655,8 +1676,6 @@ static CURLcode smtp_parse_url_options(struct connectdata *conn)
   struct smtp_conn *smtpc = &conn->proto.smtpc;
   const char *ptr = conn->options;
 
-  smtpc->sasl.resetprefs = TRUE;
-
   while(!result && ptr && *ptr) {
     const char *key = ptr;
     const char *value;
diff --git a/lib/socks.c b/lib/socks.c
index db4c808..a014aa6 100644
--- a/lib/socks.c
+++ b/lib/socks.c
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -38,6 +38,7 @@
 #include "timeval.h"
 #include "socks.h"
 #include "multiif.h" /* for getsock macros */
+#include "inet_pton.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -856,10 +857,32 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user,
     socksreq[len++] = 0; /* must be zero */
 
     if(!socks5_resolve_local) {
-      socksreq[len++] = 3; /* ATYP: domain name = 3 */
-      socksreq[len++] = (char) hostname_len; /* one byte address length */
-      memcpy(&socksreq[len], hostname, hostname_len); /* address w/o NULL */
-      len += hostname_len;
+      /* ATYP: domain name = 3,
+         IPv6 == 4,
+         IPv4 == 1 */
+      unsigned char ip4[4];
+#ifdef ENABLE_IPV6
+      if(conn->bits.ipv6_ip) {
+        char ip6[16];
+        if(1 != Curl_inet_pton(AF_INET6, hostname, ip6))
+          return CURLPX_BAD_ADDRESS_TYPE;
+        socksreq[len++] = 4;
+        memcpy(&socksreq[len], ip6, sizeof(ip6));
+        len += sizeof(ip6);
+      }
+      else
+#endif
+      if(1 == Curl_inet_pton(AF_INET, hostname, ip4)) {
+        socksreq[len++] = 1;
+        memcpy(&socksreq[len], ip4, sizeof(ip4));
+        len += sizeof(ip4);
+      }
+      else {
+        socksreq[len++] = 3;
+        socksreq[len++] = (char) hostname_len; /* one byte address length */
+        memcpy(&socksreq[len], hostname, hostname_len); /* address w/o NULL */
+        len += hostname_len;
+      }
       infof(data, "SOCKS5 connect to %s:%d (remotely resolved)",
             hostname, remote_port);
     }
diff --git a/lib/socks_gssapi.c b/lib/socks_gssapi.c
index 34bfa37..8ef2f8f 100644
--- a/lib/socks_gssapi.c
+++ b/lib/socks_gssapi.c
@@ -257,7 +257,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
       return CURLE_COULDNT_CONNECT;
     }
 
-    if(socksreq[1] != 1) { /* status / messgae type */
+    if(socksreq[1] != 1) { /* status / message type */
       failf(data, "Invalid GSS-API authentication response type (%d %d).",
             socksreq[0], socksreq[1]);
       gss_release_name(&gss_status, &server);
@@ -452,7 +452,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
     return CURLE_COULDNT_CONNECT;
   }
 
-  if(socksreq[1] != 2) { /* status / messgae type */
+  if(socksreq[1] != 2) { /* status / message type */
     failf(data, "Invalid GSS-API encryption response type (%d %d).",
           socksreq[0], socksreq[1]);
     gss_delete_sec_context(&gss_status, &gss_context, NULL);
diff --git a/lib/socks_sspi.c b/lib/socks_sspi.c
index cb225b9..ffc8703 100644
--- a/lib/socks_sspi.c
+++ b/lib/socks_sspi.c
@@ -277,7 +277,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
       return CURLE_COULDNT_CONNECT;
     }
 
-    if(socksreq[1] != 1) { /* status / messgae type */
+    if(socksreq[1] != 1) { /* status / message type */
       failf(data, "Invalid SSPI authentication response type (%u %u).",
             (unsigned int)socksreq[0], (unsigned int)socksreq[1]);
       free(service_name);
diff --git a/lib/splay.c b/lib/splay.c
index a94e2c8..bcc0795 100644
--- a/lib/splay.c
+++ b/lib/splay.c
@@ -107,7 +107,7 @@ struct Curl_tree *Curl_splayinsert(struct curltime i,
   if(!node)
     return t;
 
-  if(t != NULL) {
+  if(t) {
     t = Curl_splay(i, t);
     if(compare(i, t->key) == 0) {
       /* There already exists a node in the tree with the very same key. Build
@@ -154,7 +154,7 @@ struct Curl_tree *Curl_splaygetbest(struct curltime i,
                                     struct Curl_tree *t,
                                     struct Curl_tree **removed)
 {
-  static struct curltime tv_zero = {0, 0};
+  static const struct curltime tv_zero = {0, 0};
   struct Curl_tree *x;
 
   if(!t) {
diff --git a/lib/strerror.c b/lib/strerror.c
index 8a27197..07d73a7 100644
--- a/lib/strerror.c
+++ b/lib/strerror.c
@@ -404,6 +404,9 @@ curl_multi_strerror(CURLMcode error)
   case CURLM_BAD_FUNCTION_ARGUMENT:
     return "A libcurl function was given a bad argument";
 
+  case CURLM_ABORTED_BY_CALLBACK:
+    return "Operation was aborted by an application callback";
+
   case CURLM_LAST:
     break;
   }
@@ -453,6 +456,114 @@ curl_share_strerror(CURLSHcode error)
 #endif
 }
 
+const char *
+curl_url_strerror(CURLUcode error)
+{
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+  switch(error) {
+  case CURLUE_OK:
+    return "No error";
+
+  case CURLUE_BAD_HANDLE:
+    return "An invalid CURLU pointer was passed as argument";
+
+  case CURLUE_BAD_PARTPOINTER:
+    return "An invalid 'part' argument was passed as argument";
+
+  case CURLUE_MALFORMED_INPUT:
+    return "Malformed input to a URL function";
+
+  case CURLUE_BAD_PORT_NUMBER:
+    return "Port number was not a decimal number between 0 and 65535";
+
+  case CURLUE_UNSUPPORTED_SCHEME:
+    return "This libcurl build doesn't support the given URL scheme";
+
+  case CURLUE_URLDECODE:
+    return "URL decode error, most likely because of rubbish in the input";
+
+  case CURLUE_OUT_OF_MEMORY:
+    return "A memory function failed";
+
+  case CURLUE_USER_NOT_ALLOWED:
+    return "Credentials was passed in the URL when prohibited";
+
+  case CURLUE_UNKNOWN_PART:
+    return "An unknown part ID was passed to a URL API function";
+
+  case CURLUE_NO_SCHEME:
+    return "No scheme part in the URL";
+
+  case CURLUE_NO_USER:
+    return "No user part in the URL";
+
+  case CURLUE_NO_PASSWORD:
+    return "No password part in the URL";
+
+  case CURLUE_NO_OPTIONS:
+    return "No options part in the URL";
+
+  case CURLUE_NO_HOST:
+    return "No host part in the URL";
+
+  case CURLUE_NO_PORT:
+    return "No port part in the URL";
+
+  case CURLUE_NO_QUERY:
+    return "No query part in the URL";
+
+  case CURLUE_NO_FRAGMENT:
+    return "No fragment part in the URL";
+
+  case CURLUE_NO_ZONEID:
+    return "No zoneid part in the URL";
+
+  case CURLUE_BAD_LOGIN:
+    return "Bad login part";
+
+  case CURLUE_BAD_IPV6:
+    return "Bad IPv6 address";
+
+  case CURLUE_BAD_HOSTNAME:
+    return "Bad hostname";
+
+  case CURLUE_BAD_FILE_URL:
+    return "Bad file:// URL";
+
+  case CURLUE_BAD_SLASHES:
+    return "Unsupported number of slashes";
+
+  case CURLUE_BAD_SCHEME:
+    return "Bad scheme";
+
+  case CURLUE_BAD_PATH:
+    return "Bad path";
+
+  case CURLUE_BAD_FRAGMENT:
+    return "Bad fragment";
+
+  case CURLUE_BAD_QUERY:
+    return "Bad query";
+
+  case CURLUE_BAD_PASSWORD:
+    return "Bad password";
+
+  case CURLUE_BAD_USER:
+    return "Bad user";
+
+  case CURLUE_LAST:
+    break;
+  }
+
+  return "CURLUcode unknown";
+#else
+  if(error == CURLUE_OK)
+    return "No error";
+  else
+    return "Error";
+#endif
+}
+
 #ifdef USE_WINSOCK
 /* This is a helper function for Curl_strerror that converts Winsock error
  * codes (WSAGetLastError) to error messages.
diff --git a/lib/system_win32.c b/lib/system_win32.c
index 2939fd0..9a6dd9c 100644
--- a/lib/system_win32.c
+++ b/lib/system_win32.c
@@ -102,7 +102,9 @@ CURLcode Curl_win32_init(long flags)
       Curl_if_nametoindex = pIfNameToIndex;
   }
 
-  if(curlx_verify_windows_version(6, 0, PLATFORM_WINNT,
+  /* curlx_verify_windows_version must be called during init at least once
+     because it has its own initialization routine. */
+  if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT,
                                   VERSION_GREATER_THAN_EQUAL)) {
     Curl_isVistaOrGreater = TRUE;
   }
diff --git a/lib/tftp.c b/lib/tftp.c
index aae997d..f8c6844 100644
--- a/lib/tftp.c
+++ b/lib/tftp.c
@@ -186,7 +186,7 @@ const struct Curl_handler Curl_handler_tftp = {
   PORT_TFTP,                            /* defport */
   CURLPROTO_TFTP,                       /* protocol */
   CURLPROTO_TFTP,                       /* family */
-  PROTOPT_NONE | PROTOPT_NOURLQUERY     /* flags */
+  PROTOPT_NOTCPPROXY | PROTOPT_NOURLQUERY /* flags */
 };
 
 /**********************************************************
@@ -1304,9 +1304,9 @@ static CURLcode tftp_doing(struct Curl_easy *data, bool *dophase_done)
 
 /**********************************************************
  *
- * tftp_peform
+ * tftp_perform
  *
- * Entry point for transfer from tftp_do, sarts state mach
+ * Entry point for transfer from tftp_do, starts state mach
  *
  **********************************************************/
 static CURLcode tftp_perform(struct Curl_easy *data, bool *dophase_done)
diff --git a/lib/transfer.c b/lib/transfer.c
index 05fec79..22704fa 100644
--- a/lib/transfer.c
+++ b/lib/transfer.c
@@ -1631,7 +1631,7 @@ CURLcode Curl_follow(struct Curl_easy *data,
 
   if((type != FOLLOW_RETRY) &&
      (data->req.httpcode != 401) && (data->req.httpcode != 407) &&
-     Curl_is_absolute_url(newurl, NULL, MAX_SCHEME_LEN))
+     Curl_is_absolute_url(newurl, NULL, 0))
     /* If this is not redirect due to a 401 or 407 response and an absolute
        URL: don't allow a custom port number */
     disallowport = TRUE;
diff --git a/lib/url.c b/lib/url.c
index 37b6c0e..9f10135 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -137,6 +137,15 @@ bool curl_win32_idn_to_ascii(const char *in, char **out);
 #include "curl_memory.h"
 #include "memdebug.h"
 
+/* Count of the backend ssl objects to allocate */
+#ifdef USE_SSL
+#  ifndef CURL_DISABLE_PROXY
+#    define SSL_BACKEND_CNT 4
+#  else
+#    define SSL_BACKEND_CNT 2
+#  endif
+#endif
+
 static void conn_free(struct connectdata *conn);
 
 /* Some parts of the code (e.g. chunked encoding) assume this buffer has at
@@ -354,9 +363,7 @@ static void up_free(struct Curl_easy *data)
  * This is the internal function curl_easy_cleanup() calls. This should
  * cleanup and free all resources associated with this sessionhandle.
  *
- * NOTE: if we ever add something that attempts to write to a socket or
- * similar here, we must ignore SIGPIPE first. It is currently only done
- * when curl_easy_perform() is invoked.
+ * We ignore SIGPIPE when this is called from curl_easy_cleanup.
  */
 
 CURLcode Curl_close(struct Curl_easy **datap)
@@ -542,8 +549,10 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
    * libcurl 7.10 introduced SSL verification *by default*! This needs to be
    * switched off unless wanted.
    */
+#ifndef CURL_DISABLE_DOH
   set->doh_verifyhost = TRUE;
   set->doh_verifypeer = TRUE;
+#endif
   set->ssl.primary.verifypeer = TRUE;
   set->ssl.primary.verifyhost = TRUE;
 #ifdef USE_TLS_SRP
@@ -622,6 +631,7 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
   set->upkeep_interval_ms = CURL_UPKEEP_INTERVAL_DEFAULT;
   set->maxconnects = DEFAULT_CONNCACHE_SIZE; /* for easy handles */
   set->maxage_conn = 118;
+  set->maxlifetime_conn = 0;
   set->http09_allowed = FALSE;
   set->httpwant =
 #ifdef USE_NGHTTP2
@@ -844,7 +854,7 @@ CURLcode Curl_disconnect(struct Curl_easy *data,
     return CURLE_OK;
   }
 
-  if(conn->dns_entry != NULL) {
+  if(conn->dns_entry) {
     Curl_resolv_unlock(data, conn->dns_entry);
     conn->dns_entry = NULL;
   }
@@ -962,21 +972,36 @@ socks_proxy_info_matches(const struct proxy_info *data,
 #define socks_proxy_info_matches(x,y) FALSE
 #endif
 
-/* A connection has to have been idle for a shorter time than 'maxage_conn' to
-   be subject for reuse. The success rate is just too low after this. */
+/* A connection has to have been idle for a shorter time than 'maxage_conn'
+   (the success rate is just too low after this), or created less than
+   'maxlifetime_conn' ago, to be subject for reuse. */
 
 static bool conn_maxage(struct Curl_easy *data,
                         struct connectdata *conn,
                         struct curltime now)
 {
-  timediff_t idletime = Curl_timediff(now, conn->lastused);
+  timediff_t idletime, lifetime;
+
+  idletime = Curl_timediff(now, conn->lastused);
   idletime /= 1000; /* integer seconds is fine */
 
   if(idletime > data->set.maxage_conn) {
-    infof(data, "Too old connection (%ld seconds), disconnect it",
+    infof(data, "Too old connection (%ld seconds idle), disconnect it",
           idletime);
     return TRUE;
   }
+
+  lifetime = Curl_timediff(now, conn->created);
+  lifetime /= 1000; /* integer seconds is fine */
+
+  if(data->set.maxlifetime_conn && lifetime > data->set.maxlifetime_conn) {
+    infof(data,
+          "Too old connection (%ld seconds since creation), disconnect it",
+          lifetime);
+    return TRUE;
+  }
+
+
   return FALSE;
 }
 
@@ -1284,13 +1309,12 @@ ConnectionExists(struct Curl_easy *data,
             if(check->proxy_ssl[FIRSTSOCKET].state != ssl_connection_complete)
               continue;
           }
-          else {
-            if(!Curl_ssl_config_matches(&needle->ssl_config,
-                                        &check->ssl_config))
-              continue;
-            if(check->ssl[FIRSTSOCKET].state != ssl_connection_complete)
-              continue;
-          }
+
+          if(!Curl_ssl_config_matches(&needle->ssl_config,
+                                      &check->ssl_config))
+            continue;
+          if(check->ssl[FIRSTSOCKET].state != ssl_connection_complete)
+            continue;
         }
       }
 #endif
@@ -1667,7 +1691,7 @@ static struct connectdata *allocate_conn(struct Curl_easy *data)
      data becomes proxy backend data). */
   {
     size_t sslsize = Curl_ssl->sizeof_ssl_backend_data;
-    char *ssl = calloc(4, sslsize);
+    char *ssl = calloc(SSL_BACKEND_CNT, sslsize);
     if(!ssl) {
       free(conn);
       return NULL;
@@ -1934,7 +1958,7 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data,
     return CURLE_OUT_OF_MEMORY;
 
   if(data->set.str[STRING_DEFAULT_PROTOCOL] &&
-     !Curl_is_absolute_url(data->state.url, NULL, MAX_SCHEME_LEN)) {
+     !Curl_is_absolute_url(data->state.url, NULL, 0)) {
     char *url = aprintf("%s://%s", data->set.str[STRING_DEFAULT_PROTOCOL],
                         data->state.url);
     if(!url)
@@ -1954,7 +1978,8 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data,
                      CURLU_DISALLOW_USER : 0) |
                     (data->set.path_as_is ? CURLU_PATH_AS_IS : 0));
     if(uc) {
-      DEBUGF(infof(data, "curl_url_set rejected %s", data->state.url));
+      DEBUGF(infof(data, "curl_url_set rejected %s: %s", data->state.url,
+                   curl_url_strerror(uc)));
       return Curl_uc_to_curlcode(uc);
     }
 
@@ -2380,6 +2405,11 @@ static CURLcode parse_proxy(struct Curl_easy *data,
   CURLcode result = CURLE_OK;
   char *scheme = NULL;
 
+  if(!uhp) {
+    result = CURLE_OUT_OF_MEMORY;
+    goto error;
+  }
+
   /* When parsing the proxy, allowing non-supported schemes since we have
      these made up ones for proxies. Guess scheme for URLs without it. */
   uc = curl_url_set(uhp, CURLUPART_URL, proxy,
@@ -2571,7 +2601,7 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data,
   if(data->set.str[STRING_PROXY]) {
     proxy = strdup(data->set.str[STRING_PROXY]);
     /* if global proxy is set, this is it */
-    if(NULL == proxy) {
+    if(!proxy) {
       failf(data, "memory shortage");
       result = CURLE_OUT_OF_MEMORY;
       goto out;
@@ -2581,7 +2611,7 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data,
   if(data->set.str[STRING_PRE_PROXY]) {
     socksproxy = strdup(data->set.str[STRING_PRE_PROXY]);
     /* if global socks proxy is set, this is it */
-    if(NULL == socksproxy) {
+    if(!socksproxy) {
       failf(data, "memory shortage");
       result = CURLE_OUT_OF_MEMORY;
       goto out;
@@ -2763,7 +2793,7 @@ CURLcode Curl_parse_login_details(const char *login, const size_t len,
   size_t plen;
   size_t olen;
 
-  /* the input length check is because this is called directcly from setopt
+  /* the input length check is because this is called directly from setopt
      and isn't going through the regular string length check */
   size_t llen = strlen(login);
   if(llen > CURL_MAX_INPUT_LENGTH)
@@ -4093,7 +4123,7 @@ CURLcode Curl_connect(struct Curl_easy *data,
   /* init the single-transfer specific data */
   Curl_free_request_state(data);
   memset(&data->req, 0, sizeof(struct SingleRequest));
-  data->req.maxdownload = -1;
+  data->req.size = data->req.maxdownload = -1;
 
   /* call the stuff that needs to be called */
   result = create_conn(data, &conn, asyncp);
diff --git a/lib/urlapi-int.h b/lib/urlapi-int.h
index 4257233..bd6b601 100644
--- a/lib/urlapi-int.h
+++ b/lib/urlapi-int.h
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -22,8 +22,6 @@
  *
  ***************************************************************************/
 #include "curl_setup.h"
-/* scheme is not URL encoded, the longest libcurl supported ones are... */
-#define MAX_SCHEME_LEN 40
 
 bool Curl_is_absolute_url(const char *url, char *scheme, size_t buflen);
 
diff --git a/lib/urlapi.c b/lib/urlapi.c
index 7f03862..d29aeb2 100644
--- a/lib/urlapi.c
+++ b/lib/urlapi.c
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -30,6 +30,7 @@
 #include "escape.h"
 #include "curl_ctype.h"
 #include "inet_pton.h"
+#include "inet_ntop.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -50,6 +51,9 @@
    ((str)[1] == ':' || (str)[1] == '|') && \
    ((str)[2] == '/' || (str)[2] == '\\' || (str)[2] == 0))
 
+/* scheme is not URL encoded, the longest libcurl supported ones are... */
+#define MAX_SCHEME_LEN 40
+
 /* Internal representation of CURLU. Point to URL-encoded strings. */
 struct Curl_URL {
   char *scheme;
@@ -157,23 +161,23 @@ static size_t strlen_url(const char *url, bool relative)
       continue;
     }
 
-    switch(*ptr) {
-    case '?':
-      left = FALSE;
-      /* FALLTHROUGH */
-    default:
-      if(urlchar_needs_escaping(*ptr))
-        newlen += 2;
-      newlen++;
-      break;
-    case ' ':
+    if(*ptr == ' ') {
       if(left)
         newlen += 3;
       else
         newlen++;
-      break;
+      continue;
     }
+
+    if (*ptr == '?')
+      left = FALSE;
+
+    if(urlchar_needs_escaping(*ptr))
+      newlen += 2;
+
+    newlen++;
   }
+
   return newlen;
 }
 
@@ -202,19 +206,7 @@ static void strcpy_url(char *output, const char *url, bool relative)
       continue;
     }
 
-    switch(*iptr) {
-    case '?':
-      left = FALSE;
-      /* FALLTHROUGH */
-    default:
-      if(urlchar_needs_escaping(*iptr)) {
-        msnprintf(optr, 4, "%%%02x", *iptr);
-        optr += 3;
-      }
-      else
-        *optr++=*iptr;
-      break;
-    case ' ':
+    if(*iptr == ' ') {
       if(left) {
         *optr++='%'; /* add a '%' */
         *optr++='2'; /* add a '2' */
@@ -222,41 +214,58 @@ static void strcpy_url(char *output, const char *url, bool relative)
       }
       else
         *optr++='+'; /* add a '+' here */
-      break;
+      continue;
     }
+
+    if(*iptr == '?')
+      left = FALSE;
+
+    if(urlchar_needs_escaping(*iptr)) {
+      msnprintf(optr, 4, "%%%02x", *iptr);
+      optr += 3;
+    }
+    else
+      *optr++ = *iptr;
   }
   *optr = 0; /* null-terminate output buffer */
 
 }
 
 /*
- * Returns true if the given URL is absolute (as opposed to relative) within
- * the buffer size. Returns the scheme in the buffer if TRUE and 'buf' is
- * non-NULL.
+ * Returns true if the given URL is absolute (as opposed to relative). Returns
+ * the scheme in the buffer if TRUE and 'buf' is non-NULL. The buflen must
+ * be larger than MAX_SCHEME_LEN if buf is set.
  */
 bool Curl_is_absolute_url(const char *url, char *buf, size_t buflen)
 {
   size_t i;
+  DEBUGASSERT(!buf || (buflen > MAX_SCHEME_LEN));
+  (void)buflen; /* only used in debug-builds */
+  if(buf)
+    buf[0] = 0; /* always leave a defined value in buf */
 #ifdef WIN32
   if(STARTS_WITH_DRIVE_PREFIX(url))
     return FALSE;
 #endif
-  for(i = 0; i < buflen && url[i]; ++i) {
+  for(i = 0; i < MAX_SCHEME_LEN; ++i) {
     char s = url[i];
-    if((s == ':') && (url[i + 1] == '/')) {
-      if(buf)
-        buf[i] = 0;
-      return TRUE;
-    }
-    /* RFC 3986 3.1 explains:
-      scheme      = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
-    */
-    else if(ISALNUM(s) || (s == '+') || (s == '-') || (s == '.') ) {
-      if(buf)
-        buf[i] = (char)TOLOWER(s);
+    if(s && (ISALNUM(s) || (s == '+') || (s == '-') || (s == '.') )) {
+      /* RFC 3986 3.1 explains:
+        scheme      = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
+      */
     }
-    else
+    else {
       break;
+    }
+  }
+  if(i && (url[i] == ':') && (url[i + 1] == '/')) {
+    if(buf) {
+      buf[i] = 0;
+      while(i--) {
+        buf[i] = (char)TOLOWER(url[i]);
+      }
+    }
+    return TRUE;
   }
   return FALSE;
 }
@@ -420,6 +429,29 @@ static char *concat_url(const char *base, const char *relurl)
   return newest;
 }
 
+/* scan for byte values < 31 or 127 */
+static bool junkscan(const char *part, unsigned int flags)
+{
+  if(part) {
+    static const char badbytes[]={
+      /* */ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+      0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+      0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+      0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+      0x7f, 0x00 /* null-terminate */
+    };
+    size_t n = strlen(part);
+    size_t nfine = strcspn(part, badbytes);
+    if(nfine != n)
+      /* since we don't know which part is scanned, return a generic error
+         code */
+      return TRUE;
+    if(!(flags & CURLU_ALLOW_SPACE) && strchr(part, ' '))
+      return TRUE;
+  }
+  return FALSE;
+}
+
 /*
  * parse_hostname_login()
  *
@@ -467,7 +499,7 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u,
                                    (h && (h->flags & PROTOPT_URLOPTIONS)) ?
                                    &optionsp:NULL);
   if(ccode) {
-    result = CURLUE_MALFORMED_INPUT;
+    result = CURLUE_BAD_LOGIN;
     goto out;
   }
 
@@ -477,15 +509,28 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u,
       result = CURLUE_USER_NOT_ALLOWED;
       goto out;
     }
-
+    if(junkscan(userp, flags)) {
+      result = CURLUE_BAD_USER;
+      goto out;
+    }
     u->user = userp;
   }
 
-  if(passwdp)
+  if(passwdp) {
+    if(junkscan(passwdp, flags)) {
+      result = CURLUE_BAD_PASSWORD;
+      goto out;
+    }
     u->password = passwdp;
+  }
 
-  if(optionsp)
+  if(optionsp) {
+    if(junkscan(optionsp, flags)) {
+      result = CURLUE_BAD_LOGIN;
+      goto out;
+    }
     u->options = optionsp;
+  }
 
   return CURLUE_OK;
   out:
@@ -493,6 +538,9 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u,
   free(userp);
   free(passwdp);
   free(optionsp);
+  u->user = NULL;
+  u->password = NULL;
+  u->options = NULL;
 
   return result;
 }
@@ -516,19 +564,19 @@ UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, char *hostname,
       int zonelen = len;
       if(1 == sscanf(hostname + zonelen, "%*[^]]%c%n", &endbracket, &len)) {
         if(']' != endbracket)
-          return CURLUE_MALFORMED_INPUT;
+          return CURLUE_BAD_IPV6;
         portptr = &hostname[--zonelen + len + 1];
       }
       else
-        return CURLUE_MALFORMED_INPUT;
+        return CURLUE_BAD_IPV6;
     }
     else
-      return CURLUE_MALFORMED_INPUT;
+      return CURLUE_BAD_IPV6;
 
     /* this is a RFC2732-style specified IP-address */
     if(portptr && *portptr) {
       if(*portptr != ':')
-        return CURLUE_MALFORMED_INPUT;
+        return CURLUE_BAD_IPV6;
     }
     else
       portptr = NULL;
@@ -558,9 +606,7 @@ UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, char *hostname,
 
     port = strtol(portptr + 1, &rest, 10);  /* Port number must be decimal */
 
-    if((port <= 0) || (port > 0xffff))
-      /* Single unix standard says port numbers are 16 bits long, but we don't
-         treat port zero as OK. */
+    if(port > 0xffff)
       return CURLUE_BAD_PORT_NUMBER;
 
     if(rest[0])
@@ -579,46 +625,20 @@ UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, char *hostname,
   return CURLUE_OK;
 }
 
-/* scan for byte values < 31 or 127 */
-static bool junkscan(const char *part, unsigned int flags)
-{
-  if(part) {
-    static const char badbytes[]={
-      /* */ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
-      0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
-      0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
-      0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
-      0x7f, 0x00 /* null-terminate */
-    };
-    size_t n = strlen(part);
-    size_t nfine = strcspn(part, badbytes);
-    if(nfine != n)
-      /* since we don't know which part is scanned, return a generic error
-         code */
-      return TRUE;
-    if(!(flags & CURLU_ALLOW_SPACE) && strchr(part, ' '))
-      return TRUE;
-  }
-  return FALSE;
-}
-
 static CURLUcode hostname_check(struct Curl_URL *u, char *hostname)
 {
   size_t len;
   size_t hlen = strlen(hostname);
 
   if(hostname[0] == '[') {
-#ifdef ENABLE_IPV6
-    char dest[16]; /* fits a binary IPv6 address */
-#endif
     const char *l = "0123456789abcdefABCDEF:.";
     if(hlen < 4) /* '[::]' is the shortest possible valid string */
-      return CURLUE_MALFORMED_INPUT;
+      return CURLUE_BAD_IPV6;
     hostname++;
     hlen -= 2;
 
     if(hostname[hlen] != ']')
-      return CURLUE_MALFORMED_INPUT;
+      return CURLUE_BAD_IPV6;
 
     /* only valid letters are ok */
     len = strspn(hostname, l);
@@ -635,6 +655,7 @@ static CURLUcode hostname_check(struct Curl_URL *u, char *hostname)
         while(*h && (*h != ']') && (i < 15))
           zoneid[i++] = *h++;
         if(!i || (']' != *h))
+          /* impossible to reach? */
           return CURLUE_MALFORMED_INPUT;
         zoneid[i] = 0;
         u->zoneid = strdup(zoneid);
@@ -644,22 +665,34 @@ static CURLUcode hostname_check(struct Curl_URL *u, char *hostname)
         hostname[len + 1] = 0; /* terminate the hostname */
       }
       else
-        return CURLUE_MALFORMED_INPUT;
+        return CURLUE_BAD_IPV6;
       /* hostname is fine */
     }
 #ifdef ENABLE_IPV6
-    hostname[hlen] = 0; /* end the address there */
-    if(1 != Curl_inet_pton(AF_INET6, hostname, dest))
-      return CURLUE_MALFORMED_INPUT;
-    hostname[hlen] = ']'; /* restore ending bracket */
+    {
+      char dest[16]; /* fits a binary IPv6 address */
+      char norm[MAX_IPADR_LEN];
+      hostname[hlen] = 0; /* end the address there */
+      if(1 != Curl_inet_pton(AF_INET6, hostname, dest))
+        return CURLUE_BAD_IPV6;
+
+      /* check if it can be done shorter */
+      if(Curl_inet_ntop(AF_INET6, dest, norm, sizeof(norm)) &&
+         (strlen(norm) < hlen)) {
+        strcpy(hostname, norm);
+        hlen = strlen(norm);
+        hostname[hlen + 1] = 0;
+      }
+      hostname[hlen] = ']'; /* restore ending bracket */
+    }
 #endif
   }
   else {
     /* letters from the second string is not ok */
-    len = strcspn(hostname, " ");
+    len = strcspn(hostname, " \r\n");
     if(hlen != len)
       /* hostname with bad content */
-      return CURLUE_MALFORMED_INPUT;
+      return CURLUE_BAD_HOSTNAME;
   }
   if(!hostname[0])
     return CURLUE_NO_HOST;
@@ -756,10 +789,35 @@ static bool ipv4_normalize(const char *hostname, char *outp, size_t olen)
   return TRUE;
 }
 
+/* return strdup'ed version in 'outp', possibly percent decoded */
+static CURLUcode decode_host(char *hostname, char **outp)
+{
+  char *per = NULL;
+  if(hostname[0] != '[')
+    /* only decode if not an ipv6 numerical */
+    per = strchr(hostname, '%');
+  if(!per) {
+    *outp = strdup(hostname);
+    if(!*outp)
+      return CURLUE_OUT_OF_MEMORY;
+  }
+  else {
+    /* might be encoded */
+    size_t dlen;
+    CURLcode result = Curl_urldecode(NULL, hostname, 0,
+                                     outp, &dlen, REJECT_CTRL);
+    if(result)
+      return CURLUE_BAD_HOSTNAME;
+  }
+
+  return CURLUE_OK;
+}
+
 static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
 {
   char *path;
   bool path_alloced = FALSE;
+  bool uncpath = FALSE;
   char *hostname;
   char *query = NULL;
   char *fragment = NULL;
@@ -794,11 +852,14 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
   }
 
   /* handle the file: scheme */
-  if(url_has_scheme && strcasecompare(schemebuf, "file")) {
+  if(url_has_scheme && !strcmp(schemebuf, "file")) {
+    if(urllen <= 6)
+      /* file:/ is not enough to actually be a complete file: URL */
+      return CURLUE_BAD_FILE_URL;
+
     /* path has been allocated large enough to hold this */
     strcpy(path, &url[5]);
 
-    hostname = NULL; /* no host for file: URLs */
     u->scheme = strdup("file");
     if(!u->scheme)
       return CURLUE_OUT_OF_MEMORY;
@@ -820,10 +881,13 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
        *
        *  o the hostname matches "localhost" (case-insensitively), or
        *
-       *  o the hostname is a FQDN that resolves to this machine.
+       *  o the hostname is a FQDN that resolves to this machine, or
+       *
+       *  o it is an UNC String transformed to an URI (Windows only, RFC 8089
+       *    Appendix E.3).
        *
        * For brevity, we only consider URLs with empty, "localhost", or
-       * "127.0.0.1" hostnames as local.
+       * "127.0.0.1" hostnames as local, otherwise as an UNC String.
        *
        * Additionally, there is an exception for URLs with a Windows drive
        * letter in the authority (which was accidentally omitted from RFC 8089
@@ -832,25 +896,50 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
       if(ptr[0] != '/' && !STARTS_WITH_URL_DRIVE_PREFIX(ptr)) {
         /* the URL includes a host name, it must match "localhost" or
            "127.0.0.1" to be valid */
-        if(!checkprefix("localhost/", ptr) &&
-           !checkprefix("127.0.0.1/", ptr)) {
+        if(checkprefix("localhost/", ptr) ||
+           checkprefix("127.0.0.1/", ptr)) {
+          ptr += 9; /* now points to the slash after the host */
+        }
+        else {
+#if defined(WIN32)
+          size_t len;
+
+          /* the host name, NetBIOS computer name, can not contain disallowed
+             chars, and the delimiting slash character must be appended to the
+             host name */
+          path = strpbrk(ptr, "/\\:*?\"<>|");
+          if(!path || *path != '/')
+            return CURLUE_BAD_FILE_URL;
+
+          len = path - ptr;
+          if(len) {
+            memcpy(hostname, ptr, len);
+            hostname[len] = 0;
+            uncpath = TRUE;
+          }
+
+          ptr -= 2; /* now points to the // before the host in UNC */
+#else
           /* Invalid file://hostname/, expected localhost or 127.0.0.1 or
              none */
-          return CURLUE_MALFORMED_INPUT;
+          return CURLUE_BAD_FILE_URL;
+#endif
         }
-        ptr += 9; /* now points to the slash after the host */
       }
 
       path = ptr;
     }
 
+    if(!uncpath)
+        hostname = NULL; /* no host for file: URLs by default */
+
 #if !defined(MSDOS) && !defined(WIN32) && !defined(__CYGWIN__)
     /* Don't allow Windows drive letters when not in Windows.
      * This catches both "file:/c:" and "file:c:" */
     if(('/' == path[0] && STARTS_WITH_URL_DRIVE_PREFIX(&path[1])) ||
        STARTS_WITH_URL_DRIVE_PREFIX(path)) {
       /* File drive letters are only accepted in MSDOS/Windows */
-      return CURLUE_MALFORMED_INPUT;
+      return CURLUE_BAD_FILE_URL;
     }
 #else
     /* If the path starts with a slash and a drive letter, ditch the slash */
@@ -877,7 +966,7 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
       }
       if((i < 1) || (i>3))
         /* less than one or more than three slashes */
-        return CURLUE_MALFORMED_INPUT;
+        return CURLUE_BAD_SLASHES;
 
       schemep = schemebuf;
       if(!Curl_builtin_scheme(schemep) &&
@@ -885,13 +974,13 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
         return CURLUE_UNSUPPORTED_SCHEME;
 
       if(junkscan(schemep, flags))
-        return CURLUE_MALFORMED_INPUT;
+        return CURLUE_BAD_SCHEME;
     }
     else {
       /* no scheme! */
 
       if(!(flags & (CURLU_DEFAULT_SCHEME|CURLU_GUESS_SCHEME)))
-        return CURLUE_MALFORMED_INPUT;
+        return CURLUE_BAD_SCHEME;
       if(flags & CURLU_DEFAULT_SCHEME)
         schemep = DEFAULT_SCHEME;
 
@@ -902,7 +991,8 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
     }
     hostp = p; /* host name starts here */
 
-    while(*p && !HOSTNAME_END(*p)) /* find end of host name */
+    /* find the end of the host name + port number */
+    while(*p && !HOSTNAME_END(*p))
       p++;
 
     len = p - hostp;
@@ -912,7 +1002,7 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
     }
     else {
       if(!(flags & CURLU_NO_AUTHORITY))
-        return CURLUE_MALFORMED_INPUT;
+        return CURLUE_NO_HOST;
     }
 
     len = strlen(p);
@@ -926,9 +1016,6 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
     }
   }
 
-  if(junkscan(path, flags))
-    return CURLUE_MALFORMED_INPUT;
-
   if((flags & CURLU_URLENCODE) && path[0]) {
     /* worst case output length is 3x the original! */
     char *newp = malloc(strlen(path) * 3);
@@ -942,6 +1029,8 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
   fragment = strchr(path, '#');
   if(fragment) {
     *fragment++ = 0;
+    if(junkscan(fragment, flags))
+      return CURLUE_BAD_FRAGMENT;
     if(fragment[0]) {
       u->fragment = strdup(fragment);
       if(!u->fragment)
@@ -952,12 +1041,17 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
   query = strchr(path, '?');
   if(query) {
     *query++ = 0;
+    if(junkscan(query, flags))
+      return CURLUE_BAD_QUERY;
     /* done even if the query part is a blank string */
     u->query = strdup(query);
     if(!u->query)
       return CURLUE_OUT_OF_MEMORY;
   }
 
+  if(junkscan(path, flags))
+    return CURLUE_BAD_PATH;
+
   if(!path[0])
     /* if there's no path left set, unset */
     path = NULL;
@@ -987,12 +1081,10 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
 
   if(hostname) {
     char normalized_ipv4[sizeof("255.255.255.255") + 1];
+
     /*
      * Parse the login details and strip them out of the host name.
      */
-    if(junkscan(hostname, flags))
-      return CURLUE_MALFORMED_INPUT;
-
     result = parse_hostname_login(u, &hostname, flags);
     if(result)
       return result;
@@ -1001,22 +1093,27 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
     if(result)
       return result;
 
+    if(junkscan(hostname, flags))
+      return CURLUE_BAD_HOSTNAME;
+
     if(0 == strlen(hostname) && (flags & CURLU_NO_AUTHORITY)) {
       /* Skip hostname check, it's allowed to be empty. */
+      u->host = strdup("");
     }
     else {
-      result = hostname_check(u, hostname);
-      if(result)
-        return result;
+      if(ipv4_normalize(hostname, normalized_ipv4, sizeof(normalized_ipv4)))
+        u->host = strdup(normalized_ipv4);
+      else {
+        result = decode_host(hostname, &u->host);
+        if(result)
+          return result;
+        result = hostname_check(u, u->host);
+        if(result)
+          return result;
+      }
     }
-
-    if(ipv4_normalize(hostname, normalized_ipv4, sizeof(normalized_ipv4)))
-      u->host = strdup(normalized_ipv4);
-    else
-      u->host = strdup(hostname);
     if(!u->host)
       return CURLUE_OUT_OF_MEMORY;
-
     if((flags & CURLU_GUESS_SCHEME) && !schemep) {
       /* legacy curl-style guess based on host name */
       if(checkprefix("ftp.", hostname))
@@ -1111,6 +1208,7 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what,
   CURLUcode ifmissing = CURLUE_UNKNOWN_PART;
   char portbuf[7];
   bool urldecode = (flags & CURLU_URLDECODE)?1:0;
+  bool urlencode = (flags & CURLU_URLENCODE)?1:0;
   bool plusdecode = FALSE;
   (void)flags;
   if(!u)
@@ -1143,6 +1241,7 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what,
     break;
   case CURLUPART_ZONEID:
     ptr = u->zoneid;
+    ifmissing = CURLUE_NO_ZONEID;
     break;
   case CURLUPART_PORT:
     ptr = u->port;
@@ -1228,16 +1327,54 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what,
       if(h && !(h->flags & PROTOPT_URLOPTIONS))
         options = NULL;
 
-      if((u->host[0] == '[') && u->zoneid) {
-        /* make it '[ host %25 zoneid ]' */
-        size_t hostlen = strlen(u->host);
-        size_t alen = hostlen + 3 + strlen(u->zoneid) + 1;
-        allochost = malloc(alen);
+      if(u->host[0] == '[') {
+        if(u->zoneid) {
+          /* make it '[ host %25 zoneid ]' */
+          size_t hostlen = strlen(u->host);
+          size_t alen = hostlen + 3 + strlen(u->zoneid) + 1;
+          allochost = malloc(alen);
+          if(!allochost)
+            return CURLUE_OUT_OF_MEMORY;
+          memcpy(allochost, u->host, hostlen - 1);
+          msnprintf(&allochost[hostlen - 1], alen - hostlen + 1,
+                    "%%25%s]", u->zoneid);
+        }
+      }
+      else if(urlencode) {
+        allochost = curl_easy_escape(NULL, u->host, 0);
         if(!allochost)
           return CURLUE_OUT_OF_MEMORY;
-        memcpy(allochost, u->host, hostlen - 1);
-        msnprintf(&allochost[hostlen - 1], alen - hostlen + 1,
-                  "%%25%s]", u->zoneid);
+      }
+      else {
+        /* only encode '%' in output host name */
+        char *host = u->host;
+        size_t pcount = 0;
+        /* first, count number of percents present in the name */
+        while(*host) {
+          if(*host == '%')
+            pcount++;
+          host++;
+        }
+        /* if there were percents, encode the host name */
+        if(pcount) {
+          size_t hostlen = strlen(u->host);
+          size_t alen = hostlen + 2 * pcount + 1;
+          char *o = allochost = malloc(alen);
+          if(!allochost)
+            return CURLUE_OUT_OF_MEMORY;
+
+          host = u->host;
+          while(*host) {
+            if(*host == '%') {
+              memcpy(o, "%25", 3);
+              o += 3;
+              host++;
+              continue;
+            }
+            *o++ = *host++;
+          }
+          *o = '\0';
+        }
       }
 
       url = aprintf("%s://%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
@@ -1362,7 +1499,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
   case CURLUPART_SCHEME:
     if(strlen(part) > MAX_SCHEME_LEN)
       /* too long */
-      return CURLUE_MALFORMED_INPUT;
+      return CURLUE_BAD_SCHEME;
     if(!(flags & CURLU_NON_SUPPORT_SCHEME) &&
        /* verify that it is a fine scheme */
        !Curl_builtin_scheme(part))
@@ -1379,10 +1516,15 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
   case CURLUPART_OPTIONS:
     storep = &u->options;
     break;
-  case CURLUPART_HOST:
+  case CURLUPART_HOST: {
+    size_t len = strcspn(part, " \r\n");
+    if(strlen(part) != len)
+      /* hostname with bad content */
+      return CURLUE_BAD_HOSTNAME;
     storep = &u->host;
     Curl_safefree(u->zoneid);
     break;
+  }
   case CURLUPART_ZONEID:
     storep = &u->zoneid;
     break;
@@ -1395,7 +1537,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
       return CURLUE_BAD_PORT_NUMBER;
     if(*endp)
       /* weirdly provided number, not good! */
-      return CURLUE_MALFORMED_INPUT;
+      return CURLUE_BAD_PORT_NUMBER;
     storep = &u->port;
   }
   break;
@@ -1424,7 +1566,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
     char *redired_url;
     CURLU *handle2;
 
-    if(Curl_is_absolute_url(part, NULL, MAX_SCHEME_LEN + 1)) {
+    if(Curl_is_absolute_url(part, NULL, 0)) {
       handle2 = curl_url();
       if(!handle2)
         return CURLUE_OUT_OF_MEMORY;
@@ -1559,7 +1701,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
       else {
         if(hostname_check(u, (char *)newp)) {
           free((char *)newp);
-          return CURLUE_MALFORMED_INPUT;
+          return CURLUE_BAD_HOSTNAME;
         }
       }
     }
diff --git a/lib/urldata.h b/lib/urldata.h
index 6ffd976..cc9c888 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -330,7 +330,7 @@ struct digestdata {
   char *opaque;
   char *qop;
   char *algorithm;
-  int nc; /* nounce count */
+  int nc; /* nonce count */
   BIT(stale); /* set true for re-negotiation */
   BIT(userhash);
 #endif
@@ -518,7 +518,9 @@ struct ConnectBits {
   BIT(tls_enable_npn);  /* TLS NPN extension? */
   BIT(tls_enable_alpn); /* TLS ALPN extension? */
   BIT(connect_only);
+#ifndef CURL_DISABLE_DOH
   BIT(doh);
+#endif
 #ifdef USE_UNIX_SOCKETS
   BIT(abstract_unix_socket);
 #endif
@@ -835,6 +837,7 @@ struct Curl_handler {
 #define PROTOPT_WILDCARD (1<<12) /* protocol supports wildcard matching */
 #define PROTOPT_USERPWDCTRL (1<<13) /* Allow "control bytes" (< 32 ascii) in
                                        user name and password */
+#define PROTOPT_NOTCPPROXY (1<<14) /* this protocol can't proxy over TCP */
 
 #define CONNCHECK_NONE 0                 /* No checks */
 #define CONNCHECK_ISDEAD (1<<0)          /* Check if the connection is dead. */
@@ -1554,6 +1557,7 @@ enum dupstring {
   STRING_SSH_PRIVATE_KEY, /* path to the private key file for auth */
   STRING_SSH_PUBLIC_KEY,  /* path to the public key file for auth */
   STRING_SSH_HOST_PUBLIC_KEY_MD5, /* md5 of host public key in ascii hex */
+  STRING_SSH_HOST_PUBLIC_KEY_SHA256, /* sha256 of host public key in base64 */
   STRING_SSH_KNOWNHOSTS,  /* file name of knownhosts file */
   STRING_PROXY_SERVICE_NAME, /* Proxy service name */
   STRING_SERVICE_NAME,    /* Service name */
@@ -1651,6 +1655,8 @@ struct UserDefined {
   curl_closesocket_callback fclosesocket; /* function for closing the
                                              socket */
   void *closesocket_client;
+  curl_prereq_callback fprereq; /* pre-initial request callback */
+  void *prereq_userp; /* pre-initial request user data */
 
   void *seek_client;    /* pointer to pass to the seek callback */
   /* the 3 curl_conv_callback functions below are used on non-ASCII hosts */
@@ -1675,6 +1681,8 @@ struct UserDefined {
   long server_response_timeout; /* in milliseconds, 0 means no timeout */
   long maxage_conn;     /* in seconds, max idle time to allow a connection that
                            is to be reused */
+  long maxlifetime_conn; /* in seconds, max time since creation to allow a
+                            connection that is to be reused */
   long tftp_blksize;    /* in bytes, 0 means use default */
   curl_off_t filesize;  /* size of file to upload, -1 means unknown */
   long low_speed_limit; /* bytes/second */
@@ -1744,6 +1752,7 @@ struct UserDefined {
   unsigned int scope_id;  /* Scope id for IPv6 */
   long allowed_protocols;
   long redir_protocols;
+  long mime_options;      /* Mime option flags. */
   struct curl_slist *mail_rcpt; /* linked list of mail recipients */
   /* Common RTSP header options */
   Curl_RtspReq rtspreq; /* RTSP request type */
@@ -1851,11 +1860,12 @@ struct UserDefined {
                            header */
   BIT(abstract_unix_socket);
   BIT(disallow_username_in_url); /* disallow username in url */
+#ifndef CURL_DISABLE_DOH
   BIT(doh); /* DNS-over-HTTPS enabled */
-  BIT(doh_get); /* use GET for DoH requests, instead of POST */
   BIT(doh_verifypeer);     /* DoH certificate peer verification */
   BIT(doh_verifyhost);     /* DoH certificate hostname verification */
   BIT(doh_verifystatus);   /* DoH certificate status verification */
+#endif
   BIT(http09_allowed); /* allow HTTP/0.9 responses */
   BIT(mail_rcpt_allowfails); /* allow RCPT TO command to fail for some
                                 recipients */
diff --git a/lib/vauth/digest.c b/lib/vauth/digest.c
index a04ffab..d8aac66 100644
--- a/lib/vauth/digest.c
+++ b/lib/vauth/digest.c
@@ -230,7 +230,7 @@ static CURLcode auth_digest_get_qop_values(const char *options, int *value)
     return CURLE_OUT_OF_MEMORY;
 
   token = strtok_r(tmp, ",", &tok_buf);
-  while(token != NULL) {
+  while(token) {
     if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH))
       *value |= DIGEST_QOP_VALUE_AUTH;
     else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_INT))
@@ -556,7 +556,7 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg,
           return CURLE_OUT_OF_MEMORY;
 
         token = strtok_r(tmp, ",", &tok_buf);
-        while(token != NULL) {
+        while(token) {
           if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH)) {
             foundAuth = TRUE;
           }
@@ -666,8 +666,8 @@ static CURLcode auth_create_digest_http_message(
                   struct digestdata *digest,
                   char **outptr, size_t *outlen,
                   void (*convert_to_ascii)(unsigned char *, unsigned char *),
-                  void (*hash)(unsigned char *, const unsigned char *,
-                               const size_t))
+                  CURLcode (*hash)(unsigned char *, const unsigned char *,
+                                   const size_t))
 {
   CURLcode result;
   unsigned char hashbuf[32]; /* 32 bytes/256 bits */
@@ -722,8 +722,7 @@ static CURLcode auth_create_digest_http_message(
            unq(nonce-value) ":" unq(cnonce-value)
   */
 
-  hashthis = aprintf("%s:%s:%s", digest->userhash ? userh : userp,
-                                 digest->realm, passwdp);
+  hashthis = aprintf("%s:%s:%s", userp, digest->realm, passwdp);
   if(!hashthis)
     return CURLE_OUT_OF_MEMORY;
 
diff --git a/lib/vauth/ntlm.c b/lib/vauth/ntlm.c
index 0aa3f1c..04f6590 100644
--- a/lib/vauth/ntlm.c
+++ b/lib/vauth/ntlm.c
@@ -603,7 +603,9 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
     memcpy(tmp, &ntlm->nonce[0], 8);
     memcpy(tmp + 8, entropy, 8);
 
-    Curl_md5it(md5sum, tmp, 16);
+    result = Curl_md5it(md5sum, tmp, 16);
+    if(result)
+      return result;
 
     /* We shall only use the first 8 bytes of md5sum, but the des code in
        Curl_ntlm_core_lm_resp only encrypt the first 8 bytes */
diff --git a/lib/version_win32.c b/lib/version_win32.c
index b8157e9..79a2aa6 100644
--- a/lib/version_win32.c
+++ b/lib/version_win32.c
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2016 - 2020, Steve Holme, <steve_holme@hotmail.com>.
+ * Copyright (C) 2016 - 2021, Steve Holme, <steve_holme@hotmail.com>.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -26,11 +26,28 @@
 
 #include <curl/curl.h>
 #include "version_win32.h"
+#include "warnless.h"
 
 /* The last #include files should be: */
 #include "curl_memory.h"
 #include "memdebug.h"
 
+/* This Unicode version struct works for VerifyVersionInfoW (OSVERSIONINFOEXW)
+   and RtlVerifyVersionInfo (RTLOSVERSIONINFOEXW) */
+struct OUR_OSVERSIONINFOEXW {
+  ULONG  dwOSVersionInfoSize;
+  ULONG  dwMajorVersion;
+  ULONG  dwMinorVersion;
+  ULONG  dwBuildNumber;
+  ULONG  dwPlatformId;
+  WCHAR  szCSDVersion[128];
+  USHORT wServicePackMajor;
+  USHORT wServicePackMinor;
+  USHORT wSuiteMask;
+  UCHAR  wProductType;
+  UCHAR  wReserved;
+};
+
 /*
  * curlx_verify_windows_version()
  *
@@ -40,6 +57,8 @@
  *
  * majorVersion [in] - The major version number.
  * minorVersion [in] - The minor version number.
+ * buildVersion [in] - The build version number. If 0, this parameter is
+ *                     ignored.
  * platform     [in] - The optional platform identifier.
  * condition    [in] - The test condition used to specifier whether we are
  *                     checking a version less then, equal to or greater than
@@ -50,6 +69,7 @@
  */
 bool curlx_verify_windows_version(const unsigned int majorVersion,
                                   const unsigned int minorVersion,
+                                  const unsigned int buildVersion,
                                   const PlatformIdentifier platform,
                                   const VersionCondition condition)
 {
@@ -101,34 +121,52 @@ bool curlx_verify_windows_version(const unsigned int majorVersion,
     case VERSION_LESS_THAN:
       if(osver.dwMajorVersion < majorVersion ||
         (osver.dwMajorVersion == majorVersion &&
-         osver.dwMinorVersion < minorVersion))
+         osver.dwMinorVersion < minorVersion) ||
+        (buildVersion != 0 &&
+         (osver.dwMajorVersion == majorVersion &&
+          osver.dwMinorVersion == minorVersion &&
+          osver.dwBuildNumber < buildVersion)))
         matched = TRUE;
       break;
 
     case VERSION_LESS_THAN_EQUAL:
       if(osver.dwMajorVersion < majorVersion ||
         (osver.dwMajorVersion == majorVersion &&
-         osver.dwMinorVersion <= minorVersion))
+         osver.dwMinorVersion < minorVersion) ||
+        (osver.dwMajorVersion == majorVersion &&
+         osver.dwMinorVersion == minorVersion &&
+         (buildVersion == 0 ||
+          osver.dwBuildNumber <= buildVersion)))
         matched = TRUE;
       break;
 
     case VERSION_EQUAL:
       if(osver.dwMajorVersion == majorVersion &&
-         osver.dwMinorVersion == minorVersion)
+         osver.dwMinorVersion == minorVersion &&
+        (buildVersion == 0 ||
+         osver.dwBuildNumber == buildVersion))
         matched = TRUE;
       break;
 
     case VERSION_GREATER_THAN_EQUAL:
       if(osver.dwMajorVersion > majorVersion ||
         (osver.dwMajorVersion == majorVersion &&
-         osver.dwMinorVersion >= minorVersion))
+         osver.dwMinorVersion > minorVersion) ||
+        (osver.dwMajorVersion == majorVersion &&
+         osver.dwMinorVersion == minorVersion &&
+         (buildVersion == 0 ||
+          osver.dwBuildNumber >= buildVersion)))
         matched = TRUE;
       break;
 
     case VERSION_GREATER_THAN:
       if(osver.dwMajorVersion > majorVersion ||
         (osver.dwMajorVersion == majorVersion &&
-         osver.dwMinorVersion > minorVersion))
+         osver.dwMinorVersion > minorVersion) ||
+        (buildVersion != 0 &&
+         (osver.dwMajorVersion == majorVersion &&
+          osver.dwMinorVersion == minorVersion &&
+          osver.dwBuildNumber > buildVersion)))
         matched = TRUE;
       break;
     }
@@ -144,6 +182,7 @@ bool curlx_verify_windows_version(const unsigned int majorVersion,
       case PLATFORM_WINNT:
         if(osver.dwPlatformId != VER_PLATFORM_WIN32_NT)
           matched = FALSE;
+        break;
 
       default: /* like platform == PLATFORM_DONT_CARE */
         break;
@@ -152,16 +191,31 @@ bool curlx_verify_windows_version(const unsigned int majorVersion,
   }
 #else
   ULONGLONG cm = 0;
-  OSVERSIONINFOEX osver;
+  struct OUR_OSVERSIONINFOEXW osver;
   BYTE majorCondition;
   BYTE minorCondition;
+  BYTE buildCondition;
   BYTE spMajorCondition;
   BYTE spMinorCondition;
+  DWORD dwTypeMask = VER_MAJORVERSION | VER_MINORVERSION |
+                     VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR;
+
+  typedef LONG (APIENTRY *RTLVERIFYVERSIONINFO_FN)
+    (struct OUR_OSVERSIONINFOEXW *, ULONG, ULONGLONG);
+  static RTLVERIFYVERSIONINFO_FN pRtlVerifyVersionInfo;
+  static bool onetime = true; /* safe because first call is during init */
+
+  if(onetime) {
+    pRtlVerifyVersionInfo = CURLX_FUNCTION_CAST(RTLVERIFYVERSIONINFO_FN,
+      (GetProcAddress(GetModuleHandleA("ntdll"), "RtlVerifyVersionInfo")));
+    onetime = false;
+  }
 
   switch(condition) {
   case VERSION_LESS_THAN:
     majorCondition = VER_LESS;
     minorCondition = VER_LESS;
+    buildCondition = VER_LESS;
     spMajorCondition = VER_LESS_EQUAL;
     spMinorCondition = VER_LESS_EQUAL;
     break;
@@ -169,6 +223,7 @@ bool curlx_verify_windows_version(const unsigned int majorVersion,
   case VERSION_LESS_THAN_EQUAL:
     majorCondition = VER_LESS_EQUAL;
     minorCondition = VER_LESS_EQUAL;
+    buildCondition = VER_LESS_EQUAL;
     spMajorCondition = VER_LESS_EQUAL;
     spMinorCondition = VER_LESS_EQUAL;
     break;
@@ -176,6 +231,7 @@ bool curlx_verify_windows_version(const unsigned int majorVersion,
   case VERSION_EQUAL:
     majorCondition = VER_EQUAL;
     minorCondition = VER_EQUAL;
+    buildCondition = VER_EQUAL;
     spMajorCondition = VER_GREATER_EQUAL;
     spMinorCondition = VER_GREATER_EQUAL;
     break;
@@ -183,6 +239,7 @@ bool curlx_verify_windows_version(const unsigned int majorVersion,
   case VERSION_GREATER_THAN_EQUAL:
     majorCondition = VER_GREATER_EQUAL;
     minorCondition = VER_GREATER_EQUAL;
+    buildCondition = VER_GREATER_EQUAL;
     spMajorCondition = VER_GREATER_EQUAL;
     spMinorCondition = VER_GREATER_EQUAL;
     break;
@@ -190,6 +247,7 @@ bool curlx_verify_windows_version(const unsigned int majorVersion,
   case VERSION_GREATER_THAN:
     majorCondition = VER_GREATER;
     minorCondition = VER_GREATER;
+    buildCondition = VER_GREATER;
     spMajorCondition = VER_GREATER_EQUAL;
     spMinorCondition = VER_GREATER_EQUAL;
     break;
@@ -202,6 +260,7 @@ bool curlx_verify_windows_version(const unsigned int majorVersion,
   osver.dwOSVersionInfoSize = sizeof(osver);
   osver.dwMajorVersion = majorVersion;
   osver.dwMinorVersion = minorVersion;
+  osver.dwBuildNumber = buildVersion;
   if(platform == PLATFORM_WINDOWS)
     osver.dwPlatformId = VER_PLATFORM_WIN32_WINDOWS;
   else if(platform == PLATFORM_WINNT)
@@ -211,13 +270,43 @@ bool curlx_verify_windows_version(const unsigned int majorVersion,
   cm = VerSetConditionMask(cm, VER_MINORVERSION, minorCondition);
   cm = VerSetConditionMask(cm, VER_SERVICEPACKMAJOR, spMajorCondition);
   cm = VerSetConditionMask(cm, VER_SERVICEPACKMINOR, spMinorCondition);
-  if(platform != PLATFORM_DONT_CARE)
+
+  if(platform != PLATFORM_DONT_CARE) {
     cm = VerSetConditionMask(cm, VER_PLATFORMID, VER_EQUAL);
+    dwTypeMask |= VER_PLATFORMID;
+  }
+
+  /* Later versions of Windows have version functions that may not return the
+     real version of Windows unless the application is so manifested. We prefer
+     the real version always, so we use the Rtl variant of the function when
+     possible. Note though the function signatures have underlying fundamental
+     types that are the same, the return values are different. */
+  if(pRtlVerifyVersionInfo)
+    matched = !pRtlVerifyVersionInfo(&osver, dwTypeMask, cm);
+  else
+    matched = !!VerifyVersionInfoW((OSVERSIONINFOEXW *)&osver, dwTypeMask, cm);
+
+  /* Compare the build number separately. VerifyVersionInfo normally compares
+     major.minor in hierarchical order (eg 1.9 is less than 2.0) but does not
+     do the same for build (eg 1.9 build 222 is not less than 2.0 build 111).
+     Build comparison is only needed when build numbers are equal (eg 1.9 is
+     always less than 2.0 so build comparison is not needed). */
+  if(matched && buildVersion &&
+     (condition == VERSION_EQUAL ||
+      ((condition == VERSION_GREATER_THAN_EQUAL ||
+        condition == VERSION_LESS_THAN_EQUAL) &&
+        curlx_verify_windows_version(majorVersion, minorVersion, 0,
+                                     platform, VERSION_EQUAL)))) {
+
+    cm = VerSetConditionMask(0, VER_BUILDNUMBER, buildCondition);
+    dwTypeMask = VER_BUILDNUMBER;
+    if(pRtlVerifyVersionInfo)
+      matched = !pRtlVerifyVersionInfo(&osver, dwTypeMask, cm);
+    else
+      matched = !!VerifyVersionInfoW((OSVERSIONINFOEXW *)&osver,
+                                      dwTypeMask, cm);
+  }
 
-  if(VerifyVersionInfo(&osver, (VER_MAJORVERSION | VER_MINORVERSION |
-                                VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR),
-                       cm))
-    matched = TRUE;
 #endif
 
   return matched;
diff --git a/lib/version_win32.h b/lib/version_win32.h
index 9b1bd88..38af87f 100644
--- a/lib/version_win32.h
+++ b/lib/version_win32.h
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2016 - 2020, Steve Holme, <steve_holme@hotmail.com>.
+ * Copyright (C) 2016 - 2021, Steve Holme, <steve_holme@hotmail.com>.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -45,6 +45,7 @@ typedef enum {
 /* This is used to verify if we are running on a specific windows version */
 bool curlx_verify_windows_version(const unsigned int majorVersion,
                                   const unsigned int minorVersion,
+                                  const unsigned int buildVersion,
                                   const PlatformIdentifier platform,
                                   const VersionCondition condition);
 
diff --git a/lib/vquic/ngtcp2.c b/lib/vquic/ngtcp2.c
index 9fcfe81..1596049 100644
--- a/lib/vquic/ngtcp2.c
+++ b/lib/vquic/ngtcp2.c
@@ -29,8 +29,10 @@
 #ifdef USE_OPENSSL
 #include <openssl/err.h>
 #include <ngtcp2/ngtcp2_crypto_openssl.h>
+#include "vtls/openssl.h"
 #elif defined(USE_GNUTLS)
 #include <ngtcp2/ngtcp2_crypto_gnutls.h>
+#include "vtls/gtls.h"
 #endif
 #include "urldata.h"
 #include "sendf.h"
@@ -61,6 +63,7 @@
 #endif
 
 #define H3_ALPN_H3_29 "\x5h3-29"
+#define H3_ALPN_H3 "\x2h3"
 
 /*
  * This holds outgoing HTTP/3 stream data that is used by nghttp3 until acked.
@@ -286,6 +289,27 @@ static SSL_CTX *quic_ssl_ctx(struct Curl_easy *data)
     SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
   }
 
+  {
+    struct connectdata *conn = data->conn;
+    const char * const ssl_cafile = conn->ssl_config.CAfile;
+    const char * const ssl_capath = conn->ssl_config.CApath;
+
+    if(conn->ssl_config.verifypeer) {
+      SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
+      /* tell OpenSSL where to find CA certificates that are used to verify
+         the server's certificate. */
+      if(!SSL_CTX_load_verify_locations(ssl_ctx, ssl_cafile, ssl_capath)) {
+        /* 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 NULL;
+      }
+      infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
+      infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
+    }
+  }
   return ssl_ctx;
 }
 
@@ -303,9 +327,10 @@ static int quic_init_ssl(struct quicsocket *qs)
 
   SSL_set_app_data(qs->ssl, qs);
   SSL_set_connect_state(qs->ssl);
+  SSL_set_quic_use_legacy_codepoint(qs->ssl, 0);
 
-  alpn = (const uint8_t *)H3_ALPN_H3_29;
-  alpnlen = sizeof(H3_ALPN_H3_29) - 1;
+  alpn = (const uint8_t *)H3_ALPN_H3_29 H3_ALPN_H3;
+  alpnlen = sizeof(H3_ALPN_H3_29) - 1 + sizeof(H3_ALPN_H3) - 1;
   if(alpn)
     SSL_set_alpn_protos(qs->ssl, alpn, (int)alpnlen);
 
@@ -417,7 +442,7 @@ static int tp_send_func(gnutls_session_t ssl, gnutls_buffer_t extdata)
 
 static int quic_init_ssl(struct quicsocket *qs)
 {
-  gnutls_datum_t alpn = {NULL, 0};
+  gnutls_datum_t alpn[2];
   /* this will need some attention when HTTPS proxy over QUIC get fixed */
   const char * const hostname = qs->conn->host.name;
   int rc;
@@ -439,12 +464,10 @@ static int quic_init_ssl(struct quicsocket *qs)
   gnutls_alert_set_read_function(qs->ssl, alert_read_func);
 
   rc = gnutls_session_ext_register(qs->ssl, "QUIC Transport Parameters",
-                                   0xffa5, GNUTLS_EXT_TLS,
-                                   tp_recv_func, tp_send_func,
-                                   NULL, NULL, NULL,
-                                   GNUTLS_EXT_FLAG_TLS |
-                                   GNUTLS_EXT_FLAG_CLIENT_HELLO |
-                                   GNUTLS_EXT_FLAG_EE);
+         NGTCP2_TLSEXT_QUIC_TRANSPORT_PARAMETERS_V1, GNUTLS_EXT_TLS,
+         tp_recv_func, tp_send_func, NULL, NULL, NULL,
+         GNUTLS_EXT_FLAG_TLS | GNUTLS_EXT_FLAG_CLIENT_HELLO |
+         GNUTLS_EXT_FLAG_EE);
   if(rc < 0) {
     H3BUGF(fprintf(stderr, "gnutls_session_ext_register failed: %s\n",
                    gnutls_strerror(rc)));
@@ -484,10 +507,12 @@ static int quic_init_ssl(struct quicsocket *qs)
   }
 
   /* strip the first byte (the length) from NGHTTP3_ALPN_H3 */
-  alpn.data = (unsigned char *)H3_ALPN_H3_29 + 1;
-  alpn.size = sizeof(H3_ALPN_H3_29) - 2;
-  if(alpn.data)
-    gnutls_alpn_set_protocols(qs->ssl, &alpn, 1, 0);
+  alpn[0].data = (unsigned char *)H3_ALPN_H3_29 + 1;
+  alpn[0].size = sizeof(H3_ALPN_H3_29) - 2;
+  alpn[1].data = (unsigned char *)H3_ALPN_H3 + 1;
+  alpn[1].size = sizeof(H3_ALPN_H3) - 2;
+
+  gnutls_alpn_set_protocols(qs->ssl, alpn, 2, GNUTLS_ALPN_MANDATORY);
 
   /* set SNI */
   gnutls_server_name_set(qs->ssl, GNUTLS_NAME_DNS, hostname, strlen(hostname));
@@ -648,6 +673,20 @@ static int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t stream_id,
   return 0;
 }
 
+static void cb_rand(uint8_t *dest, size_t destlen,
+                    const ngtcp2_rand_ctx *rand_ctx)
+{
+  CURLcode result;
+  (void)rand_ctx;
+
+  result = Curl_rand(NULL, dest, destlen);
+  if(result) {
+    /* cb_rand is only used for non-cryptographic context.  If Curl_rand
+       failed, just fill 0 and call it *random*. */
+    memset(dest, 0, destlen);
+  }
+}
+
 static int cb_get_new_connection_id(ngtcp2_conn *tconn, ngtcp2_cid *cid,
                                     uint8_t *token, size_t cidlen,
                                     void *user_data)
@@ -685,7 +724,7 @@ static ngtcp2_callbacks ng_callbacks = {
   ngtcp2_crypto_recv_retry_cb,
   cb_extend_max_local_streams_bidi,
   NULL, /* extend_max_local_streams_uni */
-  NULL, /* rand  */
+  cb_rand,
   cb_get_new_connection_id,
   NULL, /* remove_connection_id */
   ngtcp2_crypto_update_key_cb, /* update_key */
@@ -703,7 +742,7 @@ static ngtcp2_callbacks ng_callbacks = {
   NULL, /* recv_datagram */
   NULL, /* ack_datagram */
   NULL, /* lost_datagram */
-  NULL, /* get_path_challenge_data */
+  ngtcp2_crypto_get_path_challenge_data_cb,
   cb_stream_stop_sending
 };
 
@@ -776,7 +815,7 @@ CURLcode Curl_quic_connect(struct Curl_easy *data,
   ngtcp2_addr_init(&path.remote, addr, addrlen);
 
   rc = ngtcp2_conn_client_new(&qs->qconn, &qs->dcid, &qs->scid, &path,
-                              NGTCP2_PROTO_VER_MIN, &ng_callbacks,
+                              NGTCP2_PROTO_VER_V1, &ng_callbacks,
                               &qs->settings, &qs->transport_params, NULL, qs);
   if(rc)
     return CURLE_QUIC_CONNECT_ERROR;
@@ -792,7 +831,7 @@ CURLcode Curl_quic_connect(struct Curl_easy *data,
 void Curl_quic_ver(char *p, size_t len)
 {
   const ngtcp2_info *ng2 = ngtcp2_version(0);
-  nghttp3_info *ht3 = nghttp3_version(0);
+  const nghttp3_info *ht3 = nghttp3_version(0);
   (void)msnprintf(p, len, "ngtcp2/%s nghttp3/%s",
                   ng2->version_str, ht3->version_str);
 }
@@ -1622,8 +1661,10 @@ static ssize_t ngh3_stream_send(struct Curl_easy *data,
   return sent;
 }
 
-static void ng_has_connected(struct connectdata *conn, int tempindex)
+static CURLcode ng_has_connected(struct Curl_easy *data,
+                                 struct connectdata *conn, int tempindex)
 {
+  CURLcode result = CURLE_OK;
   conn->recv[FIRSTSOCKET] = ngh3_stream_recv;
   conn->send[FIRSTSOCKET] = ngh3_stream_send;
   conn->handler = &Curl_handler_http3;
@@ -1631,6 +1672,27 @@ static void ng_has_connected(struct connectdata *conn, int tempindex)
   conn->httpversion = 30;
   conn->bundle->multiuse = BUNDLE_MULTIPLEX;
   conn->quic = &conn->hequic[tempindex];
+
+  if(conn->ssl_config.verifyhost) {
+#ifdef USE_OPENSSL
+    X509 *server_cert;
+    CURLcode result;
+    server_cert = SSL_get_peer_certificate(conn->quic->ssl);
+    if(!server_cert) {
+      return CURLE_PEER_FAILED_VERIFICATION;
+    }
+    result = Curl_ossl_verifyhost(data, conn, server_cert);
+    X509_free(server_cert);
+    if(result)
+      return result;
+    infof(data, "Verified certificate just fine");
+#else
+    result = Curl_gtls_verifyserver(data, conn, conn->quic->ssl, FIRSTSOCKET);
+#endif
+  }
+  else
+    infof(data, "Skipped certificate verification");
+  return result;
 }
 
 /*
@@ -1654,8 +1716,9 @@ CURLcode Curl_quic_is_connected(struct Curl_easy *data,
     goto error;
 
   if(ngtcp2_conn_get_handshake_completed(qs->qconn)) {
-    *done = TRUE;
-    ng_has_connected(conn, sockindex);
+    result = ng_has_connected(data, conn, sockindex);
+    if(!result)
+      *done = TRUE;
   }
 
   return result;
@@ -1702,6 +1765,10 @@ static CURLcode ng_process_ingress(struct Curl_easy *data,
     rv = ngtcp2_conn_read_pkt(qs->qconn, &path, &pi, buf, recvd, ts);
     if(rv) {
       /* TODO Send CONNECTION_CLOSE if possible */
+      if(rv == NGTCP2_ERR_CRYPTO)
+        /* this is a "TLS problem", but a failed certificate verification
+           is a common reason for this */
+        return CURLE_PEER_FAILED_VERIFICATION;
       return CURLE_RECV_ERROR;
     }
   }
diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c
index a772f1f..581bc1b 100644
--- a/lib/vssh/libssh2.c
+++ b/lib/vssh/libssh2.c
@@ -81,6 +81,11 @@
 #include "select.h"
 #include "warnless.h"
 #include "curl_path.h"
+#include "strcase.h"
+
+#include <curl_base64.h> /* for base64 encoding/decoding */
+#include <curl_sha256.h>
+
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -615,40 +620,141 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data)
   struct connectdata *conn = data->conn;
   struct ssh_conn *sshc = &conn->proto.sshc;
   const char *pubkey_md5 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5];
-  char md5buffer[33];
+  const char *pubkey_sha256 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256];
+
+  infof(data, "SSH MD5 public key: %s",
+    pubkey_md5 != NULL ? pubkey_md5 : "NULL");
+  infof(data, "SSH SHA256 public key: %s",
+      pubkey_sha256 != NULL ? pubkey_sha256 : "NULL");
 
-  const char *fingerprint = libssh2_hostkey_hash(sshc->ssh_session,
-      LIBSSH2_HOSTKEY_HASH_MD5);
+  if(pubkey_sha256) {
+    const char *fingerprint = NULL;
+    char *fingerprint_b64 = NULL;
+    size_t fingerprint_b64_len;
+    size_t pub_pos = 0;
+    size_t b64_pos = 0;
 
-  if(fingerprint) {
+#ifdef LIBSSH2_HOSTKEY_HASH_SHA256
     /* The fingerprint points to static storage (!), don't free() it. */
-    int i;
-    for(i = 0; i < 16; i++)
-      msnprintf(&md5buffer[i*2], 3, "%02x", (unsigned char) fingerprint[i]);
-    infof(data, "SSH MD5 fingerprint: %s", md5buffer);
-  }
+    fingerprint = libssh2_hostkey_hash(sshc->ssh_session,
+        LIBSSH2_HOSTKEY_HASH_SHA256);
+#else
+    const char *hostkey;
+    size_t len = 0;
+    unsigned char hash[32];
+
+    hostkey = libssh2_session_hostkey(sshc->ssh_session, &len, NULL);
+    if(hostkey) {
+      if(!Curl_sha256it(hash, (const unsigned char *) hostkey, len))
+        fingerprint = (char *) hash;
+    }
+#endif
 
-  /* Before we authenticate we check the hostkey's MD5 fingerprint
-   * against a known fingerprint, if available.
-   */
-  if(pubkey_md5 && strlen(pubkey_md5) == 32) {
-    if(!fingerprint || !strcasecompare(md5buffer, pubkey_md5)) {
-      if(fingerprint)
-        failf(data,
-            "Denied establishing ssh session: mismatch md5 fingerprint. "
-            "Remote %s is not equal to %s", md5buffer, pubkey_md5);
-      else
-        failf(data,
-            "Denied establishing ssh session: md5 fingerprint not available");
+    if(!fingerprint) {
+      failf(data,
+          "Denied establishing ssh session: sha256 fingerprint "
+          "not available");
+      state(data, SSH_SESSION_FREE);
+      sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
+      return sshc->actualcode;
+    }
+
+    /* The length of fingerprint is 32 bytes for SHA256.
+     * See libssh2_hostkey_hash documentation. */
+    if(Curl_base64_encode(data, fingerprint, 32, &fingerprint_b64,
+                          &fingerprint_b64_len) != CURLE_OK) {
+      state(data, SSH_SESSION_FREE);
+      sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
+      return sshc->actualcode;
+    }
+
+    if(!fingerprint_b64) {
+      failf(data, "sha256 fingerprint could not be encoded");
+      state(data, SSH_SESSION_FREE);
+      sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
+      return sshc->actualcode;
+    }
+
+    infof(data, "SSH SHA256 fingerprint: %s", fingerprint_b64);
+
+    /* Find the position of any = padding characters in the public key */
+    while((pubkey_sha256[pub_pos] != '=') && pubkey_sha256[pub_pos]) {
+      pub_pos++;
+    }
+
+    /* Find the position of any = padding characters in the base64 coded
+     * hostkey fingerprint */
+    while((fingerprint_b64[b64_pos] != '=') && fingerprint_b64[b64_pos]) {
+      b64_pos++;
+    }
+
+    /* Before we authenticate we check the hostkey's sha256 fingerprint
+     * against a known fingerprint, if available.
+     */
+    if((pub_pos != b64_pos) ||
+        Curl_strncasecompare(fingerprint_b64, pubkey_sha256, pub_pos) != 1) {
+      free(fingerprint_b64);
+
+      failf(data,
+          "Denied establishing ssh session: mismatch sha256 fingerprint. "
+          "Remote %s is not equal to %s", fingerprint_b64, pubkey_sha256);
       state(data, SSH_SESSION_FREE);
       sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
       return sshc->actualcode;
     }
-    infof(data, "MD5 checksum match!");
+
+    free(fingerprint_b64);
+
+    infof(data, "SHA256 checksum match!");
+  }
+
+  if(pubkey_md5) {
+    char md5buffer[33];
+    const char *fingerprint = NULL;
+
+    fingerprint = libssh2_hostkey_hash(sshc->ssh_session,
+        LIBSSH2_HOSTKEY_HASH_MD5);
+
+    if(fingerprint) {
+      /* The fingerprint points to static storage (!), don't free() it. */
+      int i;
+      for(i = 0; i < 16; i++) {
+        msnprintf(&md5buffer[i*2], 3, "%02x", (unsigned char) fingerprint[i]);
+      }
+
+      infof(data, "SSH MD5 fingerprint: %s", md5buffer);
+    }
+
+    /* Before we authenticate we check the hostkey's MD5 fingerprint
+     * against a known fingerprint, if available.
+     */
+    if(pubkey_md5 && strlen(pubkey_md5) == 32) {
+      if(!fingerprint || !strcasecompare(md5buffer, pubkey_md5)) {
+        if(fingerprint) {
+          failf(data,
+              "Denied establishing ssh session: mismatch md5 fingerprint. "
+              "Remote %s is not equal to %s", md5buffer, pubkey_md5);
+        }
+        else {
+          failf(data,
+              "Denied establishing ssh session: md5 fingerprint "
+              "not available");
+        }
+        state(data, SSH_SESSION_FREE);
+        sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
+        return sshc->actualcode;
+      }
+      infof(data, "MD5 checksum match!");
+    }
+  }
+
+  if(!pubkey_md5 && !pubkey_sha256) {
+    return ssh_knownhost(data);
+  }
+  else {
     /* as we already matched, we skip the check for known hosts */
     return CURLE_OK;
   }
-  return ssh_knownhost(data);
 }
 
 /*
@@ -3610,7 +3716,7 @@ void Curl_ssh_cleanup(void)
 
 void Curl_ssh_version(char *buffer, size_t buflen)
 {
-  (void)msnprintf(buffer, buflen, "libssh2/%s", LIBSSH2_VERSION);
+  (void)msnprintf(buffer, buflen, "libssh2/%s", CURL_LIBSSH2_VERSION);
 }
 
 /* The SSH session is associated with the *CONNECTION* but the callback user
diff --git a/lib/vssh/wolfssh.c b/lib/vssh/wolfssh.c
index 4b1e2ec..5b4cde9 100644
--- a/lib/vssh/wolfssh.c
+++ b/lib/vssh/wolfssh.c
@@ -449,7 +449,8 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
     switch(sshc->state) {
     case SSH_INIT:
       state(data, SSH_S_STARTUP);
-      /* FALLTHROUGH */
+      break;
+
     case SSH_S_STARTUP:
       rc = wolfSSH_connect(sshc->ssh_session);
       if(rc != WS_SUCCESS)
@@ -838,7 +839,8 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
         break;
       }
       state(data, SSH_SFTP_READDIR);
-      /* FALLTHROUGH */
+      break;
+
     case SSH_SFTP_READDIR:
       name = wolfSSH_SFTP_LS(sshc->ssh_session, sftp_scp->path);
       if(!name)
diff --git a/lib/vtls/bearssl.c b/lib/vtls/bearssl.c
index e87649e..9b772d0 100644
--- a/lib/vtls/bearssl.c
+++ b/lib/vtls/bearssl.c
@@ -608,6 +608,7 @@ static CURLcode bearssl_connect_step3(struct Curl_easy *data,
 
   if(SSL_SET_OPTION(primary.sessionid)) {
     bool incache;
+    bool added = FALSE;
     void *oldsession;
     br_ssl_session_parameters *session;
 
@@ -623,10 +624,11 @@ static CURLcode bearssl_connect_step3(struct Curl_easy *data,
       Curl_ssl_delsessionid(data, oldsession);
     ret = Curl_ssl_addsessionid(data, conn,
                                 SSL_IS_PROXY() ? TRUE : FALSE,
-                                session, 0, sockindex);
+                                session, 0, sockindex, &added);
     Curl_ssl_sessionid_unlock(data);
-    if(ret) {
+    if(!added)
       free(session);
+    if(ret) {
       return CURLE_OUT_OF_MEMORY;
     }
   }
diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c
index 1b145d8..18864aa 100644
--- a/lib/vtls/gtls.c
+++ b/lib/vtls/gtls.c
@@ -404,6 +404,7 @@ gtls_connect_step1(struct Curl_easy *data,
   const char * const hostname = SSL_HOST_NAME();
   long * const certverifyresult = &SSL_SET_OPTION_LVALUE(certverifyresult);
   const char *tls13support;
+  CURLcode result;
 
   if(connssl->state == ssl_connection_complete)
     /* to make us tolerant against being called more than once for the
@@ -496,6 +497,7 @@ gtls_connect_step1(struct Curl_easy *data,
   /* use system ca certificate store as fallback */
   if(SSL_CONN_CONFIG(verifypeer) &&
      !(SSL_CONN_CONFIG(CAfile) || SSL_CONN_CONFIG(CApath))) {
+    /* this ignores errors on purpose */
     gnutls_certificate_set_x509_system_trust(backend->cred);
   }
 #endif
@@ -557,31 +559,25 @@ gtls_connect_step1(struct Curl_easy *data,
   /* Ensure +SRP comes at the *end* of all relevant strings so that it can be
    * removed if a run-time error indicates that SRP is not supported by this
    * GnuTLS version */
-  switch(SSL_CONN_CONFIG(version)) {
-    case CURL_SSLVERSION_TLSv1_3:
-      if(!tls13support) {
-        failf(data, "This GnuTLS installation does not support TLS 1.3");
-        return CURLE_SSL_CONNECT_ERROR;
-      }
-      /* FALLTHROUGH */
-    case CURL_SSLVERSION_DEFAULT:
-    case CURL_SSLVERSION_TLSv1:
-    case CURL_SSLVERSION_TLSv1_0:
-    case CURL_SSLVERSION_TLSv1_1:
-    case CURL_SSLVERSION_TLSv1_2: {
-      CURLcode result = set_ssl_version_min_max(data, &prioritylist,
-                                                tls13support);
-      if(result)
-        return result;
-      break;
-    }
-    case CURL_SSLVERSION_SSLv2:
-    case CURL_SSLVERSION_SSLv3:
-    default:
-      failf(data, "GnuTLS does not support SSLv2 or SSLv3");
+
+  if(SSL_CONN_CONFIG(version) == CURL_SSLVERSION_SSLv2 ||
+     SSL_CONN_CONFIG(version) == CURL_SSLVERSION_SSLv3) {
+    failf(data, "GnuTLS does not support SSLv2 or SSLv3");
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+
+  if(SSL_CONN_CONFIG(version) == CURL_SSLVERSION_TLSv1_3) {
+    if(!tls13support) {
+      failf(data, "This GnuTLS installation does not support TLS 1.3");
       return CURLE_SSL_CONNECT_ERROR;
+    }
   }
 
+  /* At this point we know we have a supported TLS version, so set it */
+  result = set_ssl_version_min_max(data, &prioritylist, tls13support);
+  if(result)
+    return result;
+
 #ifdef HAVE_GNUTLS_SRP
   /* Only add SRP to the cipher list if SRP is requested. Otherwise
    * GnuTLS will disable TLS 1.3 support. */
@@ -636,7 +632,10 @@ gtls_connect_step1(struct Curl_easy *data,
     cur++;
     infof(data, "ALPN, offering %s", ALPN_HTTP_1_1);
 
-    gnutls_alpn_set_protocols(session, protocols, cur, 0);
+    if(gnutls_alpn_set_protocols(session, protocols, cur, 0)) {
+      failf(data, "failed setting ALPN");
+      return CURLE_SSL_CONNECT_ERROR;
+    }
   }
 
   if(SSL_SET_OPTION(primary.clientcert)) {
@@ -762,10 +761,10 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data,
   CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;
 
   /* if a path wasn't specified, don't pin */
-  if(NULL == pinnedpubkey)
+  if(!pinnedpubkey)
     return CURLE_OK;
 
-  if(NULL == cert)
+  if(!cert)
     return result;
 
   do {
@@ -783,7 +782,7 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data,
       break; /* failed */
 
     buff1 = malloc(len1);
-    if(NULL == buff1)
+    if(!buff1)
       break; /* failed */
 
     len2 = len1;
@@ -798,7 +797,7 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data,
     result = Curl_pin_peer_pubkey(data, pinnedpubkey, buff1, len1);
   } while(0);
 
-  if(NULL != key)
+  if(key)
     gnutls_pubkey_deinit(key);
 
   Curl_safefree(buff1);
@@ -809,10 +808,11 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data,
 static Curl_recv gtls_recv;
 static Curl_send gtls_send;
 
-static CURLcode
-gtls_connect_step3(struct Curl_easy *data,
-                   struct connectdata *conn,
-                   int sockindex)
+CURLcode
+Curl_gtls_verifyserver(struct Curl_easy *data,
+                       struct connectdata *conn,
+                       gnutls_session_t session,
+                       int sockindex)
 {
   unsigned int cert_list_size;
   const gnutls_datum_t *chainp;
@@ -824,9 +824,6 @@ gtls_connect_step3(struct Curl_easy *data,
   size_t size;
   time_t certclock;
   const char *ptr;
-  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
-  struct ssl_backend_data *backend = connssl->backend;
-  gnutls_session_t session = backend->session;
   int rc;
   gnutls_datum_t proto;
   CURLcode result = CURLE_OK;
@@ -1270,8 +1267,6 @@ gtls_connect_step3(struct Curl_easy *data,
   }
 
   conn->ssl[sockindex].state = ssl_connection_complete;
-  conn->recv[sockindex] = gtls_recv;
-  conn->send[sockindex] = gtls_send;
 
   if(SSL_SET_OPTION(primary.sessionid)) {
     /* we always unconditionally get the session id here, as even if we
@@ -1287,6 +1282,7 @@ gtls_connect_step3(struct Curl_easy *data,
 
     if(connect_sessionid) {
       bool incache;
+      bool added = FALSE;
       void *ssl_sessionid;
 
       /* extract session ID to the allocated buffer */
@@ -1306,10 +1302,11 @@ gtls_connect_step3(struct Curl_easy *data,
       result = Curl_ssl_addsessionid(data, conn,
                                      SSL_IS_PROXY() ? TRUE : FALSE,
                                      connect_sessionid, connect_idsize,
-                                     sockindex);
+                                     sockindex, &added);
       Curl_ssl_sessionid_unlock(data);
-      if(result) {
+      if(!added)
         free(connect_sessionid);
+      if(result) {
         result = CURLE_OUT_OF_MEMORY;
       }
     }
@@ -1354,9 +1351,13 @@ gtls_connect_common(struct Curl_easy *data,
 
   /* Finish connecting once the handshake is done */
   if(ssl_connect_1 == connssl->connecting_state) {
-    rc = gtls_connect_step3(data, conn, sockindex);
+    struct ssl_backend_data *backend = connssl->backend;
+    gnutls_session_t session = backend->session;
+    rc = Curl_gtls_verifyserver(data, conn, session, sockindex);
     if(rc)
       return rc;
+    conn->recv[sockindex] = gtls_recv;
+    conn->send[sockindex] = gtls_send;
   }
 
   *done = ssl_connect_1 == connssl->connecting_state;
diff --git a/lib/vtls/gtls.h b/lib/vtls/gtls.h
index 1a146a3..642d5f0 100644
--- a/lib/vtls/gtls.h
+++ b/lib/vtls/gtls.h
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -27,7 +27,11 @@
 #ifdef USE_GNUTLS
 
 #include "urldata.h"
-
+#include <gnutls/gnutls.h>
+CURLcode
+Curl_gtls_verifyserver(struct Curl_easy *data, struct connectdata *conn,
+                       gnutls_session_t session,
+                       int sockindex);
 extern const struct Curl_ssl Curl_ssl_gnutls;
 
 #endif /* USE_GNUTLS */
diff --git a/lib/vtls/mbedtls.c b/lib/vtls/mbedtls.c
index 780d13e..1d209b2 100644
--- a/lib/vtls/mbedtls.c
+++ b/lib/vtls/mbedtls.c
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2012 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2012 - 2022, Daniel Stenberg, <daniel@haxx.se>, et al.
  * Copyright (C) 2010 - 2011, Hoi-Ho Chan, <hoiho.chan@gmail.com>
  *
  * This software is licensed as described in the file COPYING, which
@@ -270,7 +270,10 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn,
 {
   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
   struct ssl_backend_data *backend = connssl->backend;
-  const char * const ssl_cafile = SSL_CONN_CONFIG(CAfile);
+  const struct curl_blob *ca_info_blob = SSL_CONN_CONFIG(ca_info_blob);
+  const char * const ssl_cafile =
+    /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */
+    (ca_info_blob ? NULL : SSL_CONN_CONFIG(CAfile));
   const bool verifypeer = SSL_CONN_CONFIG(verifypeer);
   const char * const ssl_capath = SSL_CONN_CONFIG(CApath);
   char * const ssl_cert = SSL_SET_OPTION(primary.clientcert);
@@ -316,16 +319,34 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn,
   /* Load the trusted CA */
   mbedtls_x509_crt_init(&backend->cacert);
 
-  if(ssl_cafile) {
+  if(ca_info_blob && verifypeer) {
+    /* Unfortunately, mbedtls_x509_crt_parse() requires the data to be null
+       terminated even when provided the exact length, forcing us to waste
+       extra memory here. */
+    unsigned char *newblob = malloc(ca_info_blob->len + 1);
+    if(!newblob)
+      return CURLE_OUT_OF_MEMORY;
+    memcpy(newblob, ca_info_blob->data, ca_info_blob->len);
+    newblob[ca_info_blob->len] = 0; /* null terminate */
+    ret = mbedtls_x509_crt_parse(&backend->cacert, newblob,
+                                 ca_info_blob->len + 1);
+    free(newblob);
+    if(ret<0) {
+      mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
+      failf(data, "Error importing ca cert blob - mbedTLS: (-0x%04X) %s",
+            -ret, errorbuf);
+      return ret;
+    }
+  }
+
+  if(ssl_cafile && verifypeer) {
     ret = mbedtls_x509_crt_parse_file(&backend->cacert, ssl_cafile);
 
     if(ret<0) {
       mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
       failf(data, "Error reading ca cert file %s - mbedTLS: (-0x%04X) %s",
             ssl_cafile, -ret, errorbuf);
-
-      if(verifypeer)
-        return CURLE_SSL_CACERT_BADFILE;
+      return CURLE_SSL_CACERT_BADFILE;
     }
   }
 
@@ -358,10 +379,17 @@ mbed_connect_step1(struct Curl_easy *data, struct connectdata *conn,
   }
 
   if(ssl_cert_blob) {
-    const unsigned char *blob_data =
-      (const unsigned char *)ssl_cert_blob->data;
-    ret = mbedtls_x509_crt_parse(&backend->clicert, blob_data,
+    /* Unfortunately, mbedtls_x509_crt_parse() requires the data to be null
+       terminated even when provided the exact length, forcing us to waste
+       extra memory here. */
+    unsigned char *newblob = malloc(ssl_cert_blob->len + 1);
+    if(!newblob)
+      return CURLE_OUT_OF_MEMORY;
+    memcpy(newblob, ssl_cert_blob->data, ssl_cert_blob->len);
+    newblob[ssl_cert_blob->len] = 0; /* null terminate */
+    ret = mbedtls_x509_crt_parse(&backend->clicert, newblob,
                                  ssl_cert_blob->len);
+    free(newblob);
 
     if(ret) {
       mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
@@ -671,7 +699,7 @@ mbed_connect_step2(struct Curl_easy *data, struct connectdata *conn,
     mbedtls_x509_crt *p = NULL;
     unsigned char *pubkey = NULL;
 
-#if MBEDTLS_VERSION_NUMBER >= 0x03000000
+#if MBEDTLS_VERSION_NUMBER == 0x03000000
     if(!peercert || !peercert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(p) ||
        !peercert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(len)) {
 #else
@@ -698,7 +726,7 @@ mbed_connect_step2(struct Curl_easy *data, struct connectdata *conn,
     /* Make a copy of our const peercert because mbedtls_pk_write_pubkey_der
        needs a non-const key, for now.
        https://github.com/ARMmbed/mbedtls/issues/396 */
-#if MBEDTLS_VERSION_NUMBER >= 0x03000000
+#if MBEDTLS_VERSION_NUMBER == 0x03000000
     if(mbedtls_x509_crt_parse_der(p,
                         peercert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(p),
                         peercert->MBEDTLS_PRIVATE(raw).MBEDTLS_PRIVATE(len))) {
@@ -710,7 +738,7 @@ mbed_connect_step2(struct Curl_easy *data, struct connectdata *conn,
       goto pinnedpubkey_error;
     }
 
-#if MBEDTLS_VERSION_NUMBER >= 0x03000000
+#if MBEDTLS_VERSION_NUMBER == 0x03000000
     size = mbedtls_pk_write_pubkey_der(&p->MBEDTLS_PRIVATE(pk), pubkey,
                                        PUB_DER_MAX_BYTES);
 #else
@@ -784,6 +812,7 @@ mbed_connect_step3(struct Curl_easy *data, struct connectdata *conn,
     mbedtls_ssl_session *our_ssl_sessionid;
     void *old_ssl_sessionid = NULL;
     bool isproxy = SSL_IS_PROXY() ? TRUE : FALSE;
+    bool added = FALSE;
 
     our_ssl_sessionid = malloc(sizeof(mbedtls_ssl_session));
     if(!our_ssl_sessionid)
@@ -807,11 +836,13 @@ mbed_connect_step3(struct Curl_easy *data, struct connectdata *conn,
       Curl_ssl_delsessionid(data, old_ssl_sessionid);
 
     retcode = Curl_ssl_addsessionid(data, conn, isproxy, our_ssl_sessionid,
-                                    0, sockindex);
+                                    0, sockindex, &added);
     Curl_ssl_sessionid_unlock(data);
-    if(retcode) {
+    if(!added) {
       mbedtls_ssl_session_free(our_ssl_sessionid);
       free(our_ssl_sessionid);
+    }
+    if(retcode) {
       failf(data, "failed to store ssl session");
       return retcode;
     }
@@ -1151,6 +1182,7 @@ const struct Curl_ssl Curl_ssl_mbedtls = {
   { CURLSSLBACKEND_MBEDTLS, "mbedtls" }, /* info */
 
   SSLSUPP_CA_PATH |
+  SSLSUPP_CAINFO_BLOB |
   SSLSUPP_PINNEDPUBKEY |
   SSLSUPP_SSL_CTX,
 
diff --git a/lib/vtls/mesalink.c b/lib/vtls/mesalink.c
index 3db9184..35a9165 100644
--- a/lib/vtls/mesalink.c
+++ b/lib/vtls/mesalink.c
@@ -68,8 +68,6 @@ struct ssl_backend_data
   SSL *handle;
 };
 
-#define BACKEND connssl->backend
-
 static Curl_recv mesalink_recv;
 static Curl_send mesalink_send;
 
@@ -100,9 +98,9 @@ mesalink_connect_step1(struct Curl_easy *data,
 #endif
   const char * const hostname = SSL_HOST_NAME();
   size_t hostname_len = strlen(hostname);
-
   SSL_METHOD *req_method = NULL;
   curl_socket_t sockfd = conn->sock[sockindex];
+  struct ssl_backend_data *backend = connssl->backend;
 
   if(connssl->state == ssl_connection_complete)
     return CURLE_OK;
@@ -139,22 +137,22 @@ mesalink_connect_step1(struct Curl_easy *data,
     return CURLE_OUT_OF_MEMORY;
   }
 
-  if(BACKEND->ctx)
-    SSL_CTX_free(BACKEND->ctx);
-  BACKEND->ctx = SSL_CTX_new(req_method);
+  if(backend->ctx)
+    SSL_CTX_free(backend->ctx);
+  backend->ctx = SSL_CTX_new(req_method);
 
-  if(!BACKEND->ctx) {
+  if(!backend->ctx) {
     failf(data, "SSL: couldn't create a context!");
     return CURLE_OUT_OF_MEMORY;
   }
 
   SSL_CTX_set_verify(
-    BACKEND->ctx, SSL_CONN_CONFIG(verifypeer) ?
+    backend->ctx, SSL_CONN_CONFIG(verifypeer) ?
       SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL);
 
   if(SSL_CONN_CONFIG(CAfile) || SSL_CONN_CONFIG(CApath)) {
-    if(!SSL_CTX_load_verify_locations(BACKEND->ctx, SSL_CONN_CONFIG(CAfile),
-                                                    SSL_CONN_CONFIG(CApath))) {
+    if(!SSL_CTX_load_verify_locations(backend->ctx, SSL_CONN_CONFIG(CAfile),
+                                      SSL_CONN_CONFIG(CApath))) {
       if(SSL_CONN_CONFIG(verifypeer)) {
         failf(data,
               "error setting certificate verify locations: "
@@ -181,7 +179,7 @@ mesalink_connect_step1(struct Curl_easy *data,
   if(SSL_SET_OPTION(primary.clientcert) && SSL_SET_OPTION(key)) {
     int file_type = do_file_type(SSL_SET_OPTION(cert_type));
 
-    if(SSL_CTX_use_certificate_chain_file(BACKEND->ctx,
+    if(SSL_CTX_use_certificate_chain_file(backend->ctx,
                                           SSL_SET_OPTION(primary.clientcert),
                                           file_type) != 1) {
       failf(data, "unable to use client certificate (no key or wrong pass"
@@ -190,8 +188,8 @@ mesalink_connect_step1(struct Curl_easy *data,
     }
 
     file_type = do_file_type(SSL_SET_OPTION(key_type));
-    if(SSL_CTX_use_PrivateKey_file(BACKEND->ctx, SSL_SET_OPTION(key),
-                                    file_type) != 1) {
+    if(SSL_CTX_use_PrivateKey_file(backend->ctx, SSL_SET_OPTION(key),
+                                   file_type) != 1) {
       failf(data, "unable to set private key");
       return CURLE_SSL_CONNECT_ERROR;
     }
@@ -204,7 +202,7 @@ mesalink_connect_step1(struct Curl_easy *data,
   ciphers = SSL_CONN_CONFIG(cipher_list);
   if(ciphers) {
 #ifdef MESALINK_HAVE_CIPHER
-    if(!SSL_CTX_set_cipher_list(BACKEND->ctx, ciphers)) {
+    if(!SSL_CTX_set_cipher_list(backend->ctx, ciphers)) {
       failf(data, "failed setting cipher list: %s", ciphers);
       return CURLE_SSL_CIPHER;
     }
@@ -212,10 +210,10 @@ mesalink_connect_step1(struct Curl_easy *data,
     infof(data, "Cipher selection: %s", ciphers);
   }
 
-  if(BACKEND->handle)
-    SSL_free(BACKEND->handle);
-  BACKEND->handle = SSL_new(BACKEND->ctx);
-  if(!BACKEND->handle) {
+  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;
   }
@@ -227,7 +225,7 @@ mesalink_connect_step1(struct Curl_easy *data,
 #endif
   ) {
     /* hostname is not a valid IP address */
-    if(SSL_set_tlsext_host_name(BACKEND->handle, hostname) != SSL_SUCCESS) {
+    if(SSL_set_tlsext_host_name(backend->handle, hostname) != SSL_SUCCESS) {
       failf(data,
             "WARNING: failed to configure server name indication (SNI) "
             "TLS extension\n");
@@ -244,7 +242,7 @@ mesalink_connect_step1(struct Curl_easy *data,
        || strncmp(hostname, "[::1]", 5) == 0
 #endif
     ) {
-      SSL_set_tlsext_host_name(BACKEND->handle, "localhost");
+      SSL_set_tlsext_host_name(backend->handle, "localhost");
     }
     else
 #endif
@@ -264,12 +262,12 @@ mesalink_connect_step1(struct Curl_easy *data,
                               SSL_IS_PROXY() ? TRUE : FALSE,
                               &ssl_sessionid, NULL, sockindex)) {
       /* we got a session id, use it! */
-      if(!SSL_set_session(BACKEND->handle, ssl_sessionid)) {
+      if(!SSL_set_session(backend->handle, ssl_sessionid)) {
         Curl_ssl_sessionid_unlock(data);
         failf(
           data,
           "SSL: SSL_set_session failed: %s",
-          ERR_error_string(SSL_get_error(BACKEND->handle, 0), error_buffer));
+          ERR_error_string(SSL_get_error(backend->handle, 0), error_buffer));
         return CURLE_SSL_CONNECT_ERROR;
       }
       /* Informational message */
@@ -279,7 +277,7 @@ mesalink_connect_step1(struct Curl_easy *data,
   }
 #endif /* MESALINK_HAVE_SESSION */
 
-  if(SSL_set_fd(BACKEND->handle, (int)sockfd) != SSL_SUCCESS) {
+  if(SSL_set_fd(backend->handle, (int)sockfd) != SSL_SUCCESS) {
     failf(data, "SSL: SSL_set_fd failed");
     return CURLE_SSL_CONNECT_ERROR;
   }
@@ -294,13 +292,14 @@ mesalink_connect_step2(struct Curl_easy *data,
 {
   int ret = -1;
   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+  struct ssl_backend_data *backend = connssl->backend;
 
   conn->recv[sockindex] = mesalink_recv;
   conn->send[sockindex] = mesalink_send;
 
-  ret = SSL_connect(BACKEND->handle);
+  ret = SSL_connect(backend->handle);
   if(ret != SSL_SUCCESS) {
-    int detail = SSL_get_error(BACKEND->handle, ret);
+    int detail = SSL_get_error(backend->handle, ret);
 
     if(SSL_ERROR_WANT_CONNECT == detail || SSL_ERROR_WANT_READ == detail) {
       connssl->connecting_state = ssl_connect_2_reading;
@@ -327,8 +326,8 @@ mesalink_connect_step2(struct Curl_easy *data,
   connssl->connecting_state = ssl_connect_3;
   infof(data,
         "SSL connection using %s / %s",
-        SSL_get_version(BACKEND->handle),
-        SSL_get_cipher_name(BACKEND->handle));
+        SSL_get_version(backend->handle),
+        SSL_get_cipher_name(backend->handle));
 
   return CURLE_OK;
 }
@@ -347,8 +346,9 @@ mesalink_connect_step3(struct connectdata *conn, int sockindex)
     SSL_SESSION *our_ssl_sessionid;
     void *old_ssl_sessionid = NULL;
     bool isproxy = SSL_IS_PROXY() ? TRUE : FALSE;
+    struct ssl_backend_data *backend = connssl->backend;
 
-    our_ssl_sessionid = SSL_get_session(BACKEND->handle);
+    our_ssl_sessionid = SSL_get_session(backend->handle);
 
     Curl_ssl_sessionid_lock(data);
     incache =
@@ -365,7 +365,7 @@ mesalink_connect_step3(struct connectdata *conn, int sockindex)
     if(!incache) {
       result =
         Curl_ssl_addsessionid(data, conn, isproxy, our_ssl_sessionid, 0,
-                              sockindex);
+                              sockindex, NULL);
       if(result) {
         Curl_ssl_sessionid_unlock(data);
         failf(data, "failed to store ssl session");
@@ -387,12 +387,13 @@ mesalink_send(struct Curl_easy *data, int sockindex, const void *mem,
 {
   struct connectdata *conn = data->conn;
   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+  struct ssl_backend_data *backend = connssl->backend;
   char error_buffer[MESALINK_MAX_ERROR_SZ];
   int memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len;
-  int rc = SSL_write(BACKEND->handle, mem, memlen);
+  int rc = SSL_write(backend->handle, mem, memlen);
 
   if(rc < 0) {
-    int err = SSL_get_error(BACKEND->handle, rc);
+    int err = SSL_get_error(backend->handle, rc);
     switch(err) {
     case SSL_ERROR_WANT_READ:
     case SSL_ERROR_WANT_WRITE:
@@ -415,17 +416,18 @@ static void
 mesalink_close(struct Curl_easy *data, struct connectdata *conn, int sockindex)
 {
   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+  struct ssl_backend_data *backend = connssl->backend;
 
   (void) data;
 
-  if(BACKEND->handle) {
-    (void)SSL_shutdown(BACKEND->handle);
-    SSL_free(BACKEND->handle);
-    BACKEND->handle = NULL;
+  if(backend->handle) {
+    (void)SSL_shutdown(backend->handle);
+    SSL_free(backend->handle);
+    backend->handle = NULL;
   }
-  if(BACKEND->ctx) {
-    SSL_CTX_free(BACKEND->ctx);
-    BACKEND->ctx = NULL;
+  if(backend->ctx) {
+    SSL_CTX_free(backend->ctx);
+    backend->ctx = NULL;
   }
 }
 
@@ -435,12 +437,13 @@ mesalink_recv(struct Curl_easy *data, int num, char *buf, size_t buffersize,
 {
   struct connectdata *conn = data->conn;
   struct ssl_connect_data *connssl = &conn->ssl[num];
+  struct ssl_backend_data *backend = connssl->backend;
   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);
+  int nread = SSL_read(backend->handle, buf, buffsize);
 
   if(nread <= 0) {
-    int err = SSL_get_error(BACKEND->handle, nread);
+    int err = SSL_get_error(backend->handle, nread);
 
     switch(err) {
     case SSL_ERROR_ZERO_RETURN: /* no more data */
@@ -485,12 +488,13 @@ mesalink_shutdown(struct Curl_easy *data,
 {
   int retval = 0;
   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+  struct ssl_backend_data *backend = connssl->backend;
 
   (void) data;
 
-  if(BACKEND->handle) {
-    SSL_free(BACKEND->handle);
-    BACKEND->handle = NULL;
+  if(backend->handle) {
+    SSL_free(backend->handle);
+    backend->handle = NULL;
   }
   return retval;
 }
@@ -636,8 +640,9 @@ static void *
 mesalink_get_internals(struct ssl_connect_data *connssl,
                        CURLINFO info UNUSED_PARAM)
 {
+  struct ssl_backend_data *backend = connssl->backend;
   (void)info;
-  return BACKEND->handle;
+  return backend->handle;
 }
 
 const struct Curl_ssl Curl_ssl_mesalink = {
diff --git a/lib/vtls/nss.c b/lib/vtls/nss.c
index cf65789..2b44f05 100644
--- a/lib/vtls/nss.c
+++ b/lib/vtls/nss.c
@@ -304,13 +304,14 @@ static char *nss_sslver_to_name(PRUint16 nssver)
   }
 }
 
-static SECStatus set_ciphers(struct Curl_easy *data, PRFileDesc * model,
-                             char *cipher_list)
+/* the longest cipher name this supports */
+#define MAX_CIPHER_LENGTH 128
+
+static SECStatus set_ciphers(struct Curl_easy *data, PRFileDesc *model,
+                             const char *cipher_list)
 {
   unsigned int i;
-  PRBool cipher_state[NUM_OF_CIPHERS];
-  PRBool found;
-  char *cipher;
+  const char *cipher;
 
   /* use accessors to avoid dynamic linking issues after an update of NSS */
   const PRUint16 num_implemented_ciphers = SSL_GetNumImplementedCiphers();
@@ -326,51 +327,52 @@ static SECStatus set_ciphers(struct Curl_easy *data, PRFileDesc * model,
     SSL_CipherPrefSet(model, implemented_ciphers[i], PR_FALSE);
   }
 
-  /* Set every entry in our list to false */
-  for(i = 0; i < NUM_OF_CIPHERS; i++) {
-    cipher_state[i] = PR_FALSE;
-  }
-
   cipher = cipher_list;
 
-  while(cipher_list && (cipher_list[0])) {
+  while(cipher && cipher[0]) {
+    const char *end;
+    char name[MAX_CIPHER_LENGTH + 1];
+    size_t len;
+    bool found = FALSE;
     while((*cipher) && (ISSPACE(*cipher)))
       ++cipher;
 
-    cipher_list = strpbrk(cipher, ":, ");
-    if(cipher_list) {
-      *cipher_list++ = '\0';
-    }
-
-    found = PR_FALSE;
-
-    for(i = 0; i<NUM_OF_CIPHERS; i++) {
-      if(strcasecompare(cipher, cipherlist[i].name)) {
-        cipher_state[i] = PR_TRUE;
-        found = PR_TRUE;
-        break;
-      }
-    }
+    end = strpbrk(cipher, ":, ");
+    if(end)
+      len = end - cipher;
+    else
+      len = strlen(cipher);
 
-    if(found == PR_FALSE) {
-      failf(data, "Unknown cipher in list: %s", cipher);
+    if(len > MAX_CIPHER_LENGTH) {
+      failf(data, "Bad cipher list");
       return SECFailure;
     }
-
-    if(cipher_list) {
-      cipher = cipher_list;
+    else if(len) {
+      memcpy(name, cipher, len);
+      name[len] = 0;
+
+      for(i = 0; i<NUM_OF_CIPHERS; i++) {
+        if(strcasecompare(name, cipherlist[i].name)) {
+          /* Enable the selected cipher */
+          if(SSL_CipherPrefSet(model, cipherlist[i].num, PR_TRUE) !=
+             SECSuccess) {
+            failf(data, "cipher-suite not supported by NSS: %s", name);
+            return SECFailure;
+          }
+          found = TRUE;
+          break;
+        }
+      }
     }
-  }
-
-  /* Finally actually enable the selected ciphers */
-  for(i = 0; i<NUM_OF_CIPHERS; i++) {
-    if(!cipher_state[i])
-      continue;
 
-    if(SSL_CipherPrefSet(model, cipherlist[i].num, PR_TRUE) != SECSuccess) {
-      failf(data, "cipher-suite not supported by NSS: %s", cipherlist[i].name);
+    if(!found && len) {
+      failf(data, "Unknown cipher: %s", name);
       return SECFailure;
     }
+    if(end)
+      cipher = ++end;
+    else
+      break;
   }
 
   return SECSuccess;
@@ -782,7 +784,7 @@ static char *nss_get_password(PK11SlotInfo *slot, PRBool retry, void *arg)
 {
   (void)slot; /* unused */
 
-  if(retry || NULL == arg)
+  if(retry || !arg)
     return NULL;
   else
     return (char *)PORT_Strdup((char *)arg);
@@ -955,7 +957,7 @@ static void display_cert_info(struct Curl_easy *data,
   subject = CERT_NameToAscii(&cert->subject);
   issuer = CERT_NameToAscii(&cert->issuer);
   common_name = CERT_GetCommonName(&cert->subject);
-  infof(data, "subject: %s\n", subject);
+  infof(data, "subject: %s", subject);
 
   CERT_GetCertTimes(cert, &notBefore, &notAfter);
   PR_ExplodeTime(notBefore, PR_GMTParameters, &printableTime);
@@ -1168,7 +1170,7 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock,
     struct SECKEYPrivateKeyStr *key;
 
     PK11SlotInfo *slot = nss_find_slot_by_name(pem_slotname);
-    if(NULL == slot) {
+    if(!slot) {
       failf(data, "NSS: PK11 slot not found: %s", pem_slotname);
       return SECFailure;
     }
@@ -1182,7 +1184,7 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock,
 
     cert = PK11_FindCertFromDERCertItem(slot, &cert_der, proto_win);
     SECITEM_FreeItem(&cert_der, PR_FALSE);
-    if(NULL == cert) {
+    if(!cert) {
       failf(data, "NSS: client certificate from file not found");
       PK11_FreeSlot(slot);
       return SECFailure;
@@ -1190,7 +1192,7 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock,
 
     key = PK11_FindPrivateKeyFromCert(slot, cert, NULL);
     PK11_FreeSlot(slot);
-    if(NULL == key) {
+    if(!key) {
       failf(data, "NSS: private key from file not found");
       CERT_DestroyCertificate(cert);
       return SECFailure;
@@ -1207,9 +1209,9 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock,
   /* use the default NSS hook */
   if(SECSuccess != NSS_GetClientAuthData((void *)nickname, sock, caNames,
                                           pRetCert, pRetKey)
-      || NULL == *pRetCert) {
+     || !*pRetCert) {
 
-    if(NULL == nickname)
+    if(!nickname)
       failf(data, "NSS: client certificate not found (nickname not "
             "specified)");
     else
@@ -1220,7 +1222,7 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock,
 
   /* get certificate nickname if any */
   nickname = (*pRetCert)->nickname;
-  if(NULL == nickname)
+  if(!nickname)
     nickname = "[unknown]";
 
   if(!strncmp(nickname, pem_slotname, sizeof(pem_slotname) - 1U)) {
@@ -1229,7 +1231,7 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock,
     return SECFailure;
   }
 
-  if(NULL == *pRetKey) {
+  if(!*pRetKey) {
     failf(data, "NSS: private key not found for certificate: %s", nickname);
     return SECFailure;
   }
@@ -1344,7 +1346,7 @@ static CURLcode nss_init_core(struct Curl_easy *data, const char *cert_dir)
   PRErrorCode err;
   const char *err_name;
 
-  if(nss_context != NULL)
+  if(nss_context)
     return CURLE_OK;
 
   memset((void *) &initparams, '\0', sizeof(initparams));
@@ -1360,7 +1362,7 @@ static CURLcode nss_init_core(struct Curl_easy *data, const char *cert_dir)
                                   NSS_INIT_READONLY | NSS_INIT_PK11RELOAD);
     free(certpath);
 
-    if(nss_context != NULL)
+    if(nss_context)
       return CURLE_OK;
 
     err = PR_GetError();
@@ -1372,7 +1374,7 @@ static CURLcode nss_init_core(struct Curl_easy *data, const char *cert_dir)
   nss_context = NSS_InitContext("", "", "", "", &initparams, NSS_INIT_READONLY
          | NSS_INIT_NOCERTDB   | NSS_INIT_NOMODDB       | NSS_INIT_FORCEOPEN
          | NSS_INIT_NOROOTINIT | NSS_INIT_OPTIMIZESPACE | NSS_INIT_PK11RELOAD);
-  if(nss_context != NULL)
+  if(nss_context)
     return CURLE_OK;
 
   err = PR_GetError();
@@ -2250,10 +2252,11 @@ static CURLcode nss_connect_common(struct Curl_easy *data,
   case CURLE_OK:
     break;
   case CURLE_AGAIN:
+    /* CURLE_AGAIN in non-blocking mode is not an error */
     if(!blocking)
-      /* CURLE_AGAIN in non-blocking mode is not an error */
       return CURLE_OK;
-    /* FALLTHROUGH */
+    else
+      return result;
   default:
     return result;
   }
diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c
index 87f4b02..f836c63 100644
--- a/lib/vtls/openssl.c
+++ b/lib/vtls/openssl.c
@@ -171,6 +171,21 @@
 #define OPENSSL_load_builtin_modules(x)
 #endif
 
+#if (OPENSSL_VERSION_NUMBER >= 0x30000000L)
+#define HAVE_EVP_PKEY_GET_PARAMS 1
+#else
+#define SSL_get1_peer_certificate SSL_get_peer_certificate
+#endif
+
+#ifdef HAVE_EVP_PKEY_GET_PARAMS
+#include <openssl/core_names.h>
+#define DECLARE_PKEY_PARAM_BIGNUM(name) BIGNUM *name = NULL
+#define FREE_PKEY_PARAM_BIGNUM(name) BN_clear_free(name)
+#else
+#define DECLARE_PKEY_PARAM_BIGNUM(name) const BIGNUM *name
+#define FREE_PKEY_PARAM_BIGNUM(name)
+#endif
+
 /*
  * Whether SSL_CTX_set_keylog_callback is available.
  * OpenSSL: supported since 1.1.1 https://github.com/openssl/openssl/pull/2287
@@ -227,6 +242,17 @@
 #endif
 #endif
 
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
+#define HAVE_RANDOM_INIT_BY_DEFAULT 1
+#endif
+
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && \
+    !(defined(LIBRESSL_VERSION_NUMBER) && \
+      LIBRESSL_VERSION_NUMBER < 0x2070100fL) && \
+    !defined(OPENSSL_IS_BORINGSSL)
+#define HAVE_OPENSSL_VERSION
+#endif
+
 struct ssl_backend_data {
   struct Curl_easy *logger; /* transfer handle to pass trace logs to, only
                                using sockindex 0 */
@@ -435,18 +461,21 @@ static bool rand_enough(void)
 
 static CURLcode ossl_seed(struct Curl_easy *data)
 {
-  char fname[256];
-
   /* This might get called before it has been added to a multi handle */
   if(data->multi && data->multi->ssl_seeded)
     return CURLE_OK;
 
   if(rand_enough()) {
-    /* OpenSSL 1.1.0+ will return here */
+    /* OpenSSL 1.1.0+ should return here */
     if(data->multi)
       data->multi->ssl_seeded = TRUE;
     return CURLE_OK;
   }
+#ifdef HAVE_RANDOM_INIT_BY_DEFAULT
+  /* with OpenSSL 1.1.0+, a failed RAND_status is a showstopper */
+  failf(data, "Insufficient randomness");
+  return CURLE_SSL_CONNECT_ERROR;
+#else
 
 #ifndef RANDOM_FILE
   /* if RANDOM_FILE isn't defined, we only perform this if an option tells
@@ -507,19 +536,23 @@ static CURLcode ossl_seed(struct Curl_easy *data)
     RAND_add(randb, (int)len, (double)len/2);
   } while(!rand_enough());
 
-  /* generates a default path for the random seed file */
-  fname[0] = 0; /* blank it first */
-  RAND_file_name(fname, sizeof(fname));
-  if(fname[0]) {
-    /* we got a file name to try */
-    RAND_load_file(fname, RAND_LOAD_LENGTH);
-    if(rand_enough())
-      return CURLE_OK;
+  {
+    /* generates a default path for the random seed file */
+    char fname[256];
+    fname[0] = 0; /* blank it first */
+    RAND_file_name(fname, sizeof(fname));
+    if(fname[0]) {
+      /* we got a file name to try */
+      RAND_load_file(fname, RAND_LOAD_LENGTH);
+      if(rand_enough())
+        return CURLE_OK;
+    }
   }
 
   infof(data, "libcurl is now using a weak random seed!");
   return (rand_enough() ? CURLE_OK :
-    CURLE_SSL_CONNECT_ERROR /* confusing error code */);
+          CURLE_SSL_CONNECT_ERROR /* confusing error code */);
+#endif
 }
 
 #ifndef SSL_FILETYPE_ENGINE
@@ -1088,7 +1121,8 @@ int cert_stuff(struct Curl_easy *data,
       EVP_PKEY_free(pktmp);
     }
 
-#if !defined(OPENSSL_NO_RSA) && !defined(OPENSSL_IS_BORINGSSL)
+#if !defined(OPENSSL_NO_RSA) && !defined(OPENSSL_IS_BORINGSSL) && \
+    !defined(OPENSSL_NO_DEPRECATED_3_0)
     {
       /* If RSA is used, don't check the private key if its flags indicate
        * it doesn't support it. */
@@ -1401,6 +1435,12 @@ static void ossl_closeone(struct Curl_easy *data,
   if(backend->handle) {
     char buf[32];
     set_logger(conn, data);
+    /*
+     * The conn->sock[0] socket is passed to openssl with SSL_set_fd().  Make
+     * sure the socket is not closed before calling OpenSSL functions that
+     * will use it.
+     */
+    DEBUGASSERT(conn->sock[FIRSTSOCKET] != CURL_SOCKET_BAD);
 
     /* Maybe the server has already sent a close notify alert.
        Read it to avoid an RST on the TCP connection. */
@@ -1639,9 +1679,10 @@ static bool subj_alt_hostcheck(struct Curl_easy *data,
    hostname. In this case, the iPAddress subjectAltName must be present
    in the certificate and must exactly match the IP in the URI.
 
+   This function is now used from ngtcp2 (QUIC) as well.
 */
-static CURLcode verifyhost(struct Curl_easy *data, struct connectdata *conn,
-                           X509 *server_cert)
+CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
+                              X509 *server_cert)
 {
   bool matched = FALSE;
   int target = GEN_DNS; /* target type, GEN_DNS or GEN_IPADD */
@@ -1926,7 +1967,7 @@ static CURLcode verifystatus(struct Curl_easy *data,
   }
 
   /* Compute the certificate's ID */
-  cert = SSL_get_peer_certificate(backend->handle);
+  cert = SSL_get1_peer_certificate(backend->handle);
   if(!cert) {
     failf(data, "Error getting peer certificate");
     result = CURLE_SSL_INVALIDCERTSTATUS;
@@ -2493,6 +2534,7 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
 
   if(SSL_SET_OPTION(primary.sessionid)) {
     bool incache;
+    bool added = FALSE;
     void *old_ssl_sessionid = NULL;
 
     Curl_ssl_sessionid_lock(data);
@@ -2511,9 +2553,11 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
 
     if(!incache) {
       if(!Curl_ssl_addsessionid(data, conn, isproxy, ssl_sessionid,
-                                0 /* unknown size */, sockindex)) {
-        /* the session has been put into the session cache */
-        res = 1;
+                                0 /* unknown size */, sockindex, &added)) {
+        if(added) {
+          /* the session has been put into the session cache */
+          res = 1;
+        }
       }
       else
         failf(data, "failed to store ssl session");
@@ -2936,7 +2980,7 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data,
                                NULL, cert_name, sizeof(cert_name))) {
           strcpy(cert_name, "Unknown");
         }
-        infof(data, "SSL: Checking cert %s\"\n", cert_name);
+        infof(data, "SSL: Checking cert \"%s\"", cert_name);
 #endif
 
         encoded_cert = (const unsigned char *)pContext->pbCertEncoded;
@@ -3052,60 +3096,36 @@ static CURLcode ossl_connect_step1(struct Curl_easy *data,
     }
   }
 
+  if(verifypeer && !imported_native_ca && (ssl_cafile || ssl_capath)) {
 #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 warning if certificate verification isn't required. */
-        infof(data, "error setting certificate file, continuing anyway");
-      }
-      infof(data, " CAfile: %s", ssl_cafile);
+    if(ssl_cafile &&
+       !SSL_CTX_load_verify_file(backend->ctx, ssl_cafile)) {
+      /* Fail if we insist on successfully verifying the server. */
+      failf(data, "error setting certificate file: %s", ssl_cafile);
+      return CURLE_SSL_CACERT_BADFILE;
     }
-    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 warning if certificate verification isn't required. */
-        infof(data, "error setting certificate path, continuing anyway");
-      }
-      infof(data, " CApath: %s", ssl_capath);
+    if(ssl_capath &&
+       !SSL_CTX_load_verify_dir(backend->ctx, ssl_capath)) {
+      /* Fail if we insist on successfully verifying the server. */
+      failf(data, "error setting certificate path: %s", ssl_capath);
+      return CURLE_SSL_CACERT_BADFILE;
     }
-  }
 #else
-  if(ssl_cafile || ssl_capath) {
-    /* tell SSL where to find CA certificates that are used to verify
-       the server's certificate. */
+    /* tell OpenSSL where to find CA certificates that are used to verify the
+       server's 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:");
-    }
-    else {
-      /* Everything is fine. */
-      infof(data, "successfully set certificate verify locations:");
+      /* 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;
     }
+#endif
     infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
     infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
   }
-#endif
 
 #ifdef CURL_CA_FALLBACK
   if(verifypeer &&
@@ -3476,10 +3496,7 @@ static void pubkey_show(struct Curl_easy *data,
                         int num,
                         const char *type,
                         const char *name,
-#ifdef HAVE_OPAQUE_RSA_DSA_DH
-                        const
-#endif
-                        BIGNUM *bn)
+                        const BIGNUM *bn)
 {
   char *ptr;
   char namebuf[32];
@@ -3544,12 +3561,6 @@ typedef size_t numcert_t;
 typedef int numcert_t;
 #endif
 
-#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
-#define OSSL3_CONST const
-#else
-#define OSSL3_CONST
-#endif
-
 static CURLcode get_cert_chain(struct Curl_easy *data,
                                struct ssl_connect_data *connssl)
 {
@@ -3573,6 +3584,9 @@ static CURLcode get_cert_chain(struct Curl_easy *data,
   }
 
   mem = BIO_new(BIO_s_mem());
+  if(!mem) {
+    return CURLE_OUT_OF_MEMORY;
+  }
 
   for(i = 0; i < (int)numcerts; i++) {
     ASN1_INTEGER *num;
@@ -3657,92 +3671,115 @@ static CURLcode get_cert_chain(struct Curl_easy *data,
       switch(pktype) {
       case EVP_PKEY_RSA:
       {
-        OSSL3_CONST RSA *rsa;
+#ifndef HAVE_EVP_PKEY_GET_PARAMS
+        RSA *rsa;
 #ifdef HAVE_OPAQUE_EVP_PKEY
         rsa = EVP_PKEY_get0_RSA(pubkey);
 #else
         rsa = pubkey->pkey.rsa;
-#endif
+#endif /* HAVE_OPAQUE_EVP_PKEY */
+#endif /* !HAVE_EVP_PKEY_GET_PARAMS */
 
-#ifdef HAVE_OPAQUE_RSA_DSA_DH
         {
-          const BIGNUM *n;
-          const BIGNUM *e;
-
+#ifdef HAVE_OPAQUE_RSA_DSA_DH
+          DECLARE_PKEY_PARAM_BIGNUM(n);
+          DECLARE_PKEY_PARAM_BIGNUM(e);
+#ifdef HAVE_EVP_PKEY_GET_PARAMS
+          EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_RSA_N, &n);
+          EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_RSA_E, &e);
+#else
           RSA_get0_key(rsa, &n, &e, NULL);
+#endif /* HAVE_EVP_PKEY_GET_PARAMS */
           BIO_printf(mem, "%d", BN_num_bits(n));
+#else
+          BIO_printf(mem, "%d", BN_num_bits(rsa->n));
+#endif /* HAVE_OPAQUE_RSA_DSA_DH */
           push_certinfo("RSA Public Key", i);
           print_pubkey_BN(rsa, n, i);
           print_pubkey_BN(rsa, e, i);
+          FREE_PKEY_PARAM_BIGNUM(n);
+          FREE_PKEY_PARAM_BIGNUM(e);
         }
-#else
-        BIO_printf(mem, "%d", BN_num_bits(rsa->n));
-        push_certinfo("RSA Public Key", i);
-        print_pubkey_BN(rsa, n, i);
-        print_pubkey_BN(rsa, e, i);
-#endif
 
         break;
       }
       case EVP_PKEY_DSA:
       {
 #ifndef OPENSSL_NO_DSA
-        OSSL3_CONST DSA *dsa;
+#ifndef HAVE_EVP_PKEY_GET_PARAMS
+        DSA *dsa;
 #ifdef HAVE_OPAQUE_EVP_PKEY
         dsa = EVP_PKEY_get0_DSA(pubkey);
 #else
         dsa = pubkey->pkey.dsa;
-#endif
-#ifdef HAVE_OPAQUE_RSA_DSA_DH
+#endif /* HAVE_OPAQUE_EVP_PKEY */
+#endif /* !HAVE_EVP_PKEY_GET_PARAMS */
         {
-          const BIGNUM *p;
-          const BIGNUM *q;
-          const BIGNUM *g;
-          const BIGNUM *pub_key;
-
+#ifdef HAVE_OPAQUE_RSA_DSA_DH
+          DECLARE_PKEY_PARAM_BIGNUM(p);
+          DECLARE_PKEY_PARAM_BIGNUM(q);
+          DECLARE_PKEY_PARAM_BIGNUM(g);
+          DECLARE_PKEY_PARAM_BIGNUM(pub_key);
+#ifdef HAVE_EVP_PKEY_GET_PARAMS
+          EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_P, &p);
+          EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_Q, &q);
+          EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_G, &g);
+          EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_PUB_KEY, &pub_key);
+#else
           DSA_get0_pqg(dsa, &p, &q, &g);
           DSA_get0_key(dsa, &pub_key, NULL);
-
+#endif /* HAVE_EVP_PKEY_GET_PARAMS */
+#endif /* HAVE_OPAQUE_RSA_DSA_DH */
           print_pubkey_BN(dsa, p, i);
           print_pubkey_BN(dsa, q, i);
           print_pubkey_BN(dsa, g, i);
           print_pubkey_BN(dsa, pub_key, i);
+          FREE_PKEY_PARAM_BIGNUM(p);
+          FREE_PKEY_PARAM_BIGNUM(q);
+          FREE_PKEY_PARAM_BIGNUM(g);
+          FREE_PKEY_PARAM_BIGNUM(pub_key);
         }
-#else
-        print_pubkey_BN(dsa, p, i);
-        print_pubkey_BN(dsa, q, i);
-        print_pubkey_BN(dsa, g, i);
-        print_pubkey_BN(dsa, pub_key, i);
-#endif
 #endif /* !OPENSSL_NO_DSA */
         break;
       }
       case EVP_PKEY_DH:
       {
-        OSSL3_CONST DH *dh;
+#ifndef HAVE_EVP_PKEY_GET_PARAMS
+        DH *dh;
 #ifdef HAVE_OPAQUE_EVP_PKEY
         dh = EVP_PKEY_get0_DH(pubkey);
 #else
         dh = pubkey->pkey.dh;
-#endif
-#ifdef HAVE_OPAQUE_RSA_DSA_DH
+#endif /* HAVE_OPAQUE_EVP_PKEY */
+#endif /* !HAVE_EVP_PKEY_GET_PARAMS */
         {
-          const BIGNUM *p;
-          const BIGNUM *q;
-          const BIGNUM *g;
-          const BIGNUM *pub_key;
+#ifdef HAVE_OPAQUE_RSA_DSA_DH
+          DECLARE_PKEY_PARAM_BIGNUM(p);
+          DECLARE_PKEY_PARAM_BIGNUM(q);
+          DECLARE_PKEY_PARAM_BIGNUM(g);
+          DECLARE_PKEY_PARAM_BIGNUM(pub_key);
+#ifdef HAVE_EVP_PKEY_GET_PARAMS
+          EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_P, &p);
+          EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_Q, &q);
+          EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_FFC_G, &g);
+          EVP_PKEY_get_bn_param(pubkey, OSSL_PKEY_PARAM_PUB_KEY, &pub_key);
+#else
           DH_get0_pqg(dh, &p, &q, &g);
           DH_get0_key(dh, &pub_key, NULL);
+#endif /* HAVE_EVP_PKEY_GET_PARAMS */
           print_pubkey_BN(dh, p, i);
           print_pubkey_BN(dh, q, i);
           print_pubkey_BN(dh, g, i);
+#else
+          print_pubkey_BN(dh, p, i);
+          print_pubkey_BN(dh, g, i);
+#endif /* HAVE_OPAQUE_RSA_DSA_DH */
           print_pubkey_BN(dh, pub_key, i);
+          FREE_PKEY_PARAM_BIGNUM(p);
+          FREE_PKEY_PARAM_BIGNUM(q);
+          FREE_PKEY_PARAM_BIGNUM(g);
+          FREE_PKEY_PARAM_BIGNUM(pub_key);
        }
-#else
-        print_pubkey_BN(dh, p, i);
-        print_pubkey_BN(dh, g, i);
-        print_pubkey_BN(dh, pub_key, i);
-#endif
         break;
       }
       }
@@ -3846,11 +3883,20 @@ static CURLcode servercert(struct Curl_easy *data,
   BIO *mem = BIO_new(BIO_s_mem());
   struct ssl_backend_data *backend = connssl->backend;
 
+  if(!mem) {
+    failf(data,
+          "BIO_new return NULL, " OSSL_PACKAGE
+          " error %s",
+          ossl_strerror(ERR_get_error(), error_buffer,
+                        sizeof(error_buffer)) );
+    return CURLE_OUT_OF_MEMORY;
+  }
+
   if(data->set.ssl.certinfo)
     /* we've been asked to gather certificate info! */
     (void)get_cert_chain(data, connssl);
 
-  backend->server_cert = SSL_get_peer_certificate(backend->handle);
+  backend->server_cert = SSL_get1_peer_certificate(backend->handle);
   if(!backend->server_cert) {
     BIO_free(mem);
     if(!strict)
@@ -3884,7 +3930,7 @@ static CURLcode servercert(struct Curl_easy *data,
   BIO_free(mem);
 
   if(SSL_CONN_CONFIG(verifyhost)) {
-    result = verifyhost(data, conn, backend->server_cert);
+    result = Curl_ossl_verifyhost(data, conn, backend->server_cert);
     if(result) {
       X509_free(backend->server_cert);
       backend->server_cert = NULL;
@@ -4364,13 +4410,7 @@ static ssize_t ossl_recv(struct Curl_easy *data,   /* transfer */
 static size_t ossl_version(char *buffer, size_t size)
 {
 #ifdef LIBRESSL_VERSION_NUMBER
-#if LIBRESSL_VERSION_NUMBER < 0x2070100fL
-  return msnprintf(buffer, size, "%s/%lx.%lx.%lx",
-                   OSSL_PACKAGE,
-                   (LIBRESSL_VERSION_NUMBER>>28)&0xf,
-                   (LIBRESSL_VERSION_NUMBER>>20)&0xff,
-                   (LIBRESSL_VERSION_NUMBER>>12)&0xff);
-#else /* OpenSSL_version() first appeared in LibreSSL 2.7.1 */
+#ifdef HAVE_OPENSSL_VERSION
   char *p;
   int count;
   const char *ver = OpenSSL_version(OPENSSL_VERSION);
@@ -4384,6 +4424,12 @@ static size_t ossl_version(char *buffer, size_t size)
       *p = '_';
   }
   return count;
+#else
+  return msnprintf(buffer, size, "%s/%lx.%lx.%lx",
+                   OSSL_PACKAGE,
+                   (LIBRESSL_VERSION_NUMBER>>28)&0xf,
+                   (LIBRESSL_VERSION_NUMBER>>20)&0xff,
+                   (LIBRESSL_VERSION_NUMBER>>12)&0xff);
 #endif
 #elif defined(OPENSSL_IS_BORINGSSL)
   return msnprintf(buffer, size, OSSL_PACKAGE);
diff --git a/lib/vtls/openssl.h b/lib/vtls/openssl.h
index 2f6e1b2..2805845 100644
--- a/lib/vtls/openssl.h
+++ b/lib/vtls/openssl.h
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -26,11 +26,15 @@
 
 #ifdef USE_OPENSSL
 /*
- * This header should only be needed to get included by vtls.c and openssl.c
+ * This header should only be needed to get included by vtls.c, openssl.c
+ * and ngtcp2.c
  */
 
+#include <openssl/x509v3.h>
 #include "urldata.h"
 
+CURLcode Curl_ossl_verifyhost(struct Curl_easy *data, struct connectdata *conn,
+                              X509 *server_cert);
 extern const struct Curl_ssl Curl_ssl_openssl;
 
 #endif /* USE_OPENSSL */
diff --git a/lib/vtls/rustls.c b/lib/vtls/rustls.c
index 2ac97ce..6dbb1ef 100644
--- a/lib/vtls/rustls.c
+++ b/lib/vtls/rustls.c
@@ -27,7 +27,7 @@
 #include "curl_printf.h"
 
 #include <errno.h>
-#include <crustls.h>
+#include <rustls.h>
 
 #include "inet_pton.h"
 #include "urldata.h"
@@ -138,11 +138,6 @@ cr_recv(struct Curl_easy *data, int sockindex,
     *err = CURLE_READ_ERROR;
     return -1;
   }
-  else if(tls_bytes_read == 0) {
-    failf(data, "connection closed without TLS close_notify alert");
-    *err = CURLE_READ_ERROR;
-    return -1;
-  }
 
   infof(data, "cr_recv read %ld bytes from the network", tls_bytes_read);
 
@@ -161,22 +156,21 @@ cr_recv(struct Curl_easy *data, int sockindex,
       (uint8_t *)plainbuf + plain_bytes_copied,
       plainlen - plain_bytes_copied,
       &n);
-    if(rresult == RUSTLS_RESULT_ALERT_CLOSE_NOTIFY) {
-      *err = CURLE_OK;
-      return 0;
+    if(rresult == RUSTLS_RESULT_PLAINTEXT_EMPTY) {
+      infof(data, "cr_recv got PLAINTEXT_EMPTY. will try again later.");
+      backend->data_pending = FALSE;
+      break;
     }
     else if(rresult != RUSTLS_RESULT_OK) {
-      failf(data, "error in rustls_connection_read");
+      /* n always equals 0 in this case, don't need to check it */
+      failf(data, "error in rustls_connection_read: %d", rresult);
       *err = CURLE_READ_ERROR;
       return -1;
     }
     else if(n == 0) {
-      /* rustls returns 0 from connection_read to mean "all currently
-        available data has been read." If we bring in more ciphertext with
-        read_tls, more plaintext will become available. So don't tell curl
-        this is an EOF. Instead, say "come back later." */
-      infof(data, "cr_recv got 0 bytes of plaintext");
-      backend->data_pending = FALSE;
+      /* n == 0 indicates clean EOF, but we may have read some other
+         plaintext bytes before we reached this. Break out of the loop
+         so we can figure out whether to return success or EOF. */
       break;
     }
     else {
@@ -185,15 +179,23 @@ cr_recv(struct Curl_easy *data, int sockindex,
     }
   }
 
-  /* If we wrote out 0 plaintext bytes, it might just mean we haven't yet
-     read a full TLS record. Return CURLE_AGAIN so curl doesn't treat this
-     as EOF. */
-  if(plain_bytes_copied == 0) {
+  if(plain_bytes_copied) {
+    *err = CURLE_OK;
+    return plain_bytes_copied;
+  }
+
+  /* If we wrote out 0 plaintext bytes, that means either we hit a clean EOF,
+     OR we got a RUSTLS_RESULT_PLAINTEXT_EMPTY.
+     If the latter, return CURLE_AGAIN so curl doesn't treat this as EOF. */
+  if(!backend->data_pending) {
     *err = CURLE_AGAIN;
     return -1;
   }
 
-  return plain_bytes_copied;
+  /* Zero bytes read, and no RUSTLS_RESULT_PLAINTEXT_EMPTY, means the TCP
+     connection was cleanly closed (with a close_notify alert). */
+  *err = CURLE_OK;
+  return 0;
 }
 
 /*
@@ -309,10 +311,10 @@ cr_init_backend(struct Curl_easy *data, struct connectdata *conn,
   config_builder = rustls_client_config_builder_new();
 #ifdef USE_HTTP2
   infof(data, "offering ALPN for HTTP/1.1 and HTTP/2");
-  rustls_client_config_builder_set_protocols(config_builder, alpn, 2);
+  rustls_client_config_builder_set_alpn_protocols(config_builder, alpn, 2);
 #else
   infof(data, "offering ALPN for HTTP/1.1 only");
-  rustls_client_config_builder_set_protocols(config_builder, alpn, 1);
+  rustls_client_config_builder_set_alpn_protocols(config_builder, alpn, 1);
 #endif
   if(!verifypeer) {
     rustls_client_config_builder_dangerous_set_certificate_verifier(
@@ -358,7 +360,7 @@ cr_set_negotiated_alpn(struct Curl_easy *data, struct connectdata *conn,
   size_t len = 0;
 
   rustls_connection_get_alpn_protocol(rconn, &protocol, &len);
-  if(NULL == protocol) {
+  if(!protocol) {
     infof(data, "ALPN, server did not agree to a protocol");
     return;
   }
@@ -414,9 +416,6 @@ cr_connect_nonblocking(struct Curl_easy *data, struct connectdata *conn,
     /*
     * Connection has been established according to rustls. Set send/recv
     * handlers, and update the state machine.
-    * This check has to come last because is_handshaking starts out false,
-    * then becomes true when we first write data, then becomes false again
-    * once the handshake is done.
     */
     if(!rustls_connection_is_handshaking(rconn)) {
       infof(data, "Done handshaking");
@@ -543,6 +542,12 @@ cr_close(struct Curl_easy *data, struct connectdata *conn,
   }
 }
 
+static size_t cr_version(char *buffer, size_t size)
+{
+  struct rustls_str ver = rustls_version();
+  return msnprintf(buffer, size, "%.*s", (int)ver.len, ver.data);
+}
+
 const struct Curl_ssl Curl_ssl_rustls = {
   { CURLSSLBACKEND_RUSTLS, "rustls" },
   SSLSUPP_TLS13_CIPHERSUITES,      /* supports */
@@ -550,7 +555,7 @@ const struct Curl_ssl Curl_ssl_rustls = {
 
   Curl_none_init,                  /* init */
   Curl_none_cleanup,               /* cleanup */
-  rustls_version,                  /* version */
+  cr_version,                      /* version */
   Curl_none_check_cxn,             /* check_cxn */
   Curl_none_shutdown,              /* shutdown */
   cr_data_pending,                 /* data_pending */
diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c
index 722a937..0a8e606 100644
--- a/lib/vtls/schannel.c
+++ b/lib/vtls/schannel.c
@@ -147,8 +147,6 @@
 #define ALG_CLASS_DHASH ALG_CLASS_HASH
 #endif
 
-#define BACKEND connssl->backend
-
 static Curl_recv schannel_recv;
 static Curl_send schannel_send;
 
@@ -423,6 +421,7 @@ schannel_acquire_credential_handle(struct Curl_easy *data,
   PCCERT_CONTEXT client_certs[1] = { NULL };
   SECURITY_STATUS sspi_status = SEC_E_OK;
   CURLcode result;
+  struct ssl_backend_data *backend = connssl->backend;
 
   /* setup Schannel API options */
   memset(&schannel_cred, 0, sizeof(schannel_cred));
@@ -430,7 +429,7 @@ schannel_acquire_credential_handle(struct Curl_easy *data,
 
   if(conn->ssl_config.verifypeer) {
 #ifdef HAS_MANUAL_VERIFY_API
-    if(BACKEND->use_manual_cred_validation)
+    if(backend->use_manual_cred_validation)
       schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION;
     else
 #endif
@@ -503,7 +502,7 @@ schannel_acquire_credential_handle(struct Curl_easy *data,
 
   if(SSL_CONN_CONFIG(cipher_list)) {
     result = set_ssl_ciphers(&schannel_cred, SSL_CONN_CONFIG(cipher_list),
-                             BACKEND->algIds);
+                             backend->algIds);
     if(CURLE_OK != result) {
       failf(data, "Unable to set ciphers to passed via SSL_CONN_CONFIG");
       return result;
@@ -600,7 +599,7 @@ schannel_acquire_credential_handle(struct Curl_easy *data,
       datablob.pbData = (BYTE*)certdata;
       datablob.cbData = (DWORD)certsize;
 
-      if(data->set.ssl.key_passwd != NULL)
+      if(data->set.ssl.key_passwd)
         pwd_len = strlen(data->set.ssl.key_passwd);
       pszPassword = (WCHAR*)malloc(sizeof(WCHAR)*(pwd_len + 1));
       if(pszPassword) {
@@ -704,9 +703,9 @@ schannel_acquire_credential_handle(struct Curl_easy *data,
 #endif
 
   /* allocate memory for the re-usable credential handle */
-  BACKEND->cred = (struct Curl_schannel_cred *)
+  backend->cred = (struct Curl_schannel_cred *)
     calloc(1, sizeof(struct Curl_schannel_cred));
-  if(!BACKEND->cred) {
+  if(!backend->cred) {
     failf(data, "schannel: unable to allocate memory");
 
     if(client_certs[0])
@@ -714,16 +713,14 @@ schannel_acquire_credential_handle(struct Curl_easy *data,
 
     return CURLE_OUT_OF_MEMORY;
   }
-  BACKEND->cred->refcount = 1;
+  backend->cred->refcount = 1;
 
-  /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa374716.aspx
-   */
   sspi_status =
     s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR *)UNISP_NAME,
                                        SECPKG_CRED_OUTBOUND, NULL,
                                        &schannel_cred, NULL, NULL,
-                                       &BACKEND->cred->cred_handle,
-                                       &BACKEND->cred->time_stamp);
+                                       &backend->cred->cred_handle,
+                                       &backend->cred->time_stamp);
 
   if(client_certs[0])
     CertFreeCertificateContext(client_certs[0]);
@@ -732,7 +729,7 @@ schannel_acquire_credential_handle(struct Curl_easy *data,
     char buffer[STRERROR_LEN];
     failf(data, "schannel: AcquireCredentialsHandle failed: %s",
           Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
-    Curl_safefree(BACKEND->cred);
+    Curl_safefree(backend->cred);
     switch(sspi_status) {
     case SEC_E_INSUFFICIENT_MEMORY:
       return CURLE_OUT_OF_MEMORY;
@@ -771,12 +768,13 @@ schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn,
   TCHAR *host_name;
   CURLcode result;
   char * const hostname = SSL_HOST_NAME();
+  struct ssl_backend_data *backend = connssl->backend;
 
   DEBUGF(infof(data,
                "schannel: SSL/TLS connection with %s port %hu (step 1/3)",
                hostname, conn->remote_port));
 
-  if(curlx_verify_windows_version(5, 1, PLATFORM_WINNT,
+  if(curlx_verify_windows_version(5, 1, 0, PLATFORM_WINNT,
                                   VERSION_LESS_THAN_EQUAL)) {
     /* Schannel in Windows XP (OS version 5.1) uses legacy handshakes and
        algorithms that may not be supported by all servers. */
@@ -787,29 +785,29 @@ schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn,
 #ifdef HAS_ALPN
   /* ALPN is only supported on Windows 8.1 / Server 2012 R2 and above.
      Also it doesn't seem to be supported for Wine, see curl bug #983. */
-  BACKEND->use_alpn = conn->bits.tls_enable_alpn &&
+  backend->use_alpn = conn->bits.tls_enable_alpn &&
     !GetProcAddress(GetModuleHandle(TEXT("ntdll")),
                     "wine_get_version") &&
-    curlx_verify_windows_version(6, 3, PLATFORM_WINNT,
+    curlx_verify_windows_version(6, 3, 0, PLATFORM_WINNT,
                                  VERSION_GREATER_THAN_EQUAL);
 #else
-  BACKEND->use_alpn = false;
+  backend->use_alpn = false;
 #endif
 
 #ifdef _WIN32_WCE
 #ifdef HAS_MANUAL_VERIFY_API
   /* certificate validation on CE doesn't seem to work right; we'll
    * do it following a more manual process. */
-  BACKEND->use_manual_cred_validation = true;
+  backend->use_manual_cred_validation = true;
 #else
 #error "compiler too old to support requisite manual cert verify for Win CE"
 #endif
 #else
 #ifdef HAS_MANUAL_VERIFY_API
   if(SSL_CONN_CONFIG(CAfile) || SSL_CONN_CONFIG(ca_info_blob)) {
-    if(curlx_verify_windows_version(6, 1, PLATFORM_WINNT,
+    if(curlx_verify_windows_version(6, 1, 0, PLATFORM_WINNT,
                                     VERSION_GREATER_THAN_EQUAL)) {
-      BACKEND->use_manual_cred_validation = true;
+      backend->use_manual_cred_validation = true;
     }
     else {
       failf(data, "schannel: this version of Windows is too old to support "
@@ -818,7 +816,7 @@ schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn,
     }
   }
   else
-    BACKEND->use_manual_cred_validation = false;
+    backend->use_manual_cred_validation = false;
 #else
   if(SSL_CONN_CONFIG(CAfile) || SSL_CONN_CONFIG(ca_info_blob)) {
     failf(data, "schannel: CA cert support not built in");
@@ -827,7 +825,7 @@ schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn,
 #endif
 #endif
 
-  BACKEND->cred = NULL;
+  backend->cred = NULL;
 
   /* check for an existing re-usable credential handle */
   if(SSL_SET_OPTION(primary.sessionid)) {
@@ -835,19 +833,19 @@ schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn,
     if(!Curl_ssl_getsessionid(data, conn,
                               SSL_IS_PROXY() ? TRUE : FALSE,
                               (void **)&old_cred, NULL, sockindex)) {
-      BACKEND->cred = old_cred;
+      backend->cred = old_cred;
       DEBUGF(infof(data, "schannel: re-using existing credential handle"));
 
       /* increment the reference counter of the credential/session handle */
-      BACKEND->cred->refcount++;
+      backend->cred->refcount++;
       DEBUGF(infof(data,
                    "schannel: incremented credential handle refcount = %d",
-                   BACKEND->cred->refcount));
+                   backend->cred->refcount));
     }
     Curl_ssl_sessionid_unlock(data);
   }
 
-  if(!BACKEND->cred) {
+  if(!backend->cred) {
     result = schannel_acquire_credential_handle(data, conn, sockindex);
     if(result != CURLE_OK) {
       return result;
@@ -864,7 +862,7 @@ schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn,
   }
 
 #ifdef HAS_ALPN
-  if(BACKEND->use_alpn) {
+  if(backend->use_alpn) {
     int cur = 0;
     int list_start_index = 0;
     unsigned int *extension_len = NULL;
@@ -922,18 +920,18 @@ schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn,
   InitSecBufferDesc(&outbuf_desc, &outbuf, 1);
 
   /* security request flags */
-  BACKEND->req_flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
+  backend->req_flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
     ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY |
     ISC_REQ_STREAM;
 
   if(!SSL_SET_OPTION(auto_client_cert)) {
-    BACKEND->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS;
+    backend->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS;
   }
 
   /* allocate memory for the security context handle */
-  BACKEND->ctxt = (struct Curl_schannel_ctxt *)
+  backend->ctxt = (struct Curl_schannel_ctxt *)
     calloc(1, sizeof(struct Curl_schannel_ctxt));
-  if(!BACKEND->ctxt) {
+  if(!backend->ctxt) {
     failf(data, "schannel: unable to allocate memory");
     return CURLE_OUT_OF_MEMORY;
   }
@@ -950,16 +948,16 @@ schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn,
      us problems with inbuf regardless. https://github.com/curl/curl/issues/983
   */
   sspi_status = s_pSecFn->InitializeSecurityContext(
-    &BACKEND->cred->cred_handle, NULL, host_name, BACKEND->req_flags, 0, 0,
-    (BACKEND->use_alpn ? &inbuf_desc : NULL),
-    0, &BACKEND->ctxt->ctxt_handle,
-    &outbuf_desc, &BACKEND->ret_flags, &BACKEND->ctxt->time_stamp);
+    &backend->cred->cred_handle, NULL, host_name, backend->req_flags, 0, 0,
+    (backend->use_alpn ? &inbuf_desc : NULL),
+    0, &backend->ctxt->ctxt_handle,
+    &outbuf_desc, &backend->ret_flags, &backend->ctxt->time_stamp);
 
   curlx_unicodefree(host_name);
 
   if(sspi_status != SEC_I_CONTINUE_NEEDED) {
     char buffer[STRERROR_LEN];
-    Curl_safefree(BACKEND->ctxt);
+    Curl_safefree(backend->ctxt);
     switch(sspi_status) {
     case SEC_E_INSUFFICIENT_MEMORY:
       failf(data, "schannel: initial InitializeSecurityContext failed: %s",
@@ -1003,10 +1001,10 @@ schannel_connect_step1(struct Curl_easy *data, struct connectdata *conn,
   DEBUGF(infof(data, "schannel: sent initial handshake data: "
                "sent %zd bytes", written));
 
-  BACKEND->recv_unrecoverable_err = CURLE_OK;
-  BACKEND->recv_sspi_close_notify = false;
-  BACKEND->recv_connection_closed = false;
-  BACKEND->encdata_is_incomplete = false;
+  backend->recv_unrecoverable_err = CURLE_OK;
+  backend->recv_sspi_close_notify = false;
+  backend->recv_connection_closed = false;
+  backend->encdata_is_incomplete = false;
 
   /* continue to second handshake step */
   connssl->connecting_state = ssl_connect_2;
@@ -1031,6 +1029,7 @@ schannel_connect_step2(struct Curl_easy *data, struct connectdata *conn,
   bool doread;
   char * const hostname = SSL_HOST_NAME();
   const char *pubkey_ptr;
+  struct ssl_backend_data *backend = connssl->backend;
 
   doread = (connssl->connecting_state != ssl_connect_2_writing) ? TRUE : FALSE;
 
@@ -1038,39 +1037,39 @@ schannel_connect_step2(struct Curl_easy *data, struct connectdata *conn,
                "schannel: SSL/TLS connection with %s port %hu (step 2/3)",
                hostname, conn->remote_port));
 
-  if(!BACKEND->cred || !BACKEND->ctxt)
+  if(!backend->cred || !backend->ctxt)
     return CURLE_SSL_CONNECT_ERROR;
 
   /* buffer to store previously received and decrypted data */
-  if(!BACKEND->decdata_buffer) {
-    BACKEND->decdata_offset = 0;
-    BACKEND->decdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE;
-    BACKEND->decdata_buffer = malloc(BACKEND->decdata_length);
-    if(!BACKEND->decdata_buffer) {
+  if(!backend->decdata_buffer) {
+    backend->decdata_offset = 0;
+    backend->decdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE;
+    backend->decdata_buffer = malloc(backend->decdata_length);
+    if(!backend->decdata_buffer) {
       failf(data, "schannel: unable to allocate memory");
       return CURLE_OUT_OF_MEMORY;
     }
   }
 
   /* buffer to store previously received and encrypted data */
-  if(!BACKEND->encdata_buffer) {
-    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) {
+  if(!backend->encdata_buffer) {
+    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) {
       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 <
+  if(backend->encdata_length - backend->encdata_offset <
      CURL_SCHANNEL_BUFFER_FREE_SIZE) {
     /* increase internal encrypted data buffer */
-    size_t reallocated_length = BACKEND->encdata_offset +
+    size_t reallocated_length = backend->encdata_offset +
       CURL_SCHANNEL_BUFFER_FREE_SIZE;
-    reallocated_buffer = realloc(BACKEND->encdata_buffer,
+    reallocated_buffer = realloc(backend->encdata_buffer,
                                  reallocated_length);
 
     if(!reallocated_buffer) {
@@ -1078,8 +1077,8 @@ schannel_connect_step2(struct Curl_easy *data, struct connectdata *conn,
       return CURLE_OUT_OF_MEMORY;
     }
     else {
-      BACKEND->encdata_buffer = reallocated_buffer;
-      BACKEND->encdata_length = reallocated_length;
+      backend->encdata_buffer = reallocated_buffer;
+      backend->encdata_length = reallocated_length;
     }
   }
 
@@ -1088,10 +1087,10 @@ schannel_connect_step2(struct Curl_easy *data, struct connectdata *conn,
     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,
+                               (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)
@@ -1107,18 +1106,18 @@ schannel_connect_step2(struct Curl_easy *data, struct connectdata *conn,
       }
 
       /* increase encrypted data buffer offset */
-      BACKEND->encdata_offset += nread;
-      BACKEND->encdata_is_incomplete = false;
+      backend->encdata_offset += nread;
+      backend->encdata_is_incomplete = false;
       DEBUGF(infof(data, "schannel: encrypted data got %zd", nread));
     }
 
     DEBUGF(infof(data,
                  "schannel: encrypted data buffer: offset %zu length %zu",
-                 BACKEND->encdata_offset, BACKEND->encdata_length));
+                 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[0], SECBUFFER_TOKEN, malloc(backend->encdata_offset),
+                  curlx_uztoul(backend->encdata_offset));
     InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0);
     InitSecBufferDesc(&inbuf_desc, inbuf, 2);
 
@@ -1134,19 +1133,17 @@ schannel_connect_step2(struct Curl_easy *data, struct connectdata *conn,
     }
 
     /* copy received handshake data into input buffer */
-    memcpy(inbuf[0].pvBuffer, BACKEND->encdata_buffer,
-           BACKEND->encdata_offset);
+    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);
+      &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);
 
@@ -1155,7 +1152,7 @@ schannel_connect_step2(struct Curl_easy *data, struct connectdata *conn,
 
     /* check if the handshake was incomplete */
     if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) {
-      BACKEND->encdata_is_incomplete = true;
+      backend->encdata_is_incomplete = true;
       connssl->connecting_state = ssl_connect_2_reading;
       DEBUGF(infof(data,
                    "schannel: received incomplete message, need more data"));
@@ -1166,8 +1163,8 @@ schannel_connect_step2(struct Curl_easy *data, struct connectdata *conn,
        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;
+       !(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"));
@@ -1195,7 +1192,7 @@ schannel_connect_step2(struct Curl_easy *data, struct connectdata *conn,
         }
 
         /* free obsolete buffer */
-        if(outbuf[i].pvBuffer != NULL) {
+        if(outbuf[i].pvBuffer) {
           s_pSecFn->FreeContextBuffer(outbuf[i].pvBuffer);
         }
       }
@@ -1249,11 +1246,11 @@ schannel_connect_step2(struct Curl_easy *data, struct connectdata *conn,
       */
       /* 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) -
+      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;
+        backend->encdata_offset = inbuf[1].cbBuffer;
         if(sspi_status == SEC_I_CONTINUE_NEEDED) {
           doread = FALSE;
           continue;
@@ -1261,7 +1258,7 @@ schannel_connect_step2(struct Curl_easy *data, struct connectdata *conn,
       }
     }
     else {
-      BACKEND->encdata_offset = 0;
+      backend->encdata_offset = 0;
     }
     break;
   }
@@ -1288,7 +1285,7 @@ schannel_connect_step2(struct Curl_easy *data, struct connectdata *conn,
   }
 
 #ifdef HAS_MANUAL_VERIFY_API
-  if(conn->ssl_config.verifypeer && BACKEND->use_manual_cred_validation) {
+  if(conn->ssl_config.verifypeer && backend->use_manual_cred_validation) {
     return Curl_verify_certificate(data, conn, sockindex);
   }
 #endif
@@ -1370,6 +1367,7 @@ schannel_connect_step3(struct Curl_easy *data, struct connectdata *conn,
 #ifdef HAS_ALPN
   SecPkgContext_ApplicationProtocol alpn_result;
 #endif
+  struct ssl_backend_data *backend = connssl->backend;
 
   DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
 
@@ -1377,28 +1375,28 @@ schannel_connect_step3(struct Curl_easy *data, struct connectdata *conn,
                "schannel: SSL/TLS connection with %s port %hu (step 3/3)",
                hostname, conn->remote_port));
 
-  if(!BACKEND->cred)
+  if(!backend->cred)
     return CURLE_SSL_CONNECT_ERROR;
 
   /* check if the required context attributes are met */
-  if(BACKEND->ret_flags != BACKEND->req_flags) {
-    if(!(BACKEND->ret_flags & ISC_RET_SEQUENCE_DETECT))
+  if(backend->ret_flags != backend->req_flags) {
+    if(!(backend->ret_flags & ISC_RET_SEQUENCE_DETECT))
       failf(data, "schannel: failed to setup sequence detection");
-    if(!(BACKEND->ret_flags & ISC_RET_REPLAY_DETECT))
+    if(!(backend->ret_flags & ISC_RET_REPLAY_DETECT))
       failf(data, "schannel: failed to setup replay detection");
-    if(!(BACKEND->ret_flags & ISC_RET_CONFIDENTIALITY))
+    if(!(backend->ret_flags & ISC_RET_CONFIDENTIALITY))
       failf(data, "schannel: failed to setup confidentiality");
-    if(!(BACKEND->ret_flags & ISC_RET_ALLOCATED_MEMORY))
+    if(!(backend->ret_flags & ISC_RET_ALLOCATED_MEMORY))
       failf(data, "schannel: failed to setup memory allocation");
-    if(!(BACKEND->ret_flags & ISC_RET_STREAM))
+    if(!(backend->ret_flags & ISC_RET_STREAM))
       failf(data, "schannel: failed to setup stream orientation");
     return CURLE_SSL_CONNECT_ERROR;
   }
 
 #ifdef HAS_ALPN
-  if(BACKEND->use_alpn) {
+  if(backend->use_alpn) {
     sspi_status =
-      s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle,
+      s_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle,
                                        SECPKG_ATTR_APPLICATION_PROTOCOL,
                                        &alpn_result);
 
@@ -1436,13 +1434,14 @@ schannel_connect_step3(struct Curl_easy *data, struct connectdata *conn,
   /* save the current session data for possible re-use */
   if(SSL_SET_OPTION(primary.sessionid)) {
     bool incache;
+    bool added = FALSE;
     struct Curl_schannel_cred *old_cred = NULL;
 
     Curl_ssl_sessionid_lock(data);
     incache = !(Curl_ssl_getsessionid(data, conn, isproxy, (void **)&old_cred,
                                       NULL, sockindex));
     if(incache) {
-      if(old_cred != BACKEND->cred) {
+      if(old_cred != backend->cred) {
         DEBUGF(infof(data,
                      "schannel: old credential handle is stale, removing"));
         /* we're not taking old_cred ownership here, no refcount++ is needed */
@@ -1451,17 +1450,17 @@ schannel_connect_step3(struct Curl_easy *data, struct connectdata *conn,
       }
     }
     if(!incache) {
-      result = Curl_ssl_addsessionid(data, conn, isproxy, BACKEND->cred,
+      result = Curl_ssl_addsessionid(data, conn, isproxy, backend->cred,
                                      sizeof(struct Curl_schannel_cred),
-                                     sockindex);
+                                     sockindex, &added);
       if(result) {
         Curl_ssl_sessionid_unlock(data);
         failf(data, "schannel: failed to store credential handle");
         return result;
       }
-      else {
+      else if(added) {
         /* this cred session is now also referenced by sessionid cache */
-        BACKEND->cred->refcount++;
+        backend->cred->refcount++;
         DEBUGF(infof(data,
                      "schannel: stored credential handle in session cache"));
       }
@@ -1472,7 +1471,7 @@ schannel_connect_step3(struct Curl_easy *data, struct connectdata *conn,
   if(data->set.ssl.certinfo) {
     int certs_count = 0;
     sspi_status =
-      s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle,
+      s_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle,
                                        SECPKG_ATTR_REMOTE_CERT_CONTEXT,
                                        &ccert_context);
 
@@ -1609,7 +1608,10 @@ schannel_connect_common(struct Curl_easy *data, struct connectdata *conn,
      * binding to pass the IIS extended protection checks.
      * Available on Windows 7 or later.
      */
-    conn->sslContext = &BACKEND->ctxt->ctxt_handle;
+    {
+      struct ssl_backend_data *backend = connssl->backend;
+      conn->sslContext = &backend->ctxt->ctxt_handle;
+    }
 #endif
 
     *done = TRUE;
@@ -1636,13 +1638,14 @@ schannel_send(struct Curl_easy *data, int sockindex,
   SecBufferDesc outbuf_desc;
   SECURITY_STATUS sspi_status = SEC_E_OK;
   CURLcode result;
+  struct ssl_backend_data *backend = connssl->backend;
 
   /* check if the maximum stream sizes were queried */
-  if(BACKEND->stream_sizes.cbMaximumMessage == 0) {
+  if(backend->stream_sizes.cbMaximumMessage == 0) {
     sspi_status = s_pSecFn->QueryContextAttributes(
-      &BACKEND->ctxt->ctxt_handle,
+      &backend->ctxt->ctxt_handle,
       SECPKG_ATTR_STREAM_SIZES,
-      &BACKEND->stream_sizes);
+      &backend->stream_sizes);
     if(sspi_status != SEC_E_OK) {
       *err = CURLE_SEND_ERROR;
       return -1;
@@ -1650,13 +1653,13 @@ schannel_send(struct Curl_easy *data, int sockindex,
   }
 
   /* check if the buffer is longer than the maximum message length */
-  if(len > BACKEND->stream_sizes.cbMaximumMessage) {
-    len = BACKEND->stream_sizes.cbMaximumMessage;
+  if(len > backend->stream_sizes.cbMaximumMessage) {
+    len = backend->stream_sizes.cbMaximumMessage;
   }
 
   /* calculate the complete message length and allocate a buffer for it */
-  data_len = BACKEND->stream_sizes.cbHeader + len +
-    BACKEND->stream_sizes.cbTrailer;
+  data_len = backend->stream_sizes.cbHeader + len +
+    backend->stream_sizes.cbTrailer;
   ptr = (unsigned char *) malloc(data_len);
   if(!ptr) {
     *err = CURLE_OUT_OF_MEMORY;
@@ -1665,12 +1668,12 @@ schannel_send(struct Curl_easy *data, int sockindex,
 
   /* setup output buffers (header, data, trailer, empty) */
   InitSecBuffer(&outbuf[0], SECBUFFER_STREAM_HEADER,
-                ptr, BACKEND->stream_sizes.cbHeader);
+                ptr, backend->stream_sizes.cbHeader);
   InitSecBuffer(&outbuf[1], SECBUFFER_DATA,
-                ptr + BACKEND->stream_sizes.cbHeader, curlx_uztoul(len));
+                ptr + backend->stream_sizes.cbHeader, curlx_uztoul(len));
   InitSecBuffer(&outbuf[2], SECBUFFER_STREAM_TRAILER,
-                ptr + BACKEND->stream_sizes.cbHeader + len,
-                BACKEND->stream_sizes.cbTrailer);
+                ptr + backend->stream_sizes.cbHeader + len,
+                backend->stream_sizes.cbTrailer);
   InitSecBuffer(&outbuf[3], SECBUFFER_EMPTY, NULL, 0);
   InitSecBufferDesc(&outbuf_desc, outbuf, 4);
 
@@ -1678,7 +1681,7 @@ schannel_send(struct Curl_easy *data, int sockindex,
   memcpy(outbuf[1].pvBuffer, buf, len);
 
   /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375390.aspx */
-  sspi_status = s_pSecFn->EncryptMessage(&BACKEND->ctxt->ctxt_handle, 0,
+  sspi_status = s_pSecFn->EncryptMessage(&backend->ctxt->ctxt_handle, 0,
                                          &outbuf_desc, 0);
 
   /* check if the message was encrypted */
@@ -1783,9 +1786,10 @@ schannel_recv(struct Curl_easy *data, int sockindex,
   /* we want the length of the encrypted buffer to be at least large enough
      that it can hold all the bytes requested and some TLS record overhead. */
   size_t min_encdata_length = len + CURL_SCHANNEL_BUFFER_FREE_SIZE;
+  struct ssl_backend_data *backend = connssl->backend;
 
   /****************************************************************************
-   * Don't return or set BACKEND->recv_unrecoverable_err unless in the cleanup.
+   * Don't return or set backend->recv_unrecoverable_err unless in the cleanup.
    * The pattern for return error is set *err, optional infof, goto cleanup.
    *
    * Our priority is to always return as much decrypted data to the caller as
@@ -1797,16 +1801,16 @@ schannel_recv(struct Curl_easy *data, int sockindex,
   DEBUGF(infof(data, "schannel: client wants to read %zu bytes", len));
   *err = CURLE_OK;
 
-  if(len && len <= BACKEND->decdata_offset) {
+  if(len && len <= backend->decdata_offset) {
     infof(data, "schannel: enough decrypted data is already available");
     goto cleanup;
   }
-  else if(BACKEND->recv_unrecoverable_err) {
-    *err = BACKEND->recv_unrecoverable_err;
+  else if(backend->recv_unrecoverable_err) {
+    *err = backend->recv_unrecoverable_err;
     infof(data, "schannel: an unrecoverable error occurred in a prior call");
     goto cleanup;
   }
-  else if(BACKEND->recv_sspi_close_notify) {
+  else if(backend->recv_sspi_close_notify) {
     /* once a server has indicated shutdown there is no more encrypted data */
     infof(data, "schannel: server indicated shutdown in a prior call");
     goto cleanup;
@@ -1816,17 +1820,17 @@ schannel_recv(struct Curl_easy *data, int sockindex,
      immediately because there may be data to decrypt (in the case we want to
      decrypt all encrypted cached data) so handle !len later in cleanup.
   */
-  else if(len && !BACKEND->recv_connection_closed) {
+  else if(len && !backend->recv_connection_closed) {
     /* increase enc buffer in order to fit the requested amount of data */
-    size = BACKEND->encdata_length - BACKEND->encdata_offset;
+    size = backend->encdata_length - backend->encdata_offset;
     if(size < CURL_SCHANNEL_BUFFER_FREE_SIZE ||
-       BACKEND->encdata_length < min_encdata_length) {
-      reallocated_length = BACKEND->encdata_offset +
+       backend->encdata_length < min_encdata_length) {
+      reallocated_length = backend->encdata_offset +
         CURL_SCHANNEL_BUFFER_FREE_SIZE;
       if(reallocated_length < min_encdata_length) {
         reallocated_length = min_encdata_length;
       }
-      reallocated_buffer = realloc(BACKEND->encdata_buffer,
+      reallocated_buffer = realloc(backend->encdata_buffer,
                                    reallocated_length);
       if(!reallocated_buffer) {
         *err = CURLE_OUT_OF_MEMORY;
@@ -1834,21 +1838,21 @@ schannel_recv(struct Curl_easy *data, int sockindex,
         goto cleanup;
       }
 
-      BACKEND->encdata_buffer = reallocated_buffer;
-      BACKEND->encdata_length = reallocated_length;
-      size = BACKEND->encdata_length - BACKEND->encdata_offset;
+      backend->encdata_buffer = reallocated_buffer;
+      backend->encdata_length = reallocated_length;
+      size = backend->encdata_length - backend->encdata_offset;
       DEBUGF(infof(data, "schannel: encdata_buffer resized %zu",
-                   BACKEND->encdata_length));
+                   backend->encdata_length));
     }
 
     DEBUGF(infof(data,
                  "schannel: encrypted data buffer: offset %zu length %zu",
-                 BACKEND->encdata_offset, BACKEND->encdata_length));
+                 backend->encdata_offset, backend->encdata_length));
 
     /* read encrypted data from socket */
     *err = Curl_read_plain(conn->sock[sockindex],
-                           (char *)(BACKEND->encdata_buffer +
-                                    BACKEND->encdata_offset),
+                           (char *)(backend->encdata_buffer +
+                                    backend->encdata_offset),
                            size, &nread);
     if(*err) {
       nread = -1;
@@ -1861,27 +1865,27 @@ schannel_recv(struct Curl_easy *data, int sockindex,
         infof(data, "schannel: Curl_read_plain returned error %d", *err);
     }
     else if(nread == 0) {
-      BACKEND->recv_connection_closed = true;
+      backend->recv_connection_closed = true;
       DEBUGF(infof(data, "schannel: server closed the connection"));
     }
     else if(nread > 0) {
-      BACKEND->encdata_offset += (size_t)nread;
-      BACKEND->encdata_is_incomplete = false;
+      backend->encdata_offset += (size_t)nread;
+      backend->encdata_is_incomplete = false;
       DEBUGF(infof(data, "schannel: encrypted data got %zd", nread));
     }
   }
 
   DEBUGF(infof(data,
                "schannel: encrypted data buffer: offset %zu length %zu",
-               BACKEND->encdata_offset, BACKEND->encdata_length));
+               backend->encdata_offset, backend->encdata_length));
 
   /* decrypt loop */
-  while(BACKEND->encdata_offset > 0 && sspi_status == SEC_E_OK &&
-        (!len || BACKEND->decdata_offset < len ||
-         BACKEND->recv_connection_closed)) {
+  while(backend->encdata_offset > 0 && sspi_status == SEC_E_OK &&
+        (!len || backend->decdata_offset < len ||
+         backend->recv_connection_closed)) {
     /* prepare data buffer for DecryptMessage call */
-    InitSecBuffer(&inbuf[0], SECBUFFER_DATA, BACKEND->encdata_buffer,
-                  curlx_uztoul(BACKEND->encdata_offset));
+    InitSecBuffer(&inbuf[0], SECBUFFER_DATA, backend->encdata_buffer,
+                  curlx_uztoul(backend->encdata_offset));
 
     /* we need 3 more empty input buffers for possible output */
     InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0);
@@ -1891,7 +1895,7 @@ schannel_recv(struct Curl_easy *data, int sockindex,
 
     /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375348.aspx
      */
-    sspi_status = s_pSecFn->DecryptMessage(&BACKEND->ctxt->ctxt_handle,
+    sspi_status = s_pSecFn->DecryptMessage(&backend->ctxt->ctxt_handle,
                                            &inbuf_desc, 0, NULL);
 
     /* check if everything went fine (server may want to renegotiate
@@ -1907,37 +1911,37 @@ schannel_recv(struct Curl_easy *data, int sockindex,
         /* increase buffer in order to fit the received amount of data */
         size = inbuf[1].cbBuffer > CURL_SCHANNEL_BUFFER_FREE_SIZE ?
           inbuf[1].cbBuffer : CURL_SCHANNEL_BUFFER_FREE_SIZE;
-        if(BACKEND->decdata_length - BACKEND->decdata_offset < size ||
-           BACKEND->decdata_length < len) {
+        if(backend->decdata_length - backend->decdata_offset < size ||
+           backend->decdata_length < len) {
           /* increase internal decrypted data buffer */
-          reallocated_length = BACKEND->decdata_offset + size;
+          reallocated_length = backend->decdata_offset + size;
           /* make sure that the requested amount of data fits */
           if(reallocated_length < len) {
             reallocated_length = len;
           }
-          reallocated_buffer = realloc(BACKEND->decdata_buffer,
+          reallocated_buffer = realloc(backend->decdata_buffer,
                                        reallocated_length);
           if(!reallocated_buffer) {
             *err = CURLE_OUT_OF_MEMORY;
             failf(data, "schannel: unable to re-allocate memory");
             goto cleanup;
           }
-          BACKEND->decdata_buffer = reallocated_buffer;
-          BACKEND->decdata_length = reallocated_length;
+          backend->decdata_buffer = reallocated_buffer;
+          backend->decdata_length = reallocated_length;
         }
 
         /* copy decrypted data to internal buffer */
         size = inbuf[1].cbBuffer;
         if(size) {
-          memcpy(BACKEND->decdata_buffer + BACKEND->decdata_offset,
+          memcpy(backend->decdata_buffer + backend->decdata_offset,
                  inbuf[1].pvBuffer, size);
-          BACKEND->decdata_offset += size;
+          backend->decdata_offset += size;
         }
 
         DEBUGF(infof(data, "schannel: decrypted data added: %zu", size));
         DEBUGF(infof(data,
                      "schannel: decrypted cached: offset %zu length %zu",
-                     BACKEND->decdata_offset, BACKEND->decdata_length));
+                     backend->decdata_offset, backend->decdata_length));
       }
 
       /* check for remaining encrypted data */
@@ -1948,34 +1952,34 @@ schannel_recv(struct Curl_easy *data, int sockindex,
         /* check if the remaining data is less than the total amount
          * and therefore begins after the already processed data
          */
-        if(BACKEND->encdata_offset > inbuf[3].cbBuffer) {
+        if(backend->encdata_offset > inbuf[3].cbBuffer) {
           /* move remaining encrypted data forward to the beginning of
              buffer */
-          memmove(BACKEND->encdata_buffer,
-                  (BACKEND->encdata_buffer + BACKEND->encdata_offset) -
+          memmove(backend->encdata_buffer,
+                  (backend->encdata_buffer + backend->encdata_offset) -
                   inbuf[3].cbBuffer, inbuf[3].cbBuffer);
-          BACKEND->encdata_offset = inbuf[3].cbBuffer;
+          backend->encdata_offset = inbuf[3].cbBuffer;
         }
 
         DEBUGF(infof(data,
                      "schannel: encrypted cached: offset %zu length %zu",
-                     BACKEND->encdata_offset, BACKEND->encdata_length));
+                     backend->encdata_offset, backend->encdata_length));
       }
       else {
         /* reset encrypted buffer offset, because there is no data remaining */
-        BACKEND->encdata_offset = 0;
+        backend->encdata_offset = 0;
       }
 
       /* check if server wants to renegotiate the connection context */
       if(sspi_status == SEC_I_RENEGOTIATE) {
         infof(data, "schannel: remote party requests renegotiation");
         if(*err && *err != CURLE_AGAIN) {
-          infof(data, "schannel: can't renogotiate, an error is pending");
+          infof(data, "schannel: can't renegotiate, an error is pending");
           goto cleanup;
         }
-        if(BACKEND->encdata_offset) {
+        if(backend->encdata_offset) {
           *err = CURLE_RECV_ERROR;
-          infof(data, "schannel: can't renogotiate, "
+          infof(data, "schannel: can't renegotiate, "
                 "encrypted data available");
           goto cleanup;
         }
@@ -1997,16 +2001,16 @@ schannel_recv(struct Curl_easy *data, int sockindex,
       else if(sspi_status == SEC_I_CONTEXT_EXPIRED) {
         /* In Windows 2000 SEC_I_CONTEXT_EXPIRED (close_notify) is not
            returned so we have to work around that in cleanup. */
-        BACKEND->recv_sspi_close_notify = true;
-        if(!BACKEND->recv_connection_closed) {
-          BACKEND->recv_connection_closed = true;
+        backend->recv_sspi_close_notify = true;
+        if(!backend->recv_connection_closed) {
+          backend->recv_connection_closed = true;
           infof(data, "schannel: server closed the connection");
         }
         goto cleanup;
       }
     }
     else if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) {
-      BACKEND->encdata_is_incomplete = true;
+      backend->encdata_is_incomplete = true;
       if(!*err)
         *err = CURLE_AGAIN;
       infof(data, "schannel: failed to decrypt data, need more data");
@@ -2025,11 +2029,11 @@ schannel_recv(struct Curl_easy *data, int sockindex,
 
   DEBUGF(infof(data,
                "schannel: encrypted data buffer: offset %zu length %zu",
-               BACKEND->encdata_offset, BACKEND->encdata_length));
+               backend->encdata_offset, backend->encdata_length));
 
   DEBUGF(infof(data,
                "schannel: decrypted data buffer: offset %zu length %zu",
-               BACKEND->decdata_offset, BACKEND->decdata_length));
+               backend->decdata_offset, backend->decdata_length));
 
   cleanup:
   /* Warning- there is no guarantee the encdata state is valid at this point */
@@ -2046,13 +2050,13 @@ schannel_recv(struct Curl_easy *data, int sockindex,
      assume it was graceful (close_notify) since there doesn't seem to be a
      way to tell.
   */
-  if(len && !BACKEND->decdata_offset && BACKEND->recv_connection_closed &&
-     !BACKEND->recv_sspi_close_notify) {
-    bool isWin2k = curlx_verify_windows_version(5, 0, PLATFORM_WINNT,
+  if(len && !backend->decdata_offset && backend->recv_connection_closed &&
+     !backend->recv_sspi_close_notify) {
+    bool isWin2k = curlx_verify_windows_version(5, 0, 0, PLATFORM_WINNT,
                                                 VERSION_EQUAL);
 
     if(isWin2k && sspi_status == SEC_E_OK)
-      BACKEND->recv_sspi_close_notify = true;
+      backend->recv_sspi_close_notify = true;
     else {
       *err = CURLE_RECV_ERROR;
       infof(data, "schannel: server closed abruptly (missing close_notify)");
@@ -2061,23 +2065,23 @@ schannel_recv(struct Curl_easy *data, int sockindex,
 
   /* Any error other than CURLE_AGAIN is an unrecoverable error. */
   if(*err && *err != CURLE_AGAIN)
-    BACKEND->recv_unrecoverable_err = *err;
+    backend->recv_unrecoverable_err = *err;
 
-  size = len < BACKEND->decdata_offset ? len : BACKEND->decdata_offset;
+  size = len < backend->decdata_offset ? len : backend->decdata_offset;
   if(size) {
-    memcpy(buf, BACKEND->decdata_buffer, size);
-    memmove(BACKEND->decdata_buffer, BACKEND->decdata_buffer + size,
-            BACKEND->decdata_offset - size);
-    BACKEND->decdata_offset -= size;
+    memcpy(buf, backend->decdata_buffer, size);
+    memmove(backend->decdata_buffer, backend->decdata_buffer + size,
+            backend->decdata_offset - size);
+    backend->decdata_offset -= size;
     DEBUGF(infof(data, "schannel: decrypted data returned %zu", size));
     DEBUGF(infof(data,
                  "schannel: decrypted data buffer: offset %zu length %zu",
-                 BACKEND->decdata_offset, BACKEND->decdata_length));
+                 backend->decdata_offset, backend->decdata_length));
     *err = CURLE_OK;
     return (ssize_t)size;
   }
 
-  if(!*err && !BACKEND->recv_connection_closed)
+  if(!*err && !backend->recv_connection_closed)
     *err = CURLE_AGAIN;
 
   /* It's debatable what to return when !len. We could return whatever error
@@ -2116,34 +2120,32 @@ static bool schannel_data_pending(const struct connectdata *conn,
                                   int sockindex)
 {
   const struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+  struct ssl_backend_data *backend = connssl->backend;
 
   if(connssl->use) /* SSL/TLS is in use */
-    return (BACKEND->decdata_offset > 0 ||
-            (BACKEND->encdata_offset > 0 && !BACKEND->encdata_is_incomplete));
+    return (backend->decdata_offset > 0 ||
+            (backend->encdata_offset > 0 && !backend->encdata_is_incomplete));
   else
     return FALSE;
 }
 
-static void schannel_close(struct Curl_easy *data, struct connectdata *conn,
-                           int sockindex)
-{
-  if(conn->ssl[sockindex].use)
-    /* if the SSL/TLS channel hasn't been shut down yet, do that now. */
-    Curl_ssl_shutdown(data, conn, sockindex);
-}
-
 static void schannel_session_free(void *ptr)
 {
   /* this is expected to be called under sessionid lock */
   struct Curl_schannel_cred *cred = ptr;
 
-  cred->refcount--;
-  if(cred->refcount == 0) {
-    s_pSecFn->FreeCredentialsHandle(&cred->cred_handle);
-    Curl_safefree(cred);
+  if(cred) {
+    cred->refcount--;
+    if(cred->refcount == 0) {
+      s_pSecFn->FreeCredentialsHandle(&cred->cred_handle);
+      Curl_safefree(cred);
+    }
   }
 }
 
+/* shut down the SSL connection and clean up related memory.
+   this function can be called multiple times on the same connection including
+   if the SSL connection failed (eg connection made but failed handshake). */
 static int schannel_shutdown(struct Curl_easy *data, struct connectdata *conn,
                              int sockindex)
 {
@@ -2152,13 +2154,16 @@ static int schannel_shutdown(struct Curl_easy *data, struct connectdata *conn,
    */
   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
   char * const hostname = SSL_HOST_NAME();
+  struct ssl_backend_data *backend = connssl->backend;
 
   DEBUGASSERT(data);
 
-  infof(data, "schannel: shutting down SSL/TLS connection with %s port %hu",
-        hostname, conn->remote_port);
+  if(connssl->use) {
+    infof(data, "schannel: shutting down SSL/TLS connection with %s port %hu",
+          hostname, conn->remote_port);
+  }
 
-  if(BACKEND->cred && BACKEND->ctxt) {
+  if(connssl->use && backend->cred && backend->ctxt) {
     SecBufferDesc BuffDesc;
     SecBuffer Buffer;
     SECURITY_STATUS sspi_status;
@@ -2171,7 +2176,7 @@ static int schannel_shutdown(struct Curl_easy *data, struct connectdata *conn,
     InitSecBuffer(&Buffer, SECBUFFER_TOKEN, &dwshut, sizeof(dwshut));
     InitSecBufferDesc(&BuffDesc, &Buffer, 1);
 
-    sspi_status = s_pSecFn->ApplyControlToken(&BACKEND->ctxt->ctxt_handle,
+    sspi_status = s_pSecFn->ApplyControlToken(&backend->ctxt->ctxt_handle,
                                               &BuffDesc);
 
     if(sspi_status != SEC_E_OK) {
@@ -2189,18 +2194,18 @@ static int schannel_shutdown(struct Curl_easy *data, struct connectdata *conn,
     InitSecBufferDesc(&outbuf_desc, &outbuf, 1);
 
     sspi_status = s_pSecFn->InitializeSecurityContext(
-      &BACKEND->cred->cred_handle,
-      &BACKEND->ctxt->ctxt_handle,
+      &backend->cred->cred_handle,
+      &backend->ctxt->ctxt_handle,
       host_name,
-      BACKEND->req_flags,
+      backend->req_flags,
       0,
       0,
       NULL,
       0,
-      &BACKEND->ctxt->ctxt_handle,
+      &backend->ctxt->ctxt_handle,
       &outbuf_desc,
-      &BACKEND->ret_flags,
-      &BACKEND->ctxt->time_stamp);
+      &backend->ret_flags,
+      &backend->ctxt->time_stamp);
 
     curlx_unicodefree(host_name);
 
@@ -2219,38 +2224,48 @@ static int schannel_shutdown(struct Curl_easy *data, struct connectdata *conn,
   }
 
   /* free SSPI Schannel API security context handle */
-  if(BACKEND->ctxt) {
+  if(backend->ctxt) {
     DEBUGF(infof(data, "schannel: clear security context handle"));
-    s_pSecFn->DeleteSecurityContext(&BACKEND->ctxt->ctxt_handle);
-    Curl_safefree(BACKEND->ctxt);
+    s_pSecFn->DeleteSecurityContext(&backend->ctxt->ctxt_handle);
+    Curl_safefree(backend->ctxt);
   }
 
   /* free SSPI Schannel API credential handle */
-  if(BACKEND->cred) {
+  if(backend->cred) {
     Curl_ssl_sessionid_lock(data);
-    schannel_session_free(BACKEND->cred);
+    schannel_session_free(backend->cred);
     Curl_ssl_sessionid_unlock(data);
-    BACKEND->cred = NULL;
+    backend->cred = NULL;
   }
 
   /* free internal buffer for received encrypted data */
-  if(BACKEND->encdata_buffer != NULL) {
-    Curl_safefree(BACKEND->encdata_buffer);
-    BACKEND->encdata_length = 0;
-    BACKEND->encdata_offset = 0;
-    BACKEND->encdata_is_incomplete = false;
+  if(backend->encdata_buffer) {
+    Curl_safefree(backend->encdata_buffer);
+    backend->encdata_length = 0;
+    backend->encdata_offset = 0;
+    backend->encdata_is_incomplete = false;
   }
 
   /* free internal buffer for received decrypted data */
-  if(BACKEND->decdata_buffer != NULL) {
-    Curl_safefree(BACKEND->decdata_buffer);
-    BACKEND->decdata_length = 0;
-    BACKEND->decdata_offset = 0;
+  if(backend->decdata_buffer) {
+    Curl_safefree(backend->decdata_buffer);
+    backend->decdata_length = 0;
+    backend->decdata_offset = 0;
   }
 
   return CURLE_OK;
 }
 
+static void schannel_close(struct Curl_easy *data, struct connectdata *conn,
+                           int sockindex)
+{
+  if(conn->ssl[sockindex].use)
+    /* Curl_ssl_shutdown resets the socket state and calls schannel_shutdown */
+    Curl_ssl_shutdown(data, conn, sockindex);
+  else
+    schannel_shutdown(data, conn, sockindex);
+}
+
 static int schannel_init(void)
 {
   return (Curl_sspi_global_init() == CURLE_OK ? 1 : 0);
@@ -2293,6 +2308,7 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data,
                                     const char *pinnedpubkey)
 {
   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
+  struct ssl_backend_data *backend = connssl->backend;
   CERT_CONTEXT *pCertContextServer = NULL;
 
   /* Result is returned to caller */
@@ -2310,7 +2326,7 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data,
     struct Curl_asn1Element *pubkey;
 
     sspi_status =
-      s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle,
+      s_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle,
                                        SECPKG_ATTR_REMOTE_CERT_CONTEXT,
                                        &pCertContextServer);
 
@@ -2416,8 +2432,9 @@ static CURLcode schannel_sha256sum(const unsigned char *input,
 static void *schannel_get_internals(struct ssl_connect_data *connssl,
                                     CURLINFO info UNUSED_PARAM)
 {
+  struct ssl_backend_data *backend = connssl->backend;
   (void)info;
-  return &BACKEND->ctxt->ctxt_handle;
+  return &backend->ctxt->ctxt_handle;
 }
 
 const struct Curl_ssl Curl_ssl_schannel = {
diff --git a/lib/vtls/schannel_verify.c b/lib/vtls/schannel_verify.c
index 1b283d0..4966cd4 100644
--- a/lib/vtls/schannel_verify.c
+++ b/lib/vtls/schannel_verify.c
@@ -355,7 +355,7 @@ static DWORD cert_get_name_string(struct Curl_easy *data,
   DWORD i;
 
   /* CERT_NAME_SEARCH_ALL_NAMES_FLAG is available from Windows 8 onwards. */
-  if(curlx_verify_windows_version(6, 2, PLATFORM_WINNT,
+  if(curlx_verify_windows_version(6, 2, 0, PLATFORM_WINNT,
                                   VERSION_GREATER_THAN_EQUAL)) {
 #ifdef CERT_NAME_SEARCH_ALL_NAMES_FLAG
     /* CertGetNameString will provide the 8-bit character string without
@@ -597,7 +597,8 @@ CURLcode Curl_verify_certificate(struct Curl_easy *data,
      * trusted certificates. This is only supported on Windows 7+.
      */
 
-    if(curlx_verify_windows_version(6, 1, PLATFORM_WINNT, VERSION_LESS_THAN)) {
+    if(curlx_verify_windows_version(6, 1, 0, PLATFORM_WINNT,
+                                    VERSION_LESS_THAN)) {
       failf(data, "schannel: this version of Windows is too old to support "
             "certificate verification via CA bundle file.");
       result = CURLE_SSL_CACERT_BADFILE;
diff --git a/lib/vtls/sectransp.c b/lib/vtls/sectransp.c
index 1e6ed5f..f7a20b2 100644
--- a/lib/vtls/sectransp.c
+++ b/lib/vtls/sectransp.c
@@ -997,14 +997,14 @@ CF_INLINE CFStringRef getsubject(SecCertificateRef cert)
 #else
 #if CURL_BUILD_MAC_10_7
   /* Lion & later: Get the long description if we can. */
-  if(SecCertificateCopyLongDescription != NULL)
+  if(SecCertificateCopyLongDescription)
     server_cert_summary =
       SecCertificateCopyLongDescription(NULL, cert, NULL);
   else
 #endif /* CURL_BUILD_MAC_10_7 */
 #if CURL_BUILD_MAC_10_6
   /* Snow Leopard: Get the certificate summary. */
-  if(SecCertificateCopySubjectSummary != NULL)
+  if(SecCertificateCopySubjectSummary)
     server_cert_summary = SecCertificateCopySubjectSummary(cert);
   else
 #endif /* CURL_BUILD_MAC_10_6 */
@@ -1118,7 +1118,7 @@ static OSStatus CopyIdentityWithLabel(char *label,
   /* SecItemCopyMatching() was introduced in iOS and Snow Leopard.
      kSecClassIdentity was introduced in Lion. If both exist, let's use them
      to find the certificate. */
-  if(SecItemCopyMatching != NULL && kSecClassIdentity != NULL) {
+  if(SecItemCopyMatching && kSecClassIdentity) {
     CFTypeRef keys[5];
     CFTypeRef values[5];
     CFDictionaryRef query_dict;
@@ -1248,7 +1248,7 @@ static OSStatus CopyIdentityFromPKCS12File(const char *cPath,
     CFDictionaryRef options = CFDictionaryCreate(NULL, cKeys, cValues,
       password ? 1L : 0L, NULL, NULL);
 
-    if(options != NULL) {
+    if(options) {
       status = SecPKCS12Import(pkcs_data, options, &items);
       CFRelease(options);
     }
@@ -1406,7 +1406,7 @@ set_ssl_version_min_max(struct Curl_easy *data, struct connectdata *conn,
   }
 
 #if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS
-  if(SSLSetProtocolVersionMax != NULL) {
+  if(SSLSetProtocolVersionMax) {
     SSLProtocol darwin_ver_min = kTLSProtocol1;
     SSLProtocol darwin_ver_max = kTLSProtocol1;
     CURLcode result = sectransp_version_from_curl(&darwin_ver_min,
@@ -1608,7 +1608,7 @@ static CURLcode sectransp_set_selected_ciphers(struct Curl_easy *data,
       if(tls_name) {
         table_cipher_name = ciphertable[i].name;
       }
-      else if(ciphertable[i].alias_name != NULL) {
+      else if(ciphertable[i].alias_name) {
         table_cipher_name = ciphertable[i].alias_name;
       }
       else {
@@ -1688,7 +1688,7 @@ static CURLcode sectransp_connect_step1(struct Curl_easy *data,
 #endif /* CURL_BUILD_MAC */
 
 #if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS
-  if(SSLCreateContext != NULL) {  /* use the newer API if available */
+  if(SSLCreateContext) {  /* use the newer API if available */
     if(backend->ssl_ctx)
       CFRelease(backend->ssl_ctx);
     backend->ssl_ctx = SSLCreateContext(NULL, kSSLClientSide, kSSLStreamType);
@@ -1722,7 +1722,7 @@ static CURLcode sectransp_connect_step1(struct Curl_easy *data,
 
   /* check to see if we've been told to use an explicit SSL/TLS version */
 #if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS
-  if(SSLSetProtocolVersionMax != NULL) {
+  if(SSLSetProtocolVersionMax) {
     switch(conn->ssl_config.version) {
     case CURL_SSLVERSION_TLSv1:
       (void)SSLSetProtocolVersionMin(backend->ssl_ctx, kTLSProtocol1);
@@ -1980,9 +1980,9 @@ static CURLcode sectransp_connect_step1(struct Curl_easy *data,
   Darwin 15.x.x is El Capitan (10.11)
   */
 #if CURL_BUILD_MAC
-  if(SSLSetSessionOption != NULL && darwinver_maj >= 13) {
+  if(SSLSetSessionOption && darwinver_maj >= 13) {
 #else
-  if(SSLSetSessionOption != NULL) {
+  if(SSLSetSessionOption) {
 #endif /* CURL_BUILD_MAC */
     bool break_on_auth = !conn->ssl_config.verifypeer ||
       ssl_cafile || ssl_cablob;
@@ -2065,7 +2065,7 @@ static CURLcode sectransp_connect_step1(struct Curl_easy *data,
 #if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7
   /* We want to enable 1/n-1 when using a CBC cipher unless the user
      specifically doesn't want us doing that: */
-  if(SSLSetSessionOption != NULL) {
+  if(SSLSetSessionOption) {
     SSLSetSessionOption(backend->ssl_ctx, kSSLSessionOptionSendOneByteRecord,
                         !SSL_SET_OPTION(enable_beast));
     SSLSetSessionOption(backend->ssl_ctx, kSSLSessionOptionFalseStart,
@@ -2109,7 +2109,7 @@ static CURLcode sectransp_connect_step1(struct Curl_easy *data,
       }
 
       result = Curl_ssl_addsessionid(data, conn, isproxy, ssl_sessionid,
-                                     ssl_sessionid_len, sockindex);
+                                     ssl_sessionid_len, sockindex, NULL);
       Curl_ssl_sessionid_unlock(data);
       if(result) {
         failf(data, "failed to store ssl session");
@@ -2521,7 +2521,7 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data,
   } while(0);
 
   Curl_safefree(realpubkey);
-  if(publicKeyBits != NULL)
+  if(publicKeyBits)
     CFRelease(publicKeyBits);
 
   return result;
@@ -2947,7 +2947,7 @@ collect_server_cert(struct Curl_easy *data,
      private API and doesn't work as expected. So we have to look for
      a different symbol to make sure this code is only executed under
      Lion or later. */
-  if(SecTrustEvaluateAsync != NULL) {
+  if(SecTrustEvaluateAsync) {
 #pragma unused(server_certs)
     err = SSLCopyPeerTrust(backend->ssl_ctx, &trust);
     /* For some reason, SSLCopyPeerTrust() can return noErr and yet return
@@ -3165,7 +3165,7 @@ static void sectransp_close(struct Curl_easy *data, struct connectdata *conn,
   if(backend->ssl_ctx) {
     (void)SSLClose(backend->ssl_ctx);
 #if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS
-    if(SSLCreateContext != NULL)
+    if(SSLCreateContext)
       CFRelease(backend->ssl_ctx);
 #if CURL_SUPPORT_MAC_10_8
     else
@@ -3329,7 +3329,7 @@ static CURLcode sectransp_sha256sum(const unsigned char *tmp, /* input */
 static bool sectransp_false_start(void)
 {
 #if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7
-  if(SSLSetSessionOption != NULL)
+  if(SSLSetSessionOption)
     return TRUE;
 #endif
   return FALSE;
diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c
index e5bbe1f..6007bbb 100644
--- a/lib/vtls/vtls.c
+++ b/lib/vtls/vtls.c
@@ -516,7 +516,8 @@ CURLcode Curl_ssl_addsessionid(struct Curl_easy *data,
                                const bool isProxy,
                                void *ssl_sessionid,
                                size_t idsize,
-                               int sockindex)
+                               int sockindex,
+                               bool *added)
 {
   size_t i;
   struct Curl_ssl_session *store;
@@ -536,6 +537,10 @@ CURLcode Curl_ssl_addsessionid(struct Curl_easy *data,
   const char *hostname = conn->host.name;
 #endif
   (void)sockindex;
+
+  if(added)
+    *added = FALSE;
+
   if(!data->state.session)
     return CURLE_OK;
 
@@ -609,6 +614,9 @@ CURLcode Curl_ssl_addsessionid(struct Curl_easy *data,
     return CURLE_OUT_OF_MEMORY;
   }
 
+  if(added)
+    *added = TRUE;
+
   DEBUGF(infof(data, "Added Session ID to cache for %s://%s:%d [%s]",
                store->scheme, store->name, store->remote_port,
                isProxy ? "PROXY" : "server"));
diff --git a/lib/vtls/vtls.h b/lib/vtls/vtls.h
index beaa83d..c7bbba0 100644
--- a/lib/vtls/vtls.h
+++ b/lib/vtls/vtls.h
@@ -261,7 +261,8 @@ CURLcode Curl_ssl_addsessionid(struct Curl_easy *data,
                                const bool isProxy,
                                void *ssl_sessionid,
                                size_t idsize,
-                               int sockindex);
+                               int sockindex,
+                               bool *added);
 /* Kill a single session ID entry in the cache
  * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock).
  * This will call engine-specific curlssl_session_free function, which must
diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c
index 16fbb89..8c5b915 100644
--- a/lib/vtls/wolfssl.c
+++ b/lib/vtls/wolfssl.c
@@ -202,6 +202,43 @@ static int do_file_type(const char *type)
   return -1;
 }
 
+#ifdef HAVE_LIBOQS
+struct group_name_map {
+  const word16 group;
+  const char   *name;
+};
+
+static const struct group_name_map gnm[] = {
+  { WOLFSSL_KYBER_LEVEL1, "KYBER_LEVEL1" },
+  { WOLFSSL_KYBER_LEVEL3, "KYBER_LEVEL3" },
+  { WOLFSSL_KYBER_LEVEL5, "KYBER_LEVEL5" },
+  { WOLFSSL_NTRU_HPS_LEVEL1, "NTRU_HPS_LEVEL1" },
+  { WOLFSSL_NTRU_HPS_LEVEL3, "NTRU_HPS_LEVEL3" },
+  { WOLFSSL_NTRU_HPS_LEVEL5, "NTRU_HPS_LEVEL5" },
+  { WOLFSSL_NTRU_HRSS_LEVEL3, "NTRU_HRSS_LEVEL3" },
+  { WOLFSSL_SABER_LEVEL1, "SABER_LEVEL1" },
+  { WOLFSSL_SABER_LEVEL3, "SABER_LEVEL3" },
+  { WOLFSSL_SABER_LEVEL5, "SABER_LEVEL5" },
+  { WOLFSSL_KYBER_90S_LEVEL1, "KYBER_90S_LEVEL1" },
+  { WOLFSSL_KYBER_90S_LEVEL3, "KYBER_90S_LEVEL3" },
+  { WOLFSSL_KYBER_90S_LEVEL5, "KYBER_90S_LEVEL5" },
+  { WOLFSSL_P256_NTRU_HPS_LEVEL1, "P256_NTRU_HPS_LEVEL1" },
+  { WOLFSSL_P384_NTRU_HPS_LEVEL3, "P384_NTRU_HPS_LEVEL3" },
+  { WOLFSSL_P521_NTRU_HPS_LEVEL5, "P521_NTRU_HPS_LEVEL5" },
+  { WOLFSSL_P384_NTRU_HRSS_LEVEL3, "P384_NTRU_HRSS_LEVEL3" },
+  { WOLFSSL_P256_SABER_LEVEL1, "P256_SABER_LEVEL1" },
+  { WOLFSSL_P384_SABER_LEVEL3, "P384_SABER_LEVEL3" },
+  { WOLFSSL_P521_SABER_LEVEL5, "P521_SABER_LEVEL5" },
+  { WOLFSSL_P256_KYBER_LEVEL1, "P256_KYBER_LEVEL1" },
+  { WOLFSSL_P384_KYBER_LEVEL3, "P384_KYBER_LEVEL3" },
+  { WOLFSSL_P521_KYBER_LEVEL5, "P521_KYBER_LEVEL5" },
+  { WOLFSSL_P256_KYBER_90S_LEVEL1, "P256_KYBER_90S_LEVEL1" },
+  { WOLFSSL_P384_KYBER_90S_LEVEL3, "P384_KYBER_90S_LEVEL3" },
+  { WOLFSSL_P521_KYBER_90S_LEVEL5, "P521_KYBER_90S_LEVEL5" },
+  { 0, NULL }
+};
+#endif
+
 /*
  * This function loads all the client/CA certificates and CRLs. Setup the TLS
  * layer and do all necessary magic.
@@ -210,11 +247,15 @@ static CURLcode
 wolfssl_connect_step1(struct Curl_easy *data, struct connectdata *conn,
                      int sockindex)
 {
-  char *ciphers;
+  char *ciphers, *curves;
   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
   struct ssl_backend_data *backend = connssl->backend;
   SSL_METHOD* req_method = NULL;
   curl_socket_t sockfd = conn->sock[sockindex];
+#ifdef HAVE_LIBOQS
+  word16 oqsAlg = 0;
+  size_t idx = 0;
+#endif
 #ifdef HAVE_SNI
   bool sni = FALSE;
 #define use_sni(x)  sni = (x)
@@ -327,6 +368,26 @@ wolfssl_connect_step1(struct Curl_easy *data, struct connectdata *conn,
     infof(data, "Cipher selection: %s", ciphers);
   }
 
+  curves = SSL_CONN_CONFIG(curves);
+  if(curves) {
+
+#ifdef HAVE_LIBOQS
+    for(idx = 0; gnm[idx].name != NULL; idx++) {
+      if(strncmp(curves, gnm[idx].name, strlen(gnm[idx].name)) == 0) {
+        oqsAlg = gnm[idx].group;
+        break;
+      }
+    }
+
+    if(oqsAlg == 0)
+#endif
+    {
+      if(!SSL_CTX_set1_curves_list(backend->ctx, curves)) {
+        failf(data, "failed setting curves list: '%s'", curves);
+        return CURLE_SSL_CIPHER;
+      }
+    }
+  }
 #ifndef NO_FILESYSTEM
   /* load trusted cacert */
   if(SSL_CONN_CONFIG(CAfile)) {
@@ -439,6 +500,14 @@ wolfssl_connect_step1(struct Curl_easy *data, struct connectdata *conn,
     return CURLE_OUT_OF_MEMORY;
   }
 
+#ifdef HAVE_LIBOQS
+  if(oqsAlg) {
+    if(wolfSSL_UseKeyShare(backend->handle, oqsAlg) != WOLFSSL_SUCCESS) {
+      failf(data, "unable to use oqs KEM");
+    }
+  }
+#endif
+
 #ifdef HAVE_ALPN
   if(conn->bits.tls_enable_alpn) {
     char protocols[128];
@@ -495,7 +564,7 @@ wolfssl_connect_step1(struct Curl_easy *data, struct connectdata *conn,
       /* we got a session id, use it! */
       if(!SSL_set_session(backend->handle, ssl_sessionid)) {
         Curl_ssl_delsessionid(data, ssl_sessionid);
-        infof(data, "Can't use session ID, going on without\n");
+        infof(data, "Can't use session ID, going on without");
       }
       else
         infof(data, "SSL re-using session ID");
@@ -749,7 +818,7 @@ wolfssl_connect_step3(struct Curl_easy *data, struct connectdata *conn,
 
       if(!incache) {
         result = Curl_ssl_addsessionid(data, conn, isproxy, our_ssl_sessionid,
-                                       0, sockindex);
+                                       0, sockindex, NULL);
         if(result) {
           Curl_ssl_sessionid_unlock(data);
           failf(data, "failed to store ssl session");
diff --git a/lib/x509asn1.c b/lib/x509asn1.c
index 1bdaead..0341543 100644
--- a/lib/x509asn1.c
+++ b/lib/x509asn1.c
@@ -608,7 +608,10 @@ static const char *ASN1tostr(struct Curl_asn1Element *elem, int type)
 
 /*
  * ASCII encode distinguished name at `dn' into the `buflen'-sized buffer at
- * `buf'.  Return the total string length, even if larger than `buflen'.
+ * `buf'.
+ *
+ * Returns the total string length, even if larger than `buflen' or -1 on
+ * error.
  */
 static ssize_t encodeDN(char *buf, size_t buflen, struct Curl_asn1Element *dn)
 {
@@ -692,7 +695,10 @@ static const char *DNtostr(struct Curl_asn1Element *dn)
   if(buflen >= 0) {
     buf = malloc(buflen + 1);
     if(buf) {
-      encodeDN(buf, buflen + 1, dn);
+      if(encodeDN(buf, buflen + 1, dn) == -1) {
+        free(buf);
+        return NULL;
+      }
       buf[buflen] = '\0';
     }
   }
@@ -855,26 +861,30 @@ static const char *dumpAlgo(struct Curl_asn1Element *param,
   return OID2str(oid.beg, oid.end, TRUE);
 }
 
-static void do_pubkey_field(struct Curl_easy *data, int certnum,
-                            const char *label, struct Curl_asn1Element *elem)
+/* return 0 on success, 1 on error */
+static int do_pubkey_field(struct Curl_easy *data, int certnum,
+                           const char *label, struct Curl_asn1Element *elem)
 {
   const char *output;
+  CURLcode result = CURLE_OK;
 
   /* Generate a certificate information record for the public key. */
 
   output = ASN1tostr(elem, 0);
   if(output) {
     if(data->set.ssl.certinfo)
-      Curl_ssl_push_certinfo(data, certnum, label, output);
-    if(!certnum)
+      result = Curl_ssl_push_certinfo(data, certnum, label, output);
+    if(!certnum && !result)
       infof(data, "   %s: %s", label, output);
     free((char *) output);
   }
+  return result ? 1 : 0;
 }
 
-static void do_pubkey(struct Curl_easy *data, int certnum,
-                      const char *algo, struct Curl_asn1Element *param,
-                      struct Curl_asn1Element *pubkey)
+/* return 0 on success, 1 on error */
+static int do_pubkey(struct Curl_easy *data, int certnum,
+                     const char *algo, struct Curl_asn1Element *param,
+                     struct Curl_asn1Element *pubkey)
 {
   struct Curl_asn1Element elem;
   struct Curl_asn1Element pk;
@@ -884,7 +894,7 @@ static void do_pubkey(struct Curl_easy *data, int certnum,
 
   /* Get the public key (single element). */
   if(!getASN1Element(&pk, pubkey->beg + 1, pubkey->end))
-    return;
+    return 1;
 
   if(strcasecompare(algo, "rsaEncryption")) {
     const char *q;
@@ -892,7 +902,7 @@ static void do_pubkey(struct Curl_easy *data, int certnum,
 
     p = getASN1Element(&elem, pk.beg, pk.end);
     if(!p)
-      return;
+      return 1;
 
     /* Compute key length. */
     for(q = elem.beg; !*q && q < elem.end; q++)
@@ -910,26 +920,35 @@ static void do_pubkey(struct Curl_easy *data, int certnum,
     if(data->set.ssl.certinfo) {
       q = curl_maprintf("%lu", len);
       if(q) {
-        Curl_ssl_push_certinfo(data, certnum, "RSA Public Key", q);
+        CURLcode result =
+          Curl_ssl_push_certinfo(data, certnum, "RSA Public Key", q);
         free((char *) q);
+        if(result)
+          return 1;
       }
     }
     /* Generate coefficients. */
-    do_pubkey_field(data, certnum, "rsa(n)", &elem);
+    if(do_pubkey_field(data, certnum, "rsa(n)", &elem))
+      return 1;
     if(!getASN1Element(&elem, p, pk.end))
-      return;
-    do_pubkey_field(data, certnum, "rsa(e)", &elem);
+      return 1;
+    if(do_pubkey_field(data, certnum, "rsa(e)", &elem))
+      return 1;
   }
   else if(strcasecompare(algo, "dsa")) {
     p = getASN1Element(&elem, param->beg, param->end);
     if(p) {
-      do_pubkey_field(data, certnum, "dsa(p)", &elem);
+      if(do_pubkey_field(data, certnum, "dsa(p)", &elem))
+        return 1;
       p = getASN1Element(&elem, p, param->end);
       if(p) {
-        do_pubkey_field(data, certnum, "dsa(q)", &elem);
+        if(do_pubkey_field(data, certnum, "dsa(q)", &elem))
+          return 1;
         if(getASN1Element(&elem, p, param->end)) {
-          do_pubkey_field(data, certnum, "dsa(g)", &elem);
-          do_pubkey_field(data, certnum, "dsa(pub_key)", &pk);
+          if(do_pubkey_field(data, certnum, "dsa(g)", &elem))
+            return 1;
+          if(do_pubkey_field(data, certnum, "dsa(pub_key)", &pk))
+            return 1;
         }
       }
     }
@@ -937,13 +956,17 @@ static void do_pubkey(struct Curl_easy *data, int certnum,
   else if(strcasecompare(algo, "dhpublicnumber")) {
     p = getASN1Element(&elem, param->beg, param->end);
     if(p) {
-      do_pubkey_field(data, certnum, "dh(p)", &elem);
+      if(do_pubkey_field(data, certnum, "dh(p)", &elem))
+        return 1;
       if(getASN1Element(&elem, param->beg, param->end)) {
-        do_pubkey_field(data, certnum, "dh(g)", &elem);
-        do_pubkey_field(data, certnum, "dh(pub_key)", &pk);
+        if(do_pubkey_field(data, certnum, "dh(g)", &elem))
+          return 1;
+        if(do_pubkey_field(data, certnum, "dh(pub_key)", &pk))
+          return 1;
       }
     }
   }
+  return 0;
 }
 
 CURLcode Curl_extract_certinfo(struct Curl_easy *data,
@@ -957,7 +980,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
   char *cp1;
   size_t cl1;
   char *cp2;
-  CURLcode result;
+  CURLcode result = CURLE_OK;
   unsigned long version;
   size_t i;
   size_t j;
@@ -976,8 +999,11 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
   ccp = DNtostr(&cert.subject);
   if(!ccp)
     return CURLE_OUT_OF_MEMORY;
-  if(data->set.ssl.certinfo)
-    Curl_ssl_push_certinfo(data, certnum, "Subject", ccp);
+  if(data->set.ssl.certinfo) {
+    result = Curl_ssl_push_certinfo(data, certnum, "Subject", ccp);
+    if(result)
+      return result;
+  }
   if(!certnum)
     infof(data, "%2d Subject: %s", certnum, ccp);
   free((char *) ccp);
@@ -986,11 +1012,14 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
   ccp = DNtostr(&cert.issuer);
   if(!ccp)
     return CURLE_OUT_OF_MEMORY;
-  if(data->set.ssl.certinfo)
-    Curl_ssl_push_certinfo(data, certnum, "Issuer", ccp);
+  if(data->set.ssl.certinfo) {
+    result = Curl_ssl_push_certinfo(data, certnum, "Issuer", ccp);
+  }
   if(!certnum)
     infof(data, "   Issuer: %s", ccp);
   free((char *) ccp);
+  if(result)
+    return result;
 
   /* Version (always fits in less than 32 bits). */
   version = 0;
@@ -1000,8 +1029,10 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
     ccp = curl_maprintf("%lx", version);
     if(!ccp)
       return CURLE_OUT_OF_MEMORY;
-    Curl_ssl_push_certinfo(data, certnum, "Version", ccp);
+    result = Curl_ssl_push_certinfo(data, certnum, "Version", ccp);
     free((char *) ccp);
+    if(result)
+      return result;
   }
   if(!certnum)
     infof(data, "   Version: %lu (0x%lx)", version + 1, version);
@@ -1011,10 +1042,12 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
   if(!ccp)
     return CURLE_OUT_OF_MEMORY;
   if(data->set.ssl.certinfo)
-    Curl_ssl_push_certinfo(data, certnum, "Serial Number", ccp);
+    result = Curl_ssl_push_certinfo(data, certnum, "Serial Number", ccp);
   if(!certnum)
     infof(data, "   Serial Number: %s", ccp);
   free((char *) ccp);
+  if(result)
+    return result;
 
   /* Signature algorithm .*/
   ccp = dumpAlgo(&param, cert.signatureAlgorithm.beg,
@@ -1022,30 +1055,36 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
   if(!ccp)
     return CURLE_OUT_OF_MEMORY;
   if(data->set.ssl.certinfo)
-    Curl_ssl_push_certinfo(data, certnum, "Signature Algorithm", ccp);
+    result = Curl_ssl_push_certinfo(data, certnum, "Signature Algorithm", ccp);
   if(!certnum)
     infof(data, "   Signature Algorithm: %s", ccp);
   free((char *) ccp);
+  if(result)
+    return result;
 
   /* Start Date. */
   ccp = ASN1tostr(&cert.notBefore, 0);
   if(!ccp)
     return CURLE_OUT_OF_MEMORY;
   if(data->set.ssl.certinfo)
-    Curl_ssl_push_certinfo(data, certnum, "Start Date", ccp);
+    result = Curl_ssl_push_certinfo(data, certnum, "Start Date", ccp);
   if(!certnum)
     infof(data, "   Start Date: %s", ccp);
   free((char *) ccp);
+  if(result)
+    return result;
 
   /* Expire Date. */
   ccp = ASN1tostr(&cert.notAfter, 0);
   if(!ccp)
     return CURLE_OUT_OF_MEMORY;
   if(data->set.ssl.certinfo)
-    Curl_ssl_push_certinfo(data, certnum, "Expire Date", ccp);
+    result = Curl_ssl_push_certinfo(data, certnum, "Expire Date", ccp);
   if(!certnum)
     infof(data, "   Expire Date: %s", ccp);
   free((char *) ccp);
+  if(result)
+    return result;
 
   /* Public Key Algorithm. */
   ccp = dumpAlgo(&param, cert.subjectPublicKeyAlgorithm.beg,
@@ -1053,21 +1092,31 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
   if(!ccp)
     return CURLE_OUT_OF_MEMORY;
   if(data->set.ssl.certinfo)
-    Curl_ssl_push_certinfo(data, certnum, "Public Key Algorithm", ccp);
-  if(!certnum)
-    infof(data, "   Public Key Algorithm: %s", ccp);
-  do_pubkey(data, certnum, ccp, &param, &cert.subjectPublicKey);
+    result = Curl_ssl_push_certinfo(data, certnum, "Public Key Algorithm",
+                                    ccp);
+  if(!result) {
+    int ret;
+    if(!certnum)
+      infof(data, "   Public Key Algorithm: %s", ccp);
+    ret = do_pubkey(data, certnum, ccp, &param, &cert.subjectPublicKey);
+    if(ret)
+      result = CURLE_OUT_OF_MEMORY; /* the most likely error */
+  }
   free((char *) ccp);
+  if(result)
+    return result;
 
   /* Signature. */
   ccp = ASN1tostr(&cert.signature, 0);
   if(!ccp)
     return CURLE_OUT_OF_MEMORY;
   if(data->set.ssl.certinfo)
-    Curl_ssl_push_certinfo(data, certnum, "Signature", ccp);
+    result = Curl_ssl_push_certinfo(data, certnum, "Signature", ccp);
   if(!certnum)
     infof(data, "   Signature: %s", ccp);
   free((char *) ccp);
+  if(result)
+    return result;
 
   /* Generate PEM certificate. */
   result = Curl_base64_encode(data, cert.certificate.beg,
@@ -1097,11 +1146,11 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
   cp2[i] = '\0';
   free(cp1);
   if(data->set.ssl.certinfo)
-    Curl_ssl_push_certinfo(data, certnum, "Cert", cp2);
+    result = Curl_ssl_push_certinfo(data, certnum, "Cert", cp2);
   if(!certnum)
     infof(data, "%s", cp2);
   free(cp2);
-  return CURLE_OK;
+  return result;
 }
 
 #endif /* USE_GSKIT or USE_NSS or USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL
-- 
cgit v0.12