diff --git a/LICENSE b/LICENSE
index f65f7a20f24a..99c8dfff3e10 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
 Licensing Information User Manual
 
-MySQL 8.4.5 Community
+MySQL 8.4.6 Community
      __________________________________________________________________
 
 Introduction
@@ -8,18 +8,18 @@ Introduction
    This License Information User Manual contains Oracle's product license
    and other licensing information, including licensing information for
    third-party software which may be included in this distribution of
-   MySQL 8.4.5 Community.
+   MySQL 8.4.6 Community.
 
-   Last updated: March 2025
+   Last updated: June 2025
 
 Licensing Information
 
-   This release of MySQL 8.4.5 Community is brought to you by the MySQL
+   This release of MySQL 8.4.6 Community is brought to you by the MySQL
    team at Oracle. This software is released under version 2 of the GNU
    General Public License (GPLv2), as set forth below, with the following
    additional permissions:
 
-   This distribution of MySQL 8.4.5 Community is designed to work with
+   This distribution of MySQL 8.4.6 Community is designed to work with
    certain software (including but not limited to OpenSSL) that is
    licensed under separate terms, as designated in a particular file or
    component or in the license documentation. Without limiting your rights
diff --git a/client/include/client_priv.h b/client/include/client_priv.h
index 04b598c4ac5d..65366955777e 100644
--- a/client/include/client_priv.h
+++ b/client/include/client_priv.h
@@ -188,6 +188,7 @@ enum options_client {
   OPT_INIT_COMMAND_ADD,
   OPT_OUTPUT_AS_VERSION,
   OPT_AUTHENTICATION_WEBAUTHN_CLIENT_PRESERVE_PRIVACY,
+  OPT_MYSQL_COMMANDS,
   /* Add new option above this */
   OPT_MAX_CLIENT_OPTION
 };
diff --git a/client/mysql.cc b/client/mysql.cc
index 24ae2f79b3d8..73e1b2129bac 100644
--- a/client/mysql.cc
+++ b/client/mysql.cc
@@ -192,6 +192,7 @@ static char *opt_mysql_unix_port = nullptr;
 static char *opt_bind_addr = nullptr;
 static int connect_flag = CLIENT_INTERACTIVE;
 static bool opt_binary_mode = false;
+static bool opt_commands = true;
 static bool opt_connect_expired_password = false;
 static char *current_host;
 static char *dns_srv_name;
@@ -1264,7 +1265,7 @@ inline int get_command_index(char cmd_char) {
 
 static int delimiter_index = -1;
 static int charset_index = -1;
-static bool real_binary_mode = false;
+static bool disable_commands = false;
 
 #ifdef _WIN32
 BOOL windows_ctrl_handler(DWORD fdwCtrlType) {
@@ -1796,6 +1797,9 @@ static struct my_option my_long_options[] = {
     {"column-type-info", OPT_COLUMN_TYPES, "Display column type information.",
      &column_types_flag, &column_types_flag, nullptr, GET_BOOL, NO_ARG, 0, 0, 0,
      nullptr, 0, nullptr},
+    {"commands", OPT_MYSQL_COMMANDS,
+     "Enable or disable processing of local mysql commands.", &opt_commands,
+     &opt_commands, nullptr, GET_BOOL, NO_ARG, 1, 0, 0, nullptr, 0, nullptr},
     {"comments", 'c',
      "Preserve comments. Send comments to the server."
      " The default is --comments (keep comments), disable with "
@@ -2321,7 +2325,7 @@ static int read_and_execute(bool interactive) {
   size_t line_length = 0;
   status.exit_status = 1;
 
-  real_binary_mode = !interactive && opt_binary_mode;
+  disable_commands = !interactive && (opt_binary_mode || !opt_commands);
   for (;;) {
     /* Reset as SIGINT has already got handled. */
     sigint_received = false;
@@ -2332,7 +2336,7 @@ static int read_and_execute(bool interactive) {
         In that case, we need to double check that we have a valid
         line before actually setting line_length to read_length.
         */
-      line = batch_readline(status.line_buff, real_binary_mode);
+      line = batch_readline(status.line_buff, opt_binary_mode);
       if (line) {
         line_length = status.line_buff->read_length;
 
@@ -2340,7 +2344,7 @@ static int read_and_execute(bool interactive) {
           ASCII 0x00 is not allowed appearing in queries if it is not in
           binary mode.
         */
-        if (!real_binary_mode && strlen(line) != line_length) {
+        if (!opt_binary_mode && strlen(line) != line_length) {
           status.exit_status = 1;
           String msg;
           msg.append(
@@ -2523,10 +2527,10 @@ static int read_and_execute(bool interactive) {
 
   /*
     If the function is called by 'source' command, it will return to
-    interactive mode, so real_binary_mode should be false. Otherwise, it will
-    exit the program, it is safe to set real_binary_mode to false.
+    interactive mode, so disable_commands should be false. Otherwise, it will
+    exit the program, it is safe to set disable_commands to false.
   */
-  real_binary_mode = false;
+  disable_commands = false;
   return status.exit_status;
 }
 
@@ -2553,10 +2557,10 @@ static COMMANDS *find_command(char cmd_char) {
   int index = -1;
 
   /*
-    In binary-mode, we disallow all mysql commands except '\C'
+    If specified, we disallow all mysql commands except '\C'
     and DELIMITER.
   */
-  if (real_binary_mode) {
+  if (disable_commands) {
     if (cmd_char == 'C') index = charset_index;
   } else
     index = get_command_index(cmd_char);
@@ -2657,7 +2661,7 @@ static COMMANDS *find_command(char *name) {
     this is not a delimiter command, let add_line() take care of
     parsing the row and calling find_command().
   */
-  if ((!real_binary_mode && strstr(name, "\\g")) ||
+  if ((!disable_commands && strstr(name, "\\g")) ||
       (strstr(name, delimiter) &&
        !is_delimiter_command(name, DELIMITER_NAME_LEN)))
     return (COMMANDS *)nullptr;
@@ -2671,7 +2675,7 @@ static COMMANDS *find_command(char *name) {
     len = (uint)strlen(name);
 
   int index = -1;
-  if (real_binary_mode) {
+  if (disable_commands) {
     if (is_delimiter_command(name, len)) index = delimiter_index;
   } else {
     /*
diff --git a/cmake/curl.cmake b/cmake/curl.cmake
index 164847efd4e1..8879eaeb9774 100644
--- a/cmake/curl.cmake
+++ b/cmake/curl.cmake
@@ -127,7 +127,7 @@ FUNCTION(FIND_SYSTEM_CURL ARG_CURL_INCLUDE_DIR)
   ENDIF()
 ENDFUNCTION(FIND_SYSTEM_CURL)
 
-SET(CURL_VERSION_DIR "curl-8.12.1")
+SET(CURL_VERSION_DIR "curl-8.14.1")
 FUNCTION(MYSQL_USE_BUNDLED_CURL CURL_INCLUDE_DIR)
   SET(WITH_CURL "bundled" CACHE STRING "Bundled curl library")
   ADD_SUBDIRECTORY(extra/curl)
diff --git a/extra/curl/CMakeLists.txt b/extra/curl/CMakeLists.txt
index 5a26a3dcbd37..291fec61a419 100644
--- a/extra/curl/CMakeLists.txt
+++ b/extra/curl/CMakeLists.txt
@@ -72,7 +72,7 @@ SET(CURL_USE_LIBPSL OFF)
 SET(CURL_USE_LIBPSL OFF CACHE INTERNAL "" FORCE)
 
 SET(USE_NGHTTP2 OFF)
-SET(USE_NGHTTP2 OFF OFF CACHE INTERNAL "" FORCE)
+SET(USE_NGHTTP2 OFF CACHE INTERNAL "" FORCE)
 
 SET(BUILD_EXAMPLES OFF)
 SET(BUILD_EXAMPLES OFF CACHE INTERNAL "" FORCE)
diff --git a/extra/curl/curl-8.12.1/RELEASE-NOTES b/extra/curl/curl-8.12.1/RELEASE-NOTES
deleted file mode 100644
index 771fb6600041..000000000000
--- a/extra/curl/curl-8.12.1/RELEASE-NOTES
+++ /dev/null
@@ -1,172 +0,0 @@
-curl and libcurl 8.12.1
-
- Public curl releases:         265
- Command line options:         267
- curl_easy_setopt() options:   306
- Public functions in libcurl:  96
- Contributors:                 3344
-
-This release includes the following changes:
-
-
-This release includes the following bugfixes:
-
- o all: remove FIXME and TODO comments [55]
- o asyn-thread: fix build with `CURL_DISABLE_SOCKETPAIR` [47]
- o asyn-thread: fix HTTPS RR crash [10]
- o asyn-thread: fix the returned bitmask from Curl_resolver_getsock [18]
- o asyn-thread: survive a c-ares channel set to NULL [52]
- o build: add tool_hugehelp.c into IBMi build [40]
- o checksrc.pl: warn on FIXME/TODO comments
- o cmake/Find: set `<Modulename>_FOUND` for compatibility when found via `pkg-config` [22]
- o cmake: add integration tests, run them in CI [21]
- o cmake: always reference OpenSSL and ZLIB via imported targets [24]
- o cmake: avoid unnecessary `-L` for implicit link dirs [11]
- o cmake: drop `LDAP_DEPRECATED=1` macro, to sync with autotools [23]
- o cmake: fix `HAVE_GETHOSTBYNAME_R_*` detections with `CURL_WERROR=ON` [57]
- o cmake: fix to detect `HAVE_OPENSSL_SRP` in MSVC UWP builds [62]
- o cmake: fix/add missing feature detections for Windows/MS-DOS [58]
- o cmake: initialize variables where missing [27]
- o cmake: lib order fixes for picky linkers (e.g. binutils `ld`) [26]
- o cmake: normalize before matching paths with syspaths [30]
- o cmake: respect `GNUTLS_CFLAGS` when detected via `pkg-config` [38]
- o cmake: respect `GNUTLS_LIBRARY_DIRS` in `libcurl.pc` and `curl-config` [39]
- o cmake: save a line with `CMAKE_C_IMPLICIT_LINK_DIRECTORIES` exclusion [32]
- o cmake: tidy up string append and list prepend syntax [28]
- o configure/cmake: check for realpath [19]
- o configure/cmake: set asyn-rr a feature only if httpsrr is enabled [42]
- o content_encoding: #error on too old zlib [2]
- o curl_global_sslset.md: Add SSL backend names [50]
- o CURLOPT_SSH_KNOWNHOSTS.md: strongly recommend using this [41]
- o CURLSHOPT_SHARE.md: adjust for the new SSL session cache [6]
- o docs: better explain multi-part byte range behavior [4]
- o docs: use valid example domain names [54]
- o generate.bat: remove curl_get_line.c from the curlx file list [20]
- o header.md: mention `Authorization:` and `Cookie:` special treatment [43]
- o imap: TLS upgrade fix [14]
- o INTERNALS: fix c-ares, as we actually support 1.6.0 or later [37]
- o ldap: drop support for legacy Novell LDAP SDK [25]
- o lib: include necessary headers for `inet_ntop`/`inet_pton` [8]
- o lib: silence LibreSSL collision warning on non-MSVC Windows [51]
- o libssh2: comparison is always true because rc <= -1 [56]
- o libssh2: raise lowest supported version to 1.2.8 [3]
- o libssh: drop support for libssh older than 0.9.0 [33]
- o libssh: silence `-Wconversion` with a cast (Windows 32-bit) [7]
- o netrc: return code cleanup, fix missing file error [45]
- o openssl-quic: ignore ciphers for h3 [1]
- o openssl: fix out of scope variables in goto [12]
- o pop3: TLS upgrade fix [15]
- o runtests: fix the disabling of the memory tracking [29]
- o runtests: quote commands to support paths with spaces [35]
- o scache: add magic checks [31]
- o smb: silence `-Warray-bounds` with gcc 13+ [9]
- o smtp: TLS upgrade fix [16]
- o SPONSORS.md: clarify that we don't promise goods or services [5]
- o test1516: avoid failure due to spaces in path [36]
- o test2080: simplify, avoid the null byte
- o tests: fix test 558, 1330 for MSVC, allow TrackMemory with MSVC in cmake [53]
- o tidy-up: make per-file `ARRAYSIZE` macros global as `CURL_ARRAYSIZE` [48]
- o tool_cfgable: sort struct fields by size, use bitfields for booleans [17]
- o tool_getparam: add "TLS required" flag for each such option [44]
- o tool_progress: fix percent output of large parallel transfers [61]
- o tool_ssls: switch to tool-specific get_line function [34]
- o verbose.md: mention how carriage-return might occur in headers [49]
- o vquic: make the "disable GSO" use infof, not failf [65]
- o vtls: fix multissl-init [60]
- o vtsl: eliminate 'data->state.ssl_scache' [59]
- o wakeup_write: make sure the eventfd write sends eight bytes [46]
- o wolfssl: silence compiler warning (MSVC 2019), simplify existing [13]
-
-This release includes the following known bugs:
-
- See https://curl.se/docs/knownbugs.html
-
-For all changes ever done in curl:
-
- See https://curl.se/changes.html
-
-Planned upcoming removals include:
-
- o Support for the msh3 HTTP/3 backend
- o The winbuild build system
- o TLS libraries not supporting TLS 1.3
-
- See https://curl.se/dev/deprecate.html
-
-This release would not have looked like this without help, code, reports and
-advice from friends like these:
-
-  Aaron Deadman, Andrei Korshikov, Andrew Kirillov, arlt on github,
-  Christian Schmitz, CueXXIII on Github, Dan Fandrich, Daniel Stenberg,
-  deliciouslytyped on github, Fay Stegerman, Jan Engelhardt,
-  jethrogb on github, Kai Pastor, Marcel Raad, Michael Kaufmann,
-  mschroeder-fzj on github, nono303 on github, Philip Heiduck, qhill,
-  Ralf A. Timmermann, Ray Satiro, renovate[bot], Stefan Eissing, Terence Eden,
-  thisisgk on github, Viktor Szakats, zzq1015 on github
-  (27 contributors)
-
-References to bug reports and discussions on issues:
-
- [1] = https://curl.se/bug/?i=16232
- [2] = https://curl.se/bug/?i=16202
- [3] = https://curl.se/bug/?i=16199
- [4] = https://curl.se/bug/?i=16139
- [5] = https://curl.se/bug/?i=16196
- [6] = https://curl.se/bug/?i=16245
- [7] = https://curl.se/bug/?i=16194
- [8] = https://curl.se/bug/?i=16184
- [9] = https://curl.se/bug/?i=16187
- [10] = https://curl.se/bug/?i=16169
- [11] = https://curl.se/bug/?i=16233
- [12] = https://curl.se/bug/?i=16246
- [13] = https://curl.se/bug/?i=16230
- [14] = https://curl.se/bug/?i=16213
- [15] = https://curl.se/bug/?i=16208
- [16] = https://curl.se/bug/?i=16189
- [17] = https://curl.se/bug/?i=16211
- [18] = https://curl.se/bug/?i=16227
- [19] = https://curl.se/bug/?i=16209
- [20] = https://curl.se/bug/?i=16248
- [21] = https://curl.se/bug/?i=16126
- [22] = https://curl.se/bug/?i=16153
- [23] = https://curl.se/bug/?i=16146
- [24] = https://curl.se/bug/?i=16207
- [25] = https://curl.se/bug/?i=16176
- [26] = https://curl.se/bug/?i=16182
- [27] = https://curl.se/bug/?i=16198
- [28] = https://curl.se/bug/?i=16144
- [29] = https://curl.se/bug/?i=16226
- [30] = https://curl.se/bug/?i=16191
- [31] = https://curl.se/bug/?i=16240
- [32] = https://curl.se/bug/?i=16243
- [33] = https://curl.se/bug/?i=16200
- [34] = https://curl.se/bug/?i=16201
- [35] = https://curl.se/bug/?i=16220
- [36] = https://curl.se/bug/?i=16223
- [37] = https://curl.se/bug/?i=16221
- [38] = https://curl.se/bug/?i=16242
- [39] = https://curl.se/bug/?i=16190
- [40] = https://curl.se/bug/?i=16214
- [41] = https://curl.se/bug/?i=16204
- [42] = https://curl.se/bug/?i=16183
- [43] = https://curl.se/bug/?i=16160
- [44] = https://curl.se/bug/?i=16159
- [45] = https://curl.se/bug/?i=16163
- [46] = https://curl.se/bug/?i=16237
- [47] = https://curl.se/bug/?i=16179
- [48] = https://curl.se/bug/?i=16111
- [49] = https://curl.se/bug/?i=16285
- [50] = https://curl.se/bug/?i=16256
- [51] = https://curl.se/bug/?i=16273
- [52] = https://curl.se/bug/?i=16216
- [53] = https://curl.se/bug/?i=16289
- [54] = https://curl.se/bug/?i=16269
- [55] = https://curl.se/bug/?i=16283
- [56] = https://curl.se/bug/?i=16268
- [57] = https://curl.se/bug/?i=16282
- [58] = https://curl.se/bug/?i=16278
- [59] = https://curl.se/bug/?i=16261
- [60] = https://curl.se/bug/?i=16253
- [61] = https://curl.se/bug/?i=16284
- [62] = https://curl.se/bug/?i=16293
- [65] = https://curl.se/bug/?i=16294
diff --git a/extra/curl/curl-8.12.1/lib/asyn-ares.c b/extra/curl/curl-8.12.1/lib/asyn-ares.c
deleted file mode 100644
index 640c8a9b6534..000000000000
--- a/extra/curl/curl-8.12.1/lib/asyn-ares.c
+++ /dev/null
@@ -1,996 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifdef USE_ARES
-
-/***********************************************************************
- * Only for ares-enabled builds
- * And only for functions that fulfill the asynch resolver backend API
- * as defined in asyn.h, nothing else belongs in this file!
- **********************************************************************/
-
-#include <limits.h>
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
-#ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-#ifdef __VMS
-#include <in.h>
-#include <inet.h>
-#endif
-
-#include "urldata.h"
-#include "sendf.h"
-#include "hostip.h"
-#include "hash.h"
-#include "share.h"
-#include "url.h"
-#include "multiif.h"
-#include "inet_pton.h"
-#include "connect.h"
-#include "select.h"
-#include "progress.h"
-#include "timediff.h"
-#include "httpsrr.h"
-#include "strdup.h"
-
-#include <ares.h>
-#include <ares_version.h> /* really old c-ares did not include this by
-                             itself */
-
-/*
- * Curl_ares_getsock() is called when the outside world (using
- * curl_multi_fdset()) wants to get our fd_set setup and we are talking with
- * ares. The caller must make sure that this function is only called when we
- * have a working ares channel.
- *
- * Returns: sockets-in-use-bitmap
- */
-
-int Curl_ares_getsock(struct Curl_easy *data,
-                      ares_channel channel,
-                      curl_socket_t *socks)
-{
-  struct timeval maxtime = { CURL_TIMEOUT_RESOLVE, 0 };
-  struct timeval timebuf;
-  int max = ares_getsock(channel,
-                         (ares_socket_t *)socks, MAX_SOCKSPEREASYHANDLE);
-  struct timeval *timeout = ares_timeout(channel, &maxtime, &timebuf);
-  timediff_t milli = curlx_tvtoms(timeout);
-  Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
-  return max;
-}
-
-/*
- * Curl_ares_perform()
- *
- * 1) Ask ares what sockets it currently plays with, then
- * 2) wait for the timeout period to check for action on ares' sockets.
- * 3) tell ares to act on all the sockets marked as "with action"
- *
- * return number of sockets it worked on, or -1 on error
- */
-
-int Curl_ares_perform(ares_channel channel,
-                      timediff_t timeout_ms)
-{
-  int nfds;
-  int bitmask;
-  ares_socket_t socks[ARES_GETSOCK_MAXNUM];
-  struct pollfd pfd[ARES_GETSOCK_MAXNUM];
-  int i;
-  int num = 0;
-
-  if(!channel)
-    return 0;
-
-  bitmask = ares_getsock(channel, socks, ARES_GETSOCK_MAXNUM);
-
-  for(i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
-    pfd[i].events = 0;
-    pfd[i].revents = 0;
-    if(ARES_GETSOCK_READABLE(bitmask, i)) {
-      pfd[i].fd = socks[i];
-      pfd[i].events |= POLLRDNORM|POLLIN;
-    }
-    if(ARES_GETSOCK_WRITABLE(bitmask, i)) {
-      pfd[i].fd = socks[i];
-      pfd[i].events |= POLLWRNORM|POLLOUT;
-    }
-    if(pfd[i].events)
-      num++;
-    else
-      break;
-  }
-
-  if(num) {
-    nfds = Curl_poll(pfd, (unsigned int)num, timeout_ms);
-    if(nfds < 0)
-      return -1;
-  }
-  else
-    nfds = 0;
-
-  if(!nfds)
-    /* Call ares_process() unconditionally here, even if we simply timed out
-       above, as otherwise the ares name resolve will not timeout! */
-    ares_process_fd(channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
-  else {
-    /* move through the descriptors and ask for processing on them */
-    for(i = 0; i < num; i++)
-      ares_process_fd(channel,
-                      (pfd[i].revents & (POLLRDNORM|POLLIN)) ?
-                      pfd[i].fd : ARES_SOCKET_BAD,
-                      (pfd[i].revents & (POLLWRNORM|POLLOUT)) ?
-                      pfd[i].fd : ARES_SOCKET_BAD);
-  }
-  return nfds;
-}
-
-#ifdef CURLRES_ARES
-
-#if ARES_VERSION >= 0x010500
-/* c-ares 1.5.0 or later, the callback proto is modified */
-#define HAVE_CARES_CALLBACK_TIMEOUTS 1
-#endif
-
-#if ARES_VERSION >= 0x010601
-/* IPv6 supported since 1.6.1 */
-#define HAVE_CARES_IPV6 1
-#endif
-
-#if ARES_VERSION >= 0x010704
-#define HAVE_CARES_SERVERS_CSV 1
-#define HAVE_CARES_LOCAL_DEV 1
-#define HAVE_CARES_SET_LOCAL 1
-#endif
-
-#if ARES_VERSION >= 0x010b00
-#define HAVE_CARES_PORTS_CSV 1
-#endif
-
-#if ARES_VERSION >= 0x011000
-/* 1.16.0 or later has ares_getaddrinfo */
-#define HAVE_CARES_GETADDRINFO 1
-#endif
-
-#if ARES_VERSION >= 0x011c00
-/* 1.28.0 and later have ares_query_dnsrec */
-#define HAVE_ARES_QUERY_DNSREC 1
-#ifdef USE_HTTPSRR
-#define USE_HTTPSRR_ARES 1
-#endif
-#endif
-
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
-
-/* How long we are willing to wait for additional parallel responses after
-   obtaining a "definitive" one. For old c-ares without getaddrinfo.
-
-   This is intended to equal the c-ares default timeout. cURL always uses that
-   default value. Unfortunately, c-ares does not expose its default timeout in
-   its API, but it is officially documented as 5 seconds.
-
-   See query_completed_cb() for an explanation of how this is used.
- */
-#define HAPPY_EYEBALLS_DNS_TIMEOUT 5000
-
-#define CARES_TIMEOUT_PER_ATTEMPT 2000
-
-static int ares_ver = 0;
-
-/*
- * Curl_resolver_global_init() - the generic low-level asynchronous name
- * resolve API. Called from curl_global_init() to initialize global resolver
- * environment. Initializes ares library.
- */
-int Curl_resolver_global_init(void)
-{
-#ifdef CARES_HAVE_ARES_LIBRARY_INIT
-  if(ares_library_init(ARES_LIB_INIT_ALL)) {
-    return CURLE_FAILED_INIT;
-  }
-#endif
-  ares_version(&ares_ver);
-  return CURLE_OK;
-}
-
-/*
- * Curl_resolver_global_cleanup()
- *
- * Called from curl_global_cleanup() to destroy global resolver environment.
- * Deinitializes ares library.
- */
-void Curl_resolver_global_cleanup(void)
-{
-#ifdef CARES_HAVE_ARES_LIBRARY_CLEANUP
-  ares_library_cleanup();
-#endif
-}
-
-
-static void sock_state_cb(void *data, ares_socket_t socket_fd,
-                          int readable, int writable)
-{
-  struct Curl_easy *easy = data;
-  if(!readable && !writable) {
-    DEBUGASSERT(easy);
-    Curl_multi_closed(easy, socket_fd);
-  }
-}
-
-/*
- * Curl_resolver_init()
- *
- * Called from curl_easy_init() -> Curl_open() to initialize resolver
- * URL-state specific environment ('resolver' member of the UrlState
- * structure). Fills the passed pointer by the initialized ares_channel.
- */
-CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver)
-{
-  int status;
-  struct ares_options options;
-  int optmask = ARES_OPT_SOCK_STATE_CB;
-  options.sock_state_cb = sock_state_cb;
-  options.sock_state_cb_data = easy;
-
-  /*
-     if c ares < 1.20.0: curl set timeout to CARES_TIMEOUT_PER_ATTEMPT (2s)
-
-     if c-ares >= 1.20.0 it already has the timeout to 2s, curl does not need
-     to set the timeout value;
-
-     if c-ares >= 1.24.0, user can set the timeout via /etc/resolv.conf to
-     overwrite c-ares' timeout.
-  */
-  DEBUGASSERT(ares_ver);
-  if(ares_ver < 0x011400) {
-    options.timeout = CARES_TIMEOUT_PER_ATTEMPT;
-    optmask |= ARES_OPT_TIMEOUTMS;
-  }
-
-  status = ares_init_options((ares_channel*)resolver, &options, optmask);
-  if(status != ARES_SUCCESS) {
-    if(status == ARES_ENOMEM)
-      return CURLE_OUT_OF_MEMORY;
-    else
-      return CURLE_FAILED_INIT;
-  }
-  return CURLE_OK;
-  /* make sure that all other returns from this function should destroy the
-     ares channel before returning error! */
-}
-
-/*
- * Curl_resolver_cleanup()
- *
- * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
- * URL-state specific environment ('resolver' member of the UrlState
- * structure). Destroys the ares channel.
- */
-void Curl_resolver_cleanup(void *resolver)
-{
-  ares_destroy((ares_channel)resolver);
-}
-
-/*
- * Curl_resolver_duphandle()
- *
- * Called from curl_easy_duphandle() to duplicate resolver URL-state specific
- * environment ('resolver' member of the UrlState structure). Duplicates the
- * 'from' ares channel and passes the resulting channel to the 'to' pointer.
- */
-CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to, void *from)
-{
-  (void)from;
-  /*
-   * it would be better to call ares_dup instead, but right now
-   * it is not possible to set 'sock_state_cb_data' outside of
-   * ares_init_options
-   */
-  return Curl_resolver_init(easy, to);
-}
-
-static void destroy_async_data(struct Curl_async *async);
-
-/*
- * Cancel all possibly still on-going resolves for this connection.
- */
-void Curl_resolver_cancel(struct Curl_easy *data)
-{
-  DEBUGASSERT(data);
-  if(data->state.async.resolver)
-    ares_cancel((ares_channel)data->state.async.resolver);
-  destroy_async_data(&data->state.async);
-}
-
-/*
- * We are equivalent to Curl_resolver_cancel() for the c-ares resolver. We
- * never block.
- */
-void Curl_resolver_kill(struct Curl_easy *data)
-{
-  /* We do not need to check the resolver state because we can be called safely
-     at any time and we always do the same thing. */
-  Curl_resolver_cancel(data);
-}
-
-/*
- * destroy_async_data() cleans up async resolver data.
- */
-static void destroy_async_data(struct Curl_async *async)
-{
-  if(async->tdata) {
-    struct thread_data *res = async->tdata;
-    if(res) {
-      if(res->temp_ai) {
-        Curl_freeaddrinfo(res->temp_ai);
-        res->temp_ai = NULL;
-      }
-      free(res);
-    }
-    async->tdata = NULL;
-  }
-}
-
-/*
- * Curl_resolver_getsock() is called when someone from the outside world
- * (using curl_multi_fdset()) wants to get our fd_set setup.
- */
-
-int Curl_resolver_getsock(struct Curl_easy *data, curl_socket_t *socks)
-{
-  return Curl_ares_getsock(data, (ares_channel)data->state.async.resolver,
-                           socks);
-}
-
-/*
- * Curl_resolver_is_resolved() is called repeatedly to check if a previous
- * name resolve request has completed. It should also make sure to time-out if
- * the operation seems to take too long.
- *
- * Returns normal CURLcode errors.
- */
-CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
-                                   struct Curl_dns_entry **dns)
-{
-  struct thread_data *res = data->state.async.tdata;
-  CURLcode result = CURLE_OK;
-
-  DEBUGASSERT(dns);
-  *dns = NULL;
-
-  if(Curl_ares_perform((ares_channel)data->state.async.resolver, 0) < 0)
-    return CURLE_UNRECOVERABLE_POLL;
-
-#ifndef HAVE_CARES_GETADDRINFO
-  /* Now that we have checked for any last minute results above, see if there
-     are any responses still pending when the EXPIRE_HAPPY_EYEBALLS_DNS timer
-     expires. */
-  if(res
-     && res->num_pending
-     /* This is only set to non-zero if the timer was started. */
-     && (res->happy_eyeballs_dns_time.tv_sec
-         || res->happy_eyeballs_dns_time.tv_usec)
-     && (Curl_timediff(Curl_now(), res->happy_eyeballs_dns_time)
-         >= HAPPY_EYEBALLS_DNS_TIMEOUT)) {
-    /* Remember that the EXPIRE_HAPPY_EYEBALLS_DNS timer is no longer
-       running. */
-    memset(
-      &res->happy_eyeballs_dns_time, 0, sizeof(res->happy_eyeballs_dns_time));
-
-    /* Cancel the raw c-ares request, which will fire query_completed_cb() with
-       ARES_ECANCELLED synchronously for all pending responses. This will
-       leave us with res->num_pending == 0, which is perfect for the next
-       block. */
-    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);
-    /* temp_ai ownership is moved to the connection, so we need not free-up
-       them */
-    res->temp_ai = NULL;
-
-    if(!data->state.async.dns)
-      result = Curl_resolver_error(data);
-    else {
-      *dns = data->state.async.dns;
-#ifdef USE_HTTPSRR_ARES
-      {
-        struct Curl_https_rrinfo *lhrr =
-          Curl_memdup(&res->hinfo, sizeof(struct Curl_https_rrinfo));
-        if(!lhrr)
-          result = CURLE_OUT_OF_MEMORY;
-        else
-          (*dns)->hinfo = lhrr;
-      }
-#endif
-    }
-
-    destroy_async_data(&data->state.async);
-  }
-
-  return result;
-}
-
-/*
- * Curl_resolver_wait_resolv()
- *
- * Waits for a resolve to finish. This function should be avoided since using
- * this risk getting the multi interface to "hang".
- *
- * 'entry' MUST be non-NULL.
- *
- * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
- * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
- */
-CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
-                                   struct Curl_dns_entry **entry)
-{
-  CURLcode result = CURLE_OK;
-  timediff_t timeout;
-  struct curltime now = Curl_now();
-
-  DEBUGASSERT(entry);
-  *entry = NULL; /* clear on entry */
-
-  timeout = Curl_timeleft(data, &now, TRUE);
-  if(timeout < 0) {
-    /* already expired! */
-    connclose(data->conn, "Timed out before name resolve started");
-    return CURLE_OPERATION_TIMEDOUT;
-  }
-  if(!timeout)
-    timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */
-
-  /* Wait for the name resolve query to complete. */
-  while(!result) {
-    struct timeval *tvp, tv, store;
-    int itimeout;
-    timediff_t timeout_ms;
-
-#if TIMEDIFF_T_MAX > INT_MAX
-    itimeout = (timeout > INT_MAX) ? INT_MAX : (int)timeout;
-#else
-    itimeout = (int)timeout;
-#endif
-
-    store.tv_sec = itimeout/1000;
-    store.tv_usec = (itimeout%1000)*1000;
-
-    tvp = ares_timeout((ares_channel)data->state.async.resolver, &store, &tv);
-
-    /* use the timeout period ares returned to us above if less than one
-       second is left, otherwise just use 1000ms to make sure the progress
-       callback gets called frequent enough */
-    if(!tvp->tv_sec)
-      timeout_ms = (timediff_t)(tvp->tv_usec/1000);
-    else
-      timeout_ms = 1000;
-
-    if(Curl_ares_perform((ares_channel)data->state.async.resolver,
-                         timeout_ms) < 0)
-      return CURLE_UNRECOVERABLE_POLL;
-    result = Curl_resolver_is_resolved(data, entry);
-
-    if(result || data->state.async.done)
-      break;
-
-    if(Curl_pgrsUpdate(data))
-      result = CURLE_ABORTED_BY_CALLBACK;
-    else {
-      struct curltime now2 = Curl_now();
-      timediff_t timediff = Curl_timediff(now2, now); /* spent time */
-      if(timediff <= 0)
-        timeout -= 1; /* always deduct at least 1 */
-      else if(timediff > timeout)
-        timeout = -1;
-      else
-        timeout -= timediff;
-      now = now2; /* for next loop */
-    }
-    if(timeout < 0)
-      result = CURLE_OPERATION_TIMEDOUT;
-  }
-  if(result)
-    /* failure, so we cancel the ares operation */
-    ares_cancel((ares_channel)data->state.async.resolver);
-
-  /* Operation complete, if the lookup was successful we now have the entry
-     in the cache. */
-  if(entry)
-    *entry = data->state.async.dns;
-
-  if(result)
-    /* close the connection, since we cannot return failure here without
-       cleaning up this connection properly. */
-    connclose(data->conn, "c-ares resolve failed");
-
-  return result;
-}
-
-#ifndef HAVE_CARES_GETADDRINFO
-
-/* Connects results to the list */
-static void compound_results(struct thread_data *res,
-                             struct Curl_addrinfo *ai)
-{
-  if(!ai)
-    return;
-
-#ifdef USE_IPV6 /* CURLRES_IPV6 */
-  if(res->temp_ai && res->temp_ai->ai_family == PF_INET6) {
-    /* We have results already, put the new IPv6 entries at the head of the
-       list. */
-    struct Curl_addrinfo *temp_ai_tail = res->temp_ai;
-
-    while(temp_ai_tail->ai_next)
-      temp_ai_tail = temp_ai_tail->ai_next;
-
-    temp_ai_tail->ai_next = ai;
-  }
-  else
-#endif /* CURLRES_IPV6 */
-  {
-    /* Add the new results to the list of old results. */
-    struct Curl_addrinfo *ai_tail = ai;
-    while(ai_tail->ai_next)
-      ai_tail = ai_tail->ai_next;
-
-    ai_tail->ai_next = res->temp_ai;
-    res->temp_ai = ai;
-  }
-}
-
-/*
- * ares_query_completed_cb() is the callback that ares will call when
- * the host query initiated by ares_gethostbyname() from Curl_getaddrinfo(),
- * when using ares, is completed either successfully or with failure.
- */
-static void query_completed_cb(void *arg,  /* (struct connectdata *) */
-                               int status,
-#ifdef HAVE_CARES_CALLBACK_TIMEOUTS
-                               int timeouts,
-#endif
-                               struct hostent *hostent)
-{
-  struct Curl_easy *data = (struct Curl_easy *)arg;
-  struct thread_data *res;
-
-#ifdef HAVE_CARES_CALLBACK_TIMEOUTS
-  (void)timeouts; /* ignored */
-#endif
-
-  if(ARES_EDESTRUCTION == status)
-    /* when this ares handle is getting destroyed, the 'arg' pointer may not
-       be valid so only defer it when we know the 'status' says its fine! */
-    return;
-
-  res = data->state.async.tdata;
-  if(res) {
-    res->num_pending--;
-
-    if(CURL_ASYNC_SUCCESS == status) {
-      struct Curl_addrinfo *ai = Curl_he2ai(hostent, data->state.async.port);
-      if(ai) {
-        compound_results(res, ai);
-      }
-    }
-    /* A successful result overwrites any previous error */
-    if(res->last_status != ARES_SUCCESS)
-      res->last_status = status;
-
-    /* If there are responses still pending, we presume they must be the
-       complementary IPv4 or IPv6 lookups that we started in parallel in
-       Curl_resolver_getaddrinfo() (for Happy Eyeballs). If we have got a
-       "definitive" response from one of a set of parallel queries, we need to
-       think about how long we are willing to wait for more responses. */
-    if(res->num_pending
-       /* Only these c-ares status values count as "definitive" for these
-          purposes. For example, ARES_ENODATA is what we expect when there is
-          no IPv6 entry for a domain name, and that is not a reason to get more
-          aggressive in our timeouts for the other response. Other errors are
-          either a result of bad input (which should affect all parallel
-          requests), local or network conditions, non-definitive server
-          responses, or us cancelling the request. */
-       && (status == ARES_SUCCESS || status == ARES_ENOTFOUND)) {
-      /* Right now, there can only be up to two parallel queries, so do not
-         bother handling any other cases. */
-      DEBUGASSERT(res->num_pending == 1);
-
-      /* it is possible that one of these parallel queries could succeed
-         quickly, but the other could always fail or timeout (when we are
-         talking to a pool of DNS servers that can only successfully resolve
-         IPv4 address, for example).
-
-         it is also possible that the other request could always just take
-         longer because it needs more time or only the second DNS server can
-         fulfill it successfully. But, to align with the philosophy of Happy
-         Eyeballs, we do not want to wait _too_ long or users will think
-         requests are slow when IPv6 lookups do not actually work (but IPv4
-         ones do).
-
-         So, now that we have a usable answer (some IPv4 addresses, some IPv6
-         addresses, or "no such domain"), we start a timeout for the remaining
-         pending responses. Even though it is typical that this resolved
-         request came back quickly, that needn't be the case. It might be that
-         this completing request did not get a result from the first DNS
-         server or even the first round of the whole DNS server pool. So it
-         could already be quite some time after we issued the DNS queries in
-         the first place. Without modifying c-ares, we cannot know exactly
-         where in its retry cycle we are. We could guess based on how much
-         time has gone by, but it does not really matter. Happy Eyeballs tells
-         us that, given usable information in hand, we simply do not want to
-         wait "too much longer" after we get a result.
-
-         We simply wait an additional amount of time equal to the default
-         c-ares query timeout. That is enough time for a typical parallel
-         response to arrive without being "too long". Even on a network
-         where one of the two types of queries is failing or timing out
-         constantly, this will usually mean we wait a total of the default
-         c-ares timeout (5 seconds) plus the round trip time for the successful
-         request, which seems bearable. The downside is that c-ares might race
-         with us to issue one more retry just before we give up, but it seems
-         better to "waste" that request instead of trying to guess the perfect
-         timeout to prevent it. After all, we do not even know where in the
-         c-ares retry cycle each request is.
-      */
-      res->happy_eyeballs_dns_time = Curl_now();
-      Curl_expire(data, HAPPY_EYEBALLS_DNS_TIMEOUT,
-                  EXPIRE_HAPPY_EYEBALLS_DNS);
-    }
-  }
-}
-#else
-/* c-ares 1.16.0 or later */
-
-/*
- * ares2addr() converts an address list provided by c-ares to an internal
- * libcurl compatible list
- */
-static struct Curl_addrinfo *ares2addr(struct ares_addrinfo_node *node)
-{
-  /* traverse the ares_addrinfo_node list */
-  struct ares_addrinfo_node *ai;
-  struct Curl_addrinfo *cafirst = NULL;
-  struct Curl_addrinfo *calast = NULL;
-  int error = 0;
-
-  for(ai = node; ai != NULL; ai = ai->ai_next) {
-    size_t ss_size;
-    struct Curl_addrinfo *ca;
-    /* ignore elements with unsupported address family, */
-    /* settle family-specific sockaddr structure size.  */
-    if(ai->ai_family == AF_INET)
-      ss_size = sizeof(struct sockaddr_in);
-#ifdef USE_IPV6
-    else if(ai->ai_family == AF_INET6)
-      ss_size = sizeof(struct sockaddr_in6);
-#endif
-    else
-      continue;
-
-    /* ignore elements without required address info */
-    if(!ai->ai_addr || !(ai->ai_addrlen > 0))
-      continue;
-
-    /* ignore elements with bogus address size */
-    if((size_t)ai->ai_addrlen < ss_size)
-      continue;
-
-    ca = malloc(sizeof(struct Curl_addrinfo) + ss_size);
-    if(!ca) {
-      error = EAI_MEMORY;
-      break;
-    }
-
-    /* copy each structure member individually, member ordering, */
-    /* size, or padding might be different for each platform.    */
-
-    ca->ai_flags     = ai->ai_flags;
-    ca->ai_family    = ai->ai_family;
-    ca->ai_socktype  = ai->ai_socktype;
-    ca->ai_protocol  = ai->ai_protocol;
-    ca->ai_addrlen   = (curl_socklen_t)ss_size;
-    ca->ai_addr      = NULL;
-    ca->ai_canonname = NULL;
-    ca->ai_next      = NULL;
-
-    ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
-    memcpy(ca->ai_addr, ai->ai_addr, ss_size);
-
-    /* if the return list is empty, this becomes the first element */
-    if(!cafirst)
-      cafirst = ca;
-
-    /* add this element last in the return list */
-    if(calast)
-      calast->ai_next = ca;
-    calast = ca;
-  }
-
-  /* if we failed, destroy the Curl_addrinfo list */
-  if(error) {
-    Curl_freeaddrinfo(cafirst);
-    cafirst = NULL;
-  }
-
-  return cafirst;
-}
-
-static void addrinfo_cb(void *arg, int status, int timeouts,
-                        struct ares_addrinfo *result)
-{
-  struct Curl_easy *data = (struct Curl_easy *)arg;
-  struct thread_data *res = data->state.async.tdata;
-  (void)timeouts;
-  if(ARES_SUCCESS == status) {
-    res->temp_ai = ares2addr(result->nodes);
-    res->last_status = CURL_ASYNC_SUCCESS;
-    ares_freeaddrinfo(result);
-  }
-  res->num_pending--;
-}
-
-#endif
-
-/*
- * Curl_resolver_getaddrinfo() - when using ares
- *
- * Returns name information about the given hostname and port number. If
- * successful, the 'hostent' is returned and the fourth argument will point to
- * memory we need to free after use. That memory *MUST* be freed with
- * Curl_freeaddrinfo(), nothing else.
- */
-struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
-                                                const char *hostname,
-                                                int port,
-                                                int *waitp)
-{
-  struct thread_data *res = NULL;
-  size_t namelen = strlen(hostname);
-  *waitp = 0; /* default to synchronous response */
-
-  res = calloc(1, sizeof(struct thread_data) + namelen);
-  if(res) {
-    strcpy(res->hostname, hostname);
-    data->state.async.hostname = res->hostname;
-    data->state.async.port = port;
-    data->state.async.done = FALSE;   /* not done */
-    data->state.async.status = 0;     /* clear */
-    data->state.async.dns = NULL;     /* clear */
-    data->state.async.tdata = res;
-
-    /* initial status - failed */
-    res->last_status = ARES_ENOTFOUND;
-
-#ifdef HAVE_CARES_GETADDRINFO
-    {
-      struct ares_addrinfo_hints hints;
-      char service[12];
-      int pf = PF_INET;
-      memset(&hints, 0, sizeof(hints));
-#ifdef CURLRES_IPV6
-      if((data->conn->ip_version != CURL_IPRESOLVE_V4) &&
-         Curl_ipv6works(data)) {
-        /* The stack seems to be IPv6-enabled */
-        if(data->conn->ip_version == CURL_IPRESOLVE_V6)
-          pf = PF_INET6;
-        else
-          pf = PF_UNSPEC;
-      }
-#endif /* CURLRES_IPV6 */
-      hints.ai_family = pf;
-      hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP) ?
-        SOCK_STREAM : SOCK_DGRAM;
-      /* Since the service is a numerical one, set the hint flags
-       * accordingly to save a call to getservbyname in inside C-Ares
-       */
-      hints.ai_flags = ARES_AI_NUMERICSERV;
-      msnprintf(service, sizeof(service), "%d", port);
-      res->num_pending = 1;
-      ares_getaddrinfo((ares_channel)data->state.async.resolver, hostname,
-                       service, &hints, addrinfo_cb, data);
-    }
-#else
-
-#ifdef HAVE_CARES_IPV6
-    if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
-      /* The stack seems to be IPv6-enabled */
-      res->num_pending = 2;
-
-      /* areschannel is already setup in the Curl_open() function */
-      ares_gethostbyname((ares_channel)data->state.async.resolver, hostname,
-                          PF_INET, query_completed_cb, data);
-      ares_gethostbyname((ares_channel)data->state.async.resolver, hostname,
-                          PF_INET6, query_completed_cb, data);
-    }
-    else
-#endif
-    {
-      res->num_pending = 1;
-
-      /* areschannel is already setup in the Curl_open() function */
-      ares_gethostbyname((ares_channel)data->state.async.resolver,
-                         hostname, PF_INET,
-                         query_completed_cb, data);
-    }
-#endif
-#ifdef USE_HTTPSRR_ARES
-    {
-      res->num_pending++; /* one more */
-      memset(&res->hinfo, 0, sizeof(struct Curl_https_rrinfo));
-      ares_query_dnsrec((ares_channel)data->state.async.resolver,
-                        hostname, ARES_CLASS_IN,
-                        ARES_REC_TYPE_HTTPS,
-                        Curl_dnsrec_done_cb, data, NULL);
-    }
-#endif
-    *waitp = 1; /* expect asynchronous response */
-  }
-  return NULL; /* no struct yet */
-}
-
-CURLcode Curl_set_dns_servers(struct Curl_easy *data,
-                              char *servers)
-{
-  CURLcode result = CURLE_NOT_BUILT_IN;
-  int ares_result;
-
-  /* If server is NULL, this purges all DNS servers from c-ares. Reset it to
-   * default.
-   */
-  if(!servers) {
-    Curl_resolver_cleanup(data->state.async.resolver);
-    result = Curl_resolver_init(data, &data->state.async.resolver);
-    if(!result) {
-      /* this now needs to restore the other options set to c-ares */
-      if(data->set.str[STRING_DNS_INTERFACE])
-        (void)Curl_set_dns_interface(data,
-                                     data->set.str[STRING_DNS_INTERFACE]);
-      if(data->set.str[STRING_DNS_LOCAL_IP4])
-        (void)Curl_set_dns_local_ip4(data,
-                                     data->set.str[STRING_DNS_LOCAL_IP4]);
-      if(data->set.str[STRING_DNS_LOCAL_IP6])
-        (void)Curl_set_dns_local_ip6(data,
-                                     data->set.str[STRING_DNS_LOCAL_IP6]);
-    }
-    return result;
-  }
-
-#ifdef HAVE_CARES_SERVERS_CSV
-#ifdef HAVE_CARES_PORTS_CSV
-  ares_result = ares_set_servers_ports_csv(data->state.async.resolver,
-                                           servers);
-#else
-  ares_result = ares_set_servers_csv(data->state.async.resolver, servers);
-#endif
-  switch(ares_result) {
-  case ARES_SUCCESS:
-    result = CURLE_OK;
-    break;
-  case ARES_ENOMEM:
-    result = CURLE_OUT_OF_MEMORY;
-    break;
-  case ARES_ENOTINITIALIZED:
-  case ARES_ENODATA:
-  case ARES_EBADSTR:
-  default:
-    DEBUGF(infof(data, "bad servers set"));
-    result = CURLE_BAD_FUNCTION_ARGUMENT;
-    break;
-  }
-#else /* too old c-ares version! */
-  (void)data;
-  (void)(ares_result);
-#endif
-  return result;
-}
-
-CURLcode Curl_set_dns_interface(struct Curl_easy *data,
-                                const char *interf)
-{
-#ifdef HAVE_CARES_LOCAL_DEV
-  if(!interf)
-    interf = "";
-
-  ares_set_local_dev((ares_channel)data->state.async.resolver, interf);
-
-  return CURLE_OK;
-#else /* c-ares version too old! */
-  (void)data;
-  (void)interf;
-  return CURLE_NOT_BUILT_IN;
-#endif
-}
-
-CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
-                                const char *local_ip4)
-{
-#ifdef HAVE_CARES_SET_LOCAL
-  struct in_addr a4;
-
-  if((!local_ip4) || (local_ip4[0] == 0)) {
-    a4.s_addr = 0; /* disabled: do not bind to a specific address */
-  }
-  else {
-    if(Curl_inet_pton(AF_INET, local_ip4, &a4) != 1) {
-      DEBUGF(infof(data, "bad DNS IPv4 address"));
-      return CURLE_BAD_FUNCTION_ARGUMENT;
-    }
-  }
-
-  ares_set_local_ip4((ares_channel)data->state.async.resolver,
-                     ntohl(a4.s_addr));
-
-  return CURLE_OK;
-#else /* c-ares version too old! */
-  (void)data;
-  (void)local_ip4;
-  return CURLE_NOT_BUILT_IN;
-#endif
-}
-
-CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
-                                const char *local_ip6)
-{
-#if defined(HAVE_CARES_SET_LOCAL) && defined(USE_IPV6)
-  unsigned char a6[INET6_ADDRSTRLEN];
-
-  if((!local_ip6) || (local_ip6[0] == 0)) {
-    /* disabled: do not bind to a specific address */
-    memset(a6, 0, sizeof(a6));
-  }
-  else {
-    if(Curl_inet_pton(AF_INET6, local_ip6, a6) != 1) {
-      DEBUGF(infof(data, "bad DNS IPv6 address"));
-      return CURLE_BAD_FUNCTION_ARGUMENT;
-    }
-  }
-
-  ares_set_local_ip6((ares_channel)data->state.async.resolver, a6);
-
-  return CURLE_OK;
-#else /* c-ares version too old! */
-  (void)data;
-  (void)local_ip6;
-  return CURLE_NOT_BUILT_IN;
-#endif
-}
-#endif /* CURLRES_ARES */
-
-#endif /* USE_ARES */
diff --git a/extra/curl/curl-8.12.1/lib/asyn-thread.c b/extra/curl/curl-8.12.1/lib/asyn-thread.c
deleted file mode 100644
index f98941596eb0..000000000000
--- a/extra/curl/curl-8.12.1/lib/asyn-thread.c
+++ /dev/null
@@ -1,812 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-#include "socketpair.h"
-
-/***********************************************************************
- * Only for threaded name resolves builds
- **********************************************************************/
-#ifdef CURLRES_THREADED
-
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
-#ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-#ifdef __VMS
-#include <in.h>
-#include <inet.h>
-#endif
-
-#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)
-#  include <pthread.h>
-#endif
-
-#ifdef HAVE_GETADDRINFO
-#  define RESOLVER_ENOMEM  EAI_MEMORY
-#else
-#  define RESOLVER_ENOMEM  ENOMEM
-#endif
-
-#include "urldata.h"
-#include "sendf.h"
-#include "hostip.h"
-#include "hash.h"
-#include "share.h"
-#include "url.h"
-#include "multiif.h"
-#include "inet_ntop.h"
-#include "curl_threads.h"
-#include "connect.h"
-#include "strdup.h"
-
-#ifdef USE_ARES
-#include <ares.h>
-#ifdef USE_HTTPSRR
-#define USE_HTTPSRR_ARES 1 /* the combo */
-#endif
-#endif
-
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
-
-struct resdata {
-  struct curltime start;
-};
-
-/*
- * Curl_resolver_global_init()
- * Called from curl_global_init() to initialize global resolver environment.
- * Does nothing here.
- */
-int Curl_resolver_global_init(void)
-{
-  return CURLE_OK;
-}
-
-/*
- * Curl_resolver_global_cleanup()
- * Called from curl_global_cleanup() to destroy global resolver environment.
- * Does nothing here.
- */
-void Curl_resolver_global_cleanup(void)
-{
-}
-
-/*
- * Curl_resolver_init()
- * Called from curl_easy_init() -> Curl_open() to initialize resolver
- * URL-state specific environment ('resolver' member of the UrlState
- * structure).
- */
-CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver)
-{
-  (void)easy;
-  *resolver = calloc(1, sizeof(struct resdata));
-  if(!*resolver)
-    return CURLE_OUT_OF_MEMORY;
-  return CURLE_OK;
-}
-
-/*
- * Curl_resolver_cleanup()
- * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
- * URL-state specific environment ('resolver' member of the UrlState
- * structure).
- */
-void Curl_resolver_cleanup(void *resolver)
-{
-  free(resolver);
-}
-
-/*
- * Curl_resolver_duphandle()
- * Called from curl_easy_duphandle() to duplicate resolver URL state-specific
- * environment ('resolver' member of the UrlState structure).
- */
-CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to, void *from)
-{
-  (void)from;
-  return Curl_resolver_init(easy, to);
-}
-
-static void destroy_async_data(struct Curl_easy *);
-
-/*
- * Cancel all possibly still on-going resolves for this connection.
- */
-void Curl_resolver_cancel(struct Curl_easy *data)
-{
-  destroy_async_data(data);
-}
-
-/* This function is used to init a threaded resolve */
-static bool init_resolve_thread(struct Curl_easy *data,
-                                const char *hostname, int port,
-                                const struct addrinfo *hints);
-
-
-static struct thread_sync_data *conn_thread_sync_data(struct Curl_easy *data)
-{
-  return &(data->state.async.tdata->tsd);
-}
-
-/* Destroy resolver thread synchronization data */
-static
-void destroy_thread_sync_data(struct thread_sync_data *tsd)
-{
-  if(tsd->mtx) {
-    Curl_mutex_destroy(tsd->mtx);
-    free(tsd->mtx);
-  }
-
-  free(tsd->hostname);
-
-  if(tsd->res)
-    Curl_freeaddrinfo(tsd->res);
-
-#ifndef CURL_DISABLE_SOCKETPAIR
-  /*
-   * close one end of the socket pair (may be done in resolver thread);
-   * the other end (for reading) is always closed in the parent thread.
-   */
-#ifndef USE_EVENTFD
-  if(tsd->sock_pair[1] != CURL_SOCKET_BAD) {
-    wakeup_close(tsd->sock_pair[1]);
-  }
-#endif
-#endif
-  memset(tsd, 0, sizeof(*tsd));
-}
-
-/* Initialize resolver thread synchronization data */
-static
-int init_thread_sync_data(struct thread_data *td,
-                           const char *hostname,
-                           int port,
-                           const struct addrinfo *hints)
-{
-  struct thread_sync_data *tsd = &td->tsd;
-
-  memset(tsd, 0, sizeof(*tsd));
-
-  tsd->td = td;
-  tsd->port = port;
-  /* Treat the request as done until the thread actually starts so any early
-   * cleanup gets done properly.
-   */
-  tsd->done = TRUE;
-#ifdef HAVE_GETADDRINFO
-  DEBUGASSERT(hints);
-  tsd->hints = *hints;
-#else
-  (void) hints;
-#endif
-
-  tsd->mtx = malloc(sizeof(curl_mutex_t));
-  if(!tsd->mtx)
-    goto err_exit;
-
-  Curl_mutex_init(tsd->mtx);
-
-#ifndef CURL_DISABLE_SOCKETPAIR
-  /* create socket pair or pipe */
-  if(wakeup_create(tsd->sock_pair, FALSE) < 0) {
-    tsd->sock_pair[0] = CURL_SOCKET_BAD;
-    tsd->sock_pair[1] = CURL_SOCKET_BAD;
-    goto err_exit;
-  }
-#endif
-  tsd->sock_error = CURL_ASYNC_SUCCESS;
-
-  /* Copying hostname string because original can be destroyed by parent
-   * thread during gethostbyname execution.
-   */
-  tsd->hostname = strdup(hostname);
-  if(!tsd->hostname)
-    goto err_exit;
-
-  return 1;
-
-err_exit:
-#ifndef CURL_DISABLE_SOCKETPAIR
-  if(tsd->sock_pair[0] != CURL_SOCKET_BAD) {
-    wakeup_close(tsd->sock_pair[0]);
-    tsd->sock_pair[0] = CURL_SOCKET_BAD;
-  }
-#endif
-  destroy_thread_sync_data(tsd);
-  return 0;
-}
-
-static CURLcode getaddrinfo_complete(struct Curl_easy *data)
-{
-  struct thread_sync_data *tsd = conn_thread_sync_data(data);
-  CURLcode result;
-
-  result = Curl_addrinfo_callback(data, tsd->sock_error, tsd->res);
-  /* The tsd->res structure has been copied to async.dns and perhaps the DNS
-     cache. Set our copy to NULL so destroy_thread_sync_data does not free it.
-  */
-  tsd->res = NULL;
-
-  return result;
-}
-
-
-#ifdef HAVE_GETADDRINFO
-
-/*
- * getaddrinfo_thread() resolves a name and then exits.
- *
- * For builds without ARES, but with USE_IPV6, create a resolver thread
- * and wait on it.
- */
-static
-#if defined(_WIN32_WCE) || defined(CURL_WINDOWS_UWP)
-DWORD
-#else
-unsigned int
-#endif
-CURL_STDCALL getaddrinfo_thread(void *arg)
-{
-  struct thread_sync_data *tsd = (struct thread_sync_data *)arg;
-  struct thread_data *td = tsd->td;
-  char service[12];
-  int rc;
-
-  msnprintf(service, sizeof(service), "%d", tsd->port);
-
-  rc = Curl_getaddrinfo_ex(tsd->hostname, service, &tsd->hints, &tsd->res);
-
-  if(rc) {
-    tsd->sock_error = SOCKERRNO ? SOCKERRNO : rc;
-    if(tsd->sock_error == 0)
-      tsd->sock_error = RESOLVER_ENOMEM;
-  }
-  else {
-    Curl_addrinfo_set_port(tsd->res, tsd->port);
-  }
-
-  Curl_mutex_acquire(tsd->mtx);
-  if(tsd->done) {
-    /* too late, gotta clean up the mess */
-    Curl_mutex_release(tsd->mtx);
-    destroy_thread_sync_data(tsd);
-    free(td);
-  }
-  else {
-#ifndef CURL_DISABLE_SOCKETPAIR
-    if(tsd->sock_pair[1] != CURL_SOCKET_BAD) {
-#ifdef USE_EVENTFD
-      const uint64_t buf[1] = { 1 };
-#else
-      const char buf[1] = { 1 };
-#endif
-      /* DNS has been resolved, signal client task */
-      if(wakeup_write(tsd->sock_pair[1], buf, sizeof(buf)) < 0) {
-        /* update sock_erro to errno */
-        tsd->sock_error = SOCKERRNO;
-      }
-    }
-#endif
-    tsd->done = TRUE;
-    Curl_mutex_release(tsd->mtx);
-  }
-
-  return 0;
-}
-
-#else /* HAVE_GETADDRINFO */
-
-/*
- * gethostbyname_thread() resolves a name and then exits.
- */
-static
-#if defined(_WIN32_WCE) || defined(CURL_WINDOWS_UWP)
-DWORD
-#else
-unsigned int
-#endif
-CURL_STDCALL gethostbyname_thread(void *arg)
-{
-  struct thread_sync_data *tsd = (struct thread_sync_data *)arg;
-  struct thread_data *td = tsd->td;
-
-  tsd->res = Curl_ipv4_resolve_r(tsd->hostname, tsd->port);
-
-  if(!tsd->res) {
-    tsd->sock_error = SOCKERRNO;
-    if(tsd->sock_error == 0)
-      tsd->sock_error = RESOLVER_ENOMEM;
-  }
-
-  Curl_mutex_acquire(tsd->mtx);
-  if(tsd->done) {
-    /* too late, gotta clean up the mess */
-    Curl_mutex_release(tsd->mtx);
-    destroy_thread_sync_data(tsd);
-    free(td);
-  }
-  else {
-    tsd->done = TRUE;
-    Curl_mutex_release(tsd->mtx);
-  }
-
-  return 0;
-}
-
-#endif /* HAVE_GETADDRINFO */
-
-/*
- * destroy_async_data() cleans up async resolver data and thread handle.
- */
-static void destroy_async_data(struct Curl_easy *data)
-{
-  struct Curl_async *async;
-  DEBUGASSERT(data);
-  async = &data->state.async;
-  DEBUGASSERT(async);
-  if(async->tdata) {
-    struct thread_data *td = async->tdata;
-    bool done;
-#ifndef CURL_DISABLE_SOCKETPAIR
-    curl_socket_t sock_rd = td->tsd.sock_pair[0];
-#endif
-
-#ifdef USE_HTTPSRR_ARES
-    if(data->state.async.tdata->channel)
-      ares_destroy(data->state.async.tdata->channel);
-#endif
-    /*
-     * if the thread is still blocking in the resolve syscall, detach it and
-     * let the thread do the cleanup...
-     */
-    Curl_mutex_acquire(td->tsd.mtx);
-    done = td->tsd.done;
-    td->tsd.done = TRUE;
-    Curl_mutex_release(td->tsd.mtx);
-
-    if(!done) {
-      Curl_thread_destroy(td->thread_hnd);
-    }
-    else {
-      if(td->thread_hnd != curl_thread_t_null)
-        Curl_thread_join(&td->thread_hnd);
-
-      destroy_thread_sync_data(&td->tsd);
-
-      free(async->tdata);
-    }
-#ifndef CURL_DISABLE_SOCKETPAIR
-    /*
-     * ensure CURLMOPT_SOCKETFUNCTION fires CURL_POLL_REMOVE
-     * before the FD is invalidated to avoid EBADF on EPOLL_CTL_DEL
-     */
-    Curl_multi_closed(data, sock_rd);
-    wakeup_close(sock_rd);
-#endif
-  }
-  async->tdata = NULL;
-
-  free(async->hostname);
-  async->hostname = NULL;
-}
-
-#ifdef USE_HTTPSRR_ARES
-static CURLcode resolve_httpsrr(struct Curl_easy *data,
-                                struct Curl_async *asp)
-{
-  int status = ares_init_options(&asp->tdata->channel, NULL, 0);
-  if(status != ARES_SUCCESS)
-    return CURLE_FAILED_INIT;
-
-  memset(&asp->tdata->hinfo, 0, sizeof(struct Curl_https_rrinfo));
-  ares_query_dnsrec(asp->tdata->channel,
-                    asp->hostname, ARES_CLASS_IN,
-                    ARES_REC_TYPE_HTTPS,
-                    Curl_dnsrec_done_cb, data, NULL);
-
-  return CURLE_OK;
-}
-#endif
-
-/*
- * init_resolve_thread() starts a new thread that performs the actual
- * resolve. This function returns before the resolve is done.
- *
- * Returns FALSE in case of failure, otherwise TRUE.
- */
-static bool init_resolve_thread(struct Curl_easy *data,
-                                const char *hostname, int port,
-                                const struct addrinfo *hints)
-{
-  struct thread_data *td = calloc(1, sizeof(struct thread_data));
-  int err = ENOMEM;
-  struct Curl_async *asp = &data->state.async;
-
-  data->state.async.tdata = td;
-  if(!td)
-    goto errno_exit;
-
-  asp->port = port;
-  asp->done = FALSE;
-  asp->status = 0;
-  asp->dns = NULL;
-  td->thread_hnd = curl_thread_t_null;
-
-  if(!init_thread_sync_data(td, hostname, port, hints)) {
-    asp->tdata = NULL;
-    free(td);
-    goto errno_exit;
-  }
-
-  free(asp->hostname);
-  asp->hostname = strdup(hostname);
-  if(!asp->hostname)
-    goto err_exit;
-
-  /* The thread will set this TRUE when complete. */
-  td->tsd.done = FALSE;
-
-#ifdef HAVE_GETADDRINFO
-  td->thread_hnd = Curl_thread_create(getaddrinfo_thread, &td->tsd);
-#else
-  td->thread_hnd = Curl_thread_create(gethostbyname_thread, &td->tsd);
-#endif
-
-  if(td->thread_hnd == curl_thread_t_null) {
-    /* The thread never started, so mark it as done here for proper cleanup. */
-    td->tsd.done = TRUE;
-    err = errno;
-    goto err_exit;
-  }
-#ifdef USE_HTTPSRR_ARES
-  if(resolve_httpsrr(data, asp))
-    infof(data, "Failed HTTPS RR operation");
-#endif
-  return TRUE;
-
-err_exit:
-  destroy_async_data(data);
-
-errno_exit:
-  errno = err;
-  return FALSE;
-}
-
-/*
- * 'entry' may be NULL and then no data is returned
- */
-static CURLcode thread_wait_resolv(struct Curl_easy *data,
-                                   struct Curl_dns_entry **entry,
-                                   bool report)
-{
-  struct thread_data *td;
-  CURLcode result = CURLE_OK;
-
-  DEBUGASSERT(data);
-  td = data->state.async.tdata;
-  DEBUGASSERT(td);
-  DEBUGASSERT(td->thread_hnd != curl_thread_t_null);
-
-  /* wait for the thread to resolve the name */
-  if(Curl_thread_join(&td->thread_hnd)) {
-    if(entry)
-      result = getaddrinfo_complete(data);
-  }
-  else
-    DEBUGASSERT(0);
-
-  data->state.async.done = TRUE;
-
-  if(entry)
-    *entry = data->state.async.dns;
-
-  if(!data->state.async.dns && report)
-    /* a name was not resolved, report error */
-    result = Curl_resolver_error(data);
-
-  destroy_async_data(data);
-
-  if(!data->state.async.dns && report)
-    connclose(data->conn, "asynch resolve failed");
-
-  return result;
-}
-
-
-/*
- * Until we gain a way to signal the resolver threads to stop early, we must
- * simply wait for them and ignore their results.
- */
-void Curl_resolver_kill(struct Curl_easy *data)
-{
-  struct thread_data *td = data->state.async.tdata;
-
-  /* If we are still resolving, we must wait for the threads to fully clean up,
-     unfortunately. Otherwise, we can simply cancel to clean up any resolver
-     data. */
-  if(td && td->thread_hnd != curl_thread_t_null
-     && (data->set.quick_exit != 1L))
-    (void)thread_wait_resolv(data, NULL, FALSE);
-  else
-    Curl_resolver_cancel(data);
-}
-
-/*
- * Curl_resolver_wait_resolv()
- *
- * Waits for a resolve to finish. This function should be avoided since using
- * this risk getting the multi interface to "hang".
- *
- * If 'entry' is non-NULL, make it point to the resolved dns entry
- *
- * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
- * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
- *
- * This is the version for resolves-in-a-thread.
- */
-CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
-                                   struct Curl_dns_entry **entry)
-{
-  return thread_wait_resolv(data, entry, TRUE);
-}
-
-/*
- * Curl_resolver_is_resolved() is called repeatedly to check if a previous
- * name resolve request has completed. It should also make sure to time-out if
- * the operation seems to take too long.
- */
-CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
-                                   struct Curl_dns_entry **entry)
-{
-  struct thread_data *td = data->state.async.tdata;
-  bool done = FALSE;
-
-  DEBUGASSERT(entry);
-  *entry = NULL;
-
-  if(!td) {
-    DEBUGASSERT(td);
-    return CURLE_COULDNT_RESOLVE_HOST;
-  }
-#ifdef USE_HTTPSRR_ARES
-  if(Curl_ares_perform(data->state.async.tdata->channel, 0) < 0)
-    return CURLE_UNRECOVERABLE_POLL;
-#endif
-
-  Curl_mutex_acquire(td->tsd.mtx);
-  done = td->tsd.done;
-  Curl_mutex_release(td->tsd.mtx);
-
-  if(done) {
-    getaddrinfo_complete(data);
-
-    if(!data->state.async.dns) {
-      CURLcode result = Curl_resolver_error(data);
-      destroy_async_data(data);
-      return result;
-    }
-#ifdef USE_HTTPSRR_ARES
-    {
-      struct Curl_https_rrinfo *lhrr =
-        Curl_memdup(&td->hinfo, sizeof(struct Curl_https_rrinfo));
-      if(!lhrr) {
-        destroy_async_data(data);
-        return CURLE_OUT_OF_MEMORY;
-      }
-      data->state.async.dns->hinfo = lhrr;
-    }
-#endif
-    destroy_async_data(data);
-    *entry = data->state.async.dns;
-  }
-  else {
-    /* poll for name lookup done with exponential backoff up to 250ms */
-    /* should be fine even if this converts to 32-bit */
-    timediff_t elapsed = Curl_timediff(Curl_now(),
-                                       data->progress.t_startsingle);
-    if(elapsed < 0)
-      elapsed = 0;
-
-    if(td->poll_interval == 0)
-      /* Start at 1ms poll interval */
-      td->poll_interval = 1;
-    else if(elapsed >= td->interval_end)
-      /* Back-off exponentially if last interval expired  */
-      td->poll_interval *= 2;
-
-    if(td->poll_interval > 250)
-      td->poll_interval = 250;
-
-    td->interval_end = elapsed + td->poll_interval;
-    Curl_expire(data, td->poll_interval, EXPIRE_ASYNC_NAME);
-  }
-
-  return CURLE_OK;
-}
-
-int Curl_resolver_getsock(struct Curl_easy *data, curl_socket_t *socks)
-{
-  int ret_val = 0;
-  timediff_t milli;
-  timediff_t ms;
-  struct resdata *reslv = (struct resdata *)data->state.async.resolver;
-#ifndef CURL_DISABLE_SOCKETPAIR
-  struct thread_data *td = data->state.async.tdata;
-#endif
-#if !defined(CURL_DISABLE_SOCKETPAIR) || defined(USE_HTTPSRR_ARES)
-  int socketi = 0;
-#else
-  (void)socks;
-#endif
-
-#ifdef USE_HTTPSRR_ARES
-  if(data->state.async.tdata && data->state.async.tdata->channel) {
-    ret_val = Curl_ares_getsock(data, data->state.async.tdata->channel, socks);
-    for(socketi = 0; socketi < (MAX_SOCKSPEREASYHANDLE - 1); socketi++)
-      if(!ARES_GETSOCK_READABLE(ret_val, socketi) &&
-         !ARES_GETSOCK_WRITABLE(ret_val, socketi))
-        break;
-  }
-#endif
-#ifndef CURL_DISABLE_SOCKETPAIR
-  if(td) {
-    /* return read fd to client for polling the DNS resolution status */
-    socks[socketi] = td->tsd.sock_pair[0];
-    ret_val |= GETSOCK_READSOCK(socketi);
-  }
-  else {
-#endif
-    ms = Curl_timediff(Curl_now(), reslv->start);
-    if(ms < 3)
-      milli = 0;
-    else if(ms <= 50)
-      milli = ms/3;
-    else if(ms <= 250)
-      milli = 50;
-    else
-      milli = 200;
-    Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
-#ifndef CURL_DISABLE_SOCKETPAIR
-  }
-#endif
-
-
-  return ret_val;
-}
-
-#ifndef HAVE_GETADDRINFO
-/*
- * Curl_getaddrinfo() - for platforms without getaddrinfo
- */
-struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
-                                                const char *hostname,
-                                                int port,
-                                                int *waitp)
-{
-  struct resdata *reslv = (struct resdata *)data->state.async.resolver;
-
-  *waitp = 0; /* default to synchronous response */
-
-  reslv->start = Curl_now();
-
-  /* fire up a new resolver thread! */
-  if(init_resolve_thread(data, hostname, port, NULL)) {
-    *waitp = 1; /* expect asynchronous response */
-    return NULL;
-  }
-
-  failf(data, "getaddrinfo() thread failed");
-
-  return NULL;
-}
-
-#else /* !HAVE_GETADDRINFO */
-
-/*
- * Curl_resolver_getaddrinfo() - for getaddrinfo
- */
-struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
-                                                const char *hostname,
-                                                int port,
-                                                int *waitp)
-{
-  struct addrinfo hints;
-  int pf = PF_INET;
-  struct resdata *reslv = (struct resdata *)data->state.async.resolver;
-
-  *waitp = 0; /* default to synchronous response */
-
-#ifdef CURLRES_IPV6
-  if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
-    /* The stack seems to be IPv6-enabled */
-    if(data->conn->ip_version == CURL_IPRESOLVE_V6)
-      pf = PF_INET6;
-    else
-      pf = PF_UNSPEC;
-  }
-#endif /* CURLRES_IPV6 */
-
-  memset(&hints, 0, sizeof(hints));
-  hints.ai_family = pf;
-  hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP) ?
-    SOCK_STREAM : SOCK_DGRAM;
-
-  reslv->start = Curl_now();
-  /* fire up a new resolver thread! */
-  if(init_resolve_thread(data, hostname, port, &hints)) {
-    *waitp = 1; /* expect asynchronous response */
-    return NULL;
-  }
-
-  failf(data, "getaddrinfo() thread failed to start");
-  return NULL;
-
-}
-
-#endif /* !HAVE_GETADDRINFO */
-
-CURLcode Curl_set_dns_servers(struct Curl_easy *data,
-                              char *servers)
-{
-  (void)data;
-  (void)servers;
-  return CURLE_NOT_BUILT_IN;
-
-}
-
-CURLcode Curl_set_dns_interface(struct Curl_easy *data,
-                                const char *interf)
-{
-  (void)data;
-  (void)interf;
-  return CURLE_NOT_BUILT_IN;
-}
-
-CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
-                                const char *local_ip4)
-{
-  (void)data;
-  (void)local_ip4;
-  return CURLE_NOT_BUILT_IN;
-}
-
-CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
-                                const char *local_ip6)
-{
-  (void)data;
-  (void)local_ip6;
-  return CURLE_NOT_BUILT_IN;
-}
-
-#endif /* CURLRES_THREADED */
diff --git a/extra/curl/curl-8.12.1/lib/conncache.c b/extra/curl/curl-8.12.1/lib/conncache.c
deleted file mode 100644
index b8a0515276d1..000000000000
--- a/extra/curl/curl-8.12.1/lib/conncache.c
+++ /dev/null
@@ -1,1439 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) Linus Nielsen Feltzing, <linus@haxx.se>
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#include <curl/curl.h>
-
-#include "urldata.h"
-#include "url.h"
-#include "cfilters.h"
-#include "progress.h"
-#include "multiif.h"
-#include "sendf.h"
-#include "conncache.h"
-#include "http_negotiate.h"
-#include "http_ntlm.h"
-#include "share.h"
-#include "sigpipe.h"
-#include "connect.h"
-#include "select.h"
-#include "strcase.h"
-
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
-
-
-#define CPOOL_IS_LOCKED(c)    ((c) && (c)->locked)
-
-#define CPOOL_LOCK(c)                                                   \
-  do {                                                                  \
-    if((c)) {                                                           \
-      if(CURL_SHARE_KEEP_CONNECT((c)->share))                           \
-        Curl_share_lock(((c)->idata), CURL_LOCK_DATA_CONNECT,           \
-                        CURL_LOCK_ACCESS_SINGLE);                       \
-      DEBUGASSERT(!(c)->locked);                                        \
-      (c)->locked = TRUE;                                               \
-    }                                                                   \
-  } while(0)
-
-#define CPOOL_UNLOCK(c)                                                 \
-  do {                                                                  \
-    if((c)) {                                                           \
-      DEBUGASSERT((c)->locked);                                         \
-      (c)->locked = FALSE;                                              \
-      if(CURL_SHARE_KEEP_CONNECT((c)->share))                           \
-        Curl_share_unlock((c)->idata, CURL_LOCK_DATA_CONNECT);          \
-    }                                                                   \
-  } while(0)
-
-
-/* A list of connections to the same destination. */
-struct cpool_bundle {
-  struct Curl_llist conns; /* connections in the bundle */
-  size_t dest_len; /* total length of destination, including NUL */
-  char *dest[1]; /* destination of bundle, allocated to keep dest_len bytes */
-};
-
-
-static void cpool_discard_conn(struct cpool *cpool,
-                               struct Curl_easy *data,
-                               struct connectdata *conn,
-                               bool aborted);
-static void cpool_close_and_destroy(struct cpool *cpool,
-                                    struct connectdata *conn,
-                                    struct Curl_easy *data,
-                                    bool do_shutdown);
-static void cpool_run_conn_shutdown(struct Curl_easy *data,
-                                    struct connectdata *conn,
-                                    bool *done);
-static void cpool_run_conn_shutdown_handler(struct Curl_easy *data,
-                                            struct connectdata *conn);
-static CURLMcode cpool_update_shutdown_ev(struct Curl_multi *multi,
-                                          struct Curl_easy *data,
-                                          struct connectdata *conn);
-static void cpool_shutdown_all(struct cpool *cpool,
-                               struct Curl_easy *data, int timeout_ms);
-static void cpool_close_and_destroy_all(struct cpool *cpool);
-static struct connectdata *cpool_get_oldest_idle(struct cpool *cpool);
-static size_t cpool_shutdown_dest_count(struct cpool *cpool,
-                                        const char *destination);
-
-static struct cpool_bundle *cpool_bundle_create(const char *dest,
-                                                size_t dest_len)
-{
-  struct cpool_bundle *bundle;
-  bundle = calloc(1, sizeof(*bundle) + dest_len);
-  if(!bundle)
-    return NULL;
-  Curl_llist_init(&bundle->conns, NULL);
-  bundle->dest_len = dest_len;
-  memcpy(bundle->dest, dest, dest_len);
-  return bundle;
-}
-
-static void cpool_bundle_destroy(struct cpool_bundle *bundle)
-{
-  DEBUGASSERT(!Curl_llist_count(&bundle->conns));
-  free(bundle);
-}
-
-/* Add a connection to a bundle */
-static void cpool_bundle_add(struct cpool_bundle *bundle,
-                             struct connectdata *conn)
-{
-  DEBUGASSERT(!Curl_node_llist(&conn->cpool_node));
-  Curl_llist_append(&bundle->conns, conn, &conn->cpool_node);
-  conn->bits.in_cpool = TRUE;
-}
-
-/* Remove a connection from a bundle */
-static void cpool_bundle_remove(struct cpool_bundle *bundle,
-                                struct connectdata *conn)
-{
-  (void)bundle;
-  DEBUGASSERT(Curl_node_llist(&conn->cpool_node) == &bundle->conns);
-  Curl_node_remove(&conn->cpool_node);
-  conn->bits.in_cpool = FALSE;
-}
-
-static void cpool_bundle_free_entry(void *freethis)
-{
-  cpool_bundle_destroy((struct cpool_bundle *)freethis);
-}
-
-int Curl_cpool_init(struct cpool *cpool,
-                        Curl_cpool_disconnect_cb *disconnect_cb,
-                        struct Curl_multi *multi,
-                        struct Curl_share *share,
-                        size_t size)
-{
-  DEBUGASSERT(!!multi != !!share); /* either one */
-  Curl_hash_init(&cpool->dest2bundle, size, Curl_hash_str,
-                 Curl_str_key_compare, cpool_bundle_free_entry);
-  Curl_llist_init(&cpool->shutdowns, NULL);
-
-  DEBUGASSERT(disconnect_cb);
-  if(!disconnect_cb)
-    return 1;
-
-  /* allocate a new easy handle to use when closing cached connections */
-  cpool->idata = curl_easy_init();
-  if(!cpool->idata)
-    return 1; /* bad */
-  cpool->idata->state.internal = TRUE;
-  /* This is quirky. We need an internal handle for certain operations, but we
-   * do not add it to the multi (if there is one). We give it the multi so
-   * that socket event operations can work. Probably better to have an
-   * internal handle owned by the multi that can be used for cpool
-   * operations. */
-  cpool->idata->multi = multi;
-#ifdef DEBUGBUILD
-  if(getenv("CURL_DEBUG"))
-    cpool->idata->set.verbose = TRUE;
-#endif
-
-  cpool->disconnect_cb = disconnect_cb;
-  cpool->idata->multi = cpool->multi = multi;
-  cpool->idata->share = cpool->share = share;
-
-  return 0; /* good */
-}
-
-void Curl_cpool_destroy(struct cpool *cpool)
-{
-  if(cpool) {
-    if(cpool->idata) {
-      cpool_close_and_destroy_all(cpool);
-      /* The internal closure handle is special and we need to
-       * disconnect it from multi/share before closing it down. */
-      cpool->idata->multi = NULL;
-      cpool->idata->share = NULL;
-      Curl_close(&cpool->idata);
-    }
-    Curl_hash_destroy(&cpool->dest2bundle);
-    cpool->multi = NULL;
-  }
-}
-
-static struct cpool *cpool_get_instance(struct Curl_easy *data)
-{
-  if(data) {
-    if(CURL_SHARE_KEEP_CONNECT(data->share))
-      return &data->share->cpool;
-    else if(data->multi_easy)
-      return &data->multi_easy->cpool;
-    else if(data->multi)
-      return &data->multi->cpool;
-  }
-  return NULL;
-}
-
-void Curl_cpool_xfer_init(struct Curl_easy *data)
-{
-  struct cpool *cpool = cpool_get_instance(data);
-
-  DEBUGASSERT(cpool);
-  if(cpool) {
-    CPOOL_LOCK(cpool);
-    /* the identifier inside the connection cache */
-    data->id = cpool->next_easy_id++;
-    if(cpool->next_easy_id <= 0)
-      cpool->next_easy_id = 0;
-    data->state.lastconnect_id = -1;
-
-    /* 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
-       closure handle always has the same timeouts as the most recently added
-       easy handle. */
-    cpool->idata->set.timeout = data->set.timeout;
-    cpool->idata->set.server_response_timeout =
-      data->set.server_response_timeout;
-    cpool->idata->set.no_signal = data->set.no_signal;
-
-    CPOOL_UNLOCK(cpool);
-  }
-  else {
-    /* We should not get here, but in a non-debug build, do something */
-    data->id = 0;
-    data->state.lastconnect_id = -1;
-  }
-}
-
-static struct cpool_bundle *cpool_find_bundle(struct cpool *cpool,
-                                              struct connectdata *conn)
-{
-  return Curl_hash_pick(&cpool->dest2bundle,
-                        conn->destination, conn->destination_len);
-}
-
-static struct cpool_bundle *
-cpool_add_bundle(struct cpool *cpool, struct connectdata *conn)
-{
-  struct cpool_bundle *bundle;
-
-  bundle = cpool_bundle_create(conn->destination, conn->destination_len);
-  if(!bundle)
-    return NULL;
-
-  if(!Curl_hash_add(&cpool->dest2bundle,
-                    bundle->dest, bundle->dest_len, bundle)) {
-    cpool_bundle_destroy(bundle);
-    return NULL;
-  }
-  return bundle;
-}
-
-static void cpool_remove_bundle(struct cpool *cpool,
-                                struct cpool_bundle *bundle)
-{
-  if(!cpool)
-    return;
-
-  Curl_hash_delete(&cpool->dest2bundle, bundle->dest, bundle->dest_len);
-}
-
-static struct connectdata *
-cpool_bundle_get_oldest_idle(struct cpool_bundle *bundle);
-
-int Curl_cpool_check_limits(struct Curl_easy *data,
-                            struct connectdata *conn)
-{
-  struct cpool *cpool = cpool_get_instance(data);
-  struct cpool_bundle *bundle;
-  size_t dest_limit = 0;
-  size_t total_limit = 0;
-  size_t shutdowns;
-  int result = CPOOL_LIMIT_OK;
-
-  if(!cpool)
-    return CPOOL_LIMIT_OK;
-
-  if(data && data->multi) {
-    dest_limit = data->multi->max_host_connections;
-    total_limit = data->multi->max_total_connections;
-  }
-
-  if(!dest_limit && !total_limit)
-    return CPOOL_LIMIT_OK;
-
-  CPOOL_LOCK(cpool);
-  if(dest_limit) {
-    size_t live;
-
-    bundle = cpool_find_bundle(cpool, conn);
-    live = bundle ? Curl_llist_count(&bundle->conns) : 0;
-    shutdowns = cpool_shutdown_dest_count(cpool, conn->destination);
-    while(!shutdowns && bundle && live >= dest_limit) {
-      struct connectdata *oldest_idle = NULL;
-      /* The bundle is full. Extract the oldest connection that may
-       * be removed now, if there is one. */
-      oldest_idle = cpool_bundle_get_oldest_idle(bundle);
-      if(!oldest_idle)
-        break;
-      /* disconnect the old conn and continue */
-      DEBUGF(infof(data, "Discarding connection #%"
-                   FMT_OFF_T " from %zu to reach destination "
-                   "limit of %zu", oldest_idle->connection_id,
-                   Curl_llist_count(&bundle->conns), dest_limit));
-      Curl_cpool_disconnect(data, oldest_idle, FALSE);
-
-      /* in case the bundle was destroyed in disconnect, look it up again */
-      bundle = cpool_find_bundle(cpool, conn);
-      live = bundle ? Curl_llist_count(&bundle->conns) : 0;
-      shutdowns = cpool_shutdown_dest_count(cpool, conn->destination);
-    }
-    if((live + shutdowns) >= dest_limit) {
-      result = CPOOL_LIMIT_DEST;
-      goto out;
-    }
-  }
-
-  if(total_limit) {
-    shutdowns = Curl_llist_count(&cpool->shutdowns);
-    while((cpool->num_conn + shutdowns) >= total_limit) {
-      struct connectdata *oldest_idle = cpool_get_oldest_idle(cpool);
-      if(!oldest_idle)
-        break;
-      /* disconnect the old conn and continue */
-      DEBUGF(infof(data, "Discarding connection #%"
-                   FMT_OFF_T " from %zu to reach total "
-                   "limit of %zu",
-                   oldest_idle->connection_id, cpool->num_conn, total_limit));
-      Curl_cpool_disconnect(data, oldest_idle, FALSE);
-      shutdowns = Curl_llist_count(&cpool->shutdowns);
-    }
-    if((cpool->num_conn + shutdowns) >= total_limit) {
-      result = CPOOL_LIMIT_TOTAL;
-      goto out;
-    }
-  }
-
-out:
-  CPOOL_UNLOCK(cpool);
-  return result;
-}
-
-CURLcode Curl_cpool_add_conn(struct Curl_easy *data,
-                             struct connectdata *conn)
-{
-  CURLcode result = CURLE_OK;
-  struct cpool_bundle *bundle = NULL;
-  struct cpool *cpool = cpool_get_instance(data);
-  DEBUGASSERT(conn);
-
-  DEBUGASSERT(cpool);
-  if(!cpool)
-    return CURLE_FAILED_INIT;
-
-  CPOOL_LOCK(cpool);
-  bundle = cpool_find_bundle(cpool, conn);
-  if(!bundle) {
-    bundle = cpool_add_bundle(cpool, conn);
-    if(!bundle) {
-      result = CURLE_OUT_OF_MEMORY;
-      goto out;
-    }
-  }
-
-  cpool_bundle_add(bundle, conn);
-  conn->connection_id = cpool->next_connection_id++;
-  cpool->num_conn++;
-  DEBUGF(infof(data, "Added connection %" FMT_OFF_T ". "
-               "The cache now contains %zu members",
-               conn->connection_id,
-               cpool->num_conn + Curl_llist_count(&cpool->shutdowns)));
-out:
-  CPOOL_UNLOCK(cpool);
-
-  return result;
-}
-
-static void cpool_remove_conn(struct cpool *cpool,
-                              struct connectdata *conn)
-{
-  struct Curl_llist *list = Curl_node_llist(&conn->cpool_node);
-  DEBUGASSERT(cpool);
-  if(list) {
-    /* The connection is certainly in the pool, but where? */
-    struct cpool_bundle *bundle = cpool_find_bundle(cpool, conn);
-    if(bundle && (list == &bundle->conns)) {
-      cpool_bundle_remove(bundle, conn);
-      if(!Curl_llist_count(&bundle->conns))
-        cpool_remove_bundle(cpool, bundle);
-      conn->bits.in_cpool = FALSE;
-      cpool->num_conn--;
-    }
-    else {
-      /* Not in a bundle, already in the shutdown list? */
-      DEBUGASSERT(list == &cpool->shutdowns);
-    }
-  }
-}
-
-/* This function iterates the entire connection pool and calls the function
-   func() with the connection pointer as the first argument and the supplied
-   'param' argument as the other.
-
-   The cpool lock is still held when the callback is called. It needs it,
-   so that it can safely continue traversing the lists once the callback
-   returns.
-
-   Returns TRUE if the loop was aborted due to the callback's return code.
-
-   Return 0 from func() to continue the loop, return 1 to abort it.
- */
-static bool cpool_foreach(struct Curl_easy *data,
-                          struct cpool *cpool,
-                          void *param,
-                          int (*func)(struct Curl_easy *data,
-                                      struct connectdata *conn, void *param))
-{
-  struct Curl_hash_iterator iter;
-  struct Curl_hash_element *he;
-
-  if(!cpool)
-    return FALSE;
-
-  Curl_hash_start_iterate(&cpool->dest2bundle, &iter);
-
-  he = Curl_hash_next_element(&iter);
-  while(he) {
-    struct Curl_llist_node *curr;
-    struct cpool_bundle *bundle = he->ptr;
-    he = Curl_hash_next_element(&iter);
-
-    curr = Curl_llist_head(&bundle->conns);
-    while(curr) {
-      /* Yes, we need to update curr before calling func(), because func()
-         might decide to remove the connection */
-      struct connectdata *conn = Curl_node_elem(curr);
-      curr = Curl_node_next(curr);
-
-      if(1 == func(data, conn, param)) {
-        return TRUE;
-      }
-    }
-  }
-  return FALSE;
-}
-
-/* Return a live connection in the pool or NULL. */
-static struct connectdata *cpool_get_live_conn(struct cpool *cpool)
-{
-  struct Curl_hash_iterator iter;
-  struct Curl_hash_element *he;
-  struct cpool_bundle *bundle;
-  struct Curl_llist_node *conn_node;
-
-  Curl_hash_start_iterate(&cpool->dest2bundle, &iter);
-  for(he = Curl_hash_next_element(&iter); he;
-      he = Curl_hash_next_element(&iter)) {
-    bundle = he->ptr;
-    conn_node = Curl_llist_head(&bundle->conns);
-    if(conn_node)
-      return Curl_node_elem(conn_node);
-  }
-  return NULL;
-}
-
-/*
- * A connection (already in the pool) has become idle. Do any
- * cleanups in regard to the pool's limits.
- *
- * Return TRUE if idle connection kept in pool, FALSE if closed.
- */
-bool Curl_cpool_conn_now_idle(struct Curl_easy *data,
-                              struct connectdata *conn)
-{
-  unsigned int maxconnects = !data->multi->maxconnects ?
-    data->multi->num_easy * 4 : data->multi->maxconnects;
-  struct connectdata *oldest_idle = NULL;
-  struct cpool *cpool = cpool_get_instance(data);
-  bool kept = TRUE;
-
-  conn->lastused = Curl_now(); /* it was used up until now */
-  if(cpool && maxconnects) {
-    /* may be called form a callback already under lock */
-    bool do_lock = !CPOOL_IS_LOCKED(cpool);
-    if(do_lock)
-      CPOOL_LOCK(cpool);
-    if(cpool->num_conn > maxconnects) {
-      infof(data, "Connection pool is full, closing the oldest one");
-
-      oldest_idle = cpool_get_oldest_idle(cpool);
-      kept = (oldest_idle != conn);
-      if(oldest_idle) {
-        Curl_cpool_disconnect(cpool->idata, oldest_idle, FALSE);
-      }
-    }
-    if(do_lock)
-      CPOOL_UNLOCK(cpool);
-  }
-
-  return kept;
-}
-
-/*
- * This function finds the connection in the connection bundle that has been
- * unused for the longest time.
- */
-static struct connectdata *
-cpool_bundle_get_oldest_idle(struct cpool_bundle *bundle)
-{
-  struct Curl_llist_node *curr;
-  timediff_t highscore = -1;
-  timediff_t score;
-  struct curltime now;
-  struct connectdata *oldest_idle = NULL;
-  struct connectdata *conn;
-
-  now = Curl_now();
-  curr = Curl_llist_head(&bundle->conns);
-  while(curr) {
-    conn = Curl_node_elem(curr);
-
-    if(!CONN_INUSE(conn)) {
-      /* Set higher score for the age passed since the connection was used */
-      score = Curl_timediff(now, conn->lastused);
-
-      if(score > highscore) {
-        highscore = score;
-        oldest_idle = conn;
-      }
-    }
-    curr = Curl_node_next(curr);
-  }
-  return oldest_idle;
-}
-
-static struct connectdata *cpool_get_oldest_idle(struct cpool *cpool)
-{
-  struct Curl_hash_iterator iter;
-  struct Curl_llist_node *curr;
-  struct Curl_hash_element *he;
-  struct connectdata *oldest_idle = NULL;
-  struct cpool_bundle *bundle;
-  struct curltime now;
-  timediff_t highscore =- 1;
-  timediff_t score;
-
-  now = Curl_now();
-  Curl_hash_start_iterate(&cpool->dest2bundle, &iter);
-
-  for(he = Curl_hash_next_element(&iter); he;
-      he = Curl_hash_next_element(&iter)) {
-    struct connectdata *conn;
-    bundle = he->ptr;
-
-    for(curr = Curl_llist_head(&bundle->conns); curr;
-        curr = Curl_node_next(curr)) {
-      conn = Curl_node_elem(curr);
-      if(CONN_INUSE(conn) || conn->bits.close || conn->connect_only)
-        continue;
-      /* Set higher score for the age passed since the connection was used */
-      score = Curl_timediff(now, conn->lastused);
-      if(score > highscore) {
-        highscore = score;
-        oldest_idle = conn;
-      }
-    }
-  }
-  return oldest_idle;
-}
-
-bool Curl_cpool_find(struct Curl_easy *data,
-                     const char *destination, size_t dest_len,
-                     Curl_cpool_conn_match_cb *conn_cb,
-                     Curl_cpool_done_match_cb *done_cb,
-                     void *userdata)
-{
-  struct cpool *cpool = cpool_get_instance(data);
-  struct cpool_bundle *bundle;
-  bool result = FALSE;
-
-  DEBUGASSERT(cpool);
-  DEBUGASSERT(conn_cb);
-  if(!cpool)
-    return FALSE;
-
-  CPOOL_LOCK(cpool);
-  bundle = Curl_hash_pick(&cpool->dest2bundle, (void *)destination, dest_len);
-  if(bundle) {
-    struct Curl_llist_node *curr = Curl_llist_head(&bundle->conns);
-    while(curr) {
-      struct connectdata *conn = Curl_node_elem(curr);
-      /* Get next node now. callback might discard current */
-      curr = Curl_node_next(curr);
-
-      if(conn_cb(conn, userdata)) {
-        result = TRUE;
-        break;
-      }
-    }
-  }
-
-  if(done_cb) {
-    result = done_cb(result, userdata);
-  }
-  CPOOL_UNLOCK(cpool);
-  return result;
-}
-
-/* How many connections to the given destination are in shutdown? */
-static size_t cpool_shutdown_dest_count(struct cpool *cpool,
-                                        const char *destination)
-{
-  size_t n = 0;
-  struct Curl_llist_node *e = Curl_llist_head(&cpool->shutdowns);
-  while(e) {
-    struct connectdata *conn = Curl_node_elem(e);
-    if(!strcmp(destination, conn->destination))
-      ++n;
-    e = Curl_node_next(e);
-  }
-  return n;
-}
-
-static void cpool_shutdown_discard_all(struct cpool *cpool)
-{
-  struct Curl_llist_node *e = Curl_llist_head(&cpool->shutdowns);
-  struct connectdata *conn;
-
-  if(!e)
-    return;
-
-  DEBUGF(infof(cpool->idata, "cpool_shutdown_discard_all"));
-  while(e) {
-    conn = Curl_node_elem(e);
-    Curl_node_remove(e);
-    DEBUGF(infof(cpool->idata, "discard connection #%" FMT_OFF_T,
-                 conn->connection_id));
-    cpool_close_and_destroy(cpool, conn, NULL, FALSE);
-    e = Curl_llist_head(&cpool->shutdowns);
-  }
-}
-
-static void cpool_close_and_destroy_all(struct cpool *cpool)
-{
-  struct connectdata *conn;
-  int timeout_ms = 0;
-  SIGPIPE_VARIABLE(pipe_st);
-
-  DEBUGASSERT(cpool);
-  /* Move all connections to the shutdown list */
-  sigpipe_init(&pipe_st);
-  CPOOL_LOCK(cpool);
-  conn = cpool_get_live_conn(cpool);
-  while(conn) {
-    cpool_remove_conn(cpool, conn);
-    sigpipe_apply(cpool->idata, &pipe_st);
-    connclose(conn, "kill all");
-    cpool_discard_conn(cpool, cpool->idata, conn, FALSE);
-
-    conn = cpool_get_live_conn(cpool);
-  }
-  CPOOL_UNLOCK(cpool);
-
-    /* Just for testing, run graceful shutdown */
-#ifdef DEBUGBUILD
-  {
-    char *p = getenv("CURL_GRACEFUL_SHUTDOWN");
-    if(p) {
-      long l = strtol(p, NULL, 10);
-      if(l > 0 && l < INT_MAX)
-        timeout_ms = (int)l;
-    }
-  }
-#endif
-  sigpipe_apply(cpool->idata, &pipe_st);
-  cpool_shutdown_all(cpool, cpool->idata, timeout_ms);
-
-  /* discard all connections in the shutdown list */
-  cpool_shutdown_discard_all(cpool);
-
-  Curl_hostcache_clean(cpool->idata, cpool->idata->dns.hostcache);
-  sigpipe_restore(&pipe_st);
-}
-
-
-static void cpool_shutdown_destroy_oldest(struct cpool *cpool)
-{
-  struct Curl_llist_node *e;
-  struct connectdata *conn;
-
-  e = Curl_llist_head(&cpool->shutdowns);
-  if(e) {
-    SIGPIPE_VARIABLE(pipe_st);
-    conn = Curl_node_elem(e);
-    Curl_node_remove(e);
-    sigpipe_init(&pipe_st);
-    sigpipe_apply(cpool->idata, &pipe_st);
-    cpool_close_and_destroy(cpool, conn, NULL, FALSE);
-    sigpipe_restore(&pipe_st);
-  }
-}
-
-static void cpool_discard_conn(struct cpool *cpool,
-                               struct Curl_easy *data,
-                               struct connectdata *conn,
-                               bool aborted)
-{
-  bool done = FALSE;
-
-  DEBUGASSERT(data);
-  DEBUGASSERT(cpool);
-  DEBUGASSERT(!conn->bits.in_cpool);
-
-  /*
-   * If this connection is not marked to force-close, leave it open if there
-   * are other users of it
-   */
-  if(CONN_INUSE(conn) && !aborted) {
-    DEBUGF(infof(data, "[CCACHE] not discarding #%" FMT_OFF_T
-                 " still in use by %zu transfers", conn->connection_id,
-                 CONN_INUSE(conn)));
-    return;
-  }
-
-  /* treat the connection as aborted in CONNECT_ONLY situations, we do
-   * not know what the APP did with it. */
-  if(conn->connect_only)
-    aborted = TRUE;
-  conn->bits.aborted = aborted;
-
-  /* We do not shutdown dead connections. The term 'dead' can be misleading
-   * here, as we also mark errored connections/transfers as 'dead'.
-   * If we do a shutdown for an aborted transfer, the server might think
-   * it was successful otherwise (for example an ftps: upload). This is
-   * not what we want. */
-  if(aborted)
-    done = TRUE;
-  if(!done) {
-    /* Attempt to shutdown the connection right away. */
-    Curl_attach_connection(data, conn);
-    cpool_run_conn_shutdown(data, conn, &done);
-    DEBUGF(infof(data, "[CCACHE] shutdown #%" FMT_OFF_T ", done=%d",
-                 conn->connection_id, done));
-    Curl_detach_connection(data);
-  }
-
-  if(done) {
-    cpool_close_and_destroy(cpool, conn, data, FALSE);
-    return;
-  }
-
-  /* Add the connection to our shutdown list for non-blocking shutdown
-   * during multi processing. */
-  if(data->multi && data->multi->max_total_connections > 0 &&
-     (data->multi->max_total_connections <=
-      (long)(cpool->num_conn + Curl_llist_count(&cpool->shutdowns)))) {
-    DEBUGF(infof(data, "[CCACHE] discarding oldest shutdown connection "
-                       "due to connection limit of %ld",
-                       data->multi->max_total_connections));
-    cpool_shutdown_destroy_oldest(cpool);
-  }
-
-  if(data->multi && data->multi->socket_cb) {
-    DEBUGASSERT(cpool == &data->multi->cpool);
-    /* Start with an empty shutdown pollset, so out internal closure handle
-     * is added to the sockets. */
-    memset(&conn->shutdown_poll, 0, sizeof(conn->shutdown_poll));
-    if(cpool_update_shutdown_ev(data->multi, cpool->idata, conn)) {
-      DEBUGF(infof(data, "[CCACHE] update events for shutdown failed, "
-                   "discarding #%" FMT_OFF_T,
-                   conn->connection_id));
-      cpool_close_and_destroy(cpool, conn, data, FALSE);
-      return;
-    }
-  }
-
-  Curl_llist_append(&cpool->shutdowns, conn, &conn->cpool_node);
-  DEBUGF(infof(data, "[CCACHE] added #%" FMT_OFF_T
-               " to shutdowns, now %zu conns in shutdown",
-               conn->connection_id, Curl_llist_count(&cpool->shutdowns)));
-}
-
-void Curl_cpool_disconnect(struct Curl_easy *data,
-                           struct connectdata *conn,
-                           bool aborted)
-{
-  struct cpool *cpool = cpool_get_instance(data);
-  bool do_lock;
-
-  DEBUGASSERT(cpool);
-  DEBUGASSERT(data && !data->conn);
-  if(!cpool)
-    return;
-
-  /* If this connection is not marked to force-close, leave it open if there
-   * are other users of it */
-  if(CONN_INUSE(conn) && !aborted) {
-    DEBUGASSERT(0); /* does this ever happen? */
-    DEBUGF(infof(data, "Curl_disconnect when inuse: %zu", CONN_INUSE(conn)));
-    return;
-  }
-
-  /* This method may be called while we are under lock, e.g. from a
-   * user callback in find. */
-  do_lock = !CPOOL_IS_LOCKED(cpool);
-  if(do_lock)
-    CPOOL_LOCK(cpool);
-
-  if(conn->bits.in_cpool) {
-    cpool_remove_conn(cpool, conn);
-    DEBUGASSERT(!conn->bits.in_cpool);
-  }
-
-  /* Run the callback to let it clean up anything it wants to. */
-  aborted = cpool->disconnect_cb(data, conn, aborted);
-
-  if(data->multi) {
-    /* Add it to the multi's cpool for shutdown handling */
-    infof(data, "%s connection #%" FMT_OFF_T,
-          aborted ? "closing" : "shutting down", conn->connection_id);
-    cpool_discard_conn(&data->multi->cpool, data, conn, aborted);
-  }
-  else {
-    /* No multi available. Make a best-effort shutdown + close */
-    infof(data, "closing connection #%" FMT_OFF_T, conn->connection_id);
-    cpool_close_and_destroy(NULL, conn, data, !aborted);
-  }
-
-  if(do_lock)
-    CPOOL_UNLOCK(cpool);
-}
-
-static void cpool_run_conn_shutdown_handler(struct Curl_easy *data,
-                                            struct connectdata *conn)
-{
-  if(!conn->bits.shutdown_handler) {
-    if(conn->dns_entry)
-      Curl_resolv_unlink(data, &conn->dns_entry);
-
-    /* Cleanup NTLM connection-related data */
-    Curl_http_auth_cleanup_ntlm(conn);
-
-    /* Cleanup NEGOTIATE connection-related data */
-    Curl_http_auth_cleanup_negotiate(conn);
-
-    if(conn->handler && conn->handler->disconnect) {
-      /* This is set if protocol-specific cleanups should be made */
-      DEBUGF(infof(data, "connection #%" FMT_OFF_T
-                   ", shutdown protocol handler (aborted=%d)",
-                   conn->connection_id, conn->bits.aborted));
-
-      conn->handler->disconnect(data, conn, conn->bits.aborted);
-    }
-
-    /* possible left-overs from the async name resolvers */
-    Curl_resolver_cancel(data);
-
-    conn->bits.shutdown_handler = TRUE;
-  }
-}
-
-static void cpool_run_conn_shutdown(struct Curl_easy *data,
-                                    struct connectdata *conn,
-                                    bool *done)
-{
-  CURLcode r1, r2;
-  bool done1, done2;
-
-  /* We expect to be attached when called */
-  DEBUGASSERT(data->conn == conn);
-
-  cpool_run_conn_shutdown_handler(data, conn);
-
-  if(conn->bits.shutdown_filters) {
-    *done = TRUE;
-    return;
-  }
-
-  if(!conn->connect_only && Curl_conn_is_connected(conn, FIRSTSOCKET))
-    r1 = Curl_conn_shutdown(data, FIRSTSOCKET, &done1);
-  else {
-    r1 = CURLE_OK;
-    done1 = TRUE;
-  }
-
-  if(!conn->connect_only && Curl_conn_is_connected(conn, SECONDARYSOCKET))
-    r2 = Curl_conn_shutdown(data, SECONDARYSOCKET, &done2);
-  else {
-    r2 = CURLE_OK;
-    done2 = TRUE;
-  }
-
-  /* we are done when any failed or both report success */
-  *done = (r1 || r2 || (done1 && done2));
-  if(*done)
-    conn->bits.shutdown_filters = TRUE;
-}
-
-static CURLcode cpool_add_pollfds(struct cpool *cpool,
-                                  struct curl_pollfds *cpfds)
-{
-  CURLcode result = CURLE_OK;
-
-  if(Curl_llist_head(&cpool->shutdowns)) {
-    struct Curl_llist_node *e;
-    struct easy_pollset ps;
-    struct connectdata *conn;
-
-    for(e = Curl_llist_head(&cpool->shutdowns); e;
-        e = Curl_node_next(e)) {
-      conn = Curl_node_elem(e);
-      memset(&ps, 0, sizeof(ps));
-      Curl_attach_connection(cpool->idata, conn);
-      Curl_conn_adjust_pollset(cpool->idata, &ps);
-      Curl_detach_connection(cpool->idata);
-
-      result = Curl_pollfds_add_ps(cpfds, &ps);
-      if(result) {
-        Curl_pollfds_cleanup(cpfds);
-        goto out;
-      }
-    }
-  }
-out:
-  return result;
-}
-
-CURLcode Curl_cpool_add_pollfds(struct cpool *cpool,
-                                struct curl_pollfds *cpfds)
-{
-  CURLcode result;
-  CPOOL_LOCK(cpool);
-  result = cpool_add_pollfds(cpool, cpfds);
-  CPOOL_UNLOCK(cpool);
-  return result;
-}
-
-/* return information about the shutdown connections */
-unsigned int Curl_cpool_add_waitfds(struct cpool *cpool,
-                                    struct Curl_waitfds *cwfds)
-{
-  unsigned int need = 0;
-
-  CPOOL_LOCK(cpool);
-  if(Curl_llist_head(&cpool->shutdowns)) {
-    struct Curl_llist_node *e;
-    struct easy_pollset ps;
-    struct connectdata *conn;
-
-    for(e = Curl_llist_head(&cpool->shutdowns); e;
-        e = Curl_node_next(e)) {
-      conn = Curl_node_elem(e);
-      memset(&ps, 0, sizeof(ps));
-      Curl_attach_connection(cpool->idata, conn);
-      Curl_conn_adjust_pollset(cpool->idata, &ps);
-      Curl_detach_connection(cpool->idata);
-
-      need += Curl_waitfds_add_ps(cwfds, &ps);
-    }
-  }
-  CPOOL_UNLOCK(cpool);
-  return need;
-}
-
-/* return fd_set info about the shutdown connections */
-void Curl_cpool_setfds(struct cpool *cpool,
-                       fd_set *read_fd_set, fd_set *write_fd_set,
-                       int *maxfd)
-{
-  CPOOL_LOCK(cpool);
-  if(Curl_llist_head(&cpool->shutdowns)) {
-    struct Curl_llist_node *e;
-
-    for(e = Curl_llist_head(&cpool->shutdowns); e;
-        e = Curl_node_next(e)) {
-      struct easy_pollset ps;
-      unsigned int i;
-      struct connectdata *conn = Curl_node_elem(e);
-      memset(&ps, 0, sizeof(ps));
-      Curl_attach_connection(cpool->idata, conn);
-      Curl_conn_adjust_pollset(cpool->idata, &ps);
-      Curl_detach_connection(cpool->idata);
-
-      for(i = 0; i < ps.num; i++) {
-#if defined(__DJGPP__)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Warith-conversion"
-#endif
-        if(ps.actions[i] & CURL_POLL_IN)
-          FD_SET(ps.sockets[i], read_fd_set);
-        if(ps.actions[i] & CURL_POLL_OUT)
-          FD_SET(ps.sockets[i], write_fd_set);
-#if defined(__DJGPP__)
-#pragma GCC diagnostic pop
-#endif
-        if((ps.actions[i] & (CURL_POLL_OUT | CURL_POLL_IN)) &&
-           ((int)ps.sockets[i] > *maxfd))
-          *maxfd = (int)ps.sockets[i];
-      }
-    }
-  }
-  CPOOL_UNLOCK(cpool);
-}
-
-static void cpool_perform(struct cpool *cpool)
-{
-  struct Curl_easy *data = cpool->idata;
-  struct Curl_llist_node *e = Curl_llist_head(&cpool->shutdowns);
-  struct Curl_llist_node *enext;
-  struct connectdata *conn;
-  struct curltime *nowp = NULL;
-  struct curltime now;
-  timediff_t next_from_now_ms = 0, ms;
-  bool done;
-
-  if(!e)
-    return;
-
-  DEBUGASSERT(data);
-  DEBUGF(infof(data, "[CCACHE] perform, %zu connections being shutdown",
-               Curl_llist_count(&cpool->shutdowns)));
-  while(e) {
-    enext = Curl_node_next(e);
-    conn = Curl_node_elem(e);
-    Curl_attach_connection(data, conn);
-    cpool_run_conn_shutdown(data, conn, &done);
-    DEBUGF(infof(data, "[CCACHE] shutdown #%" FMT_OFF_T ", done=%d",
-                 conn->connection_id, done));
-    Curl_detach_connection(data);
-    if(done) {
-      Curl_node_remove(e);
-      cpool_close_and_destroy(cpool, conn, NULL, FALSE);
-    }
-    else {
-      /* Not done, when does this connection time out? */
-      if(!nowp) {
-        now = Curl_now();
-        nowp = &now;
-      }
-      ms = Curl_conn_shutdown_timeleft(conn, nowp);
-      if(ms && ms < next_from_now_ms)
-        next_from_now_ms = ms;
-    }
-    e = enext;
-  }
-
-  if(next_from_now_ms)
-    Curl_expire(data, next_from_now_ms, EXPIRE_RUN_NOW);
-}
-
-void Curl_cpool_multi_perform(struct Curl_multi *multi)
-{
-  CPOOL_LOCK(&multi->cpool);
-  cpool_perform(&multi->cpool);
-  CPOOL_UNLOCK(&multi->cpool);
-}
-
-
-/*
- * Close and destroy the connection. Run the shutdown sequence once,
- * of so requested.
- */
-static void cpool_close_and_destroy(struct cpool *cpool,
-                                    struct connectdata *conn,
-                                    struct Curl_easy *data,
-                                    bool do_shutdown)
-{
-  bool done;
-
-  /* there must be a connection to close */
-  DEBUGASSERT(conn);
-  /* it must be removed from the connection pool */
-  DEBUGASSERT(!conn->bits.in_cpool);
-  /* there must be an associated transfer */
-  DEBUGASSERT(data || cpool);
-  if(!data)
-    data = cpool->idata;
-
-  /* the transfer must be detached from the connection */
-  DEBUGASSERT(data && !data->conn);
-
-  Curl_attach_connection(data, conn);
-
-  cpool_run_conn_shutdown_handler(data, conn);
-  if(do_shutdown) {
-    /* Make a last attempt to shutdown handlers and filters, if
-     * not done so already. */
-    cpool_run_conn_shutdown(data, conn, &done);
-  }
-
-  if(cpool)
-    DEBUGF(infof(data, "[CCACHE] closing #%" FMT_OFF_T,
-                 conn->connection_id));
-  else
-    DEBUGF(infof(data, "closing connection #%" FMT_OFF_T,
-                 conn->connection_id));
-  Curl_conn_close(data, SECONDARYSOCKET);
-  Curl_conn_close(data, FIRSTSOCKET);
-  Curl_detach_connection(data);
-
-  Curl_conn_free(data, conn);
-
-  if(cpool && cpool->multi) {
-    DEBUGF(infof(data, "[CCACHE] trigger multi connchanged"));
-    Curl_multi_connchanged(cpool->multi);
-  }
-}
-
-
-static CURLMcode cpool_update_shutdown_ev(struct Curl_multi *multi,
-                                          struct Curl_easy *data,
-                                          struct connectdata *conn)
-{
-  struct easy_pollset ps;
-  CURLMcode mresult;
-
-  DEBUGASSERT(data);
-  DEBUGASSERT(multi);
-  DEBUGASSERT(multi->socket_cb);
-
-  memset(&ps, 0, sizeof(ps));
-  Curl_attach_connection(data, conn);
-  Curl_conn_adjust_pollset(data, &ps);
-  Curl_detach_connection(data);
-
-  mresult = Curl_multi_pollset_ev(multi, data, &ps, &conn->shutdown_poll);
-
-  if(!mresult) /* Remember for next time */
-    memcpy(&conn->shutdown_poll, &ps, sizeof(ps));
-  return mresult;
-}
-
-void Curl_cpool_multi_socket(struct Curl_multi *multi,
-                             curl_socket_t s, int ev_bitmask)
-{
-  struct cpool *cpool = &multi->cpool;
-  struct Curl_easy *data = cpool->idata;
-  struct Curl_llist_node *e;
-  struct connectdata *conn;
-  bool done;
-
-  (void)ev_bitmask;
-  DEBUGASSERT(multi->socket_cb);
-  CPOOL_LOCK(cpool);
-  e = Curl_llist_head(&cpool->shutdowns);
-  while(e) {
-    conn = Curl_node_elem(e);
-    if(s == conn->sock[FIRSTSOCKET] || s == conn->sock[SECONDARYSOCKET]) {
-      Curl_attach_connection(data, conn);
-      cpool_run_conn_shutdown(data, conn, &done);
-      DEBUGF(infof(data, "[CCACHE] shutdown #%" FMT_OFF_T ", done=%d",
-                   conn->connection_id, done));
-      Curl_detach_connection(data);
-      if(done || cpool_update_shutdown_ev(multi, data, conn)) {
-        Curl_node_remove(e);
-        cpool_close_and_destroy(cpool, conn, NULL, FALSE);
-      }
-      break;
-    }
-    e = Curl_node_next(e);
-  }
-  CPOOL_UNLOCK(cpool);
-}
-
-#define NUM_POLLS_ON_STACK 10
-
-static CURLcode cpool_shutdown_wait(struct cpool *cpool, int timeout_ms)
-{
-  struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK];
-  struct curl_pollfds cpfds;
-  CURLcode result;
-
-  Curl_pollfds_init(&cpfds, a_few_on_stack, NUM_POLLS_ON_STACK);
-
-  result = cpool_add_pollfds(cpool, &cpfds);
-  if(result)
-    goto out;
-
-  Curl_poll(cpfds.pfds, cpfds.n, CURLMIN(timeout_ms, 1000));
-
-out:
-  Curl_pollfds_cleanup(&cpfds);
-  return result;
-}
-
-static void cpool_shutdown_all(struct cpool *cpool,
-                               struct Curl_easy *data, int timeout_ms)
-{
-  struct connectdata *conn;
-  struct curltime started = Curl_now();
-
-  if(!data)
-    return;
-  (void)data;
-
-  DEBUGF(infof(data, "cpool shutdown all"));
-
-  /* Move all connections into the shutdown queue */
-  for(conn = cpool_get_live_conn(cpool); conn;
-      conn = cpool_get_live_conn(cpool)) {
-    /* Move conn from live set to shutdown or destroy right away */
-    DEBUGF(infof(data, "moving connection #%" FMT_OFF_T
-                 " to shutdown queue", conn->connection_id));
-    cpool_remove_conn(cpool, conn);
-    cpool_discard_conn(cpool, data, conn, FALSE);
-  }
-
-  while(Curl_llist_head(&cpool->shutdowns)) {
-    timediff_t timespent;
-    int remain_ms;
-
-    cpool_perform(cpool);
-
-    if(!Curl_llist_head(&cpool->shutdowns)) {
-      DEBUGF(infof(data, "cpool shutdown ok"));
-      break;
-    }
-
-    /* wait for activity, timeout or "nothing" */
-    timespent = Curl_timediff(Curl_now(), started);
-    if(timespent >= (timediff_t)timeout_ms) {
-      DEBUGF(infof(data, "cpool shutdown %s",
-                   (timeout_ms > 0) ? "timeout" : "best effort done"));
-      break;
-    }
-
-    remain_ms = timeout_ms - (int)timespent;
-    if(cpool_shutdown_wait(cpool, remain_ms)) {
-      DEBUGF(infof(data, "cpool shutdown all, abort"));
-      break;
-    }
-  }
-
-  /* Due to errors/timeout, we might come here without being done. */
-  cpool_shutdown_discard_all(cpool);
-}
-
-struct cpool_reaper_ctx {
-  struct curltime now;
-};
-
-static int cpool_reap_dead_cb(struct Curl_easy *data,
-                              struct connectdata *conn, void *param)
-{
-  struct cpool_reaper_ctx *rctx = param;
-  if(Curl_conn_seems_dead(conn, data, &rctx->now)) {
-    /* stop the iteration here, pass back the connection that was pruned */
-    Curl_cpool_disconnect(data, conn, FALSE);
-    return 1;
-  }
-  return 0; /* continue iteration */
-}
-
-/*
- * This function scans the data's connection pool for half-open/dead
- * connections, closes and removes them.
- * The cleanup is done at most once per second.
- *
- * When called, this transfer has no connection attached.
- */
-void Curl_cpool_prune_dead(struct Curl_easy *data)
-{
-  struct cpool *cpool = cpool_get_instance(data);
-  struct cpool_reaper_ctx rctx;
-  timediff_t elapsed;
-
-  if(!cpool)
-    return;
-
-  rctx.now = Curl_now();
-  CPOOL_LOCK(cpool);
-  elapsed = Curl_timediff(rctx.now, cpool->last_cleanup);
-
-  if(elapsed >= 1000L) {
-    while(cpool_foreach(data, cpool, &rctx, cpool_reap_dead_cb))
-      ;
-    cpool->last_cleanup = rctx.now;
-  }
-  CPOOL_UNLOCK(cpool);
-}
-
-static int conn_upkeep(struct Curl_easy *data,
-                       struct connectdata *conn,
-                       void *param)
-{
-  struct curltime *now = param;
-  Curl_conn_upkeep(data, conn, now);
-  return 0; /* continue iteration */
-}
-
-CURLcode Curl_cpool_upkeep(void *data)
-{
-  struct cpool *cpool = cpool_get_instance(data);
-  struct curltime now = Curl_now();
-
-  if(!cpool)
-    return CURLE_OK;
-
-  CPOOL_LOCK(cpool);
-  cpool_foreach(data, cpool, &now, conn_upkeep);
-  CPOOL_UNLOCK(cpool);
-  return CURLE_OK;
-}
-
-struct cpool_find_ctx {
-  curl_off_t id;
-  struct connectdata *conn;
-};
-
-static int cpool_find_conn(struct Curl_easy *data,
-                           struct connectdata *conn, void *param)
-{
-  struct cpool_find_ctx *fctx = param;
-  (void)data;
-  if(conn->connection_id == fctx->id) {
-    fctx->conn = conn;
-    return 1;
-  }
-  return 0;
-}
-
-struct connectdata *Curl_cpool_get_conn(struct Curl_easy *data,
-                                        curl_off_t conn_id)
-{
-  struct cpool *cpool = cpool_get_instance(data);
-  struct cpool_find_ctx fctx;
-
-  if(!cpool)
-    return NULL;
-  fctx.id = conn_id;
-  fctx.conn = NULL;
-  CPOOL_LOCK(cpool);
-  cpool_foreach(cpool->idata, cpool, &fctx, cpool_find_conn);
-  CPOOL_UNLOCK(cpool);
-  return fctx.conn;
-}
-
-struct cpool_do_conn_ctx {
-  curl_off_t id;
-  Curl_cpool_conn_do_cb *cb;
-  void *cbdata;
-};
-
-static int cpool_do_conn(struct Curl_easy *data,
-                         struct connectdata *conn, void *param)
-{
-  struct cpool_do_conn_ctx *dctx = param;
-  (void)data;
-  if(conn->connection_id == dctx->id) {
-    dctx->cb(conn, data, dctx->cbdata);
-    return 1;
-  }
-  return 0;
-}
-
-void Curl_cpool_do_by_id(struct Curl_easy *data, curl_off_t conn_id,
-                         Curl_cpool_conn_do_cb *cb, void *cbdata)
-{
-  struct cpool *cpool = cpool_get_instance(data);
-  struct cpool_do_conn_ctx dctx;
-
-  if(!cpool)
-    return;
-  dctx.id = conn_id;
-  dctx.cb = cb;
-  dctx.cbdata = cbdata;
-  CPOOL_LOCK(cpool);
-  cpool_foreach(data, cpool, &dctx, cpool_do_conn);
-  CPOOL_UNLOCK(cpool);
-}
-
-void Curl_cpool_do_locked(struct Curl_easy *data,
-                          struct connectdata *conn,
-                          Curl_cpool_conn_do_cb *cb, void *cbdata)
-{
-  struct cpool *cpool = cpool_get_instance(data);
-  if(cpool) {
-    CPOOL_LOCK(cpool);
-    cb(conn, data, cbdata);
-    CPOOL_UNLOCK(cpool);
-  }
-  else
-    cb(conn, data, cbdata);
-}
-
-#if 0
-/* Useful for debugging the connection pool */
-void Curl_cpool_print(struct cpool *cpool)
-{
-  struct Curl_hash_iterator iter;
-  struct Curl_llist_node *curr;
-  struct Curl_hash_element *he;
-
-  if(!cpool)
-    return;
-
-  fprintf(stderr, "=Bundle cache=\n");
-
-  Curl_hash_start_iterate(cpool->dest2bundle, &iter);
-
-  he = Curl_hash_next_element(&iter);
-  while(he) {
-    struct cpool_bundle *bundle;
-    struct connectdata *conn;
-
-    bundle = he->ptr;
-
-    fprintf(stderr, "%s -", he->key);
-    curr = Curl_llist_head(bundle->conns);
-    while(curr) {
-      conn = Curl_node_elem(curr);
-
-      fprintf(stderr, " [%p %d]", (void *)conn, conn->refcount);
-      curr = Curl_node_next(curr);
-    }
-    fprintf(stderr, "\n");
-
-    he = Curl_hash_next_element(&iter);
-  }
-}
-#endif
diff --git a/extra/curl/curl-8.12.1/lib/curl_sspi.h b/extra/curl/curl-8.12.1/lib/curl_sspi.h
deleted file mode 100644
index ac39afa253e8..000000000000
--- a/extra/curl/curl-8.12.1/lib/curl_sspi.h
+++ /dev/null
@@ -1,123 +0,0 @@
-#ifndef HEADER_CURL_SSPI_H
-#define HEADER_CURL_SSPI_H
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifdef USE_WINDOWS_SSPI
-
-#include <curl/curl.h>
-
-/*
- * When including the following three headers, it is mandatory to define either
- * SECURITY_WIN32 or SECURITY_KERNEL, indicating who is compiling the code.
- */
-
-#undef SECURITY_WIN32
-#undef SECURITY_KERNEL
-#define SECURITY_WIN32 1
-#include <security.h>
-#include <sspi.h>
-#include <rpc.h>
-
-CURLcode Curl_sspi_global_init(void);
-void Curl_sspi_global_cleanup(void);
-
-/* This is used to populate the domain in a SSPI identity structure */
-CURLcode Curl_override_sspi_http_realm(const char *chlg,
-                                       SEC_WINNT_AUTH_IDENTITY *identity);
-
-/* This is used to generate an SSPI identity structure */
-CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp,
-                                   SEC_WINNT_AUTH_IDENTITY *identity);
-
-/* This is used to free an SSPI identity structure */
-void Curl_sspi_free_identity(SEC_WINNT_AUTH_IDENTITY *identity);
-
-/* Forward-declaration of global variables defined in curl_sspi.c */
-extern HMODULE Curl_hSecDll;
-extern PSecurityFunctionTable Curl_pSecFn;
-
-/* Provide some definitions missing in old headers */
-#define SP_NAME_DIGEST              "WDigest"
-#define SP_NAME_NTLM                "NTLM"
-#define SP_NAME_NEGOTIATE           "Negotiate"
-#define SP_NAME_KERBEROS            "Kerberos"
-
-#ifndef ISC_REQ_USE_HTTP_STYLE
-#define ISC_REQ_USE_HTTP_STYLE                0x01000000
-#endif
-
-#ifndef SEC_E_INVALID_PARAMETER
-# define SEC_E_INVALID_PARAMETER              ((HRESULT)0x8009035DL)
-#endif
-#ifndef SEC_E_DELEGATION_POLICY
-# define SEC_E_DELEGATION_POLICY              ((HRESULT)0x8009035EL)
-#endif
-#ifndef SEC_E_POLICY_NLTM_ONLY
-# define SEC_E_POLICY_NLTM_ONLY               ((HRESULT)0x8009035FL)
-#endif
-
-#ifndef SEC_I_SIGNATURE_NEEDED
-#define SEC_I_SIGNATURE_NEEDED                ((HRESULT)0x0009035CL)
-#endif
-
-#ifndef CRYPT_E_REVOKED
-#define CRYPT_E_REVOKED                       ((HRESULT)0x80092010L)
-#endif
-
-#ifndef CRYPT_E_NO_REVOCATION_DLL
-#define CRYPT_E_NO_REVOCATION_DLL             ((HRESULT)0x80092011L)
-#endif
-
-#ifndef CRYPT_E_NO_REVOCATION_CHECK
-#define CRYPT_E_NO_REVOCATION_CHECK           ((HRESULT)0x80092012L)
-#endif
-
-#ifndef CRYPT_E_REVOCATION_OFFLINE
-#define CRYPT_E_REVOCATION_OFFLINE            ((HRESULT)0x80092013L)
-#endif
-
-#ifndef CRYPT_E_NOT_IN_REVOCATION_DATABASE
-#define CRYPT_E_NOT_IN_REVOCATION_DATABASE    ((HRESULT)0x80092014L)
-#endif
-
-#ifdef UNICODE
-#  define SECFLAG_WINNT_AUTH_IDENTITY \
-     (unsigned long)SEC_WINNT_AUTH_IDENTITY_UNICODE
-#else
-#  define SECFLAG_WINNT_AUTH_IDENTITY \
-     (unsigned long)SEC_WINNT_AUTH_IDENTITY_ANSI
-#endif
-
-/*
- * Definitions required from ntsecapi.h are directly provided below this point
- * to avoid including ntsecapi.h due to a conflict with OpenSSL's safestack.h
- */
-#define KERB_WRAP_NO_ENCRYPT 0x80000001
-
-#endif /* USE_WINDOWS_SSPI */
-
-#endif /* HEADER_CURL_SSPI_H */
diff --git a/extra/curl/curl-8.12.1/lib/ftplistparser.c b/extra/curl/curl-8.12.1/lib/ftplistparser.c
deleted file mode 100644
index 3088470abd38..000000000000
--- a/extra/curl/curl-8.12.1/lib/ftplistparser.c
+++ /dev/null
@@ -1,1041 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-/**
- * Now implemented:
- *
- * 1) Unix version 1
- * drwxr-xr-x 1 user01 ftp  512 Jan 29 23:32 prog
- * 2) Unix version 2
- * drwxr-xr-x 1 user01 ftp  512 Jan 29 1997  prog
- * 3) Unix version 3
- * drwxr-xr-x 1      1   1  512 Jan 29 23:32 prog
- * 4) Unix symlink
- * lrwxr-xr-x 1 user01 ftp  512 Jan 29 23:32 prog -> prog2000
- * 5) DOS style
- * 01-29-97 11:32PM <DIR> prog
- */
-
-#include "curl_setup.h"
-
-#ifndef CURL_DISABLE_FTP
-
-#include <curl/curl.h>
-
-#include "urldata.h"
-#include "fileinfo.h"
-#include "llist.h"
-#include "strtoofft.h"
-#include "ftp.h"
-#include "ftplistparser.h"
-#include "curl_fnmatch.h"
-#include "curl_memory.h"
-#include "multiif.h"
-/* The last #include file should be: */
-#include "memdebug.h"
-
-typedef enum {
-  PL_UNIX_TOTALSIZE = 0,
-  PL_UNIX_FILETYPE,
-  PL_UNIX_PERMISSION,
-  PL_UNIX_HLINKS,
-  PL_UNIX_USER,
-  PL_UNIX_GROUP,
-  PL_UNIX_SIZE,
-  PL_UNIX_TIME,
-  PL_UNIX_FILENAME,
-  PL_UNIX_SYMLINK
-} pl_unix_mainstate;
-
-typedef union {
-  enum {
-    PL_UNIX_TOTALSIZE_INIT = 0,
-    PL_UNIX_TOTALSIZE_READING
-  } total_dirsize;
-
-  enum {
-    PL_UNIX_HLINKS_PRESPACE = 0,
-    PL_UNIX_HLINKS_NUMBER
-  } hlinks;
-
-  enum {
-    PL_UNIX_USER_PRESPACE = 0,
-    PL_UNIX_USER_PARSING
-  } user;
-
-  enum {
-    PL_UNIX_GROUP_PRESPACE = 0,
-    PL_UNIX_GROUP_NAME
-  } group;
-
-  enum {
-    PL_UNIX_SIZE_PRESPACE = 0,
-    PL_UNIX_SIZE_NUMBER
-  } size;
-
-  enum {
-    PL_UNIX_TIME_PREPART1 = 0,
-    PL_UNIX_TIME_PART1,
-    PL_UNIX_TIME_PREPART2,
-    PL_UNIX_TIME_PART2,
-    PL_UNIX_TIME_PREPART3,
-    PL_UNIX_TIME_PART3
-  } time;
-
-  enum {
-    PL_UNIX_FILENAME_PRESPACE = 0,
-    PL_UNIX_FILENAME_NAME,
-    PL_UNIX_FILENAME_WINDOWSEOL
-  } filename;
-
-  enum {
-    PL_UNIX_SYMLINK_PRESPACE = 0,
-    PL_UNIX_SYMLINK_NAME,
-    PL_UNIX_SYMLINK_PRETARGET1,
-    PL_UNIX_SYMLINK_PRETARGET2,
-    PL_UNIX_SYMLINK_PRETARGET3,
-    PL_UNIX_SYMLINK_PRETARGET4,
-    PL_UNIX_SYMLINK_TARGET,
-    PL_UNIX_SYMLINK_WINDOWSEOL
-  } symlink;
-} pl_unix_substate;
-
-typedef enum {
-  PL_WINNT_DATE = 0,
-  PL_WINNT_TIME,
-  PL_WINNT_DIRORSIZE,
-  PL_WINNT_FILENAME
-} pl_winNT_mainstate;
-
-typedef union {
-  enum {
-    PL_WINNT_TIME_PRESPACE = 0,
-    PL_WINNT_TIME_TIME
-  } time;
-  enum {
-    PL_WINNT_DIRORSIZE_PRESPACE = 0,
-    PL_WINNT_DIRORSIZE_CONTENT
-  } dirorsize;
-  enum {
-    PL_WINNT_FILENAME_PRESPACE = 0,
-    PL_WINNT_FILENAME_CONTENT,
-    PL_WINNT_FILENAME_WINEOL
-  } filename;
-} pl_winNT_substate;
-
-/* This struct is used in wildcard downloading - for parsing LIST response */
-struct ftp_parselist_data {
-  enum {
-    OS_TYPE_UNKNOWN = 0,
-    OS_TYPE_UNIX,
-    OS_TYPE_WIN_NT
-  } os_type;
-
-  union {
-    struct {
-      pl_unix_mainstate main;
-      pl_unix_substate sub;
-    } UNIX;
-
-    struct {
-      pl_winNT_mainstate main;
-      pl_winNT_substate sub;
-    } NT;
-  } state;
-
-  CURLcode error;
-  struct fileinfo *file_data;
-  unsigned int item_length;
-  size_t item_offset;
-  struct {
-    size_t filename;
-    size_t user;
-    size_t group;
-    size_t time;
-    size_t perm;
-    size_t symlink_target;
-  } offsets;
-};
-
-static void fileinfo_dtor(void *user, void *element)
-{
-  (void)user;
-  Curl_fileinfo_cleanup(element);
-}
-
-CURLcode Curl_wildcard_init(struct WildcardData *wc)
-{
-  Curl_llist_init(&wc->filelist, fileinfo_dtor);
-  wc->state = CURLWC_INIT;
-
-  return CURLE_OK;
-}
-
-void Curl_wildcard_dtor(struct WildcardData **wcp)
-{
-  struct WildcardData *wc = *wcp;
-  if(!wc)
-    return;
-
-  if(wc->dtor) {
-    wc->dtor(wc->ftpwc);
-    wc->dtor = ZERO_NULL;
-    wc->ftpwc = NULL;
-  }
-  DEBUGASSERT(wc->ftpwc == NULL);
-
-  Curl_llist_destroy(&wc->filelist, NULL);
-  free(wc->path);
-  wc->path = NULL;
-  free(wc->pattern);
-  wc->pattern = NULL;
-  wc->state = CURLWC_INIT;
-  free(wc);
-  *wcp = NULL;
-}
-
-struct ftp_parselist_data *Curl_ftp_parselist_data_alloc(void)
-{
-  return calloc(1, sizeof(struct ftp_parselist_data));
-}
-
-
-void Curl_ftp_parselist_data_free(struct ftp_parselist_data **parserp)
-{
-  struct ftp_parselist_data *parser = *parserp;
-  if(parser)
-    Curl_fileinfo_cleanup(parser->file_data);
-  free(parser);
-  *parserp = NULL;
-}
-
-
-CURLcode Curl_ftp_parselist_geterror(struct ftp_parselist_data *pl_data)
-{
-  return pl_data->error;
-}
-
-
-#define FTP_LP_MALFORMATED_PERM 0x01000000
-
-static unsigned int ftp_pl_get_permission(const char *str)
-{
-  unsigned int permissions = 0;
-  /* USER */
-  if(str[0] == 'r')
-    permissions |= 1 << 8;
-  else if(str[0] != '-')
-    permissions |= FTP_LP_MALFORMATED_PERM;
-  if(str[1] == 'w')
-    permissions |= 1 << 7;
-  else if(str[1] != '-')
-    permissions |= FTP_LP_MALFORMATED_PERM;
-
-  if(str[2] == 'x')
-    permissions |= 1 << 6;
-  else if(str[2] == 's') {
-    permissions |= 1 << 6;
-    permissions |= 1 << 11;
-  }
-  else if(str[2] == 'S')
-    permissions |= 1 << 11;
-  else if(str[2] != '-')
-    permissions |= FTP_LP_MALFORMATED_PERM;
-  /* GROUP */
-  if(str[3] == 'r')
-    permissions |= 1 << 5;
-  else if(str[3] != '-')
-    permissions |= FTP_LP_MALFORMATED_PERM;
-  if(str[4] == 'w')
-    permissions |= 1 << 4;
-  else if(str[4] != '-')
-    permissions |= FTP_LP_MALFORMATED_PERM;
-  if(str[5] == 'x')
-    permissions |= 1 << 3;
-  else if(str[5] == 's') {
-    permissions |= 1 << 3;
-    permissions |= 1 << 10;
-  }
-  else if(str[5] == 'S')
-    permissions |= 1 << 10;
-  else if(str[5] != '-')
-    permissions |= FTP_LP_MALFORMATED_PERM;
-  /* others */
-  if(str[6] == 'r')
-    permissions |= 1 << 2;
-  else if(str[6] != '-')
-    permissions |= FTP_LP_MALFORMATED_PERM;
-  if(str[7] == 'w')
-    permissions |= 1 << 1;
-  else if(str[7] != '-')
-      permissions |= FTP_LP_MALFORMATED_PERM;
-  if(str[8] == 'x')
-    permissions |= 1;
-  else if(str[8] == 't') {
-    permissions |= 1;
-    permissions |= 1 << 9;
-  }
-  else if(str[8] == 'T')
-    permissions |= 1 << 9;
-  else if(str[8] != '-')
-    permissions |= FTP_LP_MALFORMATED_PERM;
-
-  return permissions;
-}
-
-static CURLcode ftp_pl_insert_finfo(struct Curl_easy *data,
-                                    struct fileinfo *infop)
-{
-  curl_fnmatch_callback compare;
-  struct WildcardData *wc = data->wildcard;
-  struct ftp_wc *ftpwc = wc->ftpwc;
-  struct Curl_llist *llist = &wc->filelist;
-  struct ftp_parselist_data *parser = ftpwc->parser;
-  bool add = TRUE;
-  struct curl_fileinfo *finfo = &infop->info;
-
-  /* set the finfo pointers */
-  char *str = Curl_dyn_ptr(&infop->buf);
-  finfo->filename       = str + parser->offsets.filename;
-  finfo->strings.group  = parser->offsets.group ?
-                          str + parser->offsets.group : NULL;
-  finfo->strings.perm   = parser->offsets.perm ?
-                          str + parser->offsets.perm : NULL;
-  finfo->strings.target = parser->offsets.symlink_target ?
-                          str + parser->offsets.symlink_target : NULL;
-  finfo->strings.time   = str + parser->offsets.time;
-  finfo->strings.user   = parser->offsets.user ?
-                          str + parser->offsets.user : NULL;
-
-  /* get correct fnmatch callback */
-  compare = data->set.fnmatch;
-  if(!compare)
-    compare = Curl_fnmatch;
-
-  /* filter pattern-corresponding filenames */
-  Curl_set_in_callback(data, TRUE);
-  if(compare(data->set.fnmatch_data, wc->pattern,
-             finfo->filename) == 0) {
-    /* discard symlink which is containing multiple " -> " */
-    if((finfo->filetype == CURLFILETYPE_SYMLINK) && finfo->strings.target &&
-       (strstr(finfo->strings.target, " -> "))) {
-      add = FALSE;
-    }
-  }
-  else {
-    add = FALSE;
-  }
-  Curl_set_in_callback(data, FALSE);
-
-  if(add) {
-    Curl_llist_append(llist, finfo, &infop->list);
-  }
-  else {
-    Curl_fileinfo_cleanup(infop);
-  }
-
-  ftpwc->parser->file_data = NULL;
-  return CURLE_OK;
-}
-
-#define MAX_FTPLIST_BUFFER 10000 /* arbitrarily set */
-
-size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb,
-                          void *connptr)
-{
-  size_t bufflen = size*nmemb;
-  struct Curl_easy *data = (struct Curl_easy *)connptr;
-  struct ftp_wc *ftpwc = data->wildcard->ftpwc;
-  struct ftp_parselist_data *parser = ftpwc->parser;
-  size_t i = 0;
-  CURLcode result;
-  size_t retsize = bufflen;
-
-  if(parser->error) { /* error in previous call */
-    /* scenario:
-     * 1. call => OK..
-     * 2. call => OUT_OF_MEMORY (or other error)
-     * 3. (last) call => is skipped RIGHT HERE and the error is handled later
-     *    in wc_statemach()
-     */
-    goto fail;
-  }
-
-  if(parser->os_type == OS_TYPE_UNKNOWN && bufflen > 0) {
-    /* considering info about FILE response format */
-    parser->os_type = ISDIGIT(buffer[0]) ? OS_TYPE_WIN_NT : OS_TYPE_UNIX;
-  }
-
-  while(i < bufflen) { /* FSM */
-    char *mem;
-    size_t len; /* number of bytes of data in the dynbuf */
-    char c = buffer[i];
-    struct fileinfo *infop;
-    struct curl_fileinfo *finfo;
-    if(!parser->file_data) { /* tmp file data is not allocated yet */
-      parser->file_data = Curl_fileinfo_alloc();
-      if(!parser->file_data) {
-        parser->error = CURLE_OUT_OF_MEMORY;
-        goto fail;
-      }
-      parser->item_offset = 0;
-      parser->item_length = 0;
-      Curl_dyn_init(&parser->file_data->buf, MAX_FTPLIST_BUFFER);
-    }
-
-    infop = parser->file_data;
-    finfo = &infop->info;
-
-    if(Curl_dyn_addn(&infop->buf, &c, 1)) {
-      parser->error = CURLE_OUT_OF_MEMORY;
-      goto fail;
-    }
-    len = Curl_dyn_len(&infop->buf);
-    mem = Curl_dyn_ptr(&infop->buf);
-
-    switch(parser->os_type) {
-    case OS_TYPE_UNIX:
-      switch(parser->state.UNIX.main) {
-      case PL_UNIX_TOTALSIZE:
-        switch(parser->state.UNIX.sub.total_dirsize) {
-        case PL_UNIX_TOTALSIZE_INIT:
-          if(c == 't') {
-            parser->state.UNIX.sub.total_dirsize = PL_UNIX_TOTALSIZE_READING;
-            parser->item_length++;
-          }
-          else {
-            parser->state.UNIX.main = PL_UNIX_FILETYPE;
-            /* start FSM again not considering size of directory */
-            Curl_dyn_reset(&infop->buf);
-            continue;
-          }
-          break;
-        case PL_UNIX_TOTALSIZE_READING:
-          parser->item_length++;
-          if(c == '\r') {
-            parser->item_length--;
-            Curl_dyn_setlen(&infop->buf, --len);
-          }
-          else if(c == '\n') {
-            mem[parser->item_length - 1] = 0;
-            if(!strncmp("total ", mem, 6)) {
-              char *endptr = mem + 6;
-              /* here we can deal with directory size, pass the leading
-                 whitespace and then the digits */
-              while(ISBLANK(*endptr))
-                endptr++;
-              while(ISDIGIT(*endptr))
-                endptr++;
-              if(*endptr) {
-                parser->error = CURLE_FTP_BAD_FILE_LIST;
-                goto fail;
-              }
-              parser->state.UNIX.main = PL_UNIX_FILETYPE;
-              Curl_dyn_reset(&infop->buf);
-            }
-            else {
-              parser->error = CURLE_FTP_BAD_FILE_LIST;
-              goto fail;
-            }
-          }
-          break;
-        }
-        break;
-      case PL_UNIX_FILETYPE:
-        switch(c) {
-        case '-':
-          finfo->filetype = CURLFILETYPE_FILE;
-          break;
-        case 'd':
-          finfo->filetype = CURLFILETYPE_DIRECTORY;
-          break;
-        case 'l':
-          finfo->filetype = CURLFILETYPE_SYMLINK;
-          break;
-        case 'p':
-          finfo->filetype = CURLFILETYPE_NAMEDPIPE;
-          break;
-        case 's':
-          finfo->filetype = CURLFILETYPE_SOCKET;
-          break;
-        case 'c':
-          finfo->filetype = CURLFILETYPE_DEVICE_CHAR;
-          break;
-        case 'b':
-          finfo->filetype = CURLFILETYPE_DEVICE_BLOCK;
-          break;
-        case 'D':
-          finfo->filetype = CURLFILETYPE_DOOR;
-          break;
-        default:
-          parser->error = CURLE_FTP_BAD_FILE_LIST;
-          goto fail;
-        }
-        parser->state.UNIX.main = PL_UNIX_PERMISSION;
-        parser->item_length = 0;
-        parser->item_offset = 1;
-        break;
-      case PL_UNIX_PERMISSION:
-        parser->item_length++;
-        if(parser->item_length <= 9) {
-          if(!strchr("rwx-tTsS", c)) {
-            parser->error = CURLE_FTP_BAD_FILE_LIST;
-            goto fail;
-          }
-        }
-        else if(parser->item_length == 10) {
-          unsigned int perm;
-          if(c != ' ') {
-            parser->error = CURLE_FTP_BAD_FILE_LIST;
-            goto fail;
-          }
-          mem[10] = 0; /* terminate permissions */
-          perm = ftp_pl_get_permission(mem + parser->item_offset);
-          if(perm & FTP_LP_MALFORMATED_PERM) {
-            parser->error = CURLE_FTP_BAD_FILE_LIST;
-            goto fail;
-          }
-          parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_PERM;
-          parser->file_data->info.perm = perm;
-          parser->offsets.perm = parser->item_offset;
-
-          parser->item_length = 0;
-          parser->state.UNIX.main = PL_UNIX_HLINKS;
-          parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_PRESPACE;
-        }
-        break;
-      case PL_UNIX_HLINKS:
-        switch(parser->state.UNIX.sub.hlinks) {
-        case PL_UNIX_HLINKS_PRESPACE:
-          if(c != ' ') {
-            if(ISDIGIT(c)) {
-              parser->item_offset = len - 1;
-              parser->item_length = 1;
-              parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_NUMBER;
-            }
-            else {
-              parser->error = CURLE_FTP_BAD_FILE_LIST;
-              goto fail;
-            }
-          }
-          break;
-        case PL_UNIX_HLINKS_NUMBER:
-          parser->item_length ++;
-          if(c == ' ') {
-            char *p;
-            long int hlinks;
-            mem[parser->item_offset + parser->item_length - 1] = 0;
-            hlinks = strtol(mem + parser->item_offset, &p, 10);
-            if(p[0] == '\0' && hlinks != LONG_MAX && hlinks != LONG_MIN) {
-              parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_HLINKCOUNT;
-              parser->file_data->info.hardlinks = hlinks;
-            }
-            parser->item_length = 0;
-            parser->item_offset = 0;
-            parser->state.UNIX.main = PL_UNIX_USER;
-            parser->state.UNIX.sub.user = PL_UNIX_USER_PRESPACE;
-          }
-          else if(!ISDIGIT(c)) {
-            parser->error = CURLE_FTP_BAD_FILE_LIST;
-            goto fail;
-          }
-          break;
-        }
-        break;
-      case PL_UNIX_USER:
-        switch(parser->state.UNIX.sub.user) {
-        case PL_UNIX_USER_PRESPACE:
-          if(c != ' ') {
-            parser->item_offset = len - 1;
-            parser->item_length = 1;
-            parser->state.UNIX.sub.user = PL_UNIX_USER_PARSING;
-          }
-          break;
-        case PL_UNIX_USER_PARSING:
-          parser->item_length++;
-          if(c == ' ') {
-            mem[parser->item_offset + parser->item_length - 1] = 0;
-            parser->offsets.user = parser->item_offset;
-            parser->state.UNIX.main = PL_UNIX_GROUP;
-            parser->state.UNIX.sub.group = PL_UNIX_GROUP_PRESPACE;
-            parser->item_offset = 0;
-            parser->item_length = 0;
-          }
-          break;
-        }
-        break;
-      case PL_UNIX_GROUP:
-        switch(parser->state.UNIX.sub.group) {
-        case PL_UNIX_GROUP_PRESPACE:
-          if(c != ' ') {
-            parser->item_offset = len - 1;
-            parser->item_length = 1;
-            parser->state.UNIX.sub.group = PL_UNIX_GROUP_NAME;
-          }
-          break;
-        case PL_UNIX_GROUP_NAME:
-          parser->item_length++;
-          if(c == ' ') {
-            mem[parser->item_offset + parser->item_length - 1] = 0;
-            parser->offsets.group = parser->item_offset;
-            parser->state.UNIX.main = PL_UNIX_SIZE;
-            parser->state.UNIX.sub.size = PL_UNIX_SIZE_PRESPACE;
-            parser->item_offset = 0;
-            parser->item_length = 0;
-          }
-          break;
-        }
-        break;
-      case PL_UNIX_SIZE:
-        switch(parser->state.UNIX.sub.size) {
-        case PL_UNIX_SIZE_PRESPACE:
-          if(c != ' ') {
-            if(ISDIGIT(c)) {
-              parser->item_offset = len - 1;
-              parser->item_length = 1;
-              parser->state.UNIX.sub.size = PL_UNIX_SIZE_NUMBER;
-            }
-            else {
-              parser->error = CURLE_FTP_BAD_FILE_LIST;
-              goto fail;
-            }
-          }
-          break;
-        case PL_UNIX_SIZE_NUMBER:
-          parser->item_length++;
-          if(c == ' ') {
-            char *p;
-            curl_off_t fsize;
-            mem[parser->item_offset + parser->item_length - 1] = 0;
-            if(!curlx_strtoofft(mem + parser->item_offset,
-                                &p, 10, &fsize)) {
-              if(p[0] == '\0' && fsize != CURL_OFF_T_MAX &&
-                 fsize != CURL_OFF_T_MIN) {
-                parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_SIZE;
-                parser->file_data->info.size = fsize;
-              }
-              parser->item_length = 0;
-              parser->item_offset = 0;
-              parser->state.UNIX.main = PL_UNIX_TIME;
-              parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART1;
-            }
-          }
-          else if(!ISDIGIT(c)) {
-            parser->error = CURLE_FTP_BAD_FILE_LIST;
-            goto fail;
-          }
-          break;
-        }
-        break;
-      case PL_UNIX_TIME:
-        switch(parser->state.UNIX.sub.time) {
-        case PL_UNIX_TIME_PREPART1:
-          if(c != ' ') {
-            if(ISALNUM(c)) {
-              parser->item_offset = len -1;
-              parser->item_length = 1;
-              parser->state.UNIX.sub.time = PL_UNIX_TIME_PART1;
-            }
-            else {
-              parser->error = CURLE_FTP_BAD_FILE_LIST;
-              goto fail;
-            }
-          }
-          break;
-        case PL_UNIX_TIME_PART1:
-          parser->item_length++;
-          if(c == ' ') {
-            parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART2;
-          }
-          else if(!ISALNUM(c) && c != '.') {
-            parser->error = CURLE_FTP_BAD_FILE_LIST;
-            goto fail;
-          }
-          break;
-        case PL_UNIX_TIME_PREPART2:
-          parser->item_length++;
-          if(c != ' ') {
-            if(ISALNUM(c)) {
-              parser->state.UNIX.sub.time = PL_UNIX_TIME_PART2;
-            }
-            else {
-              parser->error = CURLE_FTP_BAD_FILE_LIST;
-              goto fail;
-            }
-          }
-          break;
-        case PL_UNIX_TIME_PART2:
-          parser->item_length++;
-          if(c == ' ') {
-            parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART3;
-          }
-          else if(!ISALNUM(c) && c != '.') {
-            parser->error = CURLE_FTP_BAD_FILE_LIST;
-            goto fail;
-          }
-          break;
-        case PL_UNIX_TIME_PREPART3:
-          parser->item_length++;
-          if(c != ' ') {
-            if(ISALNUM(c)) {
-              parser->state.UNIX.sub.time = PL_UNIX_TIME_PART3;
-            }
-            else {
-              parser->error = CURLE_FTP_BAD_FILE_LIST;
-              goto fail;
-            }
-          }
-          break;
-        case PL_UNIX_TIME_PART3:
-          parser->item_length++;
-          if(c == ' ') {
-            mem[parser->item_offset + parser->item_length -1] = 0;
-            parser->offsets.time = parser->item_offset;
-            /*
-              if(ftp_pl_gettime(parser, finfo->mem + parser->item_offset)) {
-                parser->file_data->flags |= CURLFINFOFLAG_KNOWN_TIME;
-              }
-            */
-            if(finfo->filetype == CURLFILETYPE_SYMLINK) {
-              parser->state.UNIX.main = PL_UNIX_SYMLINK;
-              parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRESPACE;
-            }
-            else {
-              parser->state.UNIX.main = PL_UNIX_FILENAME;
-              parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_PRESPACE;
-            }
-          }
-          else if(!ISALNUM(c) && c != '.' && c != ':') {
-            parser->error = CURLE_FTP_BAD_FILE_LIST;
-            goto fail;
-          }
-          break;
-        }
-        break;
-      case PL_UNIX_FILENAME:
-        switch(parser->state.UNIX.sub.filename) {
-        case PL_UNIX_FILENAME_PRESPACE:
-          if(c != ' ') {
-            parser->item_offset = len - 1;
-            parser->item_length = 1;
-            parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_NAME;
-          }
-          break;
-        case PL_UNIX_FILENAME_NAME:
-          parser->item_length++;
-          if(c == '\r') {
-            parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_WINDOWSEOL;
-          }
-          else if(c == '\n') {
-            mem[parser->item_offset + parser->item_length - 1] = 0;
-            parser->offsets.filename = parser->item_offset;
-            parser->state.UNIX.main = PL_UNIX_FILETYPE;
-            result = ftp_pl_insert_finfo(data, infop);
-            if(result) {
-              parser->error = result;
-              goto fail;
-            }
-          }
-          break;
-        case PL_UNIX_FILENAME_WINDOWSEOL:
-          if(c == '\n') {
-            mem[parser->item_offset + parser->item_length - 1] = 0;
-            parser->offsets.filename = parser->item_offset;
-            parser->state.UNIX.main = PL_UNIX_FILETYPE;
-            result = ftp_pl_insert_finfo(data, infop);
-            if(result) {
-              parser->error = result;
-              goto fail;
-            }
-          }
-          else {
-            parser->error = CURLE_FTP_BAD_FILE_LIST;
-            goto fail;
-          }
-          break;
-        }
-        break;
-      case PL_UNIX_SYMLINK:
-        switch(parser->state.UNIX.sub.symlink) {
-        case PL_UNIX_SYMLINK_PRESPACE:
-          if(c != ' ') {
-            parser->item_offset = len - 1;
-            parser->item_length = 1;
-            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
-          }
-          break;
-        case PL_UNIX_SYMLINK_NAME:
-          parser->item_length++;
-          if(c == ' ') {
-            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET1;
-          }
-          else if(c == '\r' || c == '\n') {
-            parser->error = CURLE_FTP_BAD_FILE_LIST;
-            goto fail;
-          }
-          break;
-        case PL_UNIX_SYMLINK_PRETARGET1:
-          parser->item_length++;
-          if(c == '-') {
-            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET2;
-          }
-          else if(c == '\r' || c == '\n') {
-            parser->error = CURLE_FTP_BAD_FILE_LIST;
-            goto fail;
-          }
-          else {
-            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
-          }
-          break;
-        case PL_UNIX_SYMLINK_PRETARGET2:
-          parser->item_length++;
-          if(c == '>') {
-            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET3;
-          }
-          else if(c == '\r' || c == '\n') {
-            parser->error = CURLE_FTP_BAD_FILE_LIST;
-            goto fail;
-          }
-          else {
-            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
-          }
-          break;
-        case PL_UNIX_SYMLINK_PRETARGET3:
-          parser->item_length++;
-          if(c == ' ') {
-            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET4;
-            /* now place where is symlink following */
-            mem[parser->item_offset + parser->item_length - 4] = 0;
-            parser->offsets.filename = parser->item_offset;
-            parser->item_length = 0;
-            parser->item_offset = 0;
-          }
-          else if(c == '\r' || c == '\n') {
-            parser->error = CURLE_FTP_BAD_FILE_LIST;
-            goto fail;
-          }
-          else {
-            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
-          }
-          break;
-        case PL_UNIX_SYMLINK_PRETARGET4:
-          if(c != '\r' && c != '\n') {
-            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_TARGET;
-            parser->item_offset = len - 1;
-            parser->item_length = 1;
-          }
-          else {
-            parser->error = CURLE_FTP_BAD_FILE_LIST;
-            goto fail;
-          }
-          break;
-        case PL_UNIX_SYMLINK_TARGET:
-          parser->item_length++;
-          if(c == '\r') {
-            parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_WINDOWSEOL;
-          }
-          else if(c == '\n') {
-            mem[parser->item_offset + parser->item_length - 1] = 0;
-            parser->offsets.symlink_target = parser->item_offset;
-            result = ftp_pl_insert_finfo(data, infop);
-            if(result) {
-              parser->error = result;
-              goto fail;
-            }
-            parser->state.UNIX.main = PL_UNIX_FILETYPE;
-          }
-          break;
-        case PL_UNIX_SYMLINK_WINDOWSEOL:
-          if(c == '\n') {
-            mem[parser->item_offset + parser->item_length - 1] = 0;
-            parser->offsets.symlink_target = parser->item_offset;
-            result = ftp_pl_insert_finfo(data, infop);
-            if(result) {
-              parser->error = result;
-              goto fail;
-            }
-            parser->state.UNIX.main = PL_UNIX_FILETYPE;
-          }
-          else {
-            parser->error = CURLE_FTP_BAD_FILE_LIST;
-            goto fail;
-          }
-          break;
-        }
-        break;
-      }
-      break;
-    case OS_TYPE_WIN_NT:
-      switch(parser->state.NT.main) {
-      case PL_WINNT_DATE:
-        parser->item_length++;
-        if(parser->item_length < 9) {
-          if(!strchr("0123456789-", c)) { /* only simple control */
-            parser->error = CURLE_FTP_BAD_FILE_LIST;
-            goto fail;
-          }
-        }
-        else if(parser->item_length == 9) {
-          if(c == ' ') {
-            parser->state.NT.main = PL_WINNT_TIME;
-            parser->state.NT.sub.time = PL_WINNT_TIME_PRESPACE;
-          }
-          else {
-            parser->error = CURLE_FTP_BAD_FILE_LIST;
-            goto fail;
-          }
-        }
-        else {
-          parser->error = CURLE_FTP_BAD_FILE_LIST;
-          goto fail;
-        }
-        break;
-      case PL_WINNT_TIME:
-        parser->item_length++;
-        switch(parser->state.NT.sub.time) {
-        case PL_WINNT_TIME_PRESPACE:
-          if(!ISBLANK(c)) {
-            parser->state.NT.sub.time = PL_WINNT_TIME_TIME;
-          }
-          break;
-        case PL_WINNT_TIME_TIME:
-          if(c == ' ') {
-            parser->offsets.time = parser->item_offset;
-            mem[parser->item_offset + parser->item_length -1] = 0;
-            parser->state.NT.main = PL_WINNT_DIRORSIZE;
-            parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_PRESPACE;
-            parser->item_length = 0;
-          }
-          else if(!strchr("APM0123456789:", c)) {
-            parser->error = CURLE_FTP_BAD_FILE_LIST;
-            goto fail;
-          }
-          break;
-        }
-        break;
-      case PL_WINNT_DIRORSIZE:
-        switch(parser->state.NT.sub.dirorsize) {
-        case PL_WINNT_DIRORSIZE_PRESPACE:
-          if(c != ' ') {
-            parser->item_offset = len - 1;
-            parser->item_length = 1;
-            parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_CONTENT;
-          }
-          break;
-        case PL_WINNT_DIRORSIZE_CONTENT:
-          parser->item_length ++;
-          if(c == ' ') {
-            mem[parser->item_offset + parser->item_length - 1] = 0;
-            if(strcmp("<DIR>", mem + parser->item_offset) == 0) {
-              finfo->filetype = CURLFILETYPE_DIRECTORY;
-              finfo->size = 0;
-            }
-            else {
-              char *endptr;
-              if(curlx_strtoofft(mem +
-                                 parser->item_offset,
-                                 &endptr, 10, &finfo->size)) {
-                parser->error = CURLE_FTP_BAD_FILE_LIST;
-                goto fail;
-              }
-              /* correct file type */
-              parser->file_data->info.filetype = CURLFILETYPE_FILE;
-            }
-
-            parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_SIZE;
-            parser->item_length = 0;
-            parser->state.NT.main = PL_WINNT_FILENAME;
-            parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
-          }
-          break;
-        }
-        break;
-      case PL_WINNT_FILENAME:
-        switch(parser->state.NT.sub.filename) {
-        case PL_WINNT_FILENAME_PRESPACE:
-          if(c != ' ') {
-            parser->item_offset = len -1;
-            parser->item_length = 1;
-            parser->state.NT.sub.filename = PL_WINNT_FILENAME_CONTENT;
-          }
-          break;
-        case PL_WINNT_FILENAME_CONTENT:
-          parser->item_length++;
-          if(c == '\r') {
-            parser->state.NT.sub.filename = PL_WINNT_FILENAME_WINEOL;
-            mem[len - 1] = 0;
-          }
-          else if(c == '\n') {
-            parser->offsets.filename = parser->item_offset;
-            mem[len - 1] = 0;
-            result = ftp_pl_insert_finfo(data, infop);
-            if(result) {
-              parser->error = result;
-              goto fail;
-            }
-            parser->state.NT.main = PL_WINNT_DATE;
-            parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
-          }
-          break;
-        case PL_WINNT_FILENAME_WINEOL:
-          if(c == '\n') {
-            parser->offsets.filename = parser->item_offset;
-            result = ftp_pl_insert_finfo(data, infop);
-            if(result) {
-              parser->error = result;
-              goto fail;
-            }
-            parser->state.NT.main = PL_WINNT_DATE;
-            parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
-          }
-          else {
-            parser->error = CURLE_FTP_BAD_FILE_LIST;
-            goto fail;
-          }
-          break;
-        }
-        break;
-      }
-      break;
-    default:
-      retsize = bufflen + 1;
-      goto fail;
-    }
-
-    i++;
-  }
-  return retsize;
-
-fail:
-
-  /* Clean up any allocated memory. */
-  if(parser->file_data) {
-    Curl_fileinfo_cleanup(parser->file_data);
-    parser->file_data = NULL;
-  }
-
-  return retsize;
-}
-
-#endif /* CURL_DISABLE_FTP */
diff --git a/extra/curl/curl-8.12.1/lib/hostasyn.c b/extra/curl/curl-8.12.1/lib/hostasyn.c
deleted file mode 100644
index 4d6a8e85967f..000000000000
--- a/extra/curl/curl-8.12.1/lib/hostasyn.c
+++ /dev/null
@@ -1,123 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-/***********************************************************************
- * Only for builds using asynchronous name resolves
- **********************************************************************/
-#ifdef CURLRES_ASYNCH
-
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
-#ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-#ifdef __VMS
-#include <in.h>
-#include <inet.h>
-#endif
-
-#include "urldata.h"
-#include "sendf.h"
-#include "hostip.h"
-#include "hash.h"
-#include "share.h"
-#include "url.h"
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "memdebug.h"
-
-/*
- * Curl_addrinfo_callback() gets called by ares, gethostbyname_thread()
- * or getaddrinfo_thread() when we got the name resolved (or not!).
- *
- * If the status argument is CURL_ASYNC_SUCCESS, this function takes
- * ownership of the Curl_addrinfo passed, storing the resolved data
- * in the DNS cache.
- *
- * The storage operation locks and unlocks the DNS cache.
- */
-CURLcode Curl_addrinfo_callback(struct Curl_easy *data,
-                                int status,
-                                struct Curl_addrinfo *ai)
-{
-  struct Curl_dns_entry *dns = NULL;
-  CURLcode result = CURLE_OK;
-
-  data->state.async.status = status;
-
-  if(CURL_ASYNC_SUCCESS == status) {
-    if(ai) {
-      if(data->share)
-        Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
-
-      dns = Curl_cache_addr(data, ai,
-                            data->state.async.hostname, 0,
-                            data->state.async.port, FALSE);
-      if(data->share)
-        Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
-
-      if(!dns) {
-        /* failed to store, cleanup and return error */
-        Curl_freeaddrinfo(ai);
-        result = CURLE_OUT_OF_MEMORY;
-      }
-    }
-    else {
-      result = CURLE_OUT_OF_MEMORY;
-    }
-  }
-
-  data->state.async.dns = dns;
-
- /* Set async.done TRUE last in this function since it may be used multi-
-    threaded and once this is TRUE the other thread may read fields from the
-    async struct */
-  data->state.async.done = TRUE;
-
-  /* IPv4: The input hostent struct will be freed by ares when we return from
-     this function */
-  return result;
-}
-
-/*
- * Curl_getaddrinfo() is the generic low-level name resolve API within this
- * source file. There are several versions of this function - for different
- * name resolve layers (selected at build-time). They all take this same set
- * of arguments
- */
-struct Curl_addrinfo *Curl_getaddrinfo(struct Curl_easy *data,
-                                       const char *hostname,
-                                       int port,
-                                       int *waitp)
-{
-  return Curl_resolver_getaddrinfo(data, hostname, port, waitp);
-}
-
-#endif /* CURLRES_ASYNCH */
diff --git a/extra/curl/curl-8.12.1/lib/hostsyn.c b/extra/curl/curl-8.12.1/lib/hostsyn.c
deleted file mode 100644
index ca8b0758c47b..000000000000
--- a/extra/curl/curl-8.12.1/lib/hostsyn.c
+++ /dev/null
@@ -1,104 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-/***********************************************************************
- * Only for builds using synchronous name resolves
- **********************************************************************/
-#ifdef CURLRES_SYNCH
-
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
-#ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-#ifdef __VMS
-#include <in.h>
-#include <inet.h>
-#endif
-
-#include "urldata.h"
-#include "sendf.h"
-#include "hostip.h"
-#include "hash.h"
-#include "share.h"
-#include "url.h"
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "memdebug.h"
-
-/*
- * Function provided by the resolver backend to set DNS servers to use.
- */
-CURLcode Curl_set_dns_servers(struct Curl_easy *data,
-                              char *servers)
-{
-  (void)data;
-  (void)servers;
-  return CURLE_NOT_BUILT_IN;
-
-}
-
-/*
- * Function provided by the resolver backend to set
- * outgoing interface to use for DNS requests
- */
-CURLcode Curl_set_dns_interface(struct Curl_easy *data,
-                                const char *interf)
-{
-  (void)data;
-  (void)interf;
-  return CURLE_NOT_BUILT_IN;
-}
-
-/*
- * Function provided by the resolver backend to set
- * local IPv4 address to use as source address for DNS requests
- */
-CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
-                                const char *local_ip4)
-{
-  (void)data;
-  (void)local_ip4;
-  return CURLE_NOT_BUILT_IN;
-}
-
-/*
- * Function provided by the resolver backend to set
- * local IPv6 address to use as source address for DNS requests
- */
-CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
-                                const char *local_ip6)
-{
-  (void)data;
-  (void)local_ip6;
-  return CURLE_NOT_BUILT_IN;
-}
-
-#endif /* truly sync */
diff --git a/extra/curl/curl-8.12.1/lib/httpsrr.c b/extra/curl/curl-8.12.1/lib/httpsrr.c
deleted file mode 100644
index 9884b92580b4..000000000000
--- a/extra/curl/curl-8.12.1/lib/httpsrr.c
+++ /dev/null
@@ -1,167 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifdef USE_HTTPSRR
-
-#include "urldata.h"
-#include "curl_addrinfo.h"
-#include "httpsrr.h"
-#include "connect.h"
-#include "sendf.h"
-
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
-
-CURLcode Curl_httpsrr_decode_alpn(const unsigned char *cp, size_t len,
-                                  unsigned char *alpns)
-{
-  /*
-   * spec here is as per RFC 9460, section-7.1.1
-   * encoding is a concatenated list of strings each preceded by a one
-   * octet length
-   * output is comma-sep list of the strings
-   * implementations may or may not handle quoting of comma within
-   * string values, so we might see a comma within the wire format
-   * version of a string, in which case we will precede that by a
-   * backslash - same goes for a backslash character, and of course
-   * we need to use two backslashes in strings when we mean one;-)
-   */
-  struct dynbuf dval;
-  int idnum = 0;
-
-  Curl_dyn_init(&dval, DYN_DOH_RESPONSE);
-  while(len > 0) {
-    size_t tlen = (size_t) *cp++;
-    size_t i;
-    enum alpnid id;
-    len--;
-    if(tlen > len)
-      goto err;
-    /* add escape char if needed, clunky but easier to read */
-    for(i = 0; i != tlen; i++) {
-      if('\\' == *cp || ',' == *cp) {
-        if(Curl_dyn_addn(&dval, "\\", 1))
-          goto err;
-      }
-      if(Curl_dyn_addn(&dval, cp++, 1))
-        goto err;
-    }
-    len -= tlen;
-
-    /* we only store ALPN ids we know about */
-    id = Curl_alpn2alpnid(Curl_dyn_ptr(&dval), Curl_dyn_len(&dval));
-    if(id != ALPN_none) {
-      if(idnum == MAX_HTTPSRR_ALPNS)
-        break;
-      alpns[idnum++] = (unsigned char)id;
-    }
-    Curl_dyn_reset(&dval);
-  }
-  Curl_dyn_free(&dval);
-  if(idnum < MAX_HTTPSRR_ALPNS)
-    alpns[idnum] = ALPN_none; /* terminate the list */
-  return CURLE_OK;
-err:
-  Curl_dyn_free(&dval);
-  return CURLE_BAD_CONTENT_ENCODING;
-}
-
-#ifdef USE_ARES
-
-static void httpsrr_opt(struct Curl_easy *data,
-                        const ares_dns_rr_t *rr,
-                        ares_dns_rr_key_t key, size_t idx)
-{
-  size_t len = 0;
-  const unsigned char *val = NULL;
-  unsigned short code;
-  struct thread_data *res = data->state.async.tdata;
-  struct Curl_https_rrinfo *hi = &res->hinfo;
-  code  = ares_dns_rr_get_opt(rr, key, idx, &val, &len);
-
-  switch(code) {
-  case HTTPS_RR_CODE_ALPN: /* str_list */
-    Curl_httpsrr_decode_alpn(val, len, hi->alpns);
-    infof(data, "HTTPS RR ALPN: %u %u %u %u",
-          hi->alpns[0], hi->alpns[1], hi->alpns[2], hi->alpns[3]);
-    break;
-  case HTTPS_RR_CODE_NO_DEF_ALPN:
-    infof(data, "HTTPS RR no-def-alpn");
-    break;
-  case HTTPS_RR_CODE_IPV4: /* addr4 list */
-    infof(data, "HTTPS RR IPv4");
-    break;
-  case HTTPS_RR_CODE_ECH:
-    infof(data, "HTTPS RR ECH");
-    break;
-  case HTTPS_RR_CODE_IPV6: /* addr6 list */
-    infof(data, "HTTPS RR IPv6");
-    break;
-  case HTTPS_RR_CODE_PORT:
-    infof(data, "HTTPS RR port");
-    break;
-  default:
-    infof(data, "HTTPS RR unknown code");
-    break;
-  }
-}
-
-void Curl_dnsrec_done_cb(void *arg, ares_status_t status,
-                         size_t timeouts,
-                         const ares_dns_record_t *dnsrec)
-{
-  struct Curl_easy *data = arg;
-  size_t i;
-#ifdef CURLRES_ARES
-  struct thread_data *res = data->state.async.tdata;
-
-  res->num_pending--;
-#endif
-  (void)timeouts;
-  if((ARES_SUCCESS != status) || !dnsrec)
-    return;
-
-  for(i = 0; i < ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ANSWER); i++) {
-    size_t opt;
-    const ares_dns_rr_t *rr =
-      ares_dns_record_rr_get_const(dnsrec, ARES_SECTION_ANSWER, i);
-    if(ares_dns_rr_get_type(rr) != ARES_REC_TYPE_HTTPS)
-      continue;
-    /* When SvcPriority is 0, the SVCB record is in AliasMode. Otherwise, it
-       is in ServiceMode */
-    infof(data, "HTTPS RR priority: %u",
-          ares_dns_rr_get_u16(rr, ARES_RR_HTTPS_PRIORITY));
-    for(opt = 0; opt < ares_dns_rr_get_opt_cnt(rr, ARES_RR_HTTPS_PARAMS);
-        opt++)
-      httpsrr_opt(data, rr, ARES_RR_HTTPS_PARAMS, opt);
-  }
-}
-
-#endif /* USE_ARES */
-
-#endif /* USE_HTTPSRR */
diff --git a/extra/curl/curl-8.12.1/lib/imap.h b/extra/curl/curl-8.12.1/lib/imap.h
deleted file mode 100644
index 784ee97e5506..000000000000
--- a/extra/curl/curl-8.12.1/lib/imap.h
+++ /dev/null
@@ -1,101 +0,0 @@
-#ifndef HEADER_CURL_IMAP_H
-#define HEADER_CURL_IMAP_H
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-#include "pingpong.h"
-#include "curl_sasl.h"
-
-/****************************************************************************
- * IMAP unique setup
- ***************************************************************************/
-typedef enum {
-  IMAP_STOP,         /* do nothing state, stops the state machine */
-  IMAP_SERVERGREET,  /* waiting for the initial greeting immediately after
-                        a connect */
-  IMAP_CAPABILITY,
-  IMAP_STARTTLS,
-  IMAP_UPGRADETLS,   /* asynchronously upgrade the connection to SSL/TLS
-                       (multi mode only) */
-  IMAP_AUTHENTICATE,
-  IMAP_LOGIN,
-  IMAP_LIST,
-  IMAP_SELECT,
-  IMAP_FETCH,
-  IMAP_FETCH_FINAL,
-  IMAP_APPEND,
-  IMAP_APPEND_FINAL,
-  IMAP_SEARCH,
-  IMAP_LOGOUT,
-  IMAP_LAST          /* never used */
-} imapstate;
-
-/* This IMAP struct is used in the Curl_easy. All IMAP data that is
-   connection-oriented must be in imap_conn to properly deal with the fact that
-   perhaps the Curl_easy is changed between the times the connection is
-   used. */
-struct IMAP {
-  curl_pp_transfer transfer;
-  char *mailbox;          /* Mailbox to select */
-  char *uidvalidity;      /* UIDVALIDITY to check in select */
-  char *uid;              /* Message UID to fetch */
-  char *mindex;           /* Index in mail box of mail to fetch */
-  char *section;          /* Message SECTION to fetch */
-  char *partial;          /* Message PARTIAL to fetch */
-  char *query;            /* Query to search for */
-  char *custom;           /* Custom request */
-  char *custom_params;    /* Parameters for the custom request */
-};
-
-/* imap_conn is used for struct connection-oriented data in the connectdata
-   struct */
-struct imap_conn {
-  struct pingpong pp;
-  struct SASL sasl;           /* SASL-related parameters */
-  struct dynbuf dyn;          /* for the IMAP commands */
-  char *mailbox;              /* The last selected mailbox */
-  char *mailbox_uidvalidity;  /* UIDVALIDITY parsed from select response */
-  imapstate state;            /* Always use imap.c:state() to change state! */
-  char resptag[5];            /* Response tag to wait for */
-  unsigned char preftype;     /* Preferred authentication type */
-  unsigned char cmdid;        /* Last used command ID */
-  BIT(ssldone);               /* Is connect() over SSL done? */
-  BIT(preauth);               /* Is this connection PREAUTH? */
-  BIT(tls_supported);         /* StartTLS capability supported by server */
-  BIT(login_disabled);        /* LOGIN command disabled by server */
-  BIT(ir_supported);          /* Initial response supported by server */
-};
-
-extern const struct Curl_handler Curl_handler_imap;
-extern const struct Curl_handler Curl_handler_imaps;
-
-/* Authentication type flags */
-#define IMAP_TYPE_CLEARTEXT (1 << 0)
-#define IMAP_TYPE_SASL      (1 << 1)
-
-/* Authentication type values */
-#define IMAP_TYPE_NONE      0
-#define IMAP_TYPE_ANY       (IMAP_TYPE_CLEARTEXT|IMAP_TYPE_SASL)
-
-#endif /* HEADER_CURL_IMAP_H */
diff --git a/extra/curl/curl-8.12.1/lib/pop3.h b/extra/curl/curl-8.12.1/lib/pop3.h
deleted file mode 100644
index 3d08dafa198a..000000000000
--- a/extra/curl/curl-8.12.1/lib/pop3.h
+++ /dev/null
@@ -1,93 +0,0 @@
-#ifndef HEADER_CURL_POP3_H
-#define HEADER_CURL_POP3_H
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-#include "pingpong.h"
-#include "curl_sasl.h"
-
-/****************************************************************************
- * POP3 unique setup
- ***************************************************************************/
-typedef enum {
-  POP3_STOP,         /* do nothing state, stops the state machine */
-  POP3_SERVERGREET,  /* waiting for the initial greeting immediately after
-                        a connect */
-  POP3_CAPA,
-  POP3_STARTTLS,
-  POP3_UPGRADETLS,   /* asynchronously upgrade the connection to SSL/TLS
-                       (multi mode only) */
-  POP3_AUTH,
-  POP3_APOP,
-  POP3_USER,
-  POP3_PASS,
-  POP3_COMMAND,
-  POP3_QUIT,
-  POP3_LAST          /* never used */
-} pop3state;
-
-/* This POP3 struct is used in the Curl_easy. All POP3 data that is
-   connection-oriented must be in pop3_conn to properly deal with the fact that
-   perhaps the Curl_easy is changed between the times the connection is
-   used. */
-struct POP3 {
-  curl_pp_transfer transfer;
-  char *id;               /* Message ID */
-  char *custom;           /* Custom Request */
-};
-
-/* pop3_conn is used for struct connection-oriented data in the connectdata
-   struct */
-struct pop3_conn {
-  struct pingpong pp;
-  pop3state state;        /* Always use pop3.c:state() to change state! */
-  size_t eob;             /* Number of bytes of the EOB (End Of Body) that
-                             have been received so far */
-  size_t strip;           /* Number of bytes from the start to ignore as
-                             non-body */
-  struct SASL sasl;       /* SASL-related storage */
-  char *apoptimestamp;    /* APOP timestamp from the server greeting */
-  unsigned char authtypes; /* Accepted authentication types */
-  unsigned char preftype;  /* Preferred authentication type */
-  BIT(ssldone);           /* Is connect() over SSL done? */
-  BIT(tls_supported);     /* StartTLS capability supported by server */
-};
-
-extern const struct Curl_handler Curl_handler_pop3;
-extern const struct Curl_handler Curl_handler_pop3s;
-
-/* Authentication type flags */
-#define POP3_TYPE_CLEARTEXT (1 << 0)
-#define POP3_TYPE_APOP      (1 << 1)
-#define POP3_TYPE_SASL      (1 << 2)
-
-/* Authentication type values */
-#define POP3_TYPE_NONE      0
-#define POP3_TYPE_ANY       (POP3_TYPE_CLEARTEXT|POP3_TYPE_APOP|POP3_TYPE_SASL)
-
-/* This is the 5-bytes End-Of-Body marker for POP3 */
-#define POP3_EOB "\x0d\x0a\x2e\x0d\x0a"
-#define POP3_EOB_LEN 5
-
-#endif /* HEADER_CURL_POP3_H */
diff --git a/extra/curl/curl-8.12.1/lib/smtp.h b/extra/curl/curl-8.12.1/lib/smtp.h
deleted file mode 100644
index 7c2af6807390..000000000000
--- a/extra/curl/curl-8.12.1/lib/smtp.h
+++ /dev/null
@@ -1,87 +0,0 @@
-#ifndef HEADER_CURL_SMTP_H
-#define HEADER_CURL_SMTP_H
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-#include "pingpong.h"
-#include "curl_sasl.h"
-
-/****************************************************************************
- * SMTP unique setup
- ***************************************************************************/
-typedef enum {
-  SMTP_STOP,        /* do nothing state, stops the state machine */
-  SMTP_SERVERGREET, /* waiting for the initial greeting immediately after
-                       a connect */
-  SMTP_EHLO,
-  SMTP_HELO,
-  SMTP_STARTTLS,
-  SMTP_UPGRADETLS,  /* asynchronously upgrade the connection to SSL/TLS
-                       (multi mode only) */
-  SMTP_AUTH,
-  SMTP_COMMAND,     /* VRFY, EXPN, NOOP, RSET and HELP */
-  SMTP_MAIL,        /* MAIL FROM */
-  SMTP_RCPT,        /* RCPT TO */
-  SMTP_DATA,
-  SMTP_POSTDATA,
-  SMTP_QUIT,
-  SMTP_LAST         /* never used */
-} smtpstate;
-
-/* This SMTP struct is used in the Curl_easy. All SMTP data that is
-   connection-oriented must be in smtp_conn to properly deal with the fact that
-   perhaps the Curl_easy is changed between the times the connection is
-   used. */
-struct SMTP {
-  curl_pp_transfer transfer;
-  char *custom;            /* Custom Request */
-  struct curl_slist *rcpt; /* Recipient list */
-  int rcpt_last_error;     /* The last error received for RCPT TO command */
-  size_t eob;              /* Number of bytes of the EOB (End Of Body) that
-                              have been received so far */
-  BIT(rcpt_had_ok);        /* Whether any of RCPT TO commands (depends on
-                              total number of recipients) succeeded so far */
-  BIT(trailing_crlf);      /* Specifies if the trailing CRLF is present */
-};
-
-/* smtp_conn is used for struct connection-oriented data in the connectdata
-   struct */
-struct smtp_conn {
-  struct pingpong pp;
-  struct SASL sasl;        /* SASL-related storage */
-  smtpstate state;         /* Always use smtp.c:state() to change state! */
-  char *domain;            /* Client address/name to send in the EHLO */
-  BIT(ssldone);            /* Is connect() over SSL done? */
-  BIT(tls_supported);      /* StartTLS capability supported by server */
-  BIT(size_supported);     /* If server supports SIZE extension according to
-                              RFC 1870 */
-  BIT(utf8_supported);     /* If server supports SMTPUTF8 extension according
-                              to RFC 6531 */
-  BIT(auth_supported);     /* AUTH capability supported by server */
-};
-
-extern const struct Curl_handler Curl_handler_smtp;
-extern const struct Curl_handler Curl_handler_smtps;
-
-#endif /* HEADER_CURL_SMTP_H */
diff --git a/extra/curl/curl-8.12.1/lib/strparse.c b/extra/curl/curl-8.12.1/lib/strparse.c
deleted file mode 100644
index dce08251f81f..000000000000
--- a/extra/curl/curl-8.12.1/lib/strparse.c
+++ /dev/null
@@ -1,136 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-#include "strparse.h"
-
-/* Get a word until the first DELIM or end of string. At least one byte long.
-   return non-zero on error */
-int Curl_str_until(char **linep, struct Curl_str *out,
-                   const size_t max, char delim)
-{
-  char *s = *linep;
-  size_t len = 0;
-  DEBUGASSERT(linep && *linep && out && max && delim);
-
-  out->str = NULL;
-  out->len = 0;
-  while(*s && (*s != delim)) {
-    s++;
-    if(++len > max) {
-      return STRE_BIG;
-    }
-  }
-  if(!len)
-    return STRE_SHORT;
-  out->str = *linep;
-  out->len = len;
-  *linep = s; /* point to the first byte after the word */
-  return STRE_OK;
-}
-
-/* Get a word until the first space or end of string. At least one byte long.
-   return non-zero on error */
-int Curl_str_word(char **linep, struct Curl_str *out,
-                  const size_t max)
-{
-  return Curl_str_until(linep, out, max, ' ');
-}
-
-
-/* Get a "quoted" word. No escaping possible.
-   return non-zero on error */
-int Curl_str_quotedword(char **linep, struct Curl_str *out,
-                        const size_t max)
-{
-  char *s = *linep;
-  size_t len = 0;
-  DEBUGASSERT(linep && *linep && out && max);
-
-  out->str = NULL;
-  out->len = 0;
-  if(*s != '\"')
-    return STRE_BEGQUOTE;
-  s++;
-  while(*s && (*s != '\"')) {
-    s++;
-    if(++len > max)
-      return STRE_BIG;
-  }
-  if(*s != '\"')
-    return STRE_ENDQUOTE;
-  out->str = (*linep) + 1;
-  out->len = len;
-  *linep = s + 1;
-  return STRE_OK;
-}
-
-/* Advance over a single character.
-   return non-zero on error */
-int Curl_str_single(char **linep, char byte)
-{
-  DEBUGASSERT(linep && *linep);
-  if(**linep != byte)
-    return STRE_BYTE;
-  (*linep)++; /* move over it */
-  return STRE_OK;
-}
-
-/* Advance over a single space.
-   return non-zero on error */
-int Curl_str_singlespace(char **linep)
-{
-  return Curl_str_single(linep, ' ');
-}
-
-/* Get an unsigned number. Leading zeroes are accepted.
-   return non-zero on error */
-int Curl_str_number(char **linep, size_t *nump, size_t max)
-{
-  size_t num = 0;
-  DEBUGASSERT(linep && *linep && nump);
-  *nump = 0;
-  while(ISDIGIT(**linep)) {
-    int n = **linep - '0';
-    if(num > ((SIZE_T_MAX - n) / 10))
-      return STRE_OVERFLOW;
-    num = num * 10 + n;
-    if(num > max)
-      return STRE_BIG; /** too big */
-    (*linep)++;
-  }
-  *nump = num;
-  return STRE_OK;
-}
-
-/* CR or LF
-   return non-zero on error */
-int Curl_str_newline(char **linep)
-{
-  DEBUGASSERT(linep && *linep);
-  if(ISNEWLINE(**linep)) {
-    (*linep)++;
-    return STRE_OK; /* yessir */
-  }
-  return STRE_NEWLINE;
-}
diff --git a/extra/curl/curl-8.12.1/lib/strparse.h b/extra/curl/curl-8.12.1/lib/strparse.h
deleted file mode 100644
index 189927b05992..000000000000
--- a/extra/curl/curl-8.12.1/lib/strparse.h
+++ /dev/null
@@ -1,71 +0,0 @@
-#ifndef HEADER_CURL_STRPARSE_H
-#define HEADER_CURL_STRPARSE_H
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-#include "curl_setup.h"
-
-#define STRE_OK       0
-#define STRE_BIG      1
-#define STRE_SHORT    2
-#define STRE_BEGQUOTE 3
-#define STRE_ENDQUOTE 4
-#define STRE_BYTE     5
-#define STRE_NEWLINE  6
-#define STRE_OVERFLOW 7
-
-struct Curl_str {
-  char *str;
-  size_t len;
-};
-
-/* Get a word until the first space
-   return non-zero on error */
-int Curl_str_word(char **linep, struct Curl_str *out, const size_t max);
-
-/* Get a word until the first DELIM or end of string
-   return non-zero on error */
-int Curl_str_until(char **linep, struct Curl_str *out, const size_t max,
-                   char delim);
-
-/* Get a "quoted" word. No escaping possible.
-   return non-zero on error */
-int Curl_str_quotedword(char **linep, struct Curl_str *out, const size_t max);
-
-/* Advance over a single character.
-   return non-zero on error */
-int Curl_str_single(char **linep, char byte);
-
-/* Advance over a single space.
-   return non-zero on error */
-int Curl_str_singlespace(char **linep);
-
-/* Get an unsigned number
-   return non-zero on error */
-int Curl_str_number(char **linep, size_t *nump, size_t max);
-
-/* Check for CR or LF
-   return non-zero on error */
-int Curl_str_newline(char **linep);
-
-#endif /* HEADER_CURL_STRPARSE_H */
diff --git a/extra/curl/curl-8.12.1/lib/strtok.c b/extra/curl/curl-8.12.1/lib/strtok.c
deleted file mode 100644
index d2cc71c47dc5..000000000000
--- a/extra/curl/curl-8.12.1/lib/strtok.c
+++ /dev/null
@@ -1,68 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifndef HAVE_STRTOK_R
-#include <stddef.h>
-
-#include "strtok.h"
-
-char *
-Curl_strtok_r(char *ptr, const char *sep, char **end)
-{
-  if(!ptr)
-    /* we got NULL input so then we get our last position instead */
-    ptr = *end;
-
-  /* pass all letters that are including in the separator string */
-  while(*ptr && strchr(sep, *ptr))
-    ++ptr;
-
-  if(*ptr) {
-    /* so this is where the next piece of string starts */
-    char *start = ptr;
-
-    /* set the end pointer to the first byte after the start */
-    *end = start + 1;
-
-    /* scan through the string to find where it ends, it ends on a
-       null byte or a character that exists in the separator string */
-    while(**end && !strchr(sep, **end))
-      ++*end;
-
-    if(**end) {
-      /* the end is not a null byte */
-      **end = '\0';  /* null-terminate it! */
-      ++*end;        /* advance the last pointer to beyond the null byte */
-    }
-
-    return start; /* return the position where the string starts */
-  }
-
-  /* we ended up on a null byte, there are no more strings to find! */
-  return NULL;
-}
-
-#endif /* this was only compiled if strtok_r was not present */
diff --git a/extra/curl/curl-8.12.1/lib/strtoofft.c b/extra/curl/curl-8.12.1/lib/strtoofft.c
deleted file mode 100644
index 05536c182613..000000000000
--- a/extra/curl/curl-8.12.1/lib/strtoofft.c
+++ /dev/null
@@ -1,240 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-#include <errno.h>
-#include "curl_setup.h"
-
-#include "strtoofft.h"
-
-/*
- * NOTE:
- *
- * In the ISO C standard (IEEE Std 1003.1), there is a strtoimax() function we
- * could use in case strtoll() does not exist... See
- * https://www.opengroup.org/onlinepubs/009695399/functions/strtoimax.html
- */
-
-#if (SIZEOF_CURL_OFF_T > SIZEOF_LONG)
-#  ifdef HAVE_STRTOLL
-#    define strtooff strtoll
-#  else
-#    if defined(_MSC_VER) && (_MSC_VER >= 1300)
-#      if defined(_SAL_VERSION)
-         _Check_return_ _CRTIMP __int64 __cdecl _strtoi64(
-             _In_z_ const char *_String,
-             _Out_opt_ _Deref_post_z_ char **_EndPtr, _In_ int _Radix);
-#      else
-         _CRTIMP __int64 __cdecl _strtoi64(const char *_String,
-                                           char **_EndPtr, int _Radix);
-#      endif
-#      define strtooff _strtoi64
-#    else
-#      define PRIVATE_STRTOOFF 1
-#    endif
-#  endif
-#else
-#  define strtooff strtol
-#endif
-
-#ifdef PRIVATE_STRTOOFF
-
-/* Range tests can be used for alphanum decoding if characters are consecutive,
-   like in ASCII. Else an array is scanned. Determine this condition now. */
-
-#if('9' - '0') != 9 || ('Z' - 'A') != 25 || ('z' - 'a') != 25
-
-#define NO_RANGE_TEST
-
-static const char valchars[] =
-            "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
-#endif
-
-static int get_char(char c, int base);
-
-/**
- * Custom version of the strtooff function. This extracts a curl_off_t
- * value from the given input string and returns it.
- */
-static curl_off_t strtooff(const char *nptr, char **endptr, int base)
-{
-  char *end;
-  bool is_negative = FALSE;
-  bool overflow = FALSE;
-  int i;
-  curl_off_t value = 0;
-
-  /* Skip leading whitespace. */
-  end = (char *)nptr;
-  while(ISBLANK(end[0])) {
-    end++;
-  }
-
-  /* Handle the sign, if any. */
-  if(end[0] == '-') {
-    is_negative = TRUE;
-    end++;
-  }
-  else if(end[0] == '+') {
-    end++;
-  }
-  else if(end[0] == '\0') {
-    /* We had nothing but perhaps some whitespace -- there was no number. */
-    if(endptr) {
-      *endptr = end;
-    }
-    return 0;
-  }
-
-  /* Handle special beginnings, if present and allowed. */
-  if(end[0] == '0' && end[1] == 'x') {
-    if(base == 16 || base == 0) {
-      end += 2;
-      base = 16;
-    }
-  }
-  else if(end[0] == '0') {
-    if(base == 8 || base == 0) {
-      end++;
-      base = 8;
-    }
-  }
-
-  /* Matching strtol, if the base is 0 and it does not look like
-   * the number is octal or hex, we assume it is base 10.
-   */
-  if(base == 0) {
-    base = 10;
-  }
-
-  /* Loop handling digits. */
-  for(i = get_char(end[0], base);
-      i != -1;
-      end++, i = get_char(end[0], base)) {
-
-    if(value > (CURL_OFF_T_MAX - i) / base) {
-      overflow = TRUE;
-      break;
-    }
-    value = base * value + i;
-  }
-
-  if(!overflow) {
-    if(is_negative) {
-      /* Fix the sign. */
-      value *= -1;
-    }
-  }
-  else {
-    if(is_negative)
-      value = CURL_OFF_T_MIN;
-    else
-      value = CURL_OFF_T_MAX;
-
-    errno = ERANGE;
-  }
-
-  if(endptr)
-    *endptr = end;
-
-  return value;
-}
-
-/**
- * Returns the value of c in the given base, or -1 if c cannot
- * be interpreted properly in that base (i.e., is out of range,
- * is a null, etc.).
- *
- * @param c     the character to interpret according to base
- * @param base  the base in which to interpret c
- *
- * @return  the value of c in base, or -1 if c is not in range
- */
-static int get_char(char c, int base)
-{
-#ifndef NO_RANGE_TEST
-  int value = -1;
-  if(c <= '9' && c >= '0') {
-    value = c - '0';
-  }
-  else if(c <= 'Z' && c >= 'A') {
-    value = c - 'A' + 10;
-  }
-  else if(c <= 'z' && c >= 'a') {
-    value = c - 'a' + 10;
-  }
-#else
-  const char *cp;
-  int value;
-
-  cp = memchr(valchars, c, 10 + 26 + 26);
-
-  if(!cp)
-    return -1;
-
-  value = cp - valchars;
-
-  if(value >= 10 + 26)
-    value -= 26;                /* Lowercase. */
-#endif
-
-  if(value >= base) {
-    value = -1;
-  }
-
-  return value;
-}
-#endif  /* Only present if we need strtoll, but do not have it. */
-
-/*
- * Parse a *positive* up to 64-bit number written in ASCII.
- */
-CURLofft curlx_strtoofft(const char *str, char **endp, int base,
-                         curl_off_t *num)
-{
-  char *end = NULL;
-  curl_off_t number;
-  errno = 0;
-  *num = 0; /* clear by default */
-  DEBUGASSERT(base); /* starting now, avoid base zero */
-
-  while(*str && ISBLANK(*str))
-    str++;
-  if(('-' == *str) || (ISSPACE(*str))) {
-    if(endp)
-      *endp = (char *)str; /* did not actually move */
-    return CURL_OFFT_INVAL; /* nothing parsed */
-  }
-  number = strtooff(str, &end, base);
-  if(endp)
-    *endp = end;
-  if(errno == ERANGE)
-    /* overflow/underflow */
-    return CURL_OFFT_FLOW;
-  else if(str == end)
-    /* nothing parsed */
-    return CURL_OFFT_INVAL;
-
-  *num = number;
-  return CURL_OFFT_OK;
-}
diff --git a/extra/curl/curl-8.12.1/lib/strtoofft.h b/extra/curl/curl-8.12.1/lib/strtoofft.h
deleted file mode 100644
index 71808b719c9b..000000000000
--- a/extra/curl/curl-8.12.1/lib/strtoofft.h
+++ /dev/null
@@ -1,54 +0,0 @@
-#ifndef HEADER_CURL_STRTOOFFT_H
-#define HEADER_CURL_STRTOOFFT_H
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-/*
- * Determine which string to integral data type conversion function we use
- * to implement string conversion to our curl_off_t integral data type.
- *
- * Notice that curl_off_t might be 64 or 32 bits wide, and that it might use
- * an underlying data type which might be 'long', 'int64_t', 'long long' or
- * '__int64' and more remotely other data types.
- *
- * On systems where the size of curl_off_t is greater than the size of 'long'
- * the conversion function to use is strtoll() if it is available, otherwise,
- * we emulate its functionality with our own clone.
- *
- * On systems where the size of curl_off_t is smaller or equal than the size
- * of 'long' the conversion function to use is strtol().
- */
-
-typedef enum {
-  CURL_OFFT_OK,    /* parsed fine */
-  CURL_OFFT_FLOW,  /* over or underflow */
-  CURL_OFFT_INVAL  /* nothing was parsed */
-} CURLofft;
-
-CURLofft curlx_strtoofft(const char *str, char **endp, int base,
-                         curl_off_t *num);
-
-#endif /* HEADER_CURL_STRTOOFFT_H */
diff --git a/extra/curl/curl-8.12.1/lib/vauth/ntlm.h b/extra/curl/curl-8.12.1/lib/vauth/ntlm.h
deleted file mode 100644
index 31ce921cd128..000000000000
--- a/extra/curl/curl-8.12.1/lib/vauth/ntlm.h
+++ /dev/null
@@ -1,143 +0,0 @@
-#ifndef HEADER_VAUTH_NTLM_H
-#define HEADER_VAUTH_NTLM_H
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifdef USE_NTLM
-
-/* NTLM buffer fixed size, large enough for long user + host + domain */
-#define NTLM_BUFSIZE 1024
-
-/* Stuff only required for curl_ntlm_msgs.c */
-#ifdef BUILDING_CURL_NTLM_MSGS_C
-
-/* Flag bits definitions based on
-   https://davenport.sourceforge.net/ntlm.html */
-
-#define NTLMFLAG_NEGOTIATE_UNICODE               (1<<0)
-/* Indicates that Unicode strings are supported for use in security buffer
-   data. */
-
-#define NTLMFLAG_NEGOTIATE_OEM                   (1<<1)
-/* Indicates that OEM strings are supported for use in security buffer data. */
-
-#define NTLMFLAG_REQUEST_TARGET                  (1<<2)
-/* Requests that the server's authentication realm be included in the Type 2
-   message. */
-
-/* unknown (1<<3) */
-#define NTLMFLAG_NEGOTIATE_SIGN                  (1<<4)
-/* Specifies that authenticated communication between the client and server
-   should carry a digital signature (message integrity). */
-
-#define NTLMFLAG_NEGOTIATE_SEAL                  (1<<5)
-/* Specifies that authenticated communication between the client and server
-   should be encrypted (message confidentiality). */
-
-#define NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE        (1<<6)
-/* Indicates that datagram authentication is being used. */
-
-#define NTLMFLAG_NEGOTIATE_LM_KEY                (1<<7)
-/* Indicates that the LAN Manager session key should be used for signing and
-   sealing authenticated communications. */
-
-#define NTLMFLAG_NEGOTIATE_NTLM_KEY              (1<<9)
-/* Indicates that NTLM authentication is being used. */
-
-/* unknown (1<<10) */
-
-#define NTLMFLAG_NEGOTIATE_ANONYMOUS             (1<<11)
-/* Sent by the client in the Type 3 message to indicate that an anonymous
-   context has been established. This also affects the response fields. */
-
-#define NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED       (1<<12)
-/* Sent by the client in the Type 1 message to indicate that a desired
-   authentication realm is included in the message. */
-
-#define NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED  (1<<13)
-/* Sent by the client in the Type 1 message to indicate that the client
-   workstation's name is included in the message. */
-
-#define NTLMFLAG_NEGOTIATE_LOCAL_CALL            (1<<14)
-/* Sent by the server to indicate that the server and client are on the same
-   machine. Implies that the client may use a pre-established local security
-   context rather than responding to the challenge. */
-
-#define NTLMFLAG_NEGOTIATE_ALWAYS_SIGN           (1<<15)
-/* Indicates that authenticated communication between the client and server
-   should be signed with a "dummy" signature. */
-
-#define NTLMFLAG_TARGET_TYPE_DOMAIN              (1<<16)
-/* Sent by the server in the Type 2 message to indicate that the target
-   authentication realm is a domain. */
-
-#define NTLMFLAG_TARGET_TYPE_SERVER              (1<<17)
-/* Sent by the server in the Type 2 message to indicate that the target
-   authentication realm is a server. */
-
-#define NTLMFLAG_TARGET_TYPE_SHARE               (1<<18)
-/* Sent by the server in the Type 2 message to indicate that the target
-   authentication realm is a share. Presumably, this is for share-level
-   authentication. Usage is unclear. */
-
-#define NTLMFLAG_NEGOTIATE_NTLM2_KEY             (1<<19)
-/* Indicates that the NTLM2 signing and sealing scheme should be used for
-   protecting authenticated communications. */
-
-#define NTLMFLAG_REQUEST_INIT_RESPONSE           (1<<20)
-/* unknown purpose */
-
-#define NTLMFLAG_REQUEST_ACCEPT_RESPONSE         (1<<21)
-/* unknown purpose */
-
-#define NTLMFLAG_REQUEST_NONNT_SESSION_KEY       (1<<22)
-/* unknown purpose */
-
-#define NTLMFLAG_NEGOTIATE_TARGET_INFO           (1<<23)
-/* Sent by the server in the Type 2 message to indicate that it is including a
-   Target Information block in the message. */
-
-/* unknown (1<24) */
-/* unknown (1<25) */
-/* unknown (1<26) */
-/* unknown (1<27) */
-/* unknown (1<28) */
-
-#define NTLMFLAG_NEGOTIATE_128                   (1<<29)
-/* Indicates that 128-bit encryption is supported. */
-
-#define NTLMFLAG_NEGOTIATE_KEY_EXCHANGE          (1<<30)
-/* Indicates that the client will provide an encrypted master key in
-   the "Session Key" field of the Type 3 message. */
-
-#define NTLMFLAG_NEGOTIATE_56                    (1<<31)
-/* Indicates that 56-bit encryption is supported. */
-
-#endif /* BUILDING_CURL_NTLM_MSGS_C */
-
-#endif /* USE_NTLM */
-
-#endif /* HEADER_VAUTH_NTLM_H */
diff --git a/extra/curl/curl-8.12.1/lib/vquic/vquic-tls.c b/extra/curl/curl-8.12.1/lib/vquic/vquic-tls.c
deleted file mode 100644
index ff2445d45f78..000000000000
--- a/extra/curl/curl-8.12.1/lib/vquic/vquic-tls.c
+++ /dev/null
@@ -1,371 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#if defined(USE_HTTP3) && \
-  (defined(USE_OPENSSL) || defined(USE_GNUTLS) || defined(USE_WOLFSSL))
-
-#ifdef USE_OPENSSL
-#include <openssl/err.h>
-#include "vtls/openssl.h"
-#elif defined(USE_GNUTLS)
-#include <gnutls/abstract.h>
-#include <gnutls/gnutls.h>
-#include <gnutls/x509.h>
-#include <gnutls/crypto.h>
-#include <nettle/sha2.h>
-#include "vtls/gtls.h"
-#elif defined(USE_WOLFSSL)
-#include <wolfssl/options.h>
-#include <wolfssl/ssl.h>
-#include <wolfssl/quic.h>
-#include "vtls/wolfssl.h"
-#endif
-
-#include "urldata.h"
-#include "curl_trc.h"
-#include "cfilters.h"
-#include "multiif.h"
-#include "vtls/keylog.h"
-#include "vtls/vtls.h"
-#include "vtls/vtls_scache.h"
-#include "vquic-tls.h"
-
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
-
-#if defined(USE_WOLFSSL)
-
-#define QUIC_CIPHERS                                                          \
-  "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_"               \
-  "POLY1305_SHA256:TLS_AES_128_CCM_SHA256"
-#define QUIC_GROUPS "P-256:P-384:P-521"
-
-#if defined(HAVE_SECRET_CALLBACK)
-static void keylog_callback(const WOLFSSL *ssl, const char *line)
-{
-  (void)ssl;
-  Curl_tls_keylog_write_line(line);
-}
-#endif
-
-static CURLcode wssl_init_ctx(struct curl_tls_ctx *ctx,
-                              struct Curl_cfilter *cf,
-                              struct Curl_easy *data,
-                              Curl_vquic_tls_ctx_setup *cb_setup,
-                              void *cb_user_data)
-{
-  struct ssl_primary_config *conn_config;
-  CURLcode result = CURLE_FAILED_INIT;
-
-  conn_config = Curl_ssl_cf_get_primary_config(cf);
-  if(!conn_config) {
-    result = CURLE_FAILED_INIT;
-    goto out;
-  }
-
-  ctx->wssl.ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method());
-  if(!ctx->wssl.ctx) {
-    result = CURLE_OUT_OF_MEMORY;
-    goto out;
-  }
-
-  if(cb_setup) {
-    result = cb_setup(cf, data, cb_user_data);
-    if(result)
-      goto out;
-  }
-
-  wolfSSL_CTX_set_default_verify_paths(ctx->wssl.ctx);
-
-  if(wolfSSL_CTX_set_cipher_list(ctx->wssl.ctx, conn_config->cipher_list13 ?
-                                 conn_config->cipher_list13 :
-                                 QUIC_CIPHERS) != 1) {
-    char error_buffer[256];
-    ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer));
-    failf(data, "wolfSSL failed to set ciphers: %s", error_buffer);
-    result = CURLE_BAD_FUNCTION_ARGUMENT;
-    goto out;
-  }
-
-  if(wolfSSL_CTX_set1_groups_list(ctx->wssl.ctx, conn_config->curves ?
-                                  conn_config->curves :
-                                  (char *)QUIC_GROUPS) != 1) {
-    failf(data, "wolfSSL failed to set curves");
-    result = CURLE_BAD_FUNCTION_ARGUMENT;
-    goto out;
-  }
-
-  /* Open the file if a TLS or QUIC backend has not done this before. */
-  Curl_tls_keylog_open();
-  if(Curl_tls_keylog_enabled()) {
-#if defined(HAVE_SECRET_CALLBACK)
-    wolfSSL_CTX_set_keylog_callback(ctx->wssl.ctx, keylog_callback);
-#else
-    failf(data, "wolfSSL was built without keylog callback");
-    result = CURLE_NOT_BUILT_IN;
-    goto out;
-#endif
-  }
-
-  if(conn_config->verifypeer) {
-    const char * const ssl_cafile = conn_config->CAfile;
-    const char * const ssl_capath = conn_config->CApath;
-
-    wolfSSL_CTX_set_verify(ctx->wssl.ctx, SSL_VERIFY_PEER, NULL);
-    if(ssl_cafile || ssl_capath) {
-      /* tell wolfSSL where to find CA certificates that are used to verify
-         the server's certificate. */
-      int rc =
-        wolfSSL_CTX_load_verify_locations_ex(ctx->wssl.ctx, ssl_cafile,
-                                             ssl_capath,
-                                             WOLFSSL_LOAD_FLAG_IGNORE_ERR);
-      if(SSL_SUCCESS != rc) {
-        /* 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");
-        result = CURLE_SSL_CACERT_BADFILE;
-        goto out;
-      }
-      infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
-      infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
-    }
-#ifdef CURL_CA_FALLBACK
-    else {
-      /* verifying the peer without any CA certificates will not work so
-         use wolfSSL's built-in default as fallback */
-      wolfSSL_CTX_set_default_verify_paths(ctx->wssl.ctx);
-    }
-#endif
-  }
-  else {
-    wolfSSL_CTX_set_verify(ctx->wssl.ctx, SSL_VERIFY_NONE, NULL);
-  }
-
-  /* give application a chance to interfere with SSL set up. */
-  if(data->set.ssl.fsslctx) {
-    Curl_set_in_callback(data, TRUE);
-    result = (*data->set.ssl.fsslctx)(data, ctx->wssl.ctx,
-                                      data->set.ssl.fsslctxp);
-    Curl_set_in_callback(data, FALSE);
-    if(result) {
-      failf(data, "error signaled by ssl ctx callback");
-      goto out;
-    }
-  }
-  result = CURLE_OK;
-
-out:
-  if(result && ctx->wssl.ctx) {
-    SSL_CTX_free(ctx->wssl.ctx);
-    ctx->wssl.ctx = NULL;
-  }
-  return result;
-}
-
-/** SSL callbacks ***/
-
-static CURLcode wssl_init_ssl(struct curl_tls_ctx *ctx,
-                              struct Curl_cfilter *cf,
-                              struct Curl_easy *data,
-                              struct ssl_peer *peer,
-                              const char *alpn, size_t alpn_len,
-                              void *user_data)
-{
-  struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
-
-  DEBUGASSERT(!ctx->wssl.handle);
-  DEBUGASSERT(ctx->wssl.ctx);
-  ctx->wssl.handle = wolfSSL_new(ctx->wssl.ctx);
-
-  wolfSSL_set_app_data(ctx->wssl.handle, user_data);
-  wolfSSL_set_connect_state(ctx->wssl.handle);
-  wolfSSL_set_quic_use_legacy_codepoint(ctx->wssl.handle, 0);
-
-  if(alpn)
-    wolfSSL_set_alpn_protos(ctx->wssl.handle, (const unsigned char *)alpn,
-                            (unsigned int)alpn_len);
-
-  if(peer->sni) {
-    wolfSSL_UseSNI(ctx->wssl.handle, WOLFSSL_SNI_HOST_NAME,
-                   peer->sni, (unsigned short)strlen(peer->sni));
-  }
-
-  if(ssl_config->primary.cache_session) {
-    (void)Curl_wssl_setup_session(cf, data, &ctx->wssl, peer->scache_key);
-  }
-
-  return CURLE_OK;
-}
-#endif /* defined(USE_WOLFSSL) */
-
-CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx,
-                             struct Curl_cfilter *cf,
-                             struct Curl_easy *data,
-                             struct ssl_peer *peer,
-                             const char *alpn, size_t alpn_len,
-                             Curl_vquic_tls_ctx_setup *cb_setup,
-                             void *cb_user_data, void *ssl_user_data,
-                             Curl_vquic_session_reuse_cb *session_reuse_cb)
-{
-  char tls_id[80];
-  CURLcode result;
-
-#ifdef USE_OPENSSL
-  Curl_ossl_version(tls_id, sizeof(tls_id));
-#elif defined(USE_GNUTLS)
-  Curl_gtls_version(tls_id, sizeof(tls_id));
-#elif defined(USE_WOLFSSL)
-  Curl_wssl_version(tls_id, sizeof(tls_id));
-#else
-#error "no TLS lib in used, should not happen"
-  return CURLE_FAILED_INIT;
-#endif
-  (void)session_reuse_cb;
-  result = Curl_ssl_peer_init(peer, cf, tls_id, TRNSPRT_QUIC);
-  if(result)
-    return result;
-
-#ifdef USE_OPENSSL
-  (void)result;
-  return Curl_ossl_ctx_init(&ctx->ossl, cf, data, peer,
-                            (const unsigned char *)alpn, alpn_len,
-                            cb_setup, cb_user_data, NULL, ssl_user_data);
-#elif defined(USE_GNUTLS)
-  return Curl_gtls_ctx_init(&ctx->gtls, cf, data, peer,
-                            (const unsigned char *)alpn, alpn_len,
-                            cb_setup, cb_user_data, ssl_user_data,
-                            session_reuse_cb);
-#elif defined(USE_WOLFSSL)
-  result = wssl_init_ctx(ctx, cf, data, cb_setup, cb_user_data);
-  if(result)
-    return result;
-
-  (void)session_reuse_cb;
-  return wssl_init_ssl(ctx, cf, data, peer, alpn, alpn_len, ssl_user_data);
-#else
-#error "no TLS lib in used, should not happen"
-  return CURLE_FAILED_INIT;
-#endif
-}
-
-void Curl_vquic_tls_cleanup(struct curl_tls_ctx *ctx)
-{
-#ifdef USE_OPENSSL
-  if(ctx->ossl.ssl)
-    SSL_free(ctx->ossl.ssl);
-  if(ctx->ossl.ssl_ctx)
-    SSL_CTX_free(ctx->ossl.ssl_ctx);
-#elif defined(USE_GNUTLS)
-  if(ctx->gtls.session)
-    gnutls_deinit(ctx->gtls.session);
-  Curl_gtls_shared_creds_free(&ctx->gtls.shared_creds);
-#elif defined(USE_WOLFSSL)
-  if(ctx->wssl.handle)
-    wolfSSL_free(ctx->wssl.handle);
-  if(ctx->wssl.ctx)
-    wolfSSL_CTX_free(ctx->wssl.ctx);
-#endif
-  memset(ctx, 0, sizeof(*ctx));
-}
-
-CURLcode Curl_vquic_tls_before_recv(struct curl_tls_ctx *ctx,
-                                    struct Curl_cfilter *cf,
-                                    struct Curl_easy *data)
-{
-#ifdef USE_OPENSSL
-  if(!ctx->ossl.x509_store_setup) {
-    CURLcode result = Curl_ssl_setup_x509_store(cf, data, ctx->ossl.ssl_ctx);
-    if(result)
-      return result;
-    ctx->ossl.x509_store_setup = TRUE;
-  }
-#elif defined(USE_WOLFSSL)
-  if(!ctx->wssl.x509_store_setup) {
-    CURLcode result = Curl_wssl_setup_x509_store(cf, data, &ctx->wssl);
-    if(result)
-      return result;
-  }
-#elif defined(USE_GNUTLS)
-  if(!ctx->gtls.shared_creds->trust_setup) {
-    CURLcode result = Curl_gtls_client_trust_setup(cf, data, &ctx->gtls);
-    if(result)
-      return result;
-  }
-#else
-  (void)ctx; (void)cf; (void)data;
-#endif
-  return CURLE_OK;
-}
-
-CURLcode Curl_vquic_tls_verify_peer(struct curl_tls_ctx *ctx,
-                                    struct Curl_cfilter *cf,
-                                    struct Curl_easy *data,
-                                    struct ssl_peer *peer)
-{
-  struct ssl_primary_config *conn_config;
-  CURLcode result = CURLE_OK;
-
-  conn_config = Curl_ssl_cf_get_primary_config(cf);
-  if(!conn_config)
-    return CURLE_FAILED_INIT;
-
-#ifdef USE_OPENSSL
-  (void)conn_config;
-  result = Curl_oss_check_peer_cert(cf, data, &ctx->ossl, peer);
-#elif defined(USE_GNUTLS)
-  if(conn_config->verifyhost) {
-    result = Curl_gtls_verifyserver(data, ctx->gtls.session,
-                                    conn_config, &data->set.ssl, peer,
-                                    data->set.str[STRING_SSL_PINNEDPUBLICKEY]);
-    if(result)
-      return result;
-  }
-#elif defined(USE_WOLFSSL)
-  (void)data;
-  if(conn_config->verifyhost) {
-    if(peer->sni) {
-      WOLFSSL_X509* cert = wolfSSL_get_peer_certificate(ctx->wssl.handle);
-      if(wolfSSL_X509_check_host(cert, peer->sni, strlen(peer->sni), 0, NULL)
-            == WOLFSSL_FAILURE) {
-        result = CURLE_PEER_FAILED_VERIFICATION;
-      }
-      wolfSSL_X509_free(cert);
-    }
-
-  }
-#endif
-  /* on error, remove any session we might have in the pool */
-  if(result)
-    Curl_ssl_scache_remove_all(cf, data, peer->scache_key);
-  return result;
-}
-
-
-#endif /* !USE_HTTP3 && (USE_OPENSSL || USE_GNUTLS || USE_WOLFSSL) */
diff --git a/extra/curl/curl-8.12.1/lib/ws.h b/extra/curl/curl-8.12.1/lib/ws.h
deleted file mode 100644
index c96bccab9f4d..000000000000
--- a/extra/curl/curl-8.12.1/lib/ws.h
+++ /dev/null
@@ -1,85 +0,0 @@
-#ifndef HEADER_CURL_WS_H
-#define HEADER_CURL_WS_H
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-#include "curl_setup.h"
-
-#if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP)
-
-/* a client-side WS frame decoder, parsing frame headers and
- * payload, keeping track of current position and stats */
-enum ws_dec_state {
-  WS_DEC_INIT,
-  WS_DEC_HEAD,
-  WS_DEC_PAYLOAD
-};
-
-struct ws_decoder {
-  int frame_age;        /* zero */
-  int frame_flags;      /* See the CURLWS_* defines */
-  curl_off_t payload_offset;   /* the offset parsing is at */
-  curl_off_t payload_len;
-  unsigned char head[10];
-  int head_len, head_total;
-  enum ws_dec_state state;
-};
-
-/* a client-side WS frame encoder, generating frame headers and
- * converting payloads, tracking remaining data in current frame */
-struct ws_encoder {
-  curl_off_t payload_len;  /* payload length of current frame */
-  curl_off_t payload_remain;  /* remaining payload of current */
-  unsigned int xori; /* xor index */
-  unsigned char mask[4]; /* 32-bit mask for this connection */
-  unsigned char firstbyte; /* first byte of frame we encode */
-  BIT(contfragment); /* set TRUE if the previous fragment sent was not final */
-};
-
-/* A websocket connection with en- and decoder that treat frames
- * and keep track of boundaries. */
-struct websocket {
-  struct Curl_easy *data; /* used for write callback handling */
-  struct ws_decoder dec;  /* decode of we frames */
-  struct ws_encoder enc;  /* decode of we frames */
-  struct bufq recvbuf;    /* raw data from the server */
-  struct bufq sendbuf;    /* raw data to be sent to the server */
-  struct curl_ws_frame frame;  /* the current WS FRAME received */
-  size_t sendbuf_payload; /* number of payload bytes in sendbuf */
-};
-
-CURLcode Curl_ws_request(struct Curl_easy *data, struct dynbuf *req);
-CURLcode Curl_ws_accept(struct Curl_easy *data, const char *mem, size_t len);
-
-extern const struct Curl_handler Curl_handler_ws;
-#ifdef USE_SSL
-extern const struct Curl_handler Curl_handler_wss;
-#endif
-
-
-#else
-#define Curl_ws_request(x,y) CURLE_OK
-#define Curl_ws_free(x) Curl_nop_stmt
-#endif
-
-#endif /* HEADER_CURL_WS_H */
diff --git a/extra/curl/curl-8.12.1/scripts/mk-ca-bundle.pl b/extra/curl/curl-8.12.1/scripts/mk-ca-bundle.pl
deleted file mode 100755
index 8e8afb4dd74b..000000000000
--- a/extra/curl/curl-8.12.1/scripts/mk-ca-bundle.pl
+++ /dev/null
@@ -1,672 +0,0 @@
-#!/usr/bin/env perl
-# ***************************************************************************
-# *                                  _   _ ____  _
-# *  Project                     ___| | | |  _ \| |
-# *                             / __| | | | |_) | |
-# *                            | (__| |_| |  _ <| |___
-# *                             \___|\___/|_| \_\_____|
-# *
-# * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
-# *
-# * This software is licensed as described in the file COPYING, which
-# * you should have received as part of this distribution. The terms
-# * are also available at https://curl.se/docs/copyright.html.
-# *
-# * You may opt to use, copy, modify, merge, publish, distribute and/or sell
-# * copies of the Software, and permit persons to whom the Software is
-# * furnished to do so, under the terms of the COPYING file.
-# *
-# * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
-# * KIND, either express or implied.
-# *
-# * SPDX-License-Identifier: curl
-# *
-# ***************************************************************************
-# This Perl script creates a fresh ca-bundle.crt file for use with libcurl.
-# It downloads certdata.txt from Mozilla's source tree (see URL below),
-# then parses certdata.txt and extracts CA Root Certificates into PEM format.
-# These are then processed with the OpenSSL commandline tool to produce the
-# final ca-bundle.crt file.
-# The script is based on the parse-certs script written by Roland Krikava.
-# This Perl script works on almost any platform since its only external
-# dependency is the OpenSSL commandline tool for optional text listing.
-# Hacked by Guenter Knauf.
-#
-use Encode;
-use Getopt::Std;
-use MIME::Base64;
-use strict;
-use warnings;
-use vars qw($opt_b $opt_d $opt_f $opt_h $opt_i $opt_k $opt_l $opt_m $opt_n $opt_p $opt_q $opt_s $opt_t $opt_u $opt_v $opt_w);
-use List::Util;
-use Text::Wrap;
-use Time::Local;
-my $MOD_SHA = "Digest::SHA";
-eval "require $MOD_SHA";
-if ($@) {
-  $MOD_SHA = "Digest::SHA::PurePerl";
-  eval "require $MOD_SHA";
-}
-eval "require LWP::UserAgent";
-
-my %urls = (
-  'nss' =>
-    'https://hg.mozilla.org/projects/nss/raw-file/default/lib/ckfw/builtins/certdata.txt',
-  'central' =>
-    'https://hg.mozilla.org/mozilla-central/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt',
-  'beta' =>
-    'https://hg.mozilla.org/releases/mozilla-beta/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt',
-  'release' =>
-    'https://hg.mozilla.org/releases/mozilla-release/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt',
-);
-
-$opt_d = 'release';
-
-# If the OpenSSL commandline is not in search path you can configure it here!
-my $openssl = 'openssl';
-
-my $version = '1.29';
-
-$opt_w = 76; # default base64 encoded lines length
-
-# default cert types to include in the output (default is to include CAs which
-# may issue SSL server certs)
-my $default_mozilla_trust_purposes = "SERVER_AUTH";
-my $default_mozilla_trust_levels = "TRUSTED_DELEGATOR";
-$opt_p = $default_mozilla_trust_purposes . ":" . $default_mozilla_trust_levels;
-
-my @valid_mozilla_trust_purposes = (
-  "DIGITAL_SIGNATURE",
-  "NON_REPUDIATION",
-  "KEY_ENCIPHERMENT",
-  "DATA_ENCIPHERMENT",
-  "KEY_AGREEMENT",
-  "KEY_CERT_SIGN",
-  "CRL_SIGN",
-  "SERVER_AUTH",
-  "CLIENT_AUTH",
-  "CODE_SIGNING",
-  "EMAIL_PROTECTION",
-  "IPSEC_END_SYSTEM",
-  "IPSEC_TUNNEL",
-  "IPSEC_USER",
-  "TIME_STAMPING",
-  "STEP_UP_APPROVED"
-);
-
-my @valid_mozilla_trust_levels = (
-  "TRUSTED_DELEGATOR",    # CAs
-  "NOT_TRUSTED",          # Don't trust these certs.
-  "MUST_VERIFY_TRUST",    # This explicitly tells us that it ISN'T a CA but is
-                          # otherwise ok. In other words, this should tell the
-                          # app to ignore any other sources that claim this is
-                          # a CA.
-  "TRUSTED"               # This cert is trusted, but only for itself and not
-                          # for delegates (i.e. it is not a CA).
-);
-
-my $default_signature_algorithms = $opt_s = "MD5";
-
-my @valid_signature_algorithms = (
-  "MD5",
-  "SHA1",
-  "SHA256",
-  "SHA384",
-  "SHA512"
-);
-
-$0 =~ s@.*(/|\\)@@;
-$Getopt::Std::STANDARD_HELP_VERSION = 1;
-getopts('bd:fhiklmnp:qs:tuvw:');
-
-if(!defined($opt_d)) {
-    # to make plain "-d" use not cause warnings, and actually still work
-    $opt_d = 'release';
-}
-
-# Use predefined URL or else custom URL specified on command line.
-my $url;
-if(defined($urls{$opt_d})) {
-  $url = $urls{$opt_d};
-  if(!$opt_k && $url !~ /^https:\/\//i) {
-    die "The URL for '$opt_d' is not HTTPS. Use -k to override (insecure).\n";
-  }
-}
-else {
-  $url = $opt_d;
-}
-
-if ($opt_i) {
-  print ("=" x 78 . "\n");
-  print "Script Version                   : $version\n";
-  print "Perl Version                     : $]\n";
-  print "Operating System Name            : $^O\n";
-  print "Getopt::Std.pm Version           : ${Getopt::Std::VERSION}\n";
-  print "Encode::Encoding.pm Version      : ${Encode::Encoding::VERSION}\n";
-  print "MIME::Base64.pm Version          : ${MIME::Base64::VERSION}\n";
-  print "LWP::UserAgent.pm Version        : ${LWP::UserAgent::VERSION}\n" if($LWP::UserAgent::VERSION);
-  print "LWP.pm Version                   : ${LWP::VERSION}\n" if($LWP::VERSION);
-  print "Digest::SHA.pm Version           : ${Digest::SHA::VERSION}\n" if ($Digest::SHA::VERSION);
-  print "Digest::SHA::PurePerl.pm Version : ${Digest::SHA::PurePerl::VERSION}\n" if ($Digest::SHA::PurePerl::VERSION);
-  print ("=" x 78 . "\n");
-}
-
-sub warning_message() {
-  if ( $opt_d =~ m/^risk$/i ) { # Long Form Warning and Exit
-    print "Warning: Use of this script may pose some risk:\n";
-    print "\n";
-    print "  1) If you use HTTP URLs they are subject to a man in the middle attack\n";
-    print "  2) Default to 'release', but more recent updates may be found in other trees\n";
-    print "  3) certdata.txt file format may change, lag time to update this script\n";
-    print "  4) Generally unwise to blindly trust CAs without manual review & verification\n";
-    print "  5) Mozilla apps use additional security checks aren't represented in certdata\n";
-    print "  6) Use of this script will make a security engineer grind his teeth and\n";
-    print "     swear at you.  ;)\n";
-    exit;
-  } else { # Short Form Warning
-    print "Warning: Use of this script may pose some risk, -d risk for more details.\n";
-  }
-}
-
-sub HELP_MESSAGE() {
-  print "Usage:\t${0} [-b] [-d<certdata>] [-f] [-i] [-k] [-l] [-n] [-p<purposes:levels>] [-q] [-s<algorithms>] [-t] [-u] [-v] [-w<l>] [<outputfile>]\n";
-  print "\t-b\tbackup an existing version of ca-bundle.crt\n";
-  print "\t-d\tspecify Mozilla tree to pull certdata.txt or custom URL\n";
-  print "\t\t  Valid names are:\n";
-  print "\t\t    ", join( ", ", map { ( $_ =~ m/$opt_d/ ) ? "$_ (default)" : "$_" } sort keys %urls ), "\n";
-  print "\t-f\tforce rebuild even if certdata.txt is current\n";
-  print "\t-i\tprint version info about used modules\n";
-  print "\t-k\tallow URLs other than HTTPS, enable HTTP fallback (insecure)\n";
-  print "\t-l\tprint license info about certdata.txt\n";
-  print "\t-m\tinclude meta data in output\n";
-  print "\t-n\tno download of certdata.txt (to use existing)\n";
-  print wrap("\t","\t\t", "-p\tlist of Mozilla trust purposes and levels for certificates to include in output. Takes the form of a comma separated list of purposes, a colon, and a comma separated list of levels. (default: $default_mozilla_trust_purposes:$default_mozilla_trust_levels)"), "\n";
-  print "\t\t  Valid purposes are:\n";
-  print wrap("\t\t    ","\t\t    ", join( ", ", "ALL", @valid_mozilla_trust_purposes ) ), "\n";
-  print "\t\t  Valid levels are:\n";
-  print wrap("\t\t    ","\t\t    ", join( ", ", "ALL", @valid_mozilla_trust_levels ) ), "\n";
-  print "\t-q\tbe really quiet (no progress output at all)\n";
-  print wrap("\t","\t\t", "-s\tcomma separated list of certificate signatures/hashes to output in plain text mode. (default: $default_signature_algorithms)\n");
-  print "\t\t  Valid signature algorithms are:\n";
-  print wrap("\t\t    ","\t\t    ", join( ", ", "ALL", @valid_signature_algorithms ) ), "\n";
-  print "\t-t\tinclude plain text listing of certificates\n";
-  print "\t-u\tunlink (remove) certdata.txt after processing\n";
-  print "\t-v\tbe verbose and print out processed CAs\n";
-  print "\t-w <l>\twrap base64 output lines after <l> chars (default: ${opt_w})\n";
-  exit;
-}
-
-sub VERSION_MESSAGE() {
-  print "${0} version ${version} running Perl ${]} on ${^O}\n";
-}
-
-warning_message() unless ($opt_q || $url =~ m/^(ht|f)tps:/i );
-HELP_MESSAGE() if ($opt_h);
-
-sub report($@) {
-  my $output = shift;
-
-  print STDERR $output . "\n" unless $opt_q;
-}
-
-sub is_in_list($@) {
-  my $target = shift;
-
-  return defined(List::Util::first { $target eq $_ } @_);
-}
-
-# Parses $param_string as a case insensitive comma separated list with optional
-# whitespace validates that only allowed parameters are supplied
-sub parse_csv_param($$@) {
-  my $description = shift;
-  my $param_string = shift;
-  my @valid_values = @_;
-
-  my @values = map {
-    s/^\s+//;  # strip leading spaces
-    s/\s+$//;  # strip trailing spaces
-    uc $_      # return the modified string as upper case
-  } split( ',', $param_string );
-
-  # Find all values which are not in the list of valid values or "ALL"
-  my @invalid = grep { !is_in_list($_,"ALL",@valid_values) } @values;
-
-  if ( scalar(@invalid) > 0 ) {
-    # Tell the user which parameters were invalid and print the standard help
-    # message which will exit
-    print "Error: Invalid ", $description, scalar(@invalid) == 1 ? ": " : "s: ", join( ", ", map { "\"$_\"" } @invalid ), "\n";
-    HELP_MESSAGE();
-  }
-
-  @values = @valid_values if ( is_in_list("ALL",@values) );
-
-  return @values;
-}
-
-sub sha256 {
-  my $result;
-  if ($Digest::SHA::VERSION || $Digest::SHA::PurePerl::VERSION) {
-    open(FILE, $_[0]) or die "Can't open '$_[0]': $!";
-    binmode(FILE);
-    $result = $MOD_SHA->new(256)->addfile(*FILE)->hexdigest;
-    close(FILE);
-  } else {
-    # Use OpenSSL command if Perl Digest::SHA modules not available
-    $result = `"$openssl" dgst -r -sha256 "$_[0]"`;
-    $result =~ s/^([0-9a-f]{64}) .+/$1/is;
-  }
-  return $result;
-}
-
-
-sub oldhash {
-  my $hash = "";
-  open(C, "<$_[0]") || return 0;
-  while(<C>) {
-    chomp;
-    if($_ =~ /^\#\# SHA256: (.*)/) {
-      $hash = $1;
-      last;
-    }
-  }
-  close(C);
-  return $hash;
-}
-
-if ( $opt_p !~ m/:/ ) {
-  print "Error: Mozilla trust identifier list must include both purposes and levels\n";
-  HELP_MESSAGE();
-}
-
-(my $included_mozilla_trust_purposes_string, my $included_mozilla_trust_levels_string) = split( ':', $opt_p );
-my @included_mozilla_trust_purposes = parse_csv_param( "trust purpose", $included_mozilla_trust_purposes_string, @valid_mozilla_trust_purposes );
-my @included_mozilla_trust_levels = parse_csv_param( "trust level", $included_mozilla_trust_levels_string, @valid_mozilla_trust_levels );
-
-my @included_signature_algorithms = parse_csv_param( "signature algorithm", $opt_s, @valid_signature_algorithms );
-
-sub should_output_cert(%) {
-  my %trust_purposes_by_level = @_;
-
-  foreach my $level (@included_mozilla_trust_levels) {
-    # for each level we want to output, see if any of our desired purposes are
-    # included
-    return 1 if ( defined( List::Util::first { is_in_list( $_, @included_mozilla_trust_purposes ) } @{$trust_purposes_by_level{$level}} ) );
-  }
-
-  return 0;
-}
-
-my $crt = $ARGV[0] || 'ca-bundle.crt';
-(my $txt = $url) =~ s@(.*/|\?.*)@@g;
-
-my $stdout = $crt eq '-';
-my $resp;
-my $fetched;
-
-my $oldhash = oldhash($crt);
-
-report "SHA256 of old file: $oldhash";
-
-if(!$opt_n) {
-  report "Downloading $txt ...";
-
-  # If we have an HTTPS URL then use curl
-  if($url =~ /^https:\/\//i) {
-    my $curl = `curl -V`;
-    if($curl) {
-      if($curl =~ /^Protocols:.* https( |$)/m) {
-        report "Get certdata with curl!";
-        my $proto = !$opt_k ? "--proto =https" : "";
-        my $quiet = $opt_q ? "-s" : "";
-        my @out = `curl -w %{response_code} $proto $quiet -o "$txt" "$url"`;
-        if(!$? && @out && $out[0] == 200) {
-          $fetched = 1;
-          report "Downloaded $txt";
-        }
-        else {
-          report "Failed downloading via HTTPS with curl";
-          if(-e $txt && !unlink($txt)) {
-            report "Failed to remove '$txt': $!";
-          }
-        }
-      }
-      else {
-        report "curl lacks https support";
-      }
-    }
-    else {
-      report "curl not found";
-    }
-  }
-
-  # If nothing was fetched then use LWP
-  if(!$fetched) {
-    if($url =~ /^https:\/\//i) {
-      report "Falling back to HTTP";
-      $url =~ s/^https:\/\//http:\/\//i;
-    }
-    if(!$opt_k) {
-      report "URLs other than HTTPS are disabled by default, to enable use -k";
-      exit 1;
-    }
-    report "Get certdata with LWP!";
-    if(!defined(${LWP::UserAgent::VERSION})) {
-      report "LWP is not available (LWP::UserAgent not found)";
-      exit 1;
-    }
-    my $ua  = new LWP::UserAgent(agent => "$0/$version");
-    $ua->env_proxy();
-    $resp = $ua->mirror($url, $txt);
-    if($resp && $resp->code eq '304') {
-      report "Not modified";
-      exit 0 if -e $crt && !$opt_f;
-    }
-    else {
-      $fetched = 1;
-      report "Downloaded $txt";
-    }
-    if(!$resp || $resp->code !~ /^(?:200|304)$/) {
-      report "Unable to download latest data: "
-        . ($resp? $resp->code . ' - ' . $resp->message : "LWP failed");
-      exit 1 if -e $crt || ! -r $txt;
-    }
-  }
-}
-
-my $filedate = $resp ? $resp->last_modified : (stat($txt))[9];
-my $datesrc = "as of";
-if(!$filedate) {
-    # mxr.mozilla.org gave us a time, hg.mozilla.org does not!
-    $filedate = time();
-    $datesrc="downloaded on";
-}
-
-# get the hash from the download file
-my $newhash= sha256($txt);
-
-if(!$opt_f && $oldhash eq $newhash) {
-    report "Downloaded file identical to previous run\'s source file. Exiting";
-    if($opt_u && -e $txt && !unlink($txt)) {
-        report "Failed to remove $txt: $!\n";
-    }
-    exit;
-}
-
-report "SHA256 of new file: $newhash";
-
-my $currentdate = scalar gmtime($filedate);
-
-my $format = $opt_t ? "plain text and " : "";
-if( $stdout ) {
-    open(CRT, '> -') or die "Couldn't open STDOUT: $!\n";
-} else {
-    open(CRT,">$crt.~") or die "Couldn't open $crt.~: $!\n";
-}
-print CRT <<EOT;
-##
-## Bundle of CA Root Certificates
-##
-## Certificate data from Mozilla ${datesrc}: ${currentdate} GMT
-##
-## Find updated versions here: https://curl.se/docs/caextract.html
-##
-## This is a bundle of X.509 certificates of public Certificate Authorities
-## (CA). These were automatically extracted from Mozilla's root certificates
-## file (certdata.txt).  This file can be found in the mozilla source tree:
-## ${url}
-##
-## It contains the certificates in ${format}PEM format and therefore
-## can be directly used with curl / libcurl / php_curl, or with
-## an Apache+mod_ssl webserver for SSL client authentication.
-## Just configure this file as the SSLCACertificateFile.
-##
-## Conversion done with mk-ca-bundle.pl version $version.
-## SHA256: $newhash
-##
-
-EOT
-
-report "Processing  '$txt' ...";
-my $caname;
-my $certnum = 0;
-my $skipnum = 0;
-my $start_of_cert = 0;
-my $main_block = 0;
-my $main_block_name;
-my $trust_block = 0;
-my $trust_block_name;
-my @precert;
-my $cka_value;
-my $valid = 0;
-
-open(TXT,"$txt") or die "Couldn't open $txt: $!\n";
-while (<TXT>) {
-  if (/\*\*\*\*\* BEGIN LICENSE BLOCK \*\*\*\*\*/) {
-    print CRT;
-    print if ($opt_l);
-    while (<TXT>) {
-      print CRT;
-      print if ($opt_l);
-      last if (/\*\*\*\*\* END LICENSE BLOCK \*\*\*\*\*/);
-    }
-    next;
-  }
-  # The input file format consists of blocks of Mozilla objects.
-  # The blocks are separated by blank lines but may be related.
-  elsif(/^\s*$/) {
-    $main_block = 0;
-    $trust_block = 0;
-    next;
-  }
-  # Each certificate has a main block.
-  elsif(/^# Certificate "(.*)"/) {
-    (!$main_block && !$trust_block) or die "Unexpected certificate block";
-    $main_block = 1;
-    $main_block_name = $1;
-    # Reset all other certificate variables.
-    $trust_block = 0;
-    $trust_block_name = "";
-    $valid = 0;
-    $start_of_cert = 0;
-    $caname = "";
-    $cka_value = "";
-    undef @precert;
-    next;
-  }
-  # Each certificate's main block is followed by a trust block.
-  elsif(/^# Trust for (?:Certificate )?"(.*)"/) {
-    (!$main_block && !$trust_block) or die "Unexpected trust block";
-    $trust_block = 1;
-    $trust_block_name = $1;
-    if($main_block_name ne $trust_block_name) {
-      die "cert name \"$main_block_name\" != trust name \"$trust_block_name\"";
-    }
-    next;
-  }
-  # Ignore other blocks.
-  #
-  # There is a documentation comment block, a BEGINDATA block, and a bunch of
-  # blocks starting with "# Explicitly Distrust <certname>".
-  #
-  # The latter is for certificates that have already been removed and are not
-  # included. Not all explicitly distrusted certificates are ignored at this
-  # point, just those without an actual certificate.
-  elsif(!$main_block && !$trust_block) {
-    next;
-  }
-  elsif(/^#/) {
-    # The commented lines in a main block are plaintext metadata that describes
-    # the certificate. Issuer, Subject, Fingerprint, etc.
-    if($main_block) {
-      push @precert, $_ if not /^#$/;
-      if(/^# Not Valid After : (.*)/) {
-        my $stamp = $1;
-        use Time::Piece;
-        # Not Valid After : Thu Sep 30 14:01:15 2021
-        my $t = Time::Piece->strptime($stamp, "%a %b %d %H:%M:%S %Y");
-        my $delta = ($t->epoch - time()); # negative means no longer valid
-        if($delta < 0) {
-          $skipnum++;
-          report "Skipping: $main_block_name is not valid anymore" if ($opt_v);
-          $valid = 0;
-        }
-        else {
-          $valid = 1;
-        }
-      }
-    }
-    next;
-  }
-  elsif(!$valid) {
-    next;
-  }
-
-  chomp;
-
-  if($main_block) {
-    if(/^CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE/) {
-      !$start_of_cert or die "Duplicate CKO_CERTIFICATE object";
-      $start_of_cert = 1;
-      next;
-    }
-    elsif(!$start_of_cert) {
-      next;
-    }
-    elsif(/^CKA_LABEL UTF8 \"(.*)\"/) {
-      ($caname eq "") or die "Duplicate CKA_LABEL attribute";
-      $caname = $1;
-      if($caname ne $main_block_name) {
-        die "caname \"$caname\" != cert name \"$main_block_name\"";
-      }
-      next;
-    }
-    elsif(/^CKA_VALUE MULTILINE_OCTAL/) {
-      ($cka_value eq "") or die "Duplicate CKA_VALUE attribute";
-      while (<TXT>) {
-        last if (/^END/);
-        chomp;
-        my @octets = split(/\\/);
-        shift @octets;
-        for (@octets) {
-          $cka_value .= chr(oct);
-        }
-      }
-      next;
-    }
-    else {
-      next;
-    }
-  }
-
-  if(!$trust_block || !$start_of_cert || $caname eq "" || $cka_value eq "") {
-    die "Certificate extraction failed";
-  }
-
-  my %trust_purposes_by_level;
-
-  if(/^CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST/) {
-    # now scan the trust part to determine how we should trust this cert
-    while (<TXT>) {
-      if(/^\s*$/) {
-        $trust_block = 0;
-        last;
-      }
-      if (/^CKA_TRUST_([A-Z_]+)\s+CK_TRUST\s+CKT_NSS_([A-Z_]+)\s*$/) {
-        if ( !is_in_list($1,@valid_mozilla_trust_purposes) ) {
-          report "Warning: Unrecognized trust purpose for cert: $caname. Trust purpose: $1. Trust Level: $2";
-        } elsif ( !is_in_list($2,@valid_mozilla_trust_levels) ) {
-          report "Warning: Unrecognized trust level for cert: $caname. Trust purpose: $1. Trust Level: $2";
-        } else {
-          push @{$trust_purposes_by_level{$2}}, $1;
-        }
-      }
-    }
-
-    # Sanity check that an explicitly distrusted certificate only has trust
-    # purposes with a trust level of NOT_TRUSTED.
-    #
-    # Certificate objects that are explicitly distrusted are in a certificate
-    # block that starts # Certificate "Explicitly Distrust(ed) <certname>",
-    # where "Explicitly Distrust(ed) " was prepended to the original cert name.
-    if($caname =~ /distrust/i ||
-       $main_block_name =~ /distrust/i ||
-       $trust_block_name =~ /distrust/i) {
-      my @levels = keys %trust_purposes_by_level;
-      if(scalar(@levels) != 1 || $levels[0] ne "NOT_TRUSTED") {
-        die "\"$caname\" must have all trust purposes at level NOT_TRUSTED.";
-      }
-    }
-
-    if ( !should_output_cert(%trust_purposes_by_level) ) {
-      $skipnum ++;
-      report "Skipping: $caname lacks acceptable trust level" if ($opt_v);
-    } else {
-      my $encoded = MIME::Base64::encode_base64($cka_value, '');
-      $encoded =~ s/(.{1,${opt_w}})/$1\n/g;
-      my $pem = "-----BEGIN CERTIFICATE-----\n"
-              . $encoded
-              . "-----END CERTIFICATE-----\n";
-      print CRT "\n$caname\n";
-      my $maxStringLength = length(decode('UTF-8', $caname, Encode::FB_CROAK | Encode::LEAVE_SRC));
-      print CRT ("=" x $maxStringLength . "\n");
-      if ($opt_t) {
-        foreach my $key (sort keys %trust_purposes_by_level) {
-           my $string = $key . ": " . join(", ", @{$trust_purposes_by_level{$key}});
-           print CRT $string . "\n";
-        }
-      }
-      if($opt_m) {
-        print CRT for @precert;
-      }
-      if (!$opt_t) {
-        print CRT $pem;
-      } else {
-        my $pipe = "";
-        foreach my $hash (@included_signature_algorithms) {
-          $pipe = "|$openssl x509 -" . $hash . " -fingerprint -noout -inform PEM";
-          if (!$stdout) {
-            $pipe .= " >> $crt.~";
-            close(CRT) or die "Couldn't close $crt.~: $!";
-          }
-          open(TMP, $pipe) or die "Couldn't open openssl pipe: $!";
-          print TMP $pem;
-          close(TMP) or die "Couldn't close openssl pipe: $!";
-          if (!$stdout) {
-            open(CRT, ">>$crt.~") or die "Couldn't open $crt.~: $!";
-          }
-        }
-        $pipe = "|$openssl x509 -text -inform PEM";
-        if (!$stdout) {
-          $pipe .= " >> $crt.~";
-          close(CRT) or die "Couldn't close $crt.~: $!";
-        }
-        open(TMP, $pipe) or die "Couldn't open openssl pipe: $!";
-        print TMP $pem;
-        close(TMP) or die "Couldn't close openssl pipe: $!";
-        if (!$stdout) {
-          open(CRT, ">>$crt.~") or die "Couldn't open $crt.~: $!";
-        }
-      }
-      report "Processed: $caname" if ($opt_v);
-      $certnum ++;
-    }
-  }
-}
-close(TXT) or die "Couldn't close $txt: $!\n";
-close(CRT) or die "Couldn't close $crt.~: $!\n";
-unless( $stdout ) {
-    if ($opt_b && -e $crt) {
-        my $bk = 1;
-        while (-e "$crt.~${bk}~") {
-            $bk++;
-        }
-        rename $crt, "$crt.~${bk}~" or die "Failed to create backup $crt.~$bk}~: $!\n";
-    } elsif( -e $crt ) {
-        unlink( $crt ) or die "Failed to remove $crt: $!\n";
-    }
-    rename "$crt.~", $crt or die "Failed to rename $crt.~ to $crt: $!\n";
-}
-if($opt_u && -e $txt && !unlink($txt)) {
-  report "Failed to remove $txt: $!\n";
-}
-report "Done ($certnum CA certs processed, $skipnum skipped).";
diff --git a/extra/curl/curl-8.12.1/CHANGES.md b/extra/curl/curl-8.14.1/CHANGES.md
similarity index 100%
rename from extra/curl/curl-8.12.1/CHANGES.md
rename to extra/curl/curl-8.14.1/CHANGES.md
diff --git a/extra/curl/curl-8.12.1/CMake/CMakeConfigurableFile.in b/extra/curl/curl-8.14.1/CMake/CMakeConfigurableFile.in
similarity index 100%
rename from extra/curl/curl-8.12.1/CMake/CMakeConfigurableFile.in
rename to extra/curl/curl-8.14.1/CMake/CMakeConfigurableFile.in
diff --git a/extra/curl/curl-8.12.1/CMake/CurlSymbolHiding.cmake b/extra/curl/curl-8.14.1/CMake/CurlSymbolHiding.cmake
similarity index 87%
rename from extra/curl/curl-8.12.1/CMake/CurlSymbolHiding.cmake
rename to extra/curl/curl-8.14.1/CMake/CurlSymbolHiding.cmake
index 31a97cb10708..217c8832c9ab 100644
--- a/extra/curl/curl-8.12.1/CMake/CurlSymbolHiding.cmake
+++ b/extra/curl/curl-8.14.1/CMake/CurlSymbolHiding.cmake
@@ -29,7 +29,7 @@ if(WIN32 AND (ENABLE_DEBUG OR ENABLE_CURLDEBUG))
   # e.g. curl_easy_perform_ev() or curl_dbg_*(),
   # so disable symbol hiding for debug builds and for memory tracking.
   set(CURL_HIDDEN_SYMBOLS OFF)
-elseif(DOS OR AMIGA)
+elseif(DOS OR AMIGA OR MINGW32CE)
   set(CURL_HIDDEN_SYMBOLS OFF)
 endif()
 
@@ -42,18 +42,18 @@ if(CURL_HIDDEN_SYMBOLS)
     set(CURL_HIDES_PRIVATE_SYMBOLS TRUE)
     set(CURL_EXTERN_SYMBOL "__attribute__((__visibility__(\"default\")))")
     set(CURL_CFLAG_SYMBOLS_HIDE "-fvisibility=hidden")
-  elseif(CMAKE_COMPILER_IS_GNUCC)
-    if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 3.4)
+  elseif(CMAKE_C_COMPILER_ID STREQUAL "GNU")
+    if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 3.4)
       # Note: This is considered buggy prior to 4.0 but the autotools do not care, so let us ignore that fact
       set(CURL_HIDES_PRIVATE_SYMBOLS TRUE)
       set(CURL_EXTERN_SYMBOL "__attribute__((__visibility__(\"default\")))")
       set(CURL_CFLAG_SYMBOLS_HIDE "-fvisibility=hidden")
     endif()
-  elseif(CMAKE_C_COMPILER_ID MATCHES "SunPro" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 8.0)
+  elseif(CMAKE_C_COMPILER_ID MATCHES "SunPro" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 8.0)
     set(CURL_HIDES_PRIVATE_SYMBOLS TRUE)
     set(CURL_EXTERN_SYMBOL "__global")
     set(CURL_CFLAG_SYMBOLS_HIDE "-xldscope=hidden")
-  elseif(CMAKE_C_COMPILER_ID MATCHES "Intel" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 9.0)  # Requires 9.1.045
+  elseif(CMAKE_C_COMPILER_ID MATCHES "Intel" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 9.0)  # Requires 9.1.045
     set(CURL_HIDES_PRIVATE_SYMBOLS TRUE)
     set(CURL_EXTERN_SYMBOL "__attribute__((__visibility__(\"default\")))")
     set(CURL_CFLAG_SYMBOLS_HIDE "-fvisibility=hidden")
diff --git a/extra/curl/curl-8.12.1/CMake/CurlTests.c b/extra/curl/curl-8.14.1/CMake/CurlTests.c
similarity index 86%
rename from extra/curl/curl-8.12.1/CMake/CurlTests.c
rename to extra/curl/curl-8.14.1/CMake/CurlTests.c
index c5a5257672d2..de2313080dd7 100644
--- a/extra/curl/curl-8.12.1/CMake/CurlTests.c
+++ b/extra/curl/curl-8.14.1/CMake/CurlTests.c
@@ -30,14 +30,14 @@
 /* */
 #if defined(sun) || defined(__sun__) || \
     defined(__SUNPRO_C) || defined(__SUNPRO_CC)
-# if defined(__SVR4) || defined(__srv4__)
-#  define PLATFORM_SOLARIS
-# else
-#  define PLATFORM_SUNOS4
-# endif
+#  if defined(__SVR4) || defined(__srv4__)
+#    define PLATFORM_SOLARIS
+#  else
+#    define PLATFORM_SUNOS4
+#  endif
 #endif
 #if (defined(_AIX) || defined(__xlC__)) && !defined(_AIX41)
-# define PLATFORM_AIX_V3
+#  define PLATFORM_AIX_V3
 #endif
 /* */
 #if defined(PLATFORM_SUNOS4) || defined(PLATFORM_AIX_V3)
@@ -55,72 +55,52 @@ int main(void)
 #endif
 
 /* tests for gethostbyname_r */
-#if defined(HAVE_GETHOSTBYNAME_R_3_REENTRANT) || \
-    defined(HAVE_GETHOSTBYNAME_R_5_REENTRANT) || \
-    defined(HAVE_GETHOSTBYNAME_R_6_REENTRANT)
-#   define _REENTRANT
-    /* no idea whether _REENTRANT is always set, just invent a new flag */
-#   define TEST_GETHOSTBYFOO_REENTRANT
-#endif
 #if defined(HAVE_GETHOSTBYNAME_R_3) || \
+    defined(HAVE_GETHOSTBYNAME_R_3_REENTRANT) || \
     defined(HAVE_GETHOSTBYNAME_R_5) || \
+    defined(HAVE_GETHOSTBYNAME_R_5_REENTRANT) || \
     defined(HAVE_GETHOSTBYNAME_R_6) || \
-    defined(TEST_GETHOSTBYFOO_REENTRANT)
+    defined(HAVE_GETHOSTBYNAME_R_6_REENTRANT)
 #include <sys/types.h>
 #include <netdb.h>
 int main(void)
 {
   const char *address = "example.com";
-  int length = 0;
-  int type = 0;
   struct hostent h;
   int rc = 0;
-#if defined(HAVE_GETHOSTBYNAME_R_3) || \
-    defined(HAVE_GETHOSTBYNAME_R_3_REENTRANT)
+#if   defined(HAVE_GETHOSTBYNAME_R_3) || \
+      defined(HAVE_GETHOSTBYNAME_R_3_REENTRANT)
   struct hostent_data hdata;
 #elif defined(HAVE_GETHOSTBYNAME_R_5) || \
       defined(HAVE_GETHOSTBYNAME_R_5_REENTRANT) || \
       defined(HAVE_GETHOSTBYNAME_R_6) || \
       defined(HAVE_GETHOSTBYNAME_R_6_REENTRANT)
   char buffer[8192];
-  int h_errnop;
   struct hostent *hp;
+  int h_errnop;
 #endif
 
 #if   defined(HAVE_GETHOSTBYNAME_R_3) || \
       defined(HAVE_GETHOSTBYNAME_R_3_REENTRANT)
   rc = gethostbyname_r(address, &h, &hdata);
+  (void)hdata;
 #elif defined(HAVE_GETHOSTBYNAME_R_5) || \
       defined(HAVE_GETHOSTBYNAME_R_5_REENTRANT)
   rc = gethostbyname_r(address, &h, buffer, 8192, &h_errnop);
   (void)hp; /* not used for test */
+  (void)h_errnop;
 #elif defined(HAVE_GETHOSTBYNAME_R_6) || \
       defined(HAVE_GETHOSTBYNAME_R_6_REENTRANT)
   rc = gethostbyname_r(address, &h, buffer, 8192, &hp, &h_errnop);
+  (void)hp;
+  (void)h_errnop;
 #endif
-
-  (void)length;
-  (void)type;
+  (void)h;
   (void)rc;
   return 0;
 }
 #endif
 
-#ifdef HAVE_IN_ADDR_T
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <arpa/inet.h>
-int main(void)
-{
-  if((in_addr_t *) 0)
-    return 0;
-  if(sizeof(in_addr_t))
-    return 0;
-  ;
-  return 0;
-}
-#endif
-
 #ifdef HAVE_BOOL_T
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
@@ -130,10 +110,7 @@ int main(void)
 #endif
 int main(void)
 {
-  if(sizeof(bool *))
-    return 0;
-  ;
-  return 0;
+  return (int)sizeof(bool *);
 }
 #endif
 
@@ -146,18 +123,20 @@ int main(void) { return 0; }
 #endif
 
 #ifdef HAVE_FILE_OFFSET_BITS
-#undef _FILE_OFFSET_BITS
-#define _FILE_OFFSET_BITS 64
 #include <sys/types.h>
 /* Check that off_t can represent 2**63 - 1 correctly.
    We cannot simply define LARGE_OFF_T to be 9223372036854775807,
    since some C++ compilers masquerading as C compilers
    incorrectly reject 9223372036854775807. */
 #define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
-int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
-                     && LARGE_OFF_T % 2147483647 == 1)
-                    ? 1 : -1];
-int main(void) { return 0; }
+static int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 &&
+                           LARGE_OFF_T % 2147483647 == 1)
+                          ? 1 : -1];
+int main(void)
+{
+  (void)off_t_is_large;
+  return 0;
+}
 #endif
 
 #ifdef HAVE_IOCTLSOCKET
@@ -169,7 +148,7 @@ int main(void)
   /* ioctlsocket source code */
   int socket = -1;
   unsigned long flags = ioctlsocket(socket, FIONBIO, &flags);
-  ;
+  (void)flags;
   return 0;
 }
 
@@ -182,7 +161,6 @@ int main(void)
   /* IoctlSocket source code */
   if(0 != IoctlSocket(0, 0, 0))
     return 1;
-  ;
   return 0;
 }
 #endif
@@ -198,7 +176,7 @@ int main(void)
   long flags = 0;
   if(0 != IoctlSocket(0, FIONBIO, &flags))
     return 1;
-  ;
+  (void)flags;
   return 0;
 }
 #endif
@@ -212,7 +190,7 @@ int main(void)
   unsigned long flags = 0;
   if(0 != ioctlsocket(0, FIONBIO, &flags))
     return 1;
-  ;
+  (void)flags;
   return 0;
 }
 #endif
@@ -239,7 +217,7 @@ int main(void)
   int flags = 0;
   if(0 != ioctl(0, FIONBIO, &flags))
     return 1;
-  ;
+  (void)flags;
   return 0;
 }
 #endif
@@ -267,7 +245,7 @@ int main(void)
   struct ifreq ifr;
   if(0 != ioctl(0, SIOCGIFADDR, &ifr))
     return 1;
-  ;
+  (void)ifr;
   return 0;
 }
 #endif
@@ -286,7 +264,6 @@ int main(void)
 {
   if(0 != setsockopt(0, SOL_SOCKET, SO_NONBLOCK, 0, 0))
     return 1;
-  ;
   return 0;
 }
 #endif
@@ -295,12 +272,13 @@ int main(void)
 #include <string.h>
 #include <errno.h>
 
-void check(char c) {}
+static void check(char c) { (void)c; }
 
 int main(void)
 {
   char buffer[1024];
   /* This will not compile if strerror_r does not return a char* */
+  /* !checksrc! disable ERRNOVAR 1 */
   check(strerror_r(EACCES, buffer, sizeof(buffer))[0]);
   return 0;
 }
@@ -311,12 +289,13 @@ int main(void)
 #include <errno.h>
 
 /* Float, because a pointer cannot be implicitly cast to float */
-void check(float f) {}
+static void check(float f) { (void)f; }
 
 int main(void)
 {
   char buffer[1024];
   /* This will not compile if strerror_r does not return an int */
+  /* !checksrc! disable ERRNOVAR 1 */
   check(strerror_r(EACCES, buffer, sizeof(buffer)));
   return 0;
 }
@@ -335,7 +314,7 @@ int main(void)
 #include <sys/xattr.h> /* header from libc, not from libattr */
 int main(void)
 {
-  fsetxattr(0, 0, 0, 0, 0);
+  fsetxattr(0, "", 0, 0, 0);
   return 0;
 }
 #endif
@@ -344,8 +323,9 @@ int main(void)
 #include <time.h>
 int main(void)
 {
-  struct timespec ts = {0, 0};
-  clock_gettime(CLOCK_MONOTONIC, &ts);
+  struct timespec ts;
+  (void)clock_gettime(CLOCK_MONOTONIC, &ts);
+  (void)ts;
   return 0;
 }
 #endif
diff --git a/extra/curl/curl-8.12.1/CMake/FindBearSSL.cmake b/extra/curl/curl-8.14.1/CMake/FindBearSSL.cmake
similarity index 100%
rename from extra/curl/curl-8.12.1/CMake/FindBearSSL.cmake
rename to extra/curl/curl-8.14.1/CMake/FindBearSSL.cmake
diff --git a/extra/curl/curl-8.12.1/CMake/FindBrotli.cmake b/extra/curl/curl-8.14.1/CMake/FindBrotli.cmake
similarity index 85%
rename from extra/curl/curl-8.12.1/CMake/FindBrotli.cmake
rename to extra/curl/curl-8.14.1/CMake/FindBrotli.cmake
index b72f1906fff7..690b5a9c27ce 100644
--- a/extra/curl/curl-8.12.1/CMake/FindBrotli.cmake
+++ b/extra/curl/curl-8.14.1/CMake/FindBrotli.cmake
@@ -39,24 +39,19 @@
 # - `BROTLI_CFLAGS`:         Required compiler flags.
 # - `BROTLI_VERSION`:        Version of brotli.
 
-set(BROTLI_PC_REQUIRES "libbrotlidec")
+set(BROTLI_PC_REQUIRES "libbrotlidec" "libbrotlicommon")  # order is significant: brotlidec then brotlicommon
 
 if(CURL_USE_PKGCONFIG AND
    NOT DEFINED BROTLI_INCLUDE_DIR AND
    NOT DEFINED BROTLICOMMON_LIBRARY AND
    NOT DEFINED BROTLIDEC_LIBRARY)
   find_package(PkgConfig QUIET)
-  pkg_check_modules(BROTLI "libbrotlicommon")
-  pkg_check_modules(BROTLIDEC ${BROTLI_PC_REQUIRES})
+  pkg_check_modules(BROTLI ${BROTLI_PC_REQUIRES})
 endif()
 
-if(BROTLI_FOUND AND BROTLIDEC_FOUND)
+if(BROTLI_FOUND)
   set(Brotli_FOUND TRUE)
-  list(APPEND BROTLIDEC_LIBRARIES ${BROTLI_LIBRARIES})  # order is significant: brotlidec then brotlicommon
-  list(REVERSE BROTLIDEC_LIBRARIES)
-  list(REMOVE_DUPLICATES BROTLIDEC_LIBRARIES)
-  list(REVERSE BROTLIDEC_LIBRARIES)
-  set(BROTLI_LIBRARIES ${BROTLIDEC_LIBRARIES})
+  set(BROTLI_VERSION "${BROTLI_libbrotlicommon_VERSION}")
   string(REPLACE ";" " " BROTLI_CFLAGS "${BROTLI_CFLAGS}")
   message(STATUS "Found Brotli (via pkg-config): ${BROTLI_INCLUDE_DIRS} (found version \"${BROTLI_VERSION}\")")
 else()
diff --git a/extra/curl/curl-8.12.1/CMake/FindCares.cmake b/extra/curl/curl-8.14.1/CMake/FindCares.cmake
similarity index 100%
rename from extra/curl/curl-8.12.1/CMake/FindCares.cmake
rename to extra/curl/curl-8.14.1/CMake/FindCares.cmake
diff --git a/extra/curl/curl-8.12.1/CMake/FindGSS.cmake b/extra/curl/curl-8.14.1/CMake/FindGSS.cmake
similarity index 100%
rename from extra/curl/curl-8.12.1/CMake/FindGSS.cmake
rename to extra/curl/curl-8.14.1/CMake/FindGSS.cmake
diff --git a/extra/curl/curl-8.12.1/CMake/FindLDAP.cmake b/extra/curl/curl-8.14.1/CMake/FindLDAP.cmake
similarity index 94%
rename from extra/curl/curl-8.12.1/CMake/FindLDAP.cmake
rename to extra/curl/curl-8.14.1/CMake/FindLDAP.cmake
index 4f18e04b59c0..fdc6d7be943d 100644
--- a/extra/curl/curl-8.12.1/CMake/FindLDAP.cmake
+++ b/extra/curl/curl-8.14.1/CMake/FindLDAP.cmake
@@ -39,7 +39,7 @@
 # - `LDAP_CFLAGS`:        Required compiler flags.
 # - `LDAP_VERSION`:       Version of ldap.
 
-set(LDAP_PC_REQUIRES "ldap")
+set(LDAP_PC_REQUIRES "ldap" "lber")
 
 if(CURL_USE_PKGCONFIG AND
    NOT DEFINED LDAP_INCLUDE_DIR AND
@@ -47,14 +47,10 @@ if(CURL_USE_PKGCONFIG AND
    NOT DEFINED LDAP_LBER_LIBRARY)
   find_package(PkgConfig QUIET)
   pkg_check_modules(LDAP ${LDAP_PC_REQUIRES})
-  pkg_check_modules(LDAP_LBER "lber")
 endif()
 
-if(LDAP_FOUND AND LDAP_LBER_FOUND)
-  list(APPEND LDAP_LIBRARIES ${LDAP_LBER_LIBRARIES})
-  list(REVERSE LDAP_LIBRARIES)
-  list(REMOVE_DUPLICATES LDAP_LIBRARIES)
-  list(REVERSE LDAP_LIBRARIES)
+if(LDAP_FOUND)
+  set(LDAP_VERSION "${LDAP_ldap_VERSION}")
   string(REPLACE ";" " " LDAP_CFLAGS "${LDAP_CFLAGS}")
   message(STATUS "Found LDAP (via pkg-config): ${LDAP_INCLUDE_DIRS} (found version \"${LDAP_VERSION}\")")
 else()
diff --git a/extra/curl/curl-8.12.1/CMake/FindLibgsasl.cmake b/extra/curl/curl-8.14.1/CMake/FindLibgsasl.cmake
similarity index 100%
rename from extra/curl/curl-8.12.1/CMake/FindLibgsasl.cmake
rename to extra/curl/curl-8.14.1/CMake/FindLibgsasl.cmake
diff --git a/extra/curl/curl-8.12.1/CMake/FindLibidn2.cmake b/extra/curl/curl-8.14.1/CMake/FindLibidn2.cmake
similarity index 100%
rename from extra/curl/curl-8.12.1/CMake/FindLibidn2.cmake
rename to extra/curl/curl-8.14.1/CMake/FindLibidn2.cmake
diff --git a/extra/curl/curl-8.12.1/CMake/FindLibpsl.cmake b/extra/curl/curl-8.14.1/CMake/FindLibpsl.cmake
similarity index 100%
rename from extra/curl/curl-8.12.1/CMake/FindLibpsl.cmake
rename to extra/curl/curl-8.14.1/CMake/FindLibpsl.cmake
diff --git a/extra/curl/curl-8.12.1/CMake/FindLibrtmp.cmake b/extra/curl/curl-8.14.1/CMake/FindLibrtmp.cmake
similarity index 100%
rename from extra/curl/curl-8.12.1/CMake/FindLibrtmp.cmake
rename to extra/curl/curl-8.14.1/CMake/FindLibrtmp.cmake
diff --git a/extra/curl/curl-8.12.1/CMake/FindLibssh.cmake b/extra/curl/curl-8.14.1/CMake/FindLibssh.cmake
similarity index 100%
rename from extra/curl/curl-8.12.1/CMake/FindLibssh.cmake
rename to extra/curl/curl-8.14.1/CMake/FindLibssh.cmake
diff --git a/extra/curl/curl-8.12.1/CMake/FindLibssh2.cmake b/extra/curl/curl-8.14.1/CMake/FindLibssh2.cmake
similarity index 100%
rename from extra/curl/curl-8.12.1/CMake/FindLibssh2.cmake
rename to extra/curl/curl-8.14.1/CMake/FindLibssh2.cmake
diff --git a/extra/curl/curl-8.12.1/CMake/FindLibuv.cmake b/extra/curl/curl-8.14.1/CMake/FindLibuv.cmake
similarity index 100%
rename from extra/curl/curl-8.12.1/CMake/FindLibuv.cmake
rename to extra/curl/curl-8.14.1/CMake/FindLibuv.cmake
diff --git a/extra/curl/curl-8.12.1/CMake/FindMSH3.cmake b/extra/curl/curl-8.14.1/CMake/FindMSH3.cmake
similarity index 100%
rename from extra/curl/curl-8.12.1/CMake/FindMSH3.cmake
rename to extra/curl/curl-8.14.1/CMake/FindMSH3.cmake
diff --git a/extra/curl/curl-8.12.1/CMake/FindMbedTLS.cmake b/extra/curl/curl-8.14.1/CMake/FindMbedTLS.cmake
similarity index 91%
rename from extra/curl/curl-8.12.1/CMake/FindMbedTLS.cmake
rename to extra/curl/curl-8.14.1/CMake/FindMbedTLS.cmake
index 83f13712e41f..fcd6afb3ab6b 100644
--- a/extra/curl/curl-8.12.1/CMake/FindMbedTLS.cmake
+++ b/extra/curl/curl-8.14.1/CMake/FindMbedTLS.cmake
@@ -46,7 +46,7 @@ if(DEFINED MBEDTLS_INCLUDE_DIRS AND NOT DEFINED MBEDTLS_INCLUDE_DIR)
   unset(MBEDTLS_INCLUDE_DIRS)
 endif()
 
-set(MBEDTLS_PC_REQUIRES "mbedtls")
+set(MBEDTLS_PC_REQUIRES "mbedtls" "mbedx509" "mbedcrypto")
 
 if(CURL_USE_PKGCONFIG AND
    NOT DEFINED MBEDTLS_INCLUDE_DIR AND
@@ -55,16 +55,11 @@ if(CURL_USE_PKGCONFIG AND
    NOT DEFINED MBEDCRYPTO_LIBRARY)
   find_package(PkgConfig QUIET)
   pkg_check_modules(MBEDTLS ${MBEDTLS_PC_REQUIRES})
-  pkg_check_modules(MBEDX509 "mbedx509")
-  pkg_check_modules(MBEDCRYPTO "mbedcrypto")
 endif()
 
-if(MBEDTLS_FOUND AND MBEDX509_FOUND AND MBEDCRYPTO_FOUND)
+if(MBEDTLS_FOUND)
   set(MbedTLS_FOUND TRUE)
-  list(APPEND MBEDTLS_LIBRARIES ${MBEDX509_LIBRARIES} ${MBEDCRYPTO_LIBRARIES})
-  list(REVERSE MBEDTLS_LIBRARIES)
-  list(REMOVE_DUPLICATES MBEDTLS_LIBRARIES)
-  list(REVERSE MBEDTLS_LIBRARIES)
+  set(MBEDTLS_VERSION "${MBEDTLS_mbedtls_VERSION}")
   string(REPLACE ";" " " MBEDTLS_CFLAGS "${MBEDTLS_CFLAGS}")
   message(STATUS "Found MbedTLS (via pkg-config): ${MBEDTLS_INCLUDE_DIRS} (found version \"${MBEDTLS_VERSION}\")")
 else()
diff --git a/extra/curl/curl-8.12.1/CMake/FindNGHTTP2.cmake b/extra/curl/curl-8.14.1/CMake/FindNGHTTP2.cmake
similarity index 100%
rename from extra/curl/curl-8.12.1/CMake/FindNGHTTP2.cmake
rename to extra/curl/curl-8.14.1/CMake/FindNGHTTP2.cmake
diff --git a/extra/curl/curl-8.12.1/CMake/FindNGHTTP3.cmake b/extra/curl/curl-8.14.1/CMake/FindNGHTTP3.cmake
similarity index 100%
rename from extra/curl/curl-8.12.1/CMake/FindNGHTTP3.cmake
rename to extra/curl/curl-8.14.1/CMake/FindNGHTTP3.cmake
diff --git a/extra/curl/curl-8.12.1/CMake/FindNGTCP2.cmake b/extra/curl/curl-8.14.1/CMake/FindNGTCP2.cmake
similarity index 89%
rename from extra/curl/curl-8.12.1/CMake/FindNGTCP2.cmake
rename to extra/curl/curl-8.14.1/CMake/FindNGTCP2.cmake
index 0cbb3484d1ef..3cbd408c22c5 100644
--- a/extra/curl/curl-8.12.1/CMake/FindNGTCP2.cmake
+++ b/extra/curl/curl-8.14.1/CMake/FindNGTCP2.cmake
@@ -30,6 +30,7 @@
 # - BoringSSL:  Use `libngtcp2_crypto_boringssl`. (choose this for AWS-LC)
 # - wolfSSL:    Use `libngtcp2_crypto_wolfssl`.
 # - GnuTLS:     Use `libngtcp2_crypto_gnutls`.
+# - ossl:       Use `libngtcp2_crypto_ossl`.
 #
 # Input variables:
 #
@@ -49,7 +50,7 @@
 if(NGTCP2_FIND_COMPONENTS)
   set(_ngtcp2_crypto_backend "")
   foreach(_component IN LISTS NGTCP2_FIND_COMPONENTS)
-    if(_component MATCHES "^(BoringSSL|quictls|wolfSSL|GnuTLS)")
+    if(_component MATCHES "^(BoringSSL|quictls|wolfSSL|GnuTLS|ossl)")
       if(_ngtcp2_crypto_backend)
         message(FATAL_ERROR "NGTCP2: Only one crypto library can be selected")
       endif()
@@ -65,7 +66,7 @@ endif()
 
 set(NGTCP2_PC_REQUIRES "libngtcp2")
 if(_ngtcp2_crypto_backend)
-  set(NGTCP2_CRYPTO_PC_REQUIRES "lib${_crypto_library_lower}")
+  list(APPEND NGTCP2_PC_REQUIRES "lib${_crypto_library_lower}")
 endif()
 
 if(CURL_USE_PKGCONFIG AND
@@ -73,18 +74,10 @@ if(CURL_USE_PKGCONFIG AND
    NOT DEFINED NGTCP2_LIBRARY)
   find_package(PkgConfig QUIET)
   pkg_check_modules(NGTCP2 ${NGTCP2_PC_REQUIRES})
-  if(_ngtcp2_crypto_backend)
-    pkg_check_modules("${_crypto_library_upper}" ${NGTCP2_CRYPTO_PC_REQUIRES})
-  else()
-    set("${_crypto_library_upper}_FOUND" TRUE)
-  endif()
 endif()
 
-list(APPEND NGTCP2_PC_REQUIRES ${NGTCP2_CRYPTO_PC_REQUIRES})
-
-if(NGTCP2_FOUND AND "${${_crypto_library_upper}_FOUND}")
-  list(APPEND NGTCP2_LIBRARIES "${${_crypto_library_upper}_LIBRARIES}")
-  list(REMOVE_DUPLICATES NGTCP2_LIBRARIES)
+if(NGTCP2_FOUND)
+  set(NGTCP2_VERSION "${NGTCP2_libngtcp2_VERSION}")
   string(REPLACE ";" " " NGTCP2_CFLAGS "${NGTCP2_CFLAGS}")
   message(STATUS "Found NGTCP2 (via pkg-config): ${NGTCP2_INCLUDE_DIRS} (found version \"${NGTCP2_VERSION}\")")
 else()
diff --git a/extra/curl/curl-8.12.1/CMake/FindNettle.cmake b/extra/curl/curl-8.14.1/CMake/FindNettle.cmake
similarity index 100%
rename from extra/curl/curl-8.12.1/CMake/FindNettle.cmake
rename to extra/curl/curl-8.14.1/CMake/FindNettle.cmake
diff --git a/extra/curl/curl-8.12.1/CMake/FindQuiche.cmake b/extra/curl/curl-8.14.1/CMake/FindQuiche.cmake
similarity index 100%
rename from extra/curl/curl-8.12.1/CMake/FindQuiche.cmake
rename to extra/curl/curl-8.14.1/CMake/FindQuiche.cmake
diff --git a/extra/curl/curl-8.12.1/CMake/FindRustls.cmake b/extra/curl/curl-8.14.1/CMake/FindRustls.cmake
similarity index 100%
rename from extra/curl/curl-8.12.1/CMake/FindRustls.cmake
rename to extra/curl/curl-8.14.1/CMake/FindRustls.cmake
diff --git a/extra/curl/curl-8.12.1/CMake/FindWolfSSH.cmake b/extra/curl/curl-8.14.1/CMake/FindWolfSSH.cmake
similarity index 100%
rename from extra/curl/curl-8.12.1/CMake/FindWolfSSH.cmake
rename to extra/curl/curl-8.14.1/CMake/FindWolfSSH.cmake
diff --git a/extra/curl/curl-8.12.1/CMake/FindWolfSSL.cmake b/extra/curl/curl-8.14.1/CMake/FindWolfSSL.cmake
similarity index 100%
rename from extra/curl/curl-8.12.1/CMake/FindWolfSSL.cmake
rename to extra/curl/curl-8.14.1/CMake/FindWolfSSL.cmake
diff --git a/extra/curl/curl-8.12.1/CMake/FindZstd.cmake b/extra/curl/curl-8.14.1/CMake/FindZstd.cmake
similarity index 100%
rename from extra/curl/curl-8.12.1/CMake/FindZstd.cmake
rename to extra/curl/curl-8.14.1/CMake/FindZstd.cmake
diff --git a/extra/curl/curl-8.12.1/CMake/Macros.cmake b/extra/curl/curl-8.14.1/CMake/Macros.cmake
similarity index 91%
rename from extra/curl/curl-8.12.1/CMake/Macros.cmake
rename to extra/curl/curl-8.14.1/CMake/Macros.cmake
index 8653f36b0a5a..e3a654b5f369 100644
--- a/extra/curl/curl-8.12.1/CMake/Macros.cmake
+++ b/extra/curl/curl-8.14.1/CMake/Macros.cmake
@@ -52,7 +52,7 @@ macro(curl_internal_test _curl_test)
       ${PROJECT_BINARY_DIR}
       "${CMAKE_CURRENT_SOURCE_DIR}/CMake/CurlTests.c"
       CMAKE_FLAGS
-        "-DCOMPILE_DEFINITIONS:STRING=-D${_curl_test} ${CURL_TEST_DEFINES} ${_cmake_required_definitions}"
+        "-DCOMPILE_DEFINITIONS:STRING=-D${_curl_test} ${CURL_TEST_DEFINES} ${CMAKE_REQUIRED_FLAGS} ${_cmake_required_definitions}"
         "${_curl_test_add_libraries}"
       OUTPUT_VARIABLE CURL_TEST_OUTPUT)
     if(${_curl_test})
@@ -87,3 +87,10 @@ macro(curl_required_libpaths _libpaths_arg)
     list(APPEND CMAKE_REQUIRED_LINK_DIRECTORIES "${_libpaths_arg}")
   endif()
 endmacro()
+
+# Pre-fill variables set by a check_type_size() call.
+macro(curl_prefill_type_size _type _size)
+  set(HAVE_SIZEOF_${_type} TRUE)
+  set(SIZEOF_${_type} ${_size})
+  set(SIZEOF_${_type}_CODE "#define SIZEOF_${_type} ${_size}")
+endmacro()
diff --git a/extra/curl/curl-8.12.1/CMake/OtherTests.cmake b/extra/curl/curl-8.14.1/CMake/OtherTests.cmake
similarity index 99%
rename from extra/curl/curl-8.12.1/CMake/OtherTests.cmake
rename to extra/curl/curl-8.14.1/CMake/OtherTests.cmake
index 8a7faaf657ce..26e9d821c2b2 100644
--- a/extra/curl/curl-8.12.1/CMake/OtherTests.cmake
+++ b/extra/curl/curl-8.14.1/CMake/OtherTests.cmake
@@ -107,11 +107,10 @@ if(NOT DEFINED HAVE_GETADDRINFO_THREADSAFE)
   check_c_source_compiles("${_source_epilogue}
     int main(void)
     {
-    #ifdef h_errno
-      return 0;
-    #else
+    #ifndef h_errno
       #error force compilation error
     #endif
+      return 0;
     }" HAVE_H_ERRNO)
 
   if(NOT HAVE_H_ERRNO)
@@ -127,12 +126,11 @@ if(NOT DEFINED HAVE_GETADDRINFO_THREADSAFE)
         int main(void)
         {
         #if defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200809L)
-          return 0;
         #elif defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE >= 700)
-          return 0;
         #else
           #error force compilation error
         #endif
+          return 0;
         }" HAVE_H_ERRNO_SBS_ISSUE_7)
     endif()
   endif()
diff --git a/extra/curl/curl-8.12.1/CMake/PickyWarnings.cmake b/extra/curl/curl-8.14.1/CMake/PickyWarnings.cmake
similarity index 57%
rename from extra/curl/curl-8.12.1/CMake/PickyWarnings.cmake
rename to extra/curl/curl-8.14.1/CMake/PickyWarnings.cmake
index 15f771b3e344..be26d05cb8be 100644
--- a/extra/curl/curl-8.12.1/CMake/PickyWarnings.cmake
+++ b/extra/curl/curl-8.14.1/CMake/PickyWarnings.cmake
@@ -24,28 +24,45 @@
 include(CheckCCompilerFlag)
 
 set(_picky "")
+set(_picky_nocheck "")  # not to pass to feature checks
 
-if(CURL_WERROR AND
-   ((CMAKE_COMPILER_IS_GNUCC AND
-     NOT DOS AND  # Watt-32 headers use the '#include_next' GCC extension
-     NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 5.0 AND
-     NOT CMAKE_VERSION VERSION_LESS 3.23.0) OR  # to avoid check_symbol_exists() conflicting with GCC -pedantic-errors
-   CMAKE_C_COMPILER_ID MATCHES "Clang"))
-  list(APPEND _picky "-pedantic-errors")
+if(CURL_WERROR)
+  if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.24)
+    set(CMAKE_COMPILE_WARNING_AS_ERROR ON)
+  else()
+    if(MSVC)
+      list(APPEND _picky_nocheck "-WX")
+    else()  # llvm/clang and gcc style options
+      list(APPEND _picky_nocheck "-Werror")
+    endif()
+  endif()
+
+  if((CMAKE_C_COMPILER_ID STREQUAL "GNU" AND
+      NOT DOS AND  # Watt-32 headers use the '#include_next' GCC extension
+      CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 5.0) OR
+     CMAKE_C_COMPILER_ID MATCHES "Clang")
+    list(APPEND _picky_nocheck "-pedantic-errors")
+  endif()
 endif()
 
 if(APPLE AND
-   (CMAKE_C_COMPILER_ID STREQUAL "Clang"      AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 3.6) OR
-   (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 6.3))
+   (CMAKE_C_COMPILER_ID STREQUAL "Clang"      AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 3.6) OR
+   (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 6.3))
   list(APPEND _picky "-Werror=partial-availability")  # clang 3.6  appleclang 6.3
 endif()
 
-if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang")
+if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang")
   list(APPEND _picky "-Werror-implicit-function-declaration")  # clang 1.0  gcc 2.95
 endif()
 
+if(MSVC)
+  list(APPEND _picky "-W4")  # Use the highest warning level for Visual Studio.
+elseif(BORLAND)
+  list(APPEND _picky "-w-")  # Disable warnings on Borland to avoid changing 3rd party code.
+endif()
+
 if(PICKY_COMPILER)
-  if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang")
+  if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang")
 
     # https://clang.llvm.org/docs/DiagnosticsReference.html
     # https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
@@ -93,6 +110,7 @@ if(PICKY_COMPILER)
       -Waddress                            # clang  2.7  gcc  4.3
       -Wattributes                         # clang  2.7  gcc  4.1
       -Wcast-align                         # clang  1.0  gcc  4.2
+      -Wcast-qual                          # clang  3.0  gcc  3.4.6
       -Wdeclaration-after-statement        # clang  1.0  gcc  3.4
       -Wdiv-by-zero                        # clang  2.7  gcc  4.1
       -Wempty-body                         # clang  2.7  gcc  4.3
@@ -113,6 +131,7 @@ if(PICKY_COMPILER)
       -Wtype-limits                        # clang  2.7  gcc  4.3
       -Wunreachable-code                   # clang  2.7  gcc  4.1
     # -Wunused-macros                      # clang  2.7  gcc  4.1               # Not practical
+    #   -Wno-error=unused-macros           # clang  2.7  gcc  4.1
       -Wunused-parameter                   # clang  2.7  gcc  4.1
       -Wvla                                # clang  2.8  gcc  4.3
     )
@@ -130,8 +149,8 @@ if(PICKY_COMPILER)
         )
       endif()
       # Enable based on compiler version
-      if((CMAKE_C_COMPILER_ID STREQUAL "Clang"      AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 3.6) OR
-         (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 6.3))
+      if((CMAKE_C_COMPILER_ID STREQUAL "Clang"      AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 3.6) OR
+         (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 6.3))
         list(APPEND _picky_enable
           -Wdouble-promotion               # clang  3.6  gcc  4.6  appleclang  6.3
           -Wenum-conversion                # clang  3.2  gcc 10.0  appleclang  4.6  g++ 11.0
@@ -142,29 +161,30 @@ if(PICKY_COMPILER)
           -Wunused-const-variable          # clang  3.4  gcc  6.0  appleclang  5.1
         )
       endif()
-      if((CMAKE_C_COMPILER_ID STREQUAL "Clang"      AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 3.9) OR
-         (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 8.3))
+      if((CMAKE_C_COMPILER_ID STREQUAL "Clang"      AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 3.9) OR
+         (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 8.3))
         list(APPEND _picky_enable
           -Wcomma                          # clang  3.9            appleclang  8.3
           -Wmissing-variable-declarations  # clang  3.2            appleclang  4.6
         )
       endif()
-      if((CMAKE_C_COMPILER_ID STREQUAL "Clang"      AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 7.0) OR
-         (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 10.3))
+      if((CMAKE_C_COMPILER_ID STREQUAL "Clang"      AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 7.0) OR
+         (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 10.3))
         list(APPEND _picky_enable
           -Wassign-enum                    # clang  7.0            appleclang 10.3
           -Wextra-semi-stmt                # clang  7.0            appleclang 10.3
         )
       endif()
-      if((CMAKE_C_COMPILER_ID STREQUAL "Clang"      AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 10.0) OR
-         (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 12.4))
+      if((CMAKE_C_COMPILER_ID STREQUAL "Clang"      AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 10.0) OR
+         (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 12.4))
         list(APPEND _picky_enable
           -Wimplicit-fallthrough           # clang  4.0  gcc  7.0  appleclang 12.4  # We do silencing for clang 10.0 and above only
+          -Wxor-used-as-pow                # clang 10.0  gcc 13.0
         )
       endif()
     else()  # gcc
       # Enable based on compiler version
-      if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.3)
+      if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 4.3)
         list(APPEND _picky_enable
           ${_picky_common_old}
           -Wclobbered                      #             gcc  4.3
@@ -172,26 +192,32 @@ if(PICKY_COMPILER)
           -Wold-style-declaration          #             gcc  4.3
           -Wpragmas                        # clang  3.5  gcc  4.1  appleclang  6.0
           -Wstrict-aliasing=3              #             gcc  4.0
+          -ftree-vrp                       #             gcc  4.3 (required for -Warray-bounds, included in -Wall)
         )
       endif()
-      if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.5 AND MINGW)
+      if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 4.5)
         list(APPEND _picky_enable
-          -Wno-pedantic-ms-format          #             gcc  4.5 (MinGW-only)
+          -Wjump-misses-init               #             gcc  4.5
         )
+        if(MINGW)
+          list(APPEND _picky_enable
+            -Wno-pedantic-ms-format        #             gcc  4.5 (MinGW-only)
+          )
+        endif()
       endif()
-      if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.8)
+      if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 4.8)
         list(APPEND _picky_enable
           -Wdouble-promotion               # clang  3.6  gcc  4.6  appleclang  6.3
           -Wformat=2                       # clang  3.0  gcc  4.8
           -Wtrampolines                    #             gcc  4.6
         )
       endif()
-      if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 5.0)
+      if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 5.0)
         list(APPEND _picky_enable
-          -Warray-bounds=2 -ftree-vrp      # clang  3.0  gcc  5.0 (clang default: -Warray-bounds)
+          -Warray-bounds=2                 # clang  3.0  gcc  5.0 (clang default: -Warray-bounds)
         )
       endif()
-      if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 6.0)
+      if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 6.0)
         list(APPEND _picky_enable
           -Wduplicated-cond                #             gcc  6.0
           -Wnull-dereference               # clang  3.0  gcc  6.0 (clang default)
@@ -201,7 +227,7 @@ if(PICKY_COMPILER)
           -Wunused-const-variable          # clang  3.4  gcc  6.0  appleclang  5.1
         )
       endif()
-      if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 7.0)
+      if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 7.0)
         list(APPEND _picky_enable
           -Walloc-zero                     #             gcc  7.0
           -Wduplicated-branches            #             gcc  7.0
@@ -210,19 +236,42 @@ if(PICKY_COMPILER)
           -Wrestrict                       #             gcc  7.0
         )
       endif()
-      if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 10.0)
+      if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 10.0)
         list(APPEND _picky_enable
           -Warith-conversion               #             gcc 10.0
           -Wenum-conversion                # clang  3.2  gcc 10.0  appleclang  4.6  g++ 11.0
         )
       endif()
+      if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 13.0)
+        list(APPEND _picky_enable
+          -Warray-compare                  # clang 20.0  gcc 12.0
+          -Wenum-int-mismatch              #             gcc 13.0
+          -Wxor-used-as-pow                # clang 10.0  gcc 13.0
+        )
+      endif()
+      if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 15.0)
+        list(APPEND _picky_enable
+          -Wleading-whitespace=spaces      #             gcc 15.0
+          -Wtrailing-whitespace=any        #             gcc 15.0
+          -Wunterminated-string-initialization  #        gcc 15.0
+        )
+      endif()
     endif()
 
     #
 
+    set(_picky_skipped "")
     foreach(_ccopt IN LISTS _picky_enable)
-      list(APPEND _picky "${_ccopt}")
+      string(REGEX MATCH "-W([a-z0-9-]+)" _ccmatch "${_ccopt}")
+      if(_ccmatch AND CMAKE_C_FLAGS MATCHES "-Wno-${CMAKE_MATCH_1}" AND NOT _ccopt STREQUAL "-Wall" AND NOT _ccopt MATCHES "^-Wno-")
+        string(APPEND _picky_skipped " ${_ccopt}")
+      else()
+        list(APPEND _picky "${_ccopt}")
+      endif()
     endforeach()
+    if(_picky_skipped)
+      message(STATUS "Picky compiler options skipped due to CMAKE_C_FLAGS override:${_picky_skipped}")
+    endif()
 
     foreach(_ccopt IN LISTS _picky_detect)
       # Use a unique variable name 1. for meaningful log output 2. to have a fresh, undefined variable for each detection
@@ -236,22 +285,43 @@ if(PICKY_COMPILER)
       endif()
     endforeach()
 
-    if(CMAKE_COMPILER_IS_GNUCC)
+    if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
       if(CMAKE_C_COMPILER_VERSION VERSION_LESS 4.5)
         # Avoid false positives
         list(APPEND _picky "-Wno-shadow")
         list(APPEND _picky "-Wno-unreachable-code")
       endif()
-      if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.2 AND CMAKE_C_COMPILER_VERSION VERSION_LESS 4.6)
+      if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 4.2 AND CMAKE_C_COMPILER_VERSION VERSION_LESS 4.6)
         # GCC <4.6 do not support #pragma to suppress warnings locally. Disable them globally instead.
         list(APPEND _picky "-Wno-overlength-strings")
       endif()
-      if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.0 AND CMAKE_C_COMPILER_VERSION VERSION_LESS 4.7)
+      if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 4.0 AND CMAKE_C_COMPILER_VERSION VERSION_LESS 4.7)
         list(APPEND _picky "-Wno-missing-field-initializers")  # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=36750
       endif()
-      if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.3 AND CMAKE_C_COMPILER_VERSION VERSION_LESS 4.8)
+      if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 4.3 AND CMAKE_C_COMPILER_VERSION VERSION_LESS 4.8)
         list(APPEND _picky "-Wno-type-limits")  # Avoid false positives
       endif()
+      if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 5.1 AND CMAKE_C_COMPILER_VERSION VERSION_LESS 5.5)
+        list(APPEND _picky "-Wno-conversion")  # Avoid false positives
+      endif()
+    endif()
+  elseif(MSVC AND MSVC_VERSION LESS_EQUAL 1943)  # Skip for untested/unreleased newer versions
+    list(APPEND _picky "-Wall")
+    list(APPEND _picky "-wd4061")  # enumerator 'A' in switch of enum 'B' is not explicitly handled by a case label
+    list(APPEND _picky "-wd4191")  # 'type cast': unsafe conversion from 'FARPROC' to 'void (__cdecl *)(void)'
+    list(APPEND _picky "-wd4255")  # no function prototype given: converting '()' to '(void)' (in winuser.h)
+    list(APPEND _picky "-wd4464")  # relative include path contains '..'
+    list(APPEND _picky "-wd4548")  # expression before comma has no effect; expected expression with side-effect (in FD_SET())
+    list(APPEND _picky "-wd4574")  # 'M' is defined to be '0': did you mean to use '#if M'? (in ws2tcpip.h)
+    list(APPEND _picky "-wd4668")  # 'M' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif' (in winbase.h)
+    list(APPEND _picky "-wd4710")  # 'snprintf': function not inlined
+    list(APPEND _picky "-wd4711")  # function 'A' selected for automatic inline expansion
+    list(APPEND _picky "-wd4746")  # volatile access of '<expression>' is subject to /volatile:<iso|ms> setting;
+                                   #   consider using __iso_volatile_load/store intrinsic functions (ARM64)
+    list(APPEND _picky "-wd4774")  # 'snprintf': format string expected in argument 3 is not a string literal
+    list(APPEND _picky "-wd4820")  # 'A': 'N' bytes padding added after data member 'B'
+    if(MSVC_VERSION GREATER_EQUAL 1900)
+      list(APPEND _picky "-wd5045")  # Compiler will insert Spectre mitigation for memory load if /Qspectre switch specified
     endif()
   endif()
 endif()
@@ -260,20 +330,31 @@ endif()
 if(CMAKE_C_COMPILER_ID STREQUAL "Clang" AND MSVC)
   list(APPEND _picky "-Wno-language-extension-token")  # Allow __int64
 
-  set(_picky_tmp "")
-  foreach(_ccopt IN LISTS _picky)
-    # Prefix -Wall, otherwise clang-cl interprets it as an MSVC option and translates it to -Weverything
-    if(_ccopt MATCHES "^-W" AND NOT _ccopt STREQUAL "-Wall")
-      list(APPEND _picky_tmp ${_ccopt})
-    else()
-      list(APPEND _picky_tmp "-clang:${_ccopt}")
-    endif()
+  foreach(_wlist IN ITEMS _picky_nocheck _picky)
+    set(_picky_tmp "")
+    foreach(_ccopt IN LISTS "${_wlist}")
+      # Prefix -Wall, otherwise clang-cl interprets it as an MSVC option and translates it to -Weverything
+      if(_ccopt MATCHES "^-W" AND NOT _ccopt STREQUAL "-Wall")
+        list(APPEND _picky_tmp ${_ccopt})
+      else()
+        list(APPEND _picky_tmp "-clang:${_ccopt}")
+      endif()
+    endforeach()
+    set("${_wlist}" ${_picky_tmp})
   endforeach()
-  set(_picky ${_picky_tmp})
 endif()
 
-if(_picky)
-  string(REPLACE ";" " " _picky "${_picky}")
-  string(APPEND CMAKE_C_FLAGS " ${_picky}")
-  message(STATUS "Picky compiler options: ${_picky}")
+if(_picky_nocheck OR _picky)
+  set(_picky_tmp "${_picky_nocheck}" "${_picky}")
+  string(REPLACE ";" " " _picky_tmp "${_picky_tmp}")
+  string(STRIP "${_picky_tmp}" _picky_tmp)
+  message(STATUS "Picky compiler options: ${_picky_tmp}")
+  set_property(DIRECTORY APPEND PROPERTY COMPILE_OPTIONS "${_picky_nocheck}" "${_picky}")
+
+  # Apply to all feature checks
+  string(REPLACE ";" " " _picky_tmp "${_picky}")
+  string(APPEND CMAKE_REQUIRED_FLAGS " ${_picky_tmp}")
+
+  unset(_picky)
+  unset(_picky_tmp)
 endif()
diff --git a/extra/curl/curl-8.12.1/CMake/Utilities.cmake b/extra/curl/curl-8.14.1/CMake/Utilities.cmake
similarity index 100%
rename from extra/curl/curl-8.12.1/CMake/Utilities.cmake
rename to extra/curl/curl-8.14.1/CMake/Utilities.cmake
diff --git a/extra/curl/curl-8.12.1/CMake/cmake_uninstall.cmake.in b/extra/curl/curl-8.14.1/CMake/cmake_uninstall.cmake.in
similarity index 92%
rename from extra/curl/curl-8.12.1/CMake/cmake_uninstall.cmake.in
rename to extra/curl/curl-8.14.1/CMake/cmake_uninstall.cmake.in
index d1f746fc096e..bb95d85a0a72 100644
--- a/extra/curl/curl-8.12.1/CMake/cmake_uninstall.cmake.in
+++ b/extra/curl/curl-8.14.1/CMake/cmake_uninstall.cmake.in
@@ -35,10 +35,11 @@ string(REGEX REPLACE "\n" ";" _files "${_files}")
 foreach(_file ${_files})
   message(STATUS "Uninstalling $ENV{DESTDIR}${_file}")
   if(IS_SYMLINK "$ENV{DESTDIR}${_file}" OR EXISTS "$ENV{DESTDIR}${_file}")
-    exec_program(
-      "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${_file}\""
-      OUTPUT_VARIABLE rm_out
-      RETURN_VALUE rm_retval
+    execute_process(
+      COMMAND "@CMAKE_COMMAND@" -E remove "$ENV{DESTDIR}${_file}"
+      RESULT_VARIABLE rm_retval
+      OUTPUT_QUIET
+      ERROR_QUIET
     )
     if(NOT "${rm_retval}" STREQUAL 0)
       message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${_file}")
diff --git a/extra/curl/curl-8.12.1/CMake/curl-config.cmake.in b/extra/curl/curl-8.14.1/CMake/curl-config.cmake.in
similarity index 81%
rename from extra/curl/curl-8.12.1/CMake/curl-config.cmake.in
rename to extra/curl/curl-8.14.1/CMake/curl-config.cmake.in
index a4df052c2aa6..d1582b8d41b9 100644
--- a/extra/curl/curl-8.12.1/CMake/curl-config.cmake.in
+++ b/extra/curl/curl-8.14.1/CMake/curl-config.cmake.in
@@ -25,7 +25,11 @@
 
 include(CMakeFindDependencyMacro)
 if("@USE_OPENSSL@")
-  find_dependency(OpenSSL "@OPENSSL_VERSION_MAJOR@")
+  if("@OPENSSL_VERSION_MAJOR@")
+    find_dependency(OpenSSL "@OPENSSL_VERSION_MAJOR@")
+  else()
+    find_dependency(OpenSSL)
+  endif()
 endif()
 if("@HAVE_LIBZ@")
   find_dependency(ZLIB "@ZLIB_VERSION_MAJOR@")
@@ -34,13 +38,16 @@ endif()
 include("${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake")
 
 # Alias for either shared or static library
-if(NOT TARGET @PROJECT_NAME@::libcurl)
-  add_library(@PROJECT_NAME@::libcurl ALIAS @PROJECT_NAME@::@LIB_SELECTED@)
+if(NOT TARGET @PROJECT_NAME@::@LIB_NAME@)
+  if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.11 AND CMAKE_VERSION VERSION_LESS 3.18)
+    set_target_properties(@PROJECT_NAME@::@LIB_SELECTED@ PROPERTIES IMPORTED_GLOBAL TRUE)
+  endif()
+  add_library(@PROJECT_NAME@::@LIB_NAME@ ALIAS @PROJECT_NAME@::@LIB_SELECTED@)
 endif()
 
 # For compatibility with CMake's FindCURL.cmake
 set(CURL_VERSION_STRING "@CURLVERSION@")
-set(CURL_LIBRARIES @PROJECT_NAME@::libcurl)
+set(CURL_LIBRARIES @PROJECT_NAME@::@LIB_NAME@)
 set_and_check(CURL_INCLUDE_DIRS "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@")
 
 set(CURL_SUPPORTED_PROTOCOLS "@CURL_SUPPORTED_PROTOCOLS_LIST@")
diff --git a/extra/curl/curl-8.14.1/CMake/unix-cache.cmake b/extra/curl/curl-8.14.1/CMake/unix-cache.cmake
new file mode 100644
index 000000000000..58da04e22299
--- /dev/null
+++ b/extra/curl/curl-8.14.1/CMake/unix-cache.cmake
@@ -0,0 +1,316 @@
+#***************************************************************************
+#                                  _   _ ____  _
+#  Project                     ___| | | |  _ \| |
+#                             / __| | | | |_) | |
+#                            | (__| |_| |  _ <| |___
+#                             \___|\___/|_| \_\_____|
+#
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at https://curl.se/docs/copyright.html.
+#
+# You may opt to use, copy, modify, merge, publish, distribute and/or sell
+# copies of the Software, and permit persons to whom the Software is
+# furnished to do so, under the terms of the COPYING file.
+#
+# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+# KIND, either express or implied.
+#
+# SPDX-License-Identifier: curl
+#
+###########################################################################
+# Based on CI runs for Cygwin/MSYS2, Linux, macOS, FreeBSD, NetBSD, OpenBSD
+if(NOT UNIX)
+  message(FATAL_ERROR "This file should be included on Unix platforms only")
+endif()
+
+if(APPLE OR
+   CYGWIN)
+  set(HAVE_ACCEPT4 0)
+elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR
+       CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR
+       CMAKE_SYSTEM_NAME STREQUAL "NetBSD" OR
+       CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
+  set(HAVE_ACCEPT4 1)
+endif()
+set(HAVE_ALARM 1)
+if(ANDROID)
+  set(HAVE_ARC4RANDOM 1)
+else()
+  set(HAVE_ARC4RANDOM 0)
+endif()
+set(HAVE_ARPA_INET_H 1)
+set(HAVE_ATOMIC 1)
+set(HAVE_BASENAME 1)
+set(HAVE_BOOL_T 1)
+if(NOT APPLE)
+  set(HAVE_CLOCK_GETTIME_MONOTONIC 1)
+  if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+    set(HAVE_CLOCK_GETTIME_MONOTONIC_RAW 1)
+  else()
+    set(HAVE_CLOCK_GETTIME_MONOTONIC_RAW 0)
+  endif()
+endif()
+set(HAVE_CLOSESOCKET 0)
+set(HAVE_DECL_FSEEKO 1)
+set(HAVE_DIRENT_H 1)
+if(APPLE OR
+   CYGWIN OR
+   CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
+  set(HAVE_EVENTFD 0)
+elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR
+       CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR
+       CMAKE_SYSTEM_NAME STREQUAL "NetBSD")
+  set(HAVE_EVENTFD 1)
+endif()
+set(HAVE_FCNTL 1)
+set(HAVE_FCNTL_H 1)
+set(HAVE_FCNTL_O_NONBLOCK 1)
+set(HAVE_FILE_OFFSET_BITS 1)
+set(HAVE_FNMATCH 1)
+set(HAVE_FREEADDRINFO 1)
+set(HAVE_FSEEKO 1)
+if(APPLE)
+  set(HAVE_FSETXATTR 1)
+  set(HAVE_FSETXATTR_5 0)
+  set(HAVE_FSETXATTR_6 1)
+elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR
+       CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
+  set(HAVE_FSETXATTR 0)
+  set(HAVE_FSETXATTR_5 0)
+  set(HAVE_FSETXATTR_6 0)
+elseif(CYGWIN OR
+       CMAKE_SYSTEM_NAME STREQUAL "Linux" OR
+       CMAKE_SYSTEM_NAME STREQUAL "NetBSD")
+  set(HAVE_FSETXATTR 1)
+  set(HAVE_FSETXATTR_5 1)
+  set(HAVE_FSETXATTR_6 0)
+endif()
+set(HAVE_FTRUNCATE 1)
+set(HAVE_GETADDRINFO 1)
+if(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
+  set(HAVE_GETADDRINFO_THREADSAFE 0)
+elseif(CYGWIN OR
+       CMAKE_SYSTEM_NAME STREQUAL "Linux" OR
+       CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR
+       CMAKE_SYSTEM_NAME STREQUAL "NetBSD")
+  set(HAVE_GETADDRINFO_THREADSAFE 1)
+endif()
+set(HAVE_GETEUID 1)
+if(APPLE OR
+   CYGWIN OR
+   CMAKE_SYSTEM_NAME STREQUAL "NetBSD" OR
+   CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
+  set(HAVE_GETHOSTBYNAME_R 0)
+elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR
+       CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
+  set(HAVE_GETHOSTBYNAME_R 1)
+endif()
+set(HAVE_GETHOSTBYNAME_R_3 0)
+set(HAVE_GETHOSTBYNAME_R_3_REENTRANT 0)
+set(HAVE_GETHOSTBYNAME_R_5 0)
+set(HAVE_GETHOSTBYNAME_R_5_REENTRANT 0)
+if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+  set(HAVE_GETHOSTBYNAME_R_6 1)
+  set(HAVE_GETHOSTBYNAME_R_6_REENTRANT 1)
+else()
+  set(HAVE_GETHOSTBYNAME_R_6 0)
+  set(HAVE_GETHOSTBYNAME_R_6_REENTRANT 0)
+endif()
+set(HAVE_GETHOSTNAME 1)
+if(NOT ANDROID OR ANDROID_PLATFORM_LEVEL GREATER_EQUAL 24)
+  set(HAVE_GETIFADDRS 1)
+else()
+  set(HAVE_GETIFADDRS 0)
+endif()
+if(APPLE OR
+   CYGWIN OR
+   CMAKE_SYSTEM_NAME STREQUAL "Linux" OR
+   CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR
+   CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
+  set(HAVE_GETPASS_R 0)
+elseif(CMAKE_SYSTEM_NAME STREQUAL "NetBSD")
+  set(HAVE_GETPASS_R 1)
+endif()
+set(HAVE_GETPEERNAME 1)
+set(HAVE_GETPPID 1)
+set(HAVE_GETPWUID 1)
+set(HAVE_GETPWUID_R 1)
+set(HAVE_GETRLIMIT 1)
+set(HAVE_GETSOCKNAME 1)
+set(HAVE_GETTIMEOFDAY 1)
+if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+  set(HAVE_GLIBC_STRERROR_R 1)
+else()
+  set(HAVE_GLIBC_STRERROR_R 0)
+endif()
+set(HAVE_GMTIME_R 1)
+set(HAVE_IFADDRS_H 1)
+set(HAVE_IF_NAMETOINDEX 1)
+set(HAVE_INET_NTOP 1)
+set(HAVE_INET_PTON 1)
+set(HAVE_IOCTLSOCKET 0)
+set(HAVE_IOCTLSOCKET_CAMEL 0)
+set(HAVE_IOCTLSOCKET_CAMEL_FIONBIO 0)
+set(HAVE_IOCTLSOCKET_FIONBIO 0)
+set(HAVE_IOCTL_FIONBIO 1)
+set(HAVE_IOCTL_SIOCGIFADDR 1)
+if(CYGWIN)
+  set(HAVE_IO_H 1)
+else()
+  set(HAVE_IO_H 0)
+endif()
+set(HAVE_LIBGEN_H 1)
+if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+  set(HAVE_LINUX_TCP_H 1)
+else()
+  set(HAVE_LINUX_TCP_H 0)
+endif()
+set(HAVE_LOCALE_H 1)
+set(HAVE_LONGLONG 1)
+if(APPLE)
+  set(HAVE_MACH_ABSOLUTE_TIME 1)
+endif()
+if(APPLE OR
+   CYGWIN)
+  set(HAVE_MEMRCHR 0)
+else()
+  set(HAVE_MEMRCHR 1)
+endif()
+set(HAVE_MSG_NOSIGNAL 1)
+set(HAVE_NETDB_H 1)
+if(ANDROID)
+  set(HAVE_NETINET_IN6_H 1)
+else()
+  set(HAVE_NETINET_IN6_H 0)
+endif()
+set(HAVE_NETINET_IN_H 1)
+set(HAVE_NETINET_TCP_H 1)
+set(HAVE_NETINET_UDP_H 1)
+set(HAVE_NET_IF_H 1)
+set(HAVE_OPENDIR 1)
+set(HAVE_PIPE 1)
+if(APPLE OR
+   CYGWIN)
+  set(HAVE_PIPE2 0)
+elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR
+       CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR
+       CMAKE_SYSTEM_NAME STREQUAL "NetBSD" OR
+       CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
+  set(HAVE_PIPE2 1)
+endif()
+set(HAVE_POLL 1)
+set(HAVE_POLL_H 1)
+if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
+  set(HAVE_POSIX_STRERROR_R 0)
+else()
+  set(HAVE_POSIX_STRERROR_R 1)
+endif()
+set(HAVE_PWD_H 1)
+set(HAVE_REALPATH 1)
+set(HAVE_RECV 1)
+set(HAVE_SA_FAMILY_T 1)
+set(HAVE_SCHED_YIELD 1)
+set(HAVE_SELECT 1)
+set(HAVE_SEND 1)
+if(APPLE OR
+   CYGWIN)
+  set(HAVE_SENDMMSG 0)
+else()
+  set(HAVE_SENDMMSG 1)
+endif()
+set(HAVE_SENDMSG 1)
+set(HAVE_SETLOCALE 1)
+if(CYGWIN OR
+   CMAKE_SYSTEM_NAME STREQUAL "Linux")
+  set(HAVE_SETMODE 0)
+else()
+  set(HAVE_SETMODE 1)
+endif()
+set(HAVE_SETRLIMIT 1)
+set(HAVE_SETSOCKOPT_SO_NONBLOCK 0)
+set(HAVE_SIGACTION 1)
+set(HAVE_SIGINTERRUPT 1)
+set(HAVE_SIGNAL 1)
+set(HAVE_SIGSETJMP 1)
+set(HAVE_SNPRINTF 1)
+set(HAVE_SOCKADDR_IN6_SIN6_ADDR 1)
+set(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1)
+set(HAVE_SOCKET 1)
+set(HAVE_SOCKETPAIR 1)
+set(HAVE_STDATOMIC_H 1)
+set(HAVE_STDBOOL_H 1)
+set(HAVE_STDDEF_H 1)
+set(HAVE_STDINT_H 1)
+set(HAVE_STRCASECMP 1)
+set(HAVE_STRCMPI 0)
+set(HAVE_STRDUP 1)
+set(HAVE_STRERROR_R 1)
+set(HAVE_STRICMP 0)
+set(HAVE_STRINGS_H 1)
+if(_CURL_OLD_LINUX)
+  set(HAVE_STROPTS_H 1)
+else()
+  set(HAVE_STROPTS_H 0)  # glibc 2.30 or newer. https://sourceware.org/legacy-ml/libc-alpha/2019-08/msg00029.html
+endif()
+set(HAVE_STRUCT_SOCKADDR_STORAGE 1)
+set(HAVE_STRUCT_TIMEVAL 1)
+if(ANDROID OR CMAKE_SYSTEM_NAME STREQUAL "iOS")
+  set(HAVE_SUSECONDS_T 1)
+endif()
+if(APPLE OR
+   CYGWIN OR
+   CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
+  set(HAVE_SYS_EVENTFD_H 0)
+elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR
+       CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR
+       CMAKE_SYSTEM_NAME STREQUAL "NetBSD")
+  set(HAVE_SYS_EVENTFD_H 1)
+endif()
+if(CYGWIN OR
+   CMAKE_SYSTEM_NAME STREQUAL "Linux")
+  set(HAVE_SYS_FILIO_H 0)
+else()
+  set(HAVE_SYS_FILIO_H 1)
+endif()
+set(HAVE_SYS_IOCTL_H 1)
+set(HAVE_SYS_PARAM_H 1)
+set(HAVE_SYS_POLL_H 1)
+set(HAVE_SYS_RESOURCE_H 1)
+set(HAVE_SYS_SELECT_H 1)
+set(HAVE_SYS_SOCKET_H 1)
+if(CYGWIN OR
+   CMAKE_SYSTEM_NAME STREQUAL "Linux")
+  set(HAVE_SYS_SOCKIO_H 0)
+else()
+  set(HAVE_SYS_SOCKIO_H 1)
+endif()
+set(HAVE_SYS_STAT_H 1)
+set(HAVE_SYS_TIME_H 1)
+set(HAVE_SYS_TYPES_H 1)
+set(HAVE_SYS_UN_H 1)
+if(CYGWIN)
+  set(HAVE_SYS_UTIME_H 1)
+else()
+  set(HAVE_SYS_UTIME_H 0)
+endif()
+set(HAVE_TERMIOS_H 1)
+if(CYGWIN OR
+   CMAKE_SYSTEM_NAME STREQUAL "Linux")
+  set(HAVE_TERMIO_H 1)
+else()
+  set(HAVE_TERMIO_H 0)
+endif()
+set(HAVE_TIME_T_UNSIGNED 0)
+set(HAVE_UNISTD_H 1)
+set(HAVE_UTIME 1)
+set(HAVE_UTIMES 1)
+set(HAVE_UTIME_H 1)
+set(HAVE_WRITABLE_ARGV 1)
+if(CYGWIN)
+  set(HAVE__SETMODE 1)
+endif()
+set(STDC_HEADERS 1)
+set(USE_UNIX_SOCKETS 1)
diff --git a/extra/curl/curl-8.12.1/CMake/win32-cache.cmake b/extra/curl/curl-8.14.1/CMake/win32-cache.cmake
similarity index 69%
rename from extra/curl/curl-8.12.1/CMake/win32-cache.cmake
rename to extra/curl/curl-8.14.1/CMake/win32-cache.cmake
index 272f5134d1da..163a310fdad7 100644
--- a/extra/curl/curl-8.12.1/CMake/win32-cache.cmake
+++ b/extra/curl/curl-8.14.1/CMake/win32-cache.cmake
@@ -25,55 +25,36 @@ if(NOT WIN32)
   message(FATAL_ERROR "This file should be included on Windows platform only")
 endif()
 
-set(HAVE_LOCALE_H 1)
-
 if(MINGW)
-  set(HAVE_SNPRINTF 1)
-  set(HAVE_UNISTD_H 1)
+  set(HAVE_BASENAME 1)
+  set(HAVE_BOOL_T 1)  # = HAVE_STDBOOL_H
+  set(HAVE_DIRENT_H 1)
+  set(HAVE_FTRUNCATE 1)
+  set(HAVE_GETTIMEOFDAY 1)
   set(HAVE_LIBGEN_H 1)
+  set(HAVE_OPENDIR 1)
+  set(HAVE_SNPRINTF 1)
+  set(HAVE_STDBOOL_H 1)
   set(HAVE_STDDEF_H 1)  # detected by CMake internally in check_type_size()
   set(HAVE_STDINT_H 1)  # detected by CMake internally in check_type_size()
-  set(HAVE_STDBOOL_H 1)
-  set(HAVE_BOOL_T "${HAVE_STDBOOL_H}")
-  set(HAVE_STRTOLL 1)
-  set(HAVE_BASENAME 1)
-  set(HAVE_FTRUNCATE 1)
+  set(HAVE_STRINGS_H 1)  # wrapper to string.h
   set(HAVE_SYS_PARAM_H 1)
   set(HAVE_SYS_TIME_H 1)
-  set(HAVE_GETTIMEOFDAY 1)
-  set(HAVE_STRINGS_H 1)  # wrapper to string.h
+  set(HAVE_UNISTD_H 1)
   set(HAVE_UTIME_H 1)  # wrapper to sys/utime.h
-  set(HAVE_DIRENT_H 1)
-  set(HAVE_OPENDIR 1)
-  if(MINGW64_VERSION)
-    if(NOT MINGW64_VERSION VERSION_LESS 4.0)
-      set(HAVE_STRTOK_R 1)
-    else()
-      set(HAVE_STRTOK_R 0)
-    endif()
-  endif()
-  if((CMAKE_COMPILER_IS_GNUCC              AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.9) OR
-     (CMAKE_C_COMPILER_ID STREQUAL "Clang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 3.6))
-    set(HAVE_STDATOMIC_H 1)
-    set(HAVE_ATOMIC 1)
-  else()
-    set(HAVE_STDATOMIC_H 0)
-    set(HAVE_ATOMIC 0)
-  endif()
 else()
-  set(HAVE_LIBGEN_H 0)
+  set(HAVE_DIRENT_H 0)
   set(HAVE_FTRUNCATE 0)
-  set(HAVE_SYS_PARAM_H 0)
-  set(HAVE_SYS_TIME_H 0)
   set(HAVE_GETTIMEOFDAY 0)
+  set(HAVE_LIBGEN_H 0)
+  set(HAVE_OPENDIR 0)
   set(HAVE_STRINGS_H 0)
+  set(HAVE_SYS_PARAM_H 0)
+  set(HAVE_SYS_TIME_H 0)
   set(HAVE_UTIME_H 0)
-  set(HAVE_DIRENT_H 0)
-  set(HAVE_OPENDIR 0)
   if(MSVC)
     set(HAVE_UNISTD_H 0)
     set(HAVE_STDDEF_H 1)  # detected by CMake internally in check_type_size()
-    set(HAVE_STDATOMIC_H 0)
     if(MSVC_VERSION GREATER_EQUAL 1600)
       set(HAVE_STDINT_H 1)  # detected by CMake internally in check_type_size()
     else()
@@ -81,10 +62,8 @@ else()
     endif()
     if(MSVC_VERSION GREATER_EQUAL 1800)
       set(HAVE_STDBOOL_H 1)
-      set(HAVE_STRTOLL 1)
     else()
       set(HAVE_STDBOOL_H 0)
-      set(HAVE_STRTOLL 0)
     endif()
     set(HAVE_BOOL_T "${HAVE_STDBOOL_H}")
     if(MSVC_VERSION GREATER_EQUAL 1900)
@@ -93,58 +72,99 @@ else()
       set(HAVE_SNPRINTF 0)
     endif()
     set(HAVE_BASENAME 0)
-    set(HAVE_STRTOK_R 0)
-    set(HAVE_FILE_OFFSET_BITS 0)
-    set(HAVE_ATOMIC 0)
   endif()
 endif()
 
-# Available in Windows XP and newer
-set(HAVE_GETADDRINFO 1)
-set(HAVE_FREEADDRINFO 1)
+if((CMAKE_C_COMPILER_ID STREQUAL "GNU"   AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 4.9) OR
+   (CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 3.6))
+  # MinGW or clang-cl
+  set(HAVE_STDATOMIC_H 1)
+  set(HAVE_ATOMIC 1)
+else()
+  set(HAVE_STDATOMIC_H 0)
+  set(HAVE_ATOMIC 0)
+endif()
 
-set(HAVE_SOCKETPAIR 0)
-set(HAVE_SENDMSG 0)
-set(HAVE_SENDMMSG 0)
+set(HAVE_ACCEPT4 0)
 set(HAVE_ALARM 0)
+set(HAVE_ARC4RANDOM 0)
+set(HAVE_ARPA_INET_H 0)
+set(HAVE_CLOSESOCKET 1)
+set(HAVE_EVENTFD 0)
 set(HAVE_FCNTL 0)
+set(HAVE_FCNTL_H 1)
+set(HAVE_FCNTL_O_NONBLOCK 0)
+set(HAVE_FNMATCH 0)
+set(HAVE_FREEADDRINFO 1)  # Available in Windows XP and newer
+set(HAVE_FSETXATTR 0)
+set(HAVE_GETADDRINFO 1)  # Available in Windows XP and newer
+set(HAVE_GETEUID 0)
+set(HAVE_GETHOSTBYNAME_R 0)
+set(HAVE_GETHOSTBYNAME_R_3 0)
+set(HAVE_GETHOSTBYNAME_R_3_REENTRANT 0)
+set(HAVE_GETHOSTBYNAME_R_5 0)
+set(HAVE_GETHOSTBYNAME_R_5_REENTRANT 0)
+set(HAVE_GETHOSTBYNAME_R_6 0)
+set(HAVE_GETHOSTBYNAME_R_6_REENTRANT 0)
+set(HAVE_GETHOSTNAME 1)
+set(HAVE_GETIFADDRS 0)
+set(HAVE_GETPASS_R 0)
+set(HAVE_GETPEERNAME 1)
 set(HAVE_GETPPID 0)
-set(HAVE_UTIMES 0)
+set(HAVE_GETPWUID 0)
 set(HAVE_GETPWUID_R 0)
-set(HAVE_STRERROR_R 0)
-set(HAVE_SIGINTERRUPT 0)
-set(HAVE_PIPE 0)
-set(HAVE_EVENTFD 0)
-set(HAVE_IF_NAMETOINDEX 0)
 set(HAVE_GETRLIMIT 0)
-set(HAVE_SETRLIMIT 0)
-set(HAVE_FSETXATTR 0)
-set(HAVE_SETLOCALE 1)
-set(HAVE_SETMODE 1)
-set(HAVE__SETMODE 1)
-set(HAVE_GETPEERNAME 1)
 set(HAVE_GETSOCKNAME 1)
-set(HAVE_GETHOSTNAME 1)
-
-set(HAVE_RECV 1)
-set(HAVE_SEND 1)
-set(HAVE_STROPTS_H 0)
-set(HAVE_ARC4RANDOM 0)
-set(HAVE_FNMATCH 0)
-set(HAVE_ARPA_INET_H 0)
-set(HAVE_FCNTL_H 1)
+set(HAVE_GLIBC_STRERROR_R 0)
+set(HAVE_GMTIME_R 0)
 set(HAVE_IFADDRS_H 0)
+set(HAVE_IF_NAMETOINDEX 0)
+set(HAVE_INET_NTOP 0)
+set(HAVE_INET_PTON 0)
+set(HAVE_IOCTLSOCKET 1)
+set(HAVE_IOCTLSOCKET_CAMEL 0)
+set(HAVE_IOCTLSOCKET_CAMEL_FIONBIO 0)
+set(HAVE_IOCTLSOCKET_FIONBIO 1)
+set(HAVE_IOCTL_FIONBIO 0)
+set(HAVE_IOCTL_SIOCGIFADDR 0)
 set(HAVE_IO_H 1)
+set(HAVE_LINUX_TCP_H 0)
+set(HAVE_LOCALE_H 1)
+set(HAVE_MEMRCHR 0)
+set(HAVE_MSG_NOSIGNAL 0)
 set(HAVE_NETDB_H 0)
-set(HAVE_NETINET_IN_H 0)
 set(HAVE_NETINET_IN6_H 0)
+set(HAVE_NETINET_IN_H 0)
 set(HAVE_NETINET_TCP_H 0)
 set(HAVE_NETINET_UDP_H 0)
 set(HAVE_NET_IF_H 0)
-set(HAVE_IOCTL_SIOCGIFADDR 0)
-set(HAVE_POLL_H 0)
+set(HAVE_PIPE 0)
+set(HAVE_PIPE2 0)
 set(HAVE_POLL 0)
+set(HAVE_POLL_H 0)
+set(HAVE_POSIX_STRERROR_R 0)
 set(HAVE_PWD_H 0)
+set(HAVE_RECV 1)
+set(HAVE_SELECT 1)
+set(HAVE_SEND 1)
+set(HAVE_SENDMMSG 0)
+set(HAVE_SENDMSG 0)
+set(HAVE_SETLOCALE 1)
+set(HAVE_SETMODE 1)
+set(HAVE_SETRLIMIT 0)
+set(HAVE_SETSOCKOPT_SO_NONBLOCK 0)
+set(HAVE_SIGACTION 0)
+set(HAVE_SIGINTERRUPT 0)
+set(HAVE_SIGNAL 1)
+set(HAVE_SIGSETJMP 0)
+set(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1)
+set(HAVE_SOCKET 1)
+set(HAVE_SOCKETPAIR 0)
+set(HAVE_STRDUP 1)
+set(HAVE_STRERROR_R 0)
+set(HAVE_STROPTS_H 0)
+set(HAVE_STRUCT_SOCKADDR_STORAGE 1)
+set(HAVE_STRUCT_TIMEVAL 1)
 set(HAVE_SYS_EVENTFD_H 0)
 set(HAVE_SYS_FILIO_H 0)
 set(HAVE_SYS_IOCTL_H 0)
@@ -159,47 +179,67 @@ set(HAVE_SYS_UN_H 0)
 set(HAVE_SYS_UTIME_H 1)
 set(HAVE_TERMIOS_H 0)
 set(HAVE_TERMIO_H 0)
-set(HAVE_LINUX_TCP_H 0)
-
-set(HAVE_SOCKET 1)
-set(HAVE_SELECT 1)
-set(HAVE_STRDUP 1)
-set(HAVE_MEMRCHR 0)
-set(HAVE_CLOSESOCKET 1)
-set(HAVE_SIGSETJMP 0)
-set(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1)
-set(HAVE_GETPASS_R 0)
-set(HAVE_GETPWUID 0)
-set(HAVE_GETEUID 0)
-set(HAVE_UTIME 1)
-set(HAVE_GMTIME_R 0)
-set(HAVE_GETHOSTBYNAME_R 0)
-set(HAVE_SIGNAL 1)
-set(HAVE_SIGACTION 0)
-set(HAVE_GLIBC_STRERROR_R 0)
-set(HAVE_GETIFADDRS 0)
-set(HAVE_FCNTL_O_NONBLOCK 0)
-set(HAVE_IOCTLSOCKET 1)
-set(HAVE_IOCTLSOCKET_CAMEL 0)
-set(HAVE_IOCTLSOCKET_CAMEL_FIONBIO 0)
-set(HAVE_IOCTLSOCKET_FIONBIO 1)
-set(HAVE_IOCTL_FIONBIO 0)
-set(HAVE_SETSOCKOPT_SO_NONBLOCK 0)
-set(HAVE_POSIX_STRERROR_R 0)
-set(HAVE_MSG_NOSIGNAL 0)
-set(HAVE_STRUCT_TIMEVAL 1)
-set(HAVE_STRUCT_SOCKADDR_STORAGE 1)
 set(HAVE_TIME_T_UNSIGNED 0)
-
-set(HAVE_GETHOSTBYNAME_R_3 0)
-set(HAVE_GETHOSTBYNAME_R_3_REENTRANT 0)
-set(HAVE_GETHOSTBYNAME_R_5 0)
-set(HAVE_GETHOSTBYNAME_R_5_REENTRANT 0)
-set(HAVE_GETHOSTBYNAME_R_6 0)
-set(HAVE_GETHOSTBYNAME_R_6_REENTRANT 0)
-
-set(HAVE_IN_ADDR_T 0)
+set(HAVE_UTIME 1)
+set(HAVE_UTIMES 0)
+set(HAVE__SETMODE 1)
 set(STDC_HEADERS 1)
 
-set(HAVE_SIZEOF_SUSECONDS_T 0)
+# Types and sizes
+
 set(HAVE_SIZEOF_SA_FAMILY_T 0)
+set(HAVE_SIZEOF_SUSECONDS_T 0)
+
+if(MINGW OR MSVC)
+  curl_prefill_type_size("INT" 4)
+  curl_prefill_type_size("LONG" 4)
+  curl_prefill_type_size("LONG_LONG" 8)
+  curl_prefill_type_size("__INT64" 8)
+  curl_prefill_type_size("CURL_OFF_T" 8)
+  # CURL_SOCKET_T, SIZE_T: 8 for _WIN64, 4 otherwise
+  # TIME_T: 8 for _WIN64 or UCRT or MSVC and not Windows CE, 4 otherwise
+  #   Also 4 for non-UCRT 32-bit when _USE_32BIT_TIME_T is set.
+  #   mingw-w64 sets _USE_32BIT_TIME_T unless __MINGW_USE_VC2005_COMPAT is explicit defined.
+  if(MSVC)
+    set(HAVE_SIZEOF_SSIZE_T 0)
+    set(HAVE_FILE_OFFSET_BITS 0)
+    curl_prefill_type_size("OFF_T" 4)
+    curl_prefill_type_size("ADDRESS_FAMILY" 2)
+  else()
+    # SSIZE_T: 8 for _WIN64, 4 otherwise
+    if(MINGW64_VERSION)
+      if(MINGW64_VERSION VERSION_GREATER_EQUAL 3.0)
+        set(HAVE_FILE_OFFSET_BITS 1)
+        curl_prefill_type_size("OFF_T" 8)
+      endif()
+      if(MINGW64_VERSION VERSION_GREATER_EQUAL 2.0)
+        curl_prefill_type_size("ADDRESS_FAMILY" 2)
+      else()
+        set(HAVE_SIZEOF_ADDRESS_FAMILY 0)
+      endif()
+    endif()
+  endif()
+endif()
+
+# Windows CE exceptions
+
+if(WINCE)
+  set(HAVE_FREEADDRINFO 0)
+  set(HAVE_GETADDRINFO 0)
+  set(HAVE_LOCALE_H 0)
+  set(HAVE_SETLOCALE 0)
+  set(HAVE_SETMODE 0)
+  set(HAVE_SIGNAL 0)
+  set(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 0)
+  curl_prefill_type_size("CURL_SOCKET_T" 4)
+  curl_prefill_type_size("TIME_T" 4)
+  curl_prefill_type_size("SIZE_T" 4)
+  if(MINGW32CE)
+    set(HAVE_STRTOK_R 0)
+    set(HAVE__SETMODE 0)
+    set(HAVE_FILE_OFFSET_BITS 0)
+    set(HAVE_SIZEOF_ADDRESS_FAMILY 0)
+    curl_prefill_type_size("SSIZE_T" 4)
+    curl_prefill_type_size("OFF_T" 4)
+  endif()
+endif()
diff --git a/extra/curl/curl-8.12.1/CMakeLists.txt b/extra/curl/curl-8.14.1/CMakeLists.txt
similarity index 87%
rename from extra/curl/curl-8.12.1/CMakeLists.txt
rename to extra/curl/curl-8.14.1/CMakeLists.txt
index 75ce9af136de..57c239dfdaf7 100644
--- a/extra/curl/curl-8.12.1/CMakeLists.txt
+++ b/extra/curl/curl-8.14.1/CMakeLists.txt
@@ -23,26 +23,6 @@
 ###########################################################################
 # by Tetetest and Sukender (Benoit Neil)
 
-# Note: By default this CMake build script detects the version of some
-# dependencies using `check_symbol_exists`. Those checks do not work in
-# the case that both CURL and its dependency are included as sub-projects
-# in a larger build using `FetchContent`. To support that case, additional
-# variables may be defined by the parent project, ideally in the "extra"
-# find package redirect file:
-# https://cmake.org/cmake/help/latest/module/FetchContent.html#integrating-with-find-package
-#
-# The following variables are available:
-#   HAVE_SSL_SET0_WBIO: `SSL_set0_wbio` present in OpenSSL/wolfSSL
-#   HAVE_OPENSSL_SRP: `SSL_CTX_set_srp_username` present in OpenSSL/wolfSSL
-#   HAVE_GNUTLS_SRP: `gnutls_srp_verifier` present in GnuTLS
-#   HAVE_SSL_SET_QUIC_USE_LEGACY_CODEPOINT: `SSL_set_quic_use_legacy_codepoint` present in OpenSSL/wolfSSL
-#   HAVE_QUICHE_CONN_SET_QLOG_FD: `quiche_conn_set_qlog_fd` present in quiche
-#   HAVE_ECH: ECH API checks for OpenSSL, BoringSSL or wolfSSL
-#
-# For each of the above variables, if the variable is DEFINED (either
-# to ON or OFF), the symbol detection is skipped.  If the variable is
-# NOT DEFINED, the symbol detection is performed.
-
 # cmake_minimum_required(VERSION 3.7...3.16 FATAL_ERROR)
 message(STATUS "Using CMake version ${CMAKE_VERSION}")
 
@@ -91,7 +71,38 @@ if(WINDOWS_STORE AND MINGW)  # mingw UWP build
   # CMake (as of v3.31.2) gets confused and applies the MSVC rc.exe command-line
   # template to windres. Reset it to the windres template via 'Modules/Platform/Windows-windres.cmake':
   set(CMAKE_RC_COMPILE_OBJECT "<CMAKE_RC_COMPILER> -O coff <DEFINES> <INCLUDES> <FLAGS> <SOURCE> <OBJECT>")
-elseif(DOS AND CMAKE_COMPILER_IS_GNUCC)  # DJGPP
+elseif(WIN32 AND WINCE AND CMAKE_C_COMPILER_ID STREQUAL "GNU")  # mingw32ce build
+  if(NOT MINGW32CE_LIBRARY_DIR)
+    message(FATAL_ERROR "Set MINGW32CE_LIBRARY_DIR variable to the mingw32ce platform library directory.")
+  endif()
+
+  set(MINGW 1)
+  set(MINGW32CE 1)
+
+  # Build implib with libcurl DLL. Copied from CMake's 'Modules/Platform/Windows-GNU.cmake'.
+  set(CMAKE_C_CREATE_SHARED_LIBRARY "<CMAKE_C_COMPILER> <CMAKE_SHARED_LIBRARY_C_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS>")
+  string(APPEND CMAKE_C_CREATE_SHARED_LIBRARY " <CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS> -o <TARGET> -Wl,--out-implib,<TARGET_IMPLIB>")
+  string(APPEND CMAKE_C_CREATE_SHARED_LIBRARY " ${CMAKE_GNULD_IMAGE_VERSION} <OBJECTS> <LINK_LIBRARIES>")
+
+  # Build resources. Copied from CMake's 'Modules/Platform/Windows-windres.cmake'.
+  set(CMAKE_RC_COMPILE_OBJECT "<CMAKE_RC_COMPILER> -O coff <DEFINES> <INCLUDES> <FLAGS> <SOURCE> <OBJECT>")
+  enable_language(RC)
+
+  # To compile long long integer literals
+  set_property(DIRECTORY APPEND PROPERTY COMPILE_OPTIONS "-std=gnu99")
+  string(APPEND CMAKE_REQUIRED_FLAGS " -std=gnu99")
+
+  set(CMAKE_C_COMPILE_OPTIONS_PIC "")  # CMake sets it to '-fPIC', confusing the toolchain and breaking builds. Zap it.
+
+  set(CMAKE_STATIC_LIBRARY_PREFIX "lib")
+  set(CMAKE_STATIC_LIBRARY_SUFFIX ".a")
+  set(CMAKE_SHARED_LIBRARY_PREFIX "lib")
+  set(CMAKE_SHARED_LIBRARY_SUFFIX ".dll")
+  set(CMAKE_IMPORT_LIBRARY_PREFIX "lib")
+  set(CMAKE_IMPORT_LIBRARY_SUFFIX ".dll.a")
+  set(CMAKE_FIND_LIBRARY_PREFIXES "lib" "")
+  set(CMAKE_FIND_LIBRARY_SUFFIXES ".dll.a" ".a" ".lib")
+elseif(DOS AND CMAKE_C_COMPILER_ID STREQUAL "GNU")  # DJGPP
   set(CMAKE_STATIC_LIBRARY_PREFIX "lib")
   set(CMAKE_STATIC_LIBRARY_SUFFIX ".a")
   set(CMAKE_FIND_LIBRARY_PREFIXES "lib")
@@ -119,22 +130,22 @@ endif()
 if(WIN32)
   string(APPEND _target_flags " WIN32")
 endif()
+if(WINCE)
+  string(APPEND _target_flags " WINCE")
+endif()
 if(WINDOWS_STORE)
   string(APPEND _target_flags " UWP")
 endif()
 if(CYGWIN)
   string(APPEND _target_flags " CYGWIN")
 endif()
-if(MSYS)
-  string(APPEND _target_flags " MSYS")
-endif()
 if(DOS)
   string(APPEND _target_flags " DOS")
 endif()
 if(AMIGA)
   string(APPEND _target_flags " AMIGA")
 endif()
-if(CMAKE_COMPILER_IS_GNUCC)
+if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
   string(APPEND _target_flags " GCC")
 endif()
 if(MINGW)
@@ -163,7 +174,9 @@ else()
   set(CURL_OS "\"${CMAKE_SYSTEM_NAME}\"")
 endif()
 
-include_directories("${PROJECT_SOURCE_DIR}/include")
+set(LIB_NAME "libcurl")
+
+set_property(DIRECTORY APPEND PROPERTY INCLUDE_DIRECTORIES "${PROJECT_SOURCE_DIR}/include")
 
 if(NOT DEFINED CMAKE_UNITY_BUILD_BATCH_SIZE)
   set(CMAKE_UNITY_BUILD_BATCH_SIZE 0)
@@ -190,21 +203,14 @@ option(ENABLE_ARES "Enable c-ares support" OFF)
 option(CURL_DISABLE_INSTALL "Disable installation targets" OFF)
 
 if(WIN32)
-  option(CURL_STATIC_CRT "Build libcurl with static CRT with MSVC (/MT)" OFF)
-  if(CURL_STATIC_CRT AND MSVC)
-    set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
-    string(APPEND CMAKE_C_FLAGS_RELEASE " -MT")
-    string(APPEND CMAKE_C_FLAGS_DEBUG " -MTd")
-  endif()
-
   option(ENABLE_UNICODE "Use the Unicode version of the Windows API functions" OFF)
-  if(WINDOWS_STORE)
+  if(WINDOWS_STORE OR WINCE)
     set(ENABLE_UNICODE ON)
   endif()
   if(ENABLE_UNICODE)
-    add_definitions("-DUNICODE" "-D_UNICODE")
-    if(MINGW)
-      add_compile_options("-municode")
+    set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "UNICODE" "_UNICODE")
+    if(MINGW AND NOT MINGW32CE)
+      set_property(DIRECTORY APPEND PROPERTY COMPILE_OPTIONS "-municode")
     endif()
   endif()
 
@@ -216,7 +222,7 @@ if(WIN32)
 
   set(CURL_TARGET_WINDOWS_VERSION "" CACHE STRING "Minimum target Windows version as hex string")
   if(CURL_TARGET_WINDOWS_VERSION)
-    add_definitions("-D_WIN32_WINNT=${CURL_TARGET_WINDOWS_VERSION}")
+    set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "_WIN32_WINNT=${CURL_TARGET_WINDOWS_VERSION}")
     list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D_WIN32_WINNT=${CURL_TARGET_WINDOWS_VERSION}")  # Apply to all feature checks
   endif()
 
@@ -237,7 +243,9 @@ if(WIN32)
     if(MINGW64_VERSION)
       string(REGEX MATCH "MINGW64_VERSION=[0-9]+\.[0-9]+" CURL_TEST_OUTPUT "${CURL_TEST_OUTPUT}")
       string(REGEX REPLACE "MINGW64_VERSION=" "" MINGW64_VERSION "${CURL_TEST_OUTPUT}")
-      message(STATUS "Found MINGW64_VERSION=${MINGW64_VERSION}")
+      if(MINGW64_VERSION)
+        message(STATUS "Found MINGW64_VERSION=${MINGW64_VERSION}")
+      endif()
     endif()
     unset(MINGW64_VERSION CACHE)  # Avoid storing in CMake cache
   endif()
@@ -259,27 +267,34 @@ endif()
 
 include(PickyWarnings)
 
-if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
-  string(APPEND CMAKE_C_FLAGS " -D_GNU_SOURCE")  # Required for sendmmsg()
+if(CYGWIN OR CMAKE_SYSTEM_NAME STREQUAL "Linux")
+  set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "_GNU_SOURCE")  # Required for accept4(), pipe2(), sendmmsg()
+  list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D_GNU_SOURCE")  # Apply to all feature checks
 endif()
 
 option(ENABLE_DEBUG "Enable curl debug features (for developing curl itself)" OFF)
 if(ENABLE_DEBUG)
-  message(WARNING "This curl build is Debug-enabled, do not use in production.")
+  message(WARNING "This curl build is Debug-enabled and insecure, do not use in production.")
 endif()
 option(ENABLE_CURLDEBUG "Enable TrackMemory debug feature" ${ENABLE_DEBUG})
+option(ENABLE_SERVER_DEBUG "Apply curl debug options to test servers" OFF)
 
+set(CURL_DEBUG_MACROS "")
 if(ENABLE_DEBUG)
-  set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "DEBUGBUILD")
+  list(APPEND CURL_DEBUG_MACROS "DEBUGBUILD")
 endif()
-
 if(ENABLE_CURLDEBUG)
-  set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "CURLDEBUG")
+  list(APPEND CURL_DEBUG_MACROS "CURLDEBUG")
 endif()
 
+option(CURL_TEST_BUNDLES "Build tests into single-binary bundles" OFF)
+
 option(CURL_CLANG_TIDY "Run the build through clang-tidy" OFF)
 if(CURL_CLANG_TIDY)
+  # clang-tidy is not looking into #included sources, thus not compatible with
+  # unity builds and test bundles.
   set(CMAKE_UNITY_BUILD OFF)
+  set(CURL_TEST_BUNDLES OFF)
   set(_tidy_checks "")
   list(APPEND _tidy_checks "-clang-analyzer-security.insecureAPI.strcpy")
   list(APPEND _tidy_checks "-clang-analyzer-optin.performance.Padding")
@@ -327,6 +342,19 @@ else()
   set(LIB_SELECTED ${LIB_STATIC})
 endif()
 
+if(WIN32)
+  option(CURL_STATIC_CRT "Build libcurl with static CRT with MSVC (/MT)" OFF)
+  if(CURL_STATIC_CRT AND MSVC)
+    if(MSVC_VERSION GREATER_EQUAL 1900 OR BUILD_STATIC_CURL OR NOT BUILD_CURL_EXE)
+      set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
+      set_property(DIRECTORY APPEND PROPERTY COMPILE_OPTIONS "$<$<CONFIG:Release>:-MT>")
+      set_property(DIRECTORY APPEND PROPERTY COMPILE_OPTIONS "$<$<CONFIG:Debug>:-MTd>")
+    else()
+      message(WARNING "Static CRT requires UCRT, static libcurl or no curl executable.")
+    endif()
+  endif()
+endif()
+
 # Override to force-disable or force-enable the use of pkg-config.
 if((UNIX AND NOT ANDROID AND (NOT APPLE OR CMAKE_SYSTEM_NAME MATCHES "Darwin")) OR
    VCPKG_TOOLCHAIN OR
@@ -430,9 +458,9 @@ mark_as_advanced(CURL_DISABLE_PROXY)
 option(CURL_DISABLE_IPFS "Disable IPFS" OFF)
 mark_as_advanced(CURL_DISABLE_IPFS)
 option(CURL_DISABLE_RTSP "Disable RTSP" OFF)
-mark_as_advanced(CURL_DISABLE_SHA512_256)
-option(CURL_DISABLE_SHA512_256 "Disable SHA-512/256 hash algorithm" OFF)
 mark_as_advanced(CURL_DISABLE_RTSP)
+option(CURL_DISABLE_SHA512_256 "Disable SHA-512/256 hash algorithm" OFF)
+mark_as_advanced(CURL_DISABLE_SHA512_256)
 option(CURL_DISABLE_SHUFFLE_DNS "Disable shuffle DNS feature" OFF)
 mark_as_advanced(CURL_DISABLE_SHUFFLE_DNS)
 option(CURL_DISABLE_SMB "Disable SMB" OFF)
@@ -479,7 +507,7 @@ if(HTTP_ONLY)
   set(CURL_DISABLE_TFTP ON)
 endif()
 
-if(WINDOWS_STORE)
+if(WINDOWS_STORE OR WINCE)
   set(CURL_DISABLE_TELNET ON)  # telnet code needs fixing to compile for UWP.
 endif()
 
@@ -511,14 +539,9 @@ if(ENABLE_CURL_MANUAL OR BUILD_LIBCURL_DOCS)
   endif()
 endif()
 
-# Disable warnings on Borland to avoid changing 3rd party code.
-if(BORLAND)
-  string(APPEND CMAKE_C_FLAGS " -w-")
-endif()
-
 # If we are on AIX, do the _ALL_SOURCE magic
 if(CMAKE_SYSTEM_NAME STREQUAL "AIX")
-  add_definitions("-D_ALL_SOURCE")
+  set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "_ALL_SOURCE")
 endif()
 
 # If we are on Haiku, make sure that the network library is brought in.
@@ -539,15 +562,21 @@ include(CheckSymbolExists)
 include(CheckTypeSize)
 include(CheckCSourceCompiles)
 
-option(_CURL_QUICK_DETECT "Fast-track known feature detection results (Windows, some Apple)" ON)
-if(_CURL_QUICK_DETECT)
+option(_CURL_PREFILL "Fast-track known feature detection results (Windows, some Apple)" "${WIN32}")
+if(_CURL_PREFILL)
   if(WIN32)
     include("${CMAKE_CURRENT_SOURCE_DIR}/CMake/win32-cache.cmake")
-  elseif(APPLE)
-    set(HAVE_EVENTFD 0)
-    set(HAVE_GETPASS_R 0)
-    set(HAVE_SENDMMSG 0)
+  elseif(UNIX)
+    include("${CMAKE_CURRENT_SOURCE_DIR}/CMake/unix-cache.cmake")
+    message(STATUS "Pre-filling feature detection results for UNIX")
   endif()
+elseif(WIN32)
+  message(STATUS "Pre-filling feature detection results disabled.")
+elseif(APPLE)
+  set(HAVE_EVENTFD 0)
+  set(HAVE_GETPASS_R 0)
+  set(HAVE_WRITABLE_ARGV 1)
+  set(HAVE_SENDMMSG 0)
 endif()
 
 if(AMIGA)
@@ -569,7 +598,19 @@ if(ENABLE_THREADED_RESOLVER)
 endif()
 
 # Check for all needed libraries
-if(DOS)
+if(WIN32)
+  if(WINCE)
+    set(_win32_winsock "ws2")
+  else()
+    set(_win32_winsock "ws2_32")
+  endif()
+  set(_win32_crypt32 "crypt32")
+
+  if(MINGW32CE)  # FIXME upstream: must specify the full path to avoid CMake converting "ws2" to "ws2.lib"
+    set(_win32_winsock "${MINGW32CE_LIBRARY_DIR}/lib${_win32_winsock}.a")
+    set(_win32_crypt32 "${MINGW32CE_LIBRARY_DIR}/lib${_win32_crypt32}.a")
+  endif()
+elseif(DOS)
   if(WATT_ROOT)
     set(USE_WATT32 ON)
     # FIXME upstream: must specify the full path to avoid CMake converting "watt" to "watt.lib"
@@ -590,7 +631,7 @@ elseif(AMIGA)
     set(CURL_USE_OPENSSL ON)
     set(CURL_CA_FALLBACK ON CACHE BOOL "")
   endif()
-elseif(NOT WIN32 AND NOT APPLE)
+elseif(NOT APPLE)
   check_library_exists("socket" "connect" "" HAVE_LIBSOCKET)
   if(HAVE_LIBSOCKET)
     set(CURL_LIBS "socket" ${CURL_LIBS})
@@ -604,8 +645,8 @@ if(ENABLE_IPV6)
   if(WIN32)
     check_struct_has_member("struct sockaddr_in6" "sin6_scope_id" "winsock2.h;ws2tcpip.h" HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
   else()
-    check_struct_has_member("struct sockaddr_in6" "sin6_addr" "netinet/in.h" HAVE_SOCKADDR_IN6_SIN6_ADDR)
     check_struct_has_member("struct sockaddr_in6" "sin6_scope_id" "netinet/in.h" HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
+    check_struct_has_member("struct sockaddr_in6" "sin6_addr" "netinet/in.h" HAVE_SOCKADDR_IN6_SIN6_ADDR)
     if(NOT HAVE_SOCKADDR_IN6_SIN6_ADDR)
       if(NOT DOS AND NOT AMIGA)
         message(WARNING "struct sockaddr_in6 not available, disabling IPv6 support")
@@ -625,7 +666,7 @@ if(ENABLE_IPV6)
     endif()
   endif()
 endif()
-if(ENABLE_IPV6)
+if(ENABLE_IPV6 AND NOT WINCE)
   set(USE_IPV6 ON)
 endif()
 
@@ -688,7 +729,7 @@ endif()
 if(CURL_USE_SCHANNEL)
   set(_ssl_enabled ON)
   set(USE_SCHANNEL ON)  # Windows native SSL/TLS support
-  set(USE_WINDOWS_SSPI ON)  # CURL_USE_SCHANNEL implies CURL_WINDOWS_SSPI
+  set(USE_WINDOWS_SSPI ON)  # CURL_USE_SCHANNEL requires CURL_WINDOWS_SSPI
 
   if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "schannel")
     set(_valid_default_ssl_backend TRUE)
@@ -779,9 +820,6 @@ if(CURL_USE_OPENSSL)
     set(_openssl "AmiSSL")
   else()
     set(_openssl "OpenSSL")
-    if(OPENSSL_VERSION VERSION_LESS 1.1.1)
-      message(WARNING "OpenSSL ${OPENSSL_VERSION} does not support TLS 1.3.")
-    endif()
   endif()
 endif()
 
@@ -872,8 +910,8 @@ if(CURL_USE_GNUTLS)
 
   if(NOT DEFINED HAVE_GNUTLS_SRP AND NOT CURL_DISABLE_SRP)
     cmake_push_check_state()
-    list(APPEND CMAKE_REQUIRED_INCLUDES ${GNUTLS_INCLUDE_DIRS})
-    list(APPEND CMAKE_REQUIRED_LIBRARIES ${GNUTLS_LIBRARIES})
+    list(APPEND CMAKE_REQUIRED_INCLUDES "${GNUTLS_INCLUDE_DIRS}")
+    list(APPEND CMAKE_REQUIRED_LIBRARIES "${GNUTLS_LIBRARIES}")
     check_symbol_exists("gnutls_srp_verifier" "gnutls/gnutls.h" HAVE_GNUTLS_SRP)
     cmake_pop_check_state()
   endif()
@@ -892,6 +930,22 @@ if(CURL_USE_RUSTLS)
     string(APPEND CMAKE_C_FLAGS " ${RUSTLS_CFLAGS}")
   endif()
 
+  if(NOT DEFINED HAVE_RUSTLS_SUPPORTED_HPKE)
+    if(RUSTLS_VERSION AND RUSTLS_VERSION VERSION_GREATER_EQUAL 0.15)
+      set(HAVE_RUSTLS_SUPPORTED_HPKE TRUE)
+    elseif(NOT RUSTLS_VERSION)
+      cmake_push_check_state()
+      list(APPEND CMAKE_REQUIRED_INCLUDES "${RUSTLS_INCLUDE_DIRS}")
+      list(APPEND CMAKE_REQUIRED_LIBRARIES "${RUSTLS_LIBRARIES}")
+      curl_required_libpaths("${RUSTLS_LIBRARY_DIRS}")
+      check_symbol_exists("rustls_supported_hpke" "rustls.h" HAVE_RUSTLS_SUPPORTED_HPKE)
+      cmake_pop_check_state()
+    endif()
+  endif()
+  if(NOT HAVE_RUSTLS_SUPPORTED_HPKE)
+    message(FATAL_ERROR "rustls-ffi library does not provide rustls_supported_hpke function. Required version is 0.15 or newer.")
+  endif()
+
   if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "rustls")
     set(_valid_default_ssl_backend TRUE)
   endif()
@@ -903,7 +957,7 @@ if(CURL_DEFAULT_SSL_BACKEND AND NOT _valid_default_ssl_backend)
 endif()
 
 # Keep ZLIB detection after TLS detection,
-# and before calling curl_openssl_check_symbol_exists().
+# and before calling curl_openssl_check_exists().
 
 set(HAVE_LIBZ OFF)
 # Use result from cmake/zlib.cmake
@@ -933,7 +987,7 @@ endif()
 set(HAVE_ZSTD OFF)
 # curl_dependency_option(CURL_ZSTD Zstd "zstd")
 if(ZSTD_FOUND)
-  if(NOT ZSTD_VERSION VERSION_LESS 1.0.0)
+  if(ZSTD_VERSION VERSION_GREATER_EQUAL 1.0.0)
     set(HAVE_ZSTD ON)
     list(APPEND CURL_LIBS ${ZSTD_LIBRARIES})
     list(APPEND CURL_LIBDIRS ${ZSTD_LIBRARY_DIRS})
@@ -948,8 +1002,8 @@ if(ZSTD_FOUND)
   endif()
 endif()
 
-# Check symbol in an OpenSSL-like TLS backend.
-macro(curl_openssl_check_symbol_exists _symbol _files _variable)
+# Check function in an OpenSSL-like TLS backend.
+macro(curl_openssl_check_exists)
   cmake_push_check_state()
   if(USE_OPENSSL)
     list(APPEND CMAKE_REQUIRED_LIBRARIES OpenSSL::SSL OpenSSL::Crypto)
@@ -959,78 +1013,88 @@ macro(curl_openssl_check_symbol_exists _symbol _files _variable)
     # Target "cmTC_e783c" links to target "ZLIB::ZLIB" but the target was not found.
     # list(APPEND CMAKE_REQUIRED_LIBRARIES ZLIB::ZLIB)
     endif()
-    if(WIN32)
-      list(APPEND CMAKE_REQUIRED_LIBRARIES "ws2_32")
+    if(WIN32 AND NOT WINCE)
       list(APPEND CMAKE_REQUIRED_LIBRARIES "bcrypt")  # for OpenSSL/LibreSSL
     endif()
   endif()
   if(USE_WOLFSSL)
-    list(APPEND CMAKE_REQUIRED_INCLUDES   "${WOLFSSL_INCLUDE_DIRS}")
-    list(APPEND CMAKE_REQUIRED_LIBRARIES  "${WOLFSSL_LIBRARIES}")
+    list(APPEND CMAKE_REQUIRED_INCLUDES "${WOLFSSL_INCLUDE_DIRS}")
+    list(APPEND CMAKE_REQUIRED_LIBRARIES "${WOLFSSL_LIBRARIES}")
     curl_required_libpaths("${WOLFSSL_LIBRARY_DIRS}")
     if(HAVE_LIBZ)
       list(APPEND CMAKE_REQUIRED_LIBRARIES ZLIB::ZLIB)  # Public wolfSSL headers also require zlib headers
     endif()
-    if(WIN32)
-      list(APPEND CMAKE_REQUIRED_LIBRARIES "ws2_32" "crypt32")
-    endif()
     list(APPEND CMAKE_REQUIRED_DEFINITIONS "-DHAVE_UINTPTR_T")  # to pull in stdint.h (as of wolfSSL v5.5.4)
   endif()
-  check_symbol_exists("${_symbol}" "${_files}" "${_variable}")
+  if(WIN32)
+    list(APPEND CMAKE_REQUIRED_LIBRARIES "${_win32_winsock}" "${_win32_crypt32}")  # for OpenSSL/wolfSSL
+  endif()
+  if(${ARGC} EQUAL 2)
+    check_function_exists(${ARGN})
+  else()
+    check_symbol_exists(${ARGN})  # Uses CMAKE_REQUIRED_INCLUDES and CMAKE_REQUIRED_DEFINITIONS
+  endif()
   cmake_pop_check_state()
 endmacro()
 
-# Ensure that the OpenSSL fork actually supports QUIC.
+# Ensure that OpenSSL (or fork) or wolfSSL actually supports QUICTLS API.
 macro(curl_openssl_check_quic)
-  if(NOT DEFINED HAVE_SSL_SET_QUIC_USE_LEGACY_CODEPOINT)
-    if(USE_OPENSSL)
-      curl_openssl_check_symbol_exists("SSL_set_quic_use_legacy_codepoint" "openssl/ssl.h" HAVE_SSL_SET_QUIC_USE_LEGACY_CODEPOINT)
-    endif()
-    if(USE_WOLFSSL)
-      curl_openssl_check_symbol_exists("wolfSSL_set_quic_use_legacy_codepoint" "wolfssl/options.h;wolfssl/openssl/ssl.h"
-        HAVE_SSL_SET_QUIC_USE_LEGACY_CODEPOINT)
+  if(USE_OPENSSL AND NOT USE_OPENSSL_QUIC)
+    if(OPENSSL_VERSION VERSION_GREATER_EQUAL 3.5.0)
+      if(NOT DEFINED HAVE_SSL_SET_QUIC_TLS_CBS)
+        curl_openssl_check_exists("SSL_set_quic_tls_cbs" HAVE_SSL_SET_QUIC_TLS_CBS)
+      endif()
+    else()
+      if(NOT DEFINED HAVE_SSL_SET_QUIC_USE_LEGACY_CODEPOINT)
+        curl_openssl_check_exists("SSL_set_quic_use_legacy_codepoint" HAVE_SSL_SET_QUIC_USE_LEGACY_CODEPOINT)
+      endif()
     endif()
   endif()
-  if(NOT HAVE_SSL_SET_QUIC_USE_LEGACY_CODEPOINT)
-    message(FATAL_ERROR "QUIC support is missing in OpenSSL fork. Try setting -DOPENSSL_ROOT_DIR")
+  if(USE_WOLFSSL AND NOT DEFINED HAVE_WOLFSSL_SET_QUIC_USE_LEGACY_CODEPOINT)
+    curl_openssl_check_exists("wolfSSL_set_quic_use_legacy_codepoint" HAVE_WOLFSSL_SET_QUIC_USE_LEGACY_CODEPOINT)
+  endif()
+  if(NOT HAVE_SSL_SET_QUIC_TLS_CBS AND
+     NOT HAVE_SSL_SET_QUIC_USE_LEGACY_CODEPOINT AND
+     NOT HAVE_WOLFSSL_SET_QUIC_USE_LEGACY_CODEPOINT)
+    message(FATAL_ERROR "QUICTLS API support is missing from OpenSSL/fork/wolfSSL. Try setting -DOPENSSL_ROOT_DIR")
   endif()
 endmacro()
 
 if(USE_WOLFSSL)
-  curl_openssl_check_symbol_exists("wolfSSL_DES_ecb_encrypt" "wolfssl/options.h;wolfssl/openssl/des.h" HAVE_WOLFSSL_DES_ECB_ENCRYPT)
-  curl_openssl_check_symbol_exists("wolfSSL_BIO_new" "wolfssl/options.h;wolfssl/ssl.h" HAVE_WOLFSSL_BIO)
-  curl_openssl_check_symbol_exists("wolfSSL_BIO_set_shutdown" "wolfssl/options.h;wolfssl/ssl.h" HAVE_WOLFSSL_FULL_BIO)
+  curl_openssl_check_exists("wolfSSL_get_peer_certificate" HAVE_WOLFSSL_GET_PEER_CERTIFICATE)
+  curl_openssl_check_exists("wolfSSL_UseALPN" HAVE_WOLFSSL_USEALPN)
+  curl_openssl_check_exists("wolfSSL_DES_ecb_encrypt" HAVE_WOLFSSL_DES_ECB_ENCRYPT)
+  curl_openssl_check_exists("wolfSSL_BIO_new" HAVE_WOLFSSL_BIO_NEW)
+  curl_openssl_check_exists("wolfSSL_BIO_set_shutdown" HAVE_WOLFSSL_BIO_SET_SHUTDOWN)
 endif()
 
-if(USE_OPENSSL OR USE_WOLFSSL)
+if(USE_OPENSSL)
   if(NOT DEFINED HAVE_SSL_SET0_WBIO)
-    curl_openssl_check_symbol_exists("SSL_set0_wbio" "openssl/ssl.h" HAVE_SSL_SET0_WBIO)
+    curl_openssl_check_exists("SSL_set0_wbio" HAVE_SSL_SET0_WBIO)
   endif()
   if(NOT DEFINED HAVE_OPENSSL_SRP AND NOT CURL_DISABLE_SRP)
-    curl_openssl_check_symbol_exists("SSL_CTX_set_srp_username" "openssl/ssl.h" HAVE_OPENSSL_SRP)
+    curl_openssl_check_exists("SSL_CTX_set_srp_username" "openssl/ssl.h" HAVE_OPENSSL_SRP)
   endif()
 endif()
 
 option(USE_HTTPSRR "Enable HTTPS RR support" OFF)
 option(USE_ECH "Enable ECH support" OFF)
 if(USE_ECH)
-  if(USE_OPENSSL OR USE_WOLFSSL)
+  if(USE_OPENSSL OR USE_WOLFSSL OR USE_RUSTLS)
     # Be sure that the TLS library actually supports ECH.
     if(USE_WOLFSSL)
-      curl_openssl_check_symbol_exists("wolfSSL_CTX_GenerateEchConfig" "wolfssl/options.h;wolfssl/ssl.h"
-        HAVE_WOLFSSL_CTX_GENERATEECHCONFIG)
+      curl_openssl_check_exists("wolfSSL_CTX_GenerateEchConfig" HAVE_WOLFSSL_CTX_GENERATEECHCONFIG)
     endif()
-    if(HAVE_BORINGSSL OR HAVE_AWSLC)
-      curl_openssl_check_symbol_exists("SSL_set1_ech_config_list" "openssl/ssl.h" HAVE_SSL_SET1_ECH_CONFIG_LIST)
-    elseif(HAVE_OPENSSL)
-      curl_openssl_check_symbol_exists("SSL_set1_ech_config_list" "openssl/ech.h" HAVE_SSL_SET1_ECH_CONFIG_LIST)
+    if(USE_OPENSSL)
+      curl_openssl_check_exists("SSL_set1_ech_config_list" HAVE_SSL_SET1_ECH_CONFIG_LIST)
     endif()
     if(HAVE_WOLFSSL_CTX_GENERATEECHCONFIG OR
-       HAVE_SSL_SET1_ECH_CONFIG_LIST)
+       HAVE_SSL_SET1_ECH_CONFIG_LIST OR
+       USE_RUSTLS)
       set(HAVE_ECH 1)
     endif()
     if(NOT HAVE_ECH)
-      message(FATAL_ERROR "ECH support missing in OpenSSL/BoringSSL/AWS-LC/wolfSSL")
+      message(FATAL_ERROR "ECH support missing in OpenSSL/BoringSSL/AWS-LC/wolfSSL/rustls-ffi")
     else()
       message(STATUS "ECH enabled")
       # ECH wants HTTPSRR
@@ -1038,7 +1102,7 @@ if(USE_ECH)
       message(STATUS "HTTPSRR enabled")
     endif()
   else()
-    message(FATAL_ERROR "ECH requires ECH-enablded OpenSSL, BoringSSL, AWS-LC or wolfSSL")
+    message(FATAL_ERROR "ECH requires ECH-enabled OpenSSL, BoringSSL, AWS-LC, wolfSSL or rustls-ffi")
   endif()
 endif()
 
@@ -1075,6 +1139,12 @@ if(USE_NGTCP2)
       find_package(NGTCP2 REQUIRED "wolfSSL")
     elseif(HAVE_BORINGSSL OR HAVE_AWSLC)
       find_package(NGTCP2 REQUIRED "BoringSSL")
+    elseif(OPENSSL_VERSION VERSION_GREATER_EQUAL 3.5.0 AND NOT USE_OPENSSL_QUIC)
+      find_package(NGTCP2 REQUIRED "ossl")
+      if(NGTCP2_VERSION VERSION_LESS 1.12.0)
+        message(FATAL_ERROR "ngtcp2 1.12.0 or upper required for OpenSSL")
+      endif()
+      set(OPENSSL_QUIC_API2 1)
     else()
       find_package(NGTCP2 REQUIRED "quictls")
       if(NOT HAVE_LIBRESSL)
@@ -1085,7 +1155,7 @@ if(USE_NGTCP2)
   elseif(USE_GNUTLS)
     find_package(NGTCP2 REQUIRED "GnuTLS")
   else()
-    message(FATAL_ERROR "ngtcp2 requires OpenSSL, wolfSSL or GnuTLS")
+    message(FATAL_ERROR "ngtcp2 requires a supported TLS-backend")
   endif()
   list(APPEND CURL_LIBS ${NGTCP2_LIBRARIES})
   list(APPEND CURL_LIBDIRS ${NGTCP2_LIBRARY_DIRS})
@@ -1128,8 +1198,8 @@ if(USE_QUICHE)
   endif()
   if(NOT DEFINED HAVE_QUICHE_CONN_SET_QLOG_FD)
     cmake_push_check_state()
-    list(APPEND CMAKE_REQUIRED_INCLUDES   "${QUICHE_INCLUDE_DIRS}")
-    list(APPEND CMAKE_REQUIRED_LIBRARIES  "${QUICHE_LIBRARIES}")
+    list(APPEND CMAKE_REQUIRED_INCLUDES "${QUICHE_INCLUDE_DIRS}")
+    list(APPEND CMAKE_REQUIRED_LIBRARIES "${QUICHE_LIBRARIES}")
     check_symbol_exists("quiche_conn_set_qlog_fd" "quiche.h" HAVE_QUICHE_CONN_SET_QLOG_FD)
     cmake_pop_check_state()
   endif()
@@ -1165,9 +1235,14 @@ if(USE_OPENSSL_QUIC)
 
   find_package(NGHTTP3 REQUIRED)
   set(USE_NGHTTP3 ON)
-  include_directories(SYSTEM ${NGHTTP3_INCLUDE_DIRS})
   list(APPEND CURL_LIBS ${NGHTTP3_LIBRARIES})
+  list(APPEND CURL_LIBDIRS ${NGHTTP3_LIBRARY_DIRS})
   list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${NGHTTP3_PC_REQUIRES})
+  include_directories(SYSTEM ${NGHTTP3_INCLUDE_DIRS})
+  link_directories(${NGHTTP3_LIBRARY_DIRS})
+  if(NGHTTP3_CFLAGS)
+    string(APPEND CMAKE_C_FLAGS " ${NGHTTP3_CFLAGS}")
+  endif()
 endif()
 
 if(CURL_WITH_MULTI_SSL AND (USE_NGTCP2 OR USE_QUICHE OR USE_MSH3 OR USE_OPENSSL_QUIC))
@@ -1179,7 +1254,7 @@ if(NOT CURL_DISABLE_SRP AND (HAVE_GNUTLS_SRP OR HAVE_OPENSSL_SRP))
 endif()
 
 if(NOT CURL_DISABLE_LDAP)
-  if(WIN32 AND NOT WINDOWS_STORE)
+  if(WIN32 AND NOT WINDOWS_STORE AND NOT WINCE)
     option(USE_WIN32_LDAP "Use Windows LDAP implementation" ON)
     if(USE_WIN32_LDAP)
       list(APPEND CURL_LIBS "wldap32")
@@ -1213,7 +1288,7 @@ if(NOT CURL_DISABLE_LDAP)
       # LDAP feature checks
 
       list(APPEND CMAKE_REQUIRED_DEFINITIONS "-DLDAP_DEPRECATED=1")
-      list(APPEND CMAKE_REQUIRED_LIBRARIES ${LDAP_LIBRARIES})
+      list(APPEND CMAKE_REQUIRED_LIBRARIES "${LDAP_LIBRARIES}")
       curl_required_libpaths("${LDAP_LIBRARY_DIRS}")
 
       check_function_exists("ldap_url_parse" HAVE_LDAP_URL_PARSE)
@@ -1396,7 +1471,7 @@ if(CURL_USE_GSSAPI)
       set(HAVE_GSSGNU 1)
     else()
       cmake_push_check_state()
-      list(APPEND CMAKE_REQUIRED_INCLUDES ${GSS_INCLUDE_DIRS})
+      list(APPEND CMAKE_REQUIRED_INCLUDES "${GSS_INCLUDE_DIRS}")
 
       set(_include_list "")
       check_include_file("gssapi/gssapi.h" HAVE_GSSAPI_GSSAPI_H)
@@ -1416,7 +1491,7 @@ if(CURL_USE_GSSAPI)
 
         if(NOT DEFINED HAVE_GSS_C_NT_HOSTBASED_SERVICE)
           string(APPEND CMAKE_REQUIRED_FLAGS " ${GSS_CFLAGS}")
-          list(APPEND CMAKE_REQUIRED_LIBRARIES ${GSS_LIBRARIES})
+          list(APPEND CMAKE_REQUIRED_LIBRARIES "${GSS_LIBRARIES}")
           curl_required_libpaths("${GSS_LIBRARY_DIRS}")
           check_symbol_exists("GSS_C_NT_HOSTBASED_SERVICE" "${_include_list}" HAVE_GSS_C_NT_HOSTBASED_SERVICE)
         endif()
@@ -1465,7 +1540,7 @@ if(USE_LIBRTMP)
 endif()
 
 option(ENABLE_UNIX_SOCKETS "Enable Unix domain sockets support" ON)
-if(ENABLE_UNIX_SOCKETS)
+if(ENABLE_UNIX_SOCKETS AND NOT WINCE)
   if(WIN32 OR DOS)
     set(USE_UNIX_SOCKETS ON)
   else()
@@ -1575,24 +1650,9 @@ if(WIN32)
   list(APPEND CURL_INCLUDES "winsock2.h")
   list(APPEND CURL_INCLUDES "ws2tcpip.h")
 
-  if(HAVE_WIN32_WINNT)
-    if(HAVE_WIN32_WINNT LESS 0x0501)
-      # Windows XP is required for freeaddrinfo, getaddrinfo
-      message(FATAL_ERROR "Building for Windows XP or newer is required.")
-    endif()
-
-    # Pre-fill detection results based on target OS version
-    if(MINGW OR MSVC)
-      if(HAVE_WIN32_WINNT LESS 0x0600)
-        set(HAVE_INET_NTOP 0)
-        set(HAVE_INET_PTON 0)
-      else()  # Windows Vista or newer
-        set(HAVE_INET_NTOP 1)
-        set(HAVE_INET_PTON 1)
-      endif()
-      unset(HAVE_INET_NTOP CACHE)
-      unset(HAVE_INET_PTON CACHE)
-    endif()
+  if(HAVE_WIN32_WINNT AND HAVE_WIN32_WINNT LESS 0x0501 AND NOT WINCE)
+    # Windows XP is required for freeaddrinfo, getaddrinfo
+    message(FATAL_ERROR "Building for Windows XP or newer is required.")
   endif()
 endif()
 
@@ -1634,6 +1694,7 @@ check_include_file("poll.h"           HAVE_POLL_H)
 check_include_file("pwd.h"            HAVE_PWD_H)
 check_include_file("stdatomic.h"      HAVE_STDATOMIC_H)
 check_include_file("stdbool.h"        HAVE_STDBOOL_H)
+check_include_file("stdint.h"         HAVE_STDINT_H)
 check_include_file("strings.h"        HAVE_STRINGS_H)
 check_include_file("stropts.h"        HAVE_STROPTS_H)
 check_include_file("termio.h"         HAVE_TERMIO_H)
@@ -1690,13 +1751,14 @@ endif()
 
 # Apply to all feature checks
 if(WIN32)
-  list(APPEND CMAKE_REQUIRED_LIBRARIES "ws2_32")
+  list(APPEND CMAKE_REQUIRED_LIBRARIES "${_win32_winsock}")
 elseif(HAVE_LIBSOCKET)
   list(APPEND CMAKE_REQUIRED_LIBRARIES "socket")
 elseif(DOS)
   list(APPEND CMAKE_REQUIRED_LIBRARIES "${WATT_ROOT}/lib/libwatt.a")
 endif()
 
+check_function_exists("accept4"       HAVE_ACCEPT4)
 check_function_exists("fnmatch"       HAVE_FNMATCH)
 check_symbol_exists("basename"        "${CURL_INCLUDES};string.h" HAVE_BASENAME)  # libgen.h unistd.h
 check_symbol_exists("opendir"         "dirent.h" HAVE_OPENDIR)
@@ -1709,7 +1771,6 @@ check_function_exists("sendmsg"       HAVE_SENDMSG)
 check_function_exists("sendmmsg"      HAVE_SENDMMSG)
 check_symbol_exists("select"          "${CURL_INCLUDES}" HAVE_SELECT)  # proto/bsdsocket.h sys/select.h sys/socket.h
 check_symbol_exists("strdup"          "string.h" HAVE_STRDUP)
-check_symbol_exists("strtok_r"        "string.h" HAVE_STRTOK_R)
 check_symbol_exists("memrchr"         "string.h" HAVE_MEMRCHR)
 check_symbol_exists("alarm"           "unistd.h" HAVE_ALARM)
 check_symbol_exists("fcntl"           "fcntl.h" HAVE_FCNTL)
@@ -1730,7 +1791,6 @@ check_symbol_exists("gethostbyname_r" "netdb.h" HAVE_GETHOSTBYNAME_R)
 check_symbol_exists("gethostname"     "${CURL_INCLUDES}" HAVE_GETHOSTNAME)  # winsock2.h unistd.h proto/bsdsocket.h
 
 check_symbol_exists("signal"          "signal.h" HAVE_SIGNAL)
-check_symbol_exists("strtoll"         "stdlib.h" HAVE_STRTOLL)
 check_symbol_exists("strerror_r"      "stdlib.h;string.h" HAVE_STRERROR_R)
 check_symbol_exists("sigaction"       "signal.h" HAVE_SIGACTION)
 check_symbol_exists("siginterrupt"    "signal.h" HAVE_SIGINTERRUPT)
@@ -1738,13 +1798,13 @@ check_symbol_exists("getaddrinfo"     "${CURL_INCLUDES};stdlib.h;string.h" HAVE_
 check_symbol_exists("getifaddrs"      "${CURL_INCLUDES};stdlib.h" HAVE_GETIFADDRS)  # ifaddrs.h
 check_symbol_exists("freeaddrinfo"    "${CURL_INCLUDES}" HAVE_FREEADDRINFO)  # ws2tcpip.h sys/socket.h netdb.h
 check_function_exists("pipe"          HAVE_PIPE)
+check_function_exists("pipe2"         HAVE_PIPE2)
 check_function_exists("eventfd"       HAVE_EVENTFD)
 check_symbol_exists("ftruncate"       "unistd.h" HAVE_FTRUNCATE)
 check_symbol_exists("getpeername"     "${CURL_INCLUDES}" HAVE_GETPEERNAME)  # winsock2.h unistd.h proto/bsdsocket.h
 check_symbol_exists("getsockname"     "${CURL_INCLUDES}" HAVE_GETSOCKNAME)  # winsock2.h unistd.h proto/bsdsocket.h
 check_function_exists("getrlimit"       HAVE_GETRLIMIT)
 check_function_exists("setlocale"       HAVE_SETLOCALE)
-check_function_exists("setmode"         HAVE_SETMODE)
 check_function_exists("setrlimit"       HAVE_SETRLIMIT)
 
 if(NOT WIN32)
@@ -1756,8 +1816,11 @@ if(NOT WIN32)
   check_symbol_exists("strcmpi"         "string.h" HAVE_STRCMPI)
 endif()
 
-if(WIN32 OR CYGWIN)
-  check_function_exists("_setmode" HAVE__SETMODE)
+if(NOT MINGW32CE)  # Avoid false detections
+  check_function_exists("setmode" HAVE_SETMODE)
+  if(WIN32 OR CYGWIN)
+    check_function_exists("_setmode" HAVE__SETMODE)
+  endif()
 endif()
 
 if(AMIGA)
@@ -1776,8 +1839,10 @@ endif()
 if(APPLE)
   check_function_exists("mach_absolute_time" HAVE_MACH_ABSOLUTE_TIME)
 endif()
-check_symbol_exists("inet_ntop" "${CURL_INCLUDES};stdlib.h;string.h" HAVE_INET_NTOP)  # arpa/inet.h netinet/in.h sys/socket.h
-check_symbol_exists("inet_pton" "${CURL_INCLUDES};stdlib.h;string.h" HAVE_INET_PTON)  # arpa/inet.h netinet/in.h sys/socket.h
+if(NOT WIN32)
+  check_symbol_exists("inet_ntop" "${CURL_INCLUDES};stdlib.h;string.h" HAVE_INET_NTOP)  # arpa/inet.h netinet/in.h sys/socket.h
+  check_symbol_exists("inet_pton" "${CURL_INCLUDES};stdlib.h;string.h" HAVE_INET_PTON)  # arpa/inet.h netinet/in.h sys/socket.h
+endif()
 
 check_symbol_exists("fsetxattr" "sys/xattr.h" HAVE_FSETXATTR)
 if(HAVE_FSETXATTR)
@@ -1810,18 +1875,44 @@ foreach(_curl_test IN ITEMS
     HAVE_GETHOSTBYNAME_R_3
     HAVE_GETHOSTBYNAME_R_5
     HAVE_GETHOSTBYNAME_R_6
-    HAVE_GETHOSTBYNAME_R_3_REENTRANT
-    HAVE_GETHOSTBYNAME_R_5_REENTRANT
-    HAVE_GETHOSTBYNAME_R_6_REENTRANT
-    HAVE_IN_ADDR_T
     HAVE_BOOL_T
     STDC_HEADERS
-    HAVE_FILE_OFFSET_BITS
     HAVE_ATOMIC
     )
   curl_internal_test(${_curl_test})
 endforeach()
 
+# Check for reentrant
+cmake_push_check_state()
+list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D_REENTRANT")
+foreach(_curl_test IN ITEMS
+    HAVE_GETHOSTBYNAME_R_3
+    HAVE_GETHOSTBYNAME_R_5
+    HAVE_GETHOSTBYNAME_R_6)
+  curl_internal_test(${_curl_test}_REENTRANT)
+  if(NOT ${_curl_test} AND ${_curl_test}_REENTRANT)
+    set(NEED_REENTRANT 1)
+  endif()
+endforeach()
+cmake_pop_check_state()
+
+if(NEED_REENTRANT)
+  foreach(_curl_test IN ITEMS
+      HAVE_GETHOSTBYNAME_R_3
+      HAVE_GETHOSTBYNAME_R_5
+      HAVE_GETHOSTBYNAME_R_6)
+    set(${_curl_test} 0)
+    if(${_curl_test}_REENTRANT)
+      set(${_curl_test} 1)
+    endif()
+  endforeach()
+endif()
+
+cmake_push_check_state()
+list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D_FILE_OFFSET_BITS=64")
+curl_internal_test(HAVE_FILE_OFFSET_BITS)
+cmake_pop_check_state()
+
 cmake_push_check_state()
 if(HAVE_FILE_OFFSET_BITS)
   set(_FILE_OFFSET_BITS 64)
@@ -1875,30 +1966,6 @@ endif()
 curl_internal_test(HAVE_GLIBC_STRERROR_R)
 curl_internal_test(HAVE_POSIX_STRERROR_R)
 
-# Check for reentrant
-foreach(_curl_test IN ITEMS
-    HAVE_GETHOSTBYNAME_R_3
-    HAVE_GETHOSTBYNAME_R_5
-    HAVE_GETHOSTBYNAME_R_6)
-  if(NOT ${_curl_test})
-    if(${_curl_test}_REENTRANT)
-      set(NEED_REENTRANT 1)
-    endif()
-  endif()
-endforeach()
-
-if(NEED_REENTRANT)
-  foreach(_curl_test IN ITEMS
-      HAVE_GETHOSTBYNAME_R_3
-      HAVE_GETHOSTBYNAME_R_5
-      HAVE_GETHOSTBYNAME_R_6)
-    set(${_curl_test} 0)
-    if(${_curl_test}_REENTRANT)
-      set(${_curl_test} 1)
-    endif()
-  endforeach()
-endif()
-
 if(NOT WIN32)
   curl_internal_test(HAVE_CLOCK_GETTIME_MONOTONIC)  # Check clock_gettime(CLOCK_MONOTONIC, x) support
 endif()
@@ -1909,26 +1976,6 @@ endif()
 
 # Some other minor tests
 
-if(NOT HAVE_IN_ADDR_T)
-  set(in_addr_t "unsigned long")
-endif()
-
-if(CMAKE_COMPILER_IS_GNUCC AND APPLE)
-  include(CheckCCompilerFlag)
-  check_c_compiler_flag("-Wno-long-double" HAVE_C_FLAG_Wno_long_double)
-  if(HAVE_C_FLAG_Wno_long_double)
-    # The Mac version of GCC warns about use of long double. Disable it.
-    get_source_file_property(_mprintf_compile_flags "mprintf.c" COMPILE_FLAGS)
-    if(_mprintf_compile_flags)
-      string(APPEND _mprintf_compile_flags " -Wno-long-double")
-    else()
-      set(_mprintf_compile_flags "-Wno-long-double")
-    endif()
-    set_source_files_properties("mprintf.c" PROPERTIES
-      COMPILE_FLAGS ${_mprintf_compile_flags})
-  endif()
-endif()
-
 if(_cmake_try_compile_target_type_save)
   set(CMAKE_TRY_COMPILE_TARGET_TYPE ${_cmake_try_compile_target_type_save})
   unset(_cmake_try_compile_target_type_save)
@@ -1936,19 +1983,18 @@ endif()
 
 include(CMake/OtherTests.cmake)
 
-add_definitions("-DHAVE_CONFIG_H")
+set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "HAVE_CONFIG_H")
 
 if(WIN32)
-  list(APPEND CURL_LIBS "ws2_32" "bcrypt")
+  list(APPEND CURL_LIBS "${_win32_winsock}")
+  if(NOT WINCE)
+    list(APPEND CURL_LIBS "bcrypt")
+  endif()
 
-  # _fseeki64() requires VS2005
-  if(NOT MSVC OR (MSVC_VERSION GREATER_EQUAL 1400))
+  if(NOT WINCE)
     set(USE_WIN32_LARGE_FILES ON)
   endif()
 
-  # Use the manifest embedded in the Windows Resource
-  string(APPEND CMAKE_RC_FLAGS " -DCURL_EMBED_MANIFEST")
-
   # We use crypto functions that are not available for UWP apps
   if(NOT WINDOWS_STORE)
     set(USE_WIN32_CRYPTO ON)
@@ -1956,32 +2002,15 @@ if(WIN32)
 
   # Link required libraries for USE_WIN32_CRYPTO or USE_SCHANNEL
   if(USE_WIN32_CRYPTO OR USE_SCHANNEL)
-    list(APPEND CURL_LIBS "advapi32" "crypt32")
-  endif()
-endif()
-
-if(MSVC)
-  # Disable default manifest added by CMake
-  string(APPEND CMAKE_EXE_LINKER_FLAGS " -MANIFEST:NO")
-
-  if(CMAKE_C_FLAGS MATCHES "[/-]W[0-4]")
-    string(REGEX REPLACE "[/-]W[0-4]" "-W4" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
-  else()
-    string(APPEND CMAKE_C_FLAGS " -W4")
-  endif()
-
-  # Use multithreaded compilation on VS2008+
-  if(CMAKE_C_COMPILER_ID STREQUAL "MSVC" AND MSVC_VERSION GREATER_EQUAL 1500 AND NOT WIN32_CLANG)
-    string(APPEND CMAKE_C_FLAGS " -MP")
+    if(NOT WINCE)
+      list(APPEND CURL_LIBS "advapi32")
+    endif()
+    list(APPEND CURL_LIBS "${_win32_crypt32}")
   endif()
 endif()
 
-if(CURL_WERROR)
-  if(MSVC)
-    string(APPEND CMAKE_C_FLAGS " -WX")
-  else()
-    string(APPEND CMAKE_C_FLAGS " -Werror")  # This assumes clang or gcc style options
-  endif()
+if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")  # MSVC but exclude clang-cl
+  set_property(DIRECTORY APPEND PROPERTY COMPILE_OPTIONS "-MP")  # Parallel compilation
 endif()
 
 if(CURL_LTO)
@@ -2008,9 +2037,9 @@ function(curl_transform_makefile_inc _input_file _output_file)
   string(REPLACE "$(top_srcdir)"   "\${PROJECT_SOURCE_DIR}" _makefile_inc_text ${_makefile_inc_text})
   string(REPLACE "$(top_builddir)" "\${PROJECT_BINARY_DIR}" _makefile_inc_text ${_makefile_inc_text})
 
-  string(REGEX REPLACE "\\\\\n" "!π!α!" _makefile_inc_text ${_makefile_inc_text})
+  string(REGEX REPLACE "\\\\\n" "!^!^!" _makefile_inc_text ${_makefile_inc_text})
   string(REGEX REPLACE "([a-zA-Z_][a-zA-Z0-9_]*)[\t ]*=[\t ]*([^\n]*)" "set(\\1 \\2)" _makefile_inc_text ${_makefile_inc_text})
-  string(REPLACE "!π!α!" "\n" _makefile_inc_text ${_makefile_inc_text})
+  string(REPLACE "!^!^!" "\n" _makefile_inc_text ${_makefile_inc_text})
 
   # Replace $() with ${}
   string(REGEX REPLACE "\\$\\(([a-zA-Z_][a-zA-Z0-9_]*)\\)" "\${\\1}" _makefile_inc_text ${_makefile_inc_text})
@@ -2030,7 +2059,7 @@ set(_project_config "${_generated_dir}/${PROJECT_NAME}Config.cmake")
 set(_version_config "${_generated_dir}/${PROJECT_NAME}ConfigVersion.cmake")
 
 option(BUILD_TESTING "Build tests" ON)
-if(BUILD_TESTING AND PERL_FOUND AND NOT CURL_DISABLE_TESTS)
+if(BUILD_TESTING AND PERL_FOUND)
   set(CURL_BUILD_TESTING ON)
 else()
   set(CURL_BUILD_TESTING OFF)
@@ -2042,6 +2071,10 @@ if(HAVE_MANUAL_TOOLS)
   add_subdirectory(docs)
 endif()
 
+add_subdirectory(scripts)  # for shell completions
+
+list(REMOVE_DUPLICATES CURL_LIBDIRS)
+
 add_subdirectory(lib)
 
 if(BUILD_CURL_EXE)
@@ -2146,16 +2179,16 @@ curl_add_if("SPNEGO"        NOT CURL_DISABLE_NEGOTIATE_AUTH AND
                             (HAVE_GSSAPI OR USE_WINDOWS_SSPI))
 curl_add_if("Kerberos"      NOT CURL_DISABLE_KERBEROS_AUTH AND
                             (HAVE_GSSAPI OR USE_WINDOWS_SSPI))
-curl_add_if("NTLM"          NOT (CURL_DISABLE_NTLM) AND
+curl_add_if("NTLM"          NOT CURL_DISABLE_NTLM AND
                             (_use_curl_ntlm_core OR USE_WINDOWS_SSPI))
 curl_add_if("TLS-SRP"       USE_TLS_SRP)
 curl_add_if("HTTP2"         USE_NGHTTP2)
 curl_add_if("HTTP3"         USE_NGTCP2 OR USE_QUICHE OR USE_MSH3 OR USE_OPENSSL_QUIC)
 curl_add_if("MultiSSL"      CURL_WITH_MULTI_SSL)
-curl_add_if("HTTPS-proxy"   _ssl_enabled AND (USE_OPENSSL OR USE_GNUTLS
+curl_add_if("HTTPS-proxy"   NOT CURL_DISABLE_PROXY AND _ssl_enabled AND (USE_OPENSSL OR USE_GNUTLS
                             OR USE_SCHANNEL OR USE_RUSTLS OR USE_BEARSSL OR
                             USE_MBEDTLS OR USE_SECTRANSP OR
-                            (USE_WOLFSSL AND HAVE_WOLFSSL_BIO)))
+                            (USE_WOLFSSL AND HAVE_WOLFSSL_BIO_NEW)))
 curl_add_if("Unicode"       ENABLE_UNICODE)
 curl_add_if("threadsafe"    HAVE_ATOMIC OR
                             (USE_THREADS_POSIX AND HAVE_PTHREAD_H) OR
@@ -2168,7 +2201,7 @@ curl_add_if("PSL"           USE_LIBPSL)
 curl_add_if("CAcert"        CURL_CA_EMBED_SET)
 curl_add_if("SSLS-EXPORT"   _ssl_enabled AND USE_SSLS_EXPORT)
 if(_items)
-  if(NOT CMAKE_VERSION VERSION_LESS 3.13)
+  if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13)
     list(SORT _items CASE INSENSITIVE)
   else()
     list(SORT _items)
@@ -2182,7 +2215,7 @@ message(STATUS "Features: ${SUPPORT_FEATURES}")
 set(_items "")
 curl_add_if("Schannel"         _ssl_enabled AND USE_SCHANNEL)
 curl_add_if("${_openssl}"      _ssl_enabled AND USE_OPENSSL AND OPENSSL_VERSION VERSION_LESS 3.0.0)
-curl_add_if("${_openssl} v3+"  _ssl_enabled AND USE_OPENSSL AND NOT OPENSSL_VERSION VERSION_LESS 3.0.0)
+curl_add_if("${_openssl} v3+"  _ssl_enabled AND USE_OPENSSL AND OPENSSL_VERSION VERSION_GREATER_EQUAL 3.0.0)
 curl_add_if("Secure Transport" _ssl_enabled AND USE_SECTRANSP)
 curl_add_if("mbedTLS"          _ssl_enabled AND USE_MBEDTLS)
 curl_add_if("BearSSL"          _ssl_enabled AND USE_BEARSSL)
@@ -2191,7 +2224,7 @@ curl_add_if("GnuTLS"           _ssl_enabled AND USE_GNUTLS)
 curl_add_if("rustls"           _ssl_enabled AND USE_RUSTLS)
 
 if(_items)
-  if(NOT CMAKE_VERSION VERSION_LESS 3.13)
+  if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13)
     list(SORT _items CASE INSENSITIVE)
   else()
     list(SORT _items)
@@ -2269,7 +2302,7 @@ if(NOT CURL_DISABLE_INSTALL)
   endforeach()
 
   foreach(_libdir IN LISTS _custom_libdirs CURL_LIBDIRS)
-    if(NOT CMAKE_VERSION VERSION_LESS 3.20)
+    if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.20)
       cmake_path(SET _libdir NORMALIZE "${_libdir}")
     endif()
     list(FIND _sys_libdirs "${_libdir}" _libdir_index)
@@ -2306,7 +2339,7 @@ if(NOT CURL_DISABLE_INSTALL)
       get_filename_component(_libdir ${_lib} DIRECTORY)
       get_filename_component(_libname ${_lib} NAME_WE)
       if(_libname MATCHES "^lib")
-        if(NOT CMAKE_VERSION VERSION_LESS 3.20)
+        if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.20)
           cmake_path(SET _libdir NORMALIZE "${_libdir}")
         endif()
         list(FIND _sys_libdirs "${_libdir}" _libdir_index)
@@ -2435,6 +2468,7 @@ if(NOT CURL_DISABLE_INSTALL)
 
   # Consumed custom variables:
   #   CURLVERSION
+  #   LIB_NAME
   #   LIB_SELECTED
   #   TARGETS_EXPORT_NAME
   #   USE_OPENSSL OPENSSL_VERSION_MAJOR
@@ -2465,7 +2499,7 @@ if(NOT CURL_DISABLE_INSTALL)
       COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_BINARY_DIR}/CMake/cmake_uninstall.cmake")
   endif()
 
-  install(FILES "${PROJECT_SOURCE_DIR}/scripts/mk-ca-bundle.pl"
+  install(FILES "${PROJECT_SOURCE_DIR}/scripts/wcurl"
     DESTINATION ${CMAKE_INSTALL_BINDIR}
     PERMISSIONS
       OWNER_READ OWNER_WRITE OWNER_EXECUTE
diff --git a/extra/curl/curl-8.12.1/COPYING b/extra/curl/curl-8.14.1/COPYING
similarity index 100%
rename from extra/curl/curl-8.12.1/COPYING
rename to extra/curl/curl-8.14.1/COPYING
diff --git a/extra/curl/curl-8.12.1/Dockerfile b/extra/curl/curl-8.14.1/Dockerfile
similarity index 88%
rename from extra/curl/curl-8.12.1/Dockerfile
rename to extra/curl/curl-8.14.1/Dockerfile
index 84f0c9789385..0b971896fd3c 100644
--- a/extra/curl/curl-8.12.1/Dockerfile
+++ b/extra/curl/curl-8.14.1/Dockerfile
@@ -17,14 +17,14 @@
 #
 # or get into a shell in the build environment, for example
 #
-#   docker run --rm -it -u $(id -u):$(id -g) -v (pwd):/usr/src -w /usr/src curl/curl bash
+#   docker run --rm -it -u $(id -u):$(id -g) -v $(pwd):/usr/src -w /usr/src curl/curl bash
 #   $ autoreconf -fi
 #   $ ./configure --without-ssl --without-libpsl
 #   $ make
 #   $ ./scripts/maketgz 8.7.1
 
 # To update, get the latest digest e.g. from https://hub.docker.com/_/debian/tags
-FROM debian:bookworm-slim@sha256:b73bf02f32434c9be21adf83b9aedf33e731784d8d2dacbbd3ce5f4993f2a2de
+FROM debian:bookworm-slim@sha256:90522eeb7e5923ee2b871c639059537b30521272f10ca86fdbbbb2b75a8c40cd
 
 RUN apt-get update -qq && apt-get install -qq -y --no-install-recommends \
     build-essential make autoconf automake libtool git perl zip zlib1g-dev gawk && \
diff --git a/extra/curl/curl-8.12.1/Makefile.am b/extra/curl/curl-8.14.1/Makefile.am
similarity index 98%
rename from extra/curl/curl-8.12.1/Makefile.am
rename to extra/curl/curl-8.14.1/Makefile.am
index f69f0736105f..dbb0f0c6dddd 100644
--- a/extra/curl/curl-8.12.1/Makefile.am
+++ b/extra/curl/curl-8.14.1/Makefile.am
@@ -44,7 +44,6 @@ CMAKE_DIST =                                    \
  CMake/FindLibssh.cmake                         \
  CMake/FindLibssh2.cmake                        \
  CMake/FindLibuv.cmake                          \
- CMake/FindMbedTLS.cmake                        \
  CMake/FindMSH3.cmake                           \
  CMake/FindMbedTLS.cmake                        \
  CMake/FindNGHTTP2.cmake                        \
@@ -60,6 +59,7 @@ CMAKE_DIST =                                    \
  CMake/OtherTests.cmake                         \
  CMake/PickyWarnings.cmake                      \
  CMake/Utilities.cmake                          \
+ CMake/unix-cache.cmake                         \
  CMake/win32-cache.cmake                        \
  CMakeLists.txt                                 \
  tests/cmake/CMakeLists.txt                     \
@@ -221,3 +221,6 @@ checksrc:
 tidy:
 	(cd src && $(MAKE) tidy)
 	(cd lib && $(MAKE) tidy)
+
+clean-local:
+	(cd tests && $(MAKE) clean)
diff --git a/extra/curl/curl-8.12.1/Makefile.in b/extra/curl/curl-8.14.1/Makefile.in
similarity index 98%
rename from extra/curl/curl-8.12.1/Makefile.in
rename to extra/curl/curl-8.14.1/Makefile.in
index e26fa36b7535..536aafe5c2c0 100644
--- a/extra/curl/curl-8.12.1/Makefile.in
+++ b/extra/curl/curl-8.14.1/Makefile.in
@@ -462,7 +462,6 @@ CMAKE_DIST = \
  CMake/FindLibssh.cmake                         \
  CMake/FindLibssh2.cmake                        \
  CMake/FindLibuv.cmake                          \
- CMake/FindMbedTLS.cmake                        \
  CMake/FindMSH3.cmake                           \
  CMake/FindMbedTLS.cmake                        \
  CMake/FindNGHTTP2.cmake                        \
@@ -478,6 +477,7 @@ CMAKE_DIST = \
  CMake/OtherTests.cmake                         \
  CMake/PickyWarnings.cmake                      \
  CMake/Utilities.cmake                          \
+ CMake/unix-cache.cmake                         \
  CMake/win32-cache.cmake                        \
  CMakeLists.txt                                 \
  tests/cmake/CMakeLists.txt                     \
@@ -963,7 +963,7 @@ maintainer-clean-generic:
 	@echo "it deletes files that may require special tools to rebuild."
 clean: clean-recursive
 
-clean-am: clean-generic clean-libtool mostlyclean-am
+clean-am: clean-generic clean-libtool clean-local mostlyclean-am
 
 distclean: distclean-recursive
 	-rm -f $(am__CONFIG_DISTCLEAN_FILES)
@@ -1038,10 +1038,10 @@ uninstall-am: uninstall-binSCRIPTS uninstall-pkgconfigDATA
 
 .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
 	am--refresh check check-am clean clean-cscope clean-generic \
-	clean-libtool cscope cscopelist-am ctags ctags-am dist \
-	dist-all dist-bzip2 dist-gzip dist-hook dist-lzip dist-shar \
-	dist-tarZ dist-xz dist-zip dist-zstd distcheck distclean \
-	distclean-generic distclean-libtool distclean-tags \
+	clean-libtool clean-local cscope cscopelist-am ctags ctags-am \
+	dist dist-all dist-bzip2 dist-gzip dist-hook dist-lzip \
+	dist-shar dist-tarZ dist-xz dist-zip dist-zstd distcheck \
+	distclean distclean-generic distclean-libtool distclean-tags \
 	distcleancheck distdir distuninstallcheck dvi dvi-am html \
 	html-am info info-am install install-am install-binSCRIPTS \
 	install-data install-data-am install-data-hook install-dvi \
@@ -1175,6 +1175,9 @@ tidy:
 	(cd src && $(MAKE) tidy)
 	(cd lib && $(MAKE) tidy)
 
+clean-local:
+	(cd tests && $(MAKE) clean)
+
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
 .NOEXPORT:
diff --git a/extra/curl/curl-8.12.1/README b/extra/curl/curl-8.14.1/README
similarity index 100%
rename from extra/curl/curl-8.12.1/README
rename to extra/curl/curl-8.14.1/README
diff --git a/extra/curl/curl-8.14.1/RELEASE-NOTES b/extra/curl/curl-8.14.1/RELEASE-NOTES
new file mode 100644
index 000000000000..44078c662190
--- /dev/null
+++ b/extra/curl/curl-8.14.1/RELEASE-NOTES
@@ -0,0 +1,114 @@
+curl and libcurl 8.14.1
+
+ Public curl releases:         268
+ Command line options:         269
+ curl_easy_setopt() options:   308
+ Public functions in libcurl:  96
+ Contributors:                 3431
+
+This release includes the following changes:
+
+
+This release includes the following bugfixes:
+
+ o asyn-thrdd: fix cleanup when RR fails due to OOM [20]
+ o autotools: recognize more Linux targets when setting `-D_GNU_SOURCE` [35]
+ o BUG-BOUNTY.md. mention the medium bounty amount in 2025 [5]
+ o cmake: fix missed version number for multi-pkg-config detections [14]
+ o cmdline-docs: mention HTTP resumed uploads to be shaky [21]
+ o curl: make -N handled correctly [34]
+ o curl: upload from '.' fix [9]
+ o dllmain: exclude from Cygwin builds [32]
+ o docs/tests: remove mention of hyper [23]
+ o docs: fix typos [12]
+ o ftp: fix teardown of DATA connection in done [31]
+ o http: fail early when rewind of input failed when following redirects [2]
+ o license: update some copyright links to curl.se [24]
+ o memanalyze.pl: fix getaddrinfo/freeaddrinfo checks [25]
+ o misc: fix spelling [15]
+ o misc: we write *an* IPv6 address [10]
+ o multi: fix add_handle resizing [3]
+ o spelling: 'a' vs 'an' [8]
+ o spelling: call it null-terminate consistently [6]
+ o test1510: fix expectation [19]
+ o tests: await portfile to be complete [1]
+ o tests: fix checks for https-mtls proto [30]
+ o tests: improve server start reliability [18]
+ o tests: move test docs into /docs [16]
+ o tests: re-enable 1510, document heimdal memleak [22]
+ o tests: test mtls also w/ clientAuth EKU only [28]
+ o tests: test mtls with --insecure [29]
+ o tls BIOs: handle BIO_CTRL_EOF correctly [33]
+ o tool_getparam: make --no-anyauth not be accepted [13]
+ o tool_getparam: refactored, simplified [4]
+ o tool_getparam: remove two nextarg NULL checks [11]
+ o VULN-DISCLOSURE-POLICY.md: the distros list wants <= 7 days embargo [26]
+ o wolfssl: fix sending of early data [7]
+ o ws: handle blocked sends better [27]
+ o ws: tests and fixes [17]
+
+This release includes the following known bugs:
+
+ See https://curl.se/docs/knownbugs.html
+
+For all changes ever done in curl:
+
+ See https://curl.se/changes.html
+
+Planned upcoming removals include:
+
+ o Support for the msh3 HTTP/3 backend
+ o Supporting curl builds using VS2008
+ o The Secure Transport and BearSSL TLS backends
+ o The winbuild build system
+ o Windows CE support
+
+ See https://curl.se/dev/deprecate.html
+
+This release would not have looked like this without help, code, reports and
+advice from friends like these:
+
+  Calvin Ruocco, Dan Fandrich, Daniel Stenberg, denandz on github,
+  Ethan Everett, Jacob Mealey, Jeremy Drake, Jeroen Ooms, John Bampton,
+  Kadambini Nema, Michael Kaufmann, Rasmus Melchior Jacobsen, Ray Satiro,
+  Samuel Henrique, Stefan Eissing, Viktor Szakats, x-xiang on github,
+  Yedaya Katsman, Yuyi Wang, z2_
+  (20 contributors)
+
+References to bug reports and discussions on issues:
+
+ [1] = https://curl.se/bug/?i=17492
+ [2] = https://curl.se/bug/?i=17472
+ [3] = https://curl.se/bug/?i=17473
+ [4] = https://curl.se/bug/?i=17448
+ [5] = https://curl.se/bug/?i=17470
+ [6] = https://curl.se/bug/?i=17489
+ [7] = https://curl.se/bug/?i=17481
+ [8] = https://curl.se/bug/?i=17487
+ [9] = https://curl.se/bug/?i=17513
+ [10] = https://curl.se/bug/?i=17484
+ [11] = https://curl.se/bug/?i=17483
+ [12] = https://curl.se/bug/?i=17480
+ [13] = https://curl.se/bug/?i=17508
+ [14] = https://curl.se/bug/?i=16980
+ [15] = https://curl.se/bug/?i=17478
+ [16] = https://curl.se/bug/?i=17463
+ [17] = https://curl.se/bug/?i=17136
+ [18] = https://curl.se/bug/?i=17516
+ [19] = https://curl.se/bug/?i=17515
+ [20] = https://curl.se/bug/?i=17507
+ [21] = https://curl.se/bug/?i=17521
+ [22] = https://curl.se/bug/?i=17462
+ [23] = https://curl.se/bug/?i=17500
+ [24] = https://curl.se/bug/?i=17502
+ [25] = https://curl.se/bug/?i=17503
+ [26] = https://curl.se/bug/?i=17497
+ [27] = https://curl.se/bug/?i=17496
+ [28] = https://curl.se/bug/?i=17493
+ [29] = https://curl.se/bug/?i=17493
+ [30] = https://curl.se/bug/?i=17493
+ [31] = https://curl.se/bug/?i=17482
+ [32] = https://curl.se/bug/?i=17262
+ [33] = https://curl.se/bug/?i=17471
+ [34] = https://curl.se/bug/?i=17527
+ [35] = https://curl.se/bug/?i=17512
diff --git a/extra/curl/curl-8.12.1/acinclude.m4 b/extra/curl/curl-8.14.1/acinclude.m4
similarity index 90%
rename from extra/curl/curl-8.12.1/acinclude.m4
rename to extra/curl/curl-8.14.1/acinclude.m4
index b550761a009f..53c302cc03a7 100644
--- a/extra/curl/curl-8.12.1/acinclude.m4
+++ b/extra/curl/curl-8.14.1/acinclude.m4
@@ -48,7 +48,7 @@ AC_DEFUN([CURL_CHECK_DEF], [
   tmp_exp=""
   AC_PREPROC_IFELSE([
     AC_LANG_SOURCE(
-ifelse($2,,,[$2])[[
+    ifelse($2,,,[$2])[[
       #ifdef $1
       CURL_DEF_TOKEN $1
       #endif
@@ -88,14 +88,13 @@ AC_DEFUN([CURL_CHECK_DEF_CC], [
   ifelse($3,,[AC_MSG_CHECKING([for compiler definition of $1])])
   AC_COMPILE_IFELSE([
     AC_LANG_SOURCE(
-ifelse($2,,,[$2])[[
+    ifelse($2,,,[$2])[[
       int main(void)
       {
-      #ifdef $1
-        return 0;
-      #else
+      #ifndef $1
         #error force compilation error
       #endif
+        return 0;
       }
     ]])
   ],[
@@ -126,12 +125,11 @@ AC_DEFUN([CURL_CHECK_LIB_XNET], [
       int main(void)
       {
       #if defined(__hpux) && defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE >= 600)
-        return 0;
       #elif defined(__hpux) && defined(_XOPEN_SOURCE_EXTENDED)
-        return 0;
       #else
         #error force compilation error
       #endif
+        return 0;
       }
     ]])
   ],[
@@ -180,7 +178,8 @@ AC_DEFUN([CURL_CHECK_NATIVE_WINDOWS], [
       AC_LANG_PROGRAM([[
       ]],[[
         #ifdef _WIN32
-          int dummy=1;
+          int dummy = 1;
+          (void)dummy;
         #else
           #error Not a native Windows build target.
         #endif
@@ -306,6 +305,7 @@ AC_DEFUN([CURL_CHECK_HEADER_LDAP], [
       ]],[[
         LDAP *ldp = ldap_init("0.0.0.0", LDAP_PORT);
         int res = ldap_unbind(ldp);
+        (void)res;
       ]])
     ],[
       curl_cv_header_ldap_h="yes"
@@ -354,6 +354,7 @@ AC_DEFUN([CURL_CHECK_HEADER_LDAP_SSL], [
         #include <ldap_ssl.h>
       ]],[[
         LDAP *ldp = ldapssl_init("0.0.0.0", LDAPS_PORT, 1);
+        (void)ldp;
       ]])
     ],[
       curl_cv_header_ldap_ssl_h="yes"
@@ -433,6 +434,7 @@ AC_DEFUN([CURL_CHECK_LIBS_WINLDAP], [
           LDAP *ldp = ldap_init("0.0.0.0", LDAP_PORT);
           ULONG res = ldap_unbind(ldp);
           ber_free(bep, 1);
+          (void)res;
         ]])
       ],[
         curl_cv_ldap_LIBS="$x_nlibs"
@@ -543,6 +545,7 @@ AC_DEFUN([CURL_CHECK_LIBS_LDAP], [
           LDAP *ldp = ldap_init("0.0.0.0", LDAP_PORT);
           int res = ldap_unbind(ldp);
           ber_free(bep, 1);
+          (void)res;
         ]])
       ],[
         curl_cv_ldap_LIBS="$x_nlibs"
@@ -686,7 +689,8 @@ AC_DEFUN([CURL_CHECK_FUNC_SEND], [
       #endif
       #endif
     ]],[[
-      send(0, 0, 0, 0);
+      char s[] = "";
+      send(0, (void *)s, 0, 0);
     ]])
   ],[
     AC_MSG_RESULT([yes])
@@ -729,7 +733,8 @@ AC_DEFUN([CURL_CHECK_MSG_NOSIGNAL], [
         #endif
         #endif
       ]],[[
-        int flag=MSG_NOSIGNAL;
+        int flag = MSG_NOSIGNAL;
+        (void)flag;
       ]])
     ],[
       curl_cv_msg_nosignal="yes"
@@ -777,6 +782,7 @@ AC_DEFUN([CURL_CHECK_STRUCT_TIMEVAL], [
         struct timeval ts;
         ts.tv_sec  = 0;
         ts.tv_usec = 0;
+        (void)ts;
       ]])
     ],[
       curl_cv_struct_timeval="yes"
@@ -793,84 +799,6 @@ AC_DEFUN([CURL_CHECK_STRUCT_TIMEVAL], [
 ])
 
 
-dnl TYPE_IN_ADDR_T
-dnl -------------------------------------------------
-dnl Check for in_addr_t: it is used to receive the return code of inet_addr()
-dnl and a few other things.
-
-AC_DEFUN([TYPE_IN_ADDR_T], [
-  AC_CHECK_TYPE([in_addr_t], ,[
-    dnl in_addr_t not available
-    AC_CACHE_CHECK([for in_addr_t equivalent],
-      [curl_cv_in_addr_t_equiv], [
-      curl_cv_in_addr_t_equiv="unknown"
-      for t in "unsigned long" int size_t unsigned long; do
-        if test "$curl_cv_in_addr_t_equiv" = "unknown"; then
-          AC_LINK_IFELSE([
-            AC_LANG_PROGRAM([[
-              #undef inline
-              #ifdef _WIN32
-              #ifndef WIN32_LEAN_AND_MEAN
-              #define WIN32_LEAN_AND_MEAN
-              #endif
-              #include <winsock2.h>
-              #else
-              #ifdef HAVE_SYS_TYPES_H
-              #include <sys/types.h>
-              #endif
-              #ifdef HAVE_SYS_SOCKET_H
-              #include <sys/socket.h>
-              #endif
-              #ifdef HAVE_NETINET_IN_H
-              #include <netinet/in.h>
-              #endif
-              #ifdef HAVE_ARPA_INET_H
-              #include <arpa/inet.h>
-              #endif
-              #endif
-            ]],[[
-              $t data = inet_addr ("1.2.3.4");
-            ]])
-          ],[
-            curl_cv_in_addr_t_equiv="$t"
-          ])
-        fi
-      done
-    ])
-    case "$curl_cv_in_addr_t_equiv" in
-      unknown)
-        AC_MSG_ERROR([Cannot find a type to use in place of in_addr_t])
-        ;;
-      *)
-        AC_DEFINE_UNQUOTED(in_addr_t, $curl_cv_in_addr_t_equiv,
-          [Type to use in place of in_addr_t when system does not provide it.])
-        ;;
-    esac
-  ],[
-    #undef inline
-    #ifdef _WIN32
-    #ifndef WIN32_LEAN_AND_MEAN
-    #define WIN32_LEAN_AND_MEAN
-    #endif
-    #include <winsock2.h>
-    #else
-    #ifdef HAVE_SYS_TYPES_H
-    #include <sys/types.h>
-    #endif
-    #ifdef HAVE_SYS_SOCKET_H
-    #include <sys/socket.h>
-    #endif
-    #ifdef HAVE_NETINET_IN_H
-    #include <netinet/in.h>
-    #endif
-    #ifdef HAVE_ARPA_INET_H
-    #include <arpa/inet.h>
-    #endif
-    #endif
-  ])
-])
-
-
 dnl CURL_CHECK_FUNC_CLOCK_GETTIME_MONOTONIC
 dnl -------------------------------------------------
 dnl Check if monotonic clock_gettime is available.
@@ -879,28 +807,29 @@ AC_DEFUN([CURL_CHECK_FUNC_CLOCK_GETTIME_MONOTONIC], [
   AC_CHECK_HEADERS(sys/types.h sys/time.h)
   AC_MSG_CHECKING([for monotonic clock_gettime])
   #
-  if test "x$dontwant_rt" = "xno" ; then
-    AC_COMPILE_IFELSE([
-      AC_LANG_PROGRAM([[
-        #ifdef HAVE_SYS_TYPES_H
-        #include <sys/types.h>
-        #endif
-        #ifdef HAVE_SYS_TIME_H
-        #include <sys/time.h>
-        #endif
-        #include <time.h>
-      ]],[[
-        struct timespec ts;
-        (void)clock_gettime(CLOCK_MONOTONIC, &ts);
-      ]])
-    ],[
-      AC_MSG_RESULT([yes])
-      curl_func_clock_gettime="yes"
-    ],[
-      AC_MSG_RESULT([no])
-      curl_func_clock_gettime="no"
-    ])
-  fi
+
+  AC_COMPILE_IFELSE([
+    AC_LANG_PROGRAM([[
+      #ifdef HAVE_SYS_TYPES_H
+      #include <sys/types.h>
+      #endif
+      #ifdef HAVE_SYS_TIME_H
+      #include <sys/time.h>
+      #endif
+      #include <time.h>
+    ]],[[
+      struct timespec ts;
+      (void)clock_gettime(CLOCK_MONOTONIC, &ts);
+      (void)ts;
+    ]])
+  ],[
+    AC_MSG_RESULT([yes])
+    curl_func_clock_gettime="yes"
+  ],[
+    AC_MSG_RESULT([no])
+    curl_func_clock_gettime="no"
+  ])
+
   dnl Definition of HAVE_CLOCK_GETTIME_MONOTONIC is intentionally postponed
   dnl until library linking and run-time checks for clock_gettime succeed.
 ])
@@ -913,28 +842,27 @@ AC_DEFUN([CURL_CHECK_FUNC_CLOCK_GETTIME_MONOTONIC_RAW], [
   AC_CHECK_HEADERS(sys/types.h sys/time.h)
   AC_MSG_CHECKING([for raw monotonic clock_gettime])
   #
-  if test "x$dontwant_rt" = "xno" ; then
-    AC_COMPILE_IFELSE([
-      AC_LANG_PROGRAM([[
-        #ifdef HAVE_SYS_TYPES_H
-        #include <sys/types.h>
-        #endif
-        #ifdef HAVE_SYS_TIME_H
-        #include <sys/time.h>
-        #endif
-        #include <time.h>
-      ]],[[
-        struct timespec ts;
-        (void)clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
-      ]])
-    ],[
-      AC_MSG_RESULT([yes])
-      AC_DEFINE_UNQUOTED(HAVE_CLOCK_GETTIME_MONOTONIC_RAW, 1,
-        [Define to 1 if you have the clock_gettime function and raw monotonic timer.])
-    ],[
-      AC_MSG_RESULT([no])
-    ])
-  fi
+  AC_COMPILE_IFELSE([
+    AC_LANG_PROGRAM([[
+      #ifdef HAVE_SYS_TYPES_H
+      #include <sys/types.h>
+      #endif
+      #ifdef HAVE_SYS_TIME_H
+      #include <sys/time.h>
+      #endif
+      #include <time.h>
+    ]],[[
+      struct timespec ts;
+      (void)clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
+      (void)ts;
+    ]])
+  ],[
+    AC_MSG_RESULT([yes])
+    AC_DEFINE_UNQUOTED(HAVE_CLOCK_GETTIME_MONOTONIC_RAW, 1,
+      [Define to 1 if you have the clock_gettime function and raw monotonic timer.])
+  ],[
+    AC_MSG_RESULT([no])
+  ])
 ])
 
 
@@ -972,6 +900,7 @@ AC_DEFUN([CURL_CHECK_LIBS_CLOCK_GETTIME_MONOTONIC], [
           ]],[[
             struct timespec ts;
             (void)clock_gettime(CLOCK_MONOTONIC, &ts);
+            (void)ts;
           ]])
         ],[
           curl_cv_gclk_LIBS="$x_xlibs"
@@ -992,13 +921,18 @@ AC_DEFUN([CURL_CHECK_LIBS_CLOCK_GETTIME_MONOTONIC], [
         curl_func_clock_gettime="yes"
         ;;
       *)
-        if test -z "$curl_cv_save_LIBS"; then
-          LIBS="$curl_cv_gclk_LIBS"
+        if test "x$dontwant_rt" = "xyes" ; then
+          AC_MSG_WARN([needs -lrt but asked not to use it, HAVE_CLOCK_GETTIME_MONOTONIC will not be defined])
+          curl_func_clock_gettime="no"
         else
-          LIBS="$curl_cv_gclk_LIBS $curl_cv_save_LIBS"
+          if test -z "$curl_cv_save_LIBS"; then
+            LIBS="$curl_cv_gclk_LIBS"
+          else
+            LIBS="$curl_cv_gclk_LIBS $curl_cv_save_LIBS"
+          fi
+          AC_MSG_RESULT([$curl_cv_gclk_LIBS])
+          curl_func_clock_gettime="yes"
         fi
-        AC_MSG_RESULT([$curl_cv_gclk_LIBS])
-        curl_func_clock_gettime="yes"
         ;;
     esac
     #
@@ -1018,10 +952,10 @@ AC_DEFUN([CURL_CHECK_LIBS_CLOCK_GETTIME_MONOTONIC], [
           #include <time.h>
         ]],[[
           struct timespec ts;
-          if (0 == clock_gettime(CLOCK_MONOTONIC, &ts))
-            exit(0);
-          else
-            exit(1);
+          if(0 == clock_gettime(CLOCK_MONOTONIC, &ts))
+            return 0;
+          (void)ts;
+          return 1;
         ]])
       ],[
         AC_MSG_RESULT([yes])
@@ -1168,7 +1102,7 @@ AC_DEFUN([CURL_VERIFY_RUNTIMELIBS], [
     dnl point also is available run-time!
     AC_MSG_CHECKING([run-time libs availability])
     CURL_RUN_IFELSE([
-      int main()
+      int main(void)
       {
         return 0;
       }
@@ -1380,16 +1314,13 @@ AC_DEFUN([CURL_CHECK_WIN32_LARGEFILE], [
   AC_REQUIRE([CURL_CHECK_NATIVE_WINDOWS])dnl
   if test "$curl_cv_native_windows" = 'yes'; then
     AC_MSG_CHECKING([whether build target supports Win32 large files])
-    case $host_os in
-      mingw32ce*|cegcc*)
-        dnl Windows CE does not support large files
-        curl_win32_has_largefile='no'
-        ;;
-      *)
-        dnl All mingw-w64 versions support large files
-        curl_win32_has_largefile='yes'
-        ;;
-    esac
+    if test "$curl_cv_wince" = 'yes'; then
+      dnl Windows CE does not support large files
+      curl_win32_has_largefile='no'
+    else
+      dnl All mingw-w64 versions support large files
+      curl_win32_has_largefile='yes'
+    fi
     case "$curl_win32_has_largefile" in
       yes)
         if test x"$enable_largefile" = 'xno'; then
@@ -1523,9 +1454,9 @@ dnl Save build info for test runner to pick up and log
 
 AC_DEFUN([CURL_PREPARE_BUILDINFO], [
   curl_pflags=""
-  case $host in
-    *-apple-*) curl_pflags="${curl_pflags} APPLE";;
-  esac
+  if test "$curl_cv_apple" = 'yes'; then
+    curl_pflags="${curl_pflags} APPLE"
+  fi
   case $host in
     *-*-*bsd*|*-*-aix*|*-*-hpux*|*-*-interix*|*-*-irix*|*-*-linux*|*-*-solaris*|*-*-sunos*|*-apple-*|*-*-cygwin*|*-*-msys*)
       curl_pflags="${curl_pflags} UNIX";;
@@ -1546,14 +1477,24 @@ AC_DEFUN([CURL_PREPARE_BUILDINFO], [
   if test "$curl_cv_native_windows" = 'yes'; then
     curl_pflags="${curl_pflags} WIN32"
   fi
+  if test "$curl_cv_wince" = 'yes'; then
+    curl_pflags="${curl_pflags} WINCE"
+  fi
   if test "$curl_cv_winuwp" = 'yes'; then
     curl_pflags="${curl_pflags} UWP"
   fi
+  case $host in
+    *-*-*bsd*|*-*-aix*|*-*-hpux*|*-*-interix*|*-*-irix*|*-*-linux*|*-*-solaris*|*-*-sunos*|*-apple-*|*-*-cygwin*|*-*-msys*)
+      curl_pflags="${curl_pflags} UNIX";;
+  esac
+  case $host in
+    *-*-*bsd*)
+      curl_pflags="${curl_pflags} BSD";;
+  esac
   if test "$curl_cv_cygwin" = 'yes'; then
     curl_pflags="${curl_pflags} CYGWIN"
   fi
   case $host_os in
-    msys*) curl_pflags="${curl_pflags} MSYS";;
     msdos*) curl_pflags="${curl_pflags} DOS";;
     amiga*) curl_pflags="${curl_pflags} AMIGA";;
   esac
@@ -1639,27 +1580,13 @@ dnl with very low deployment targets.
 dnl
 
 AC_DEFUN([CURL_DARWIN_CFLAGS], [
-
-  tst_cflags="no"
-  case $host in
-    *-apple-*)
-      tst_cflags="yes"
-      ;;
-  esac
-
-  AC_MSG_CHECKING([for good-to-use Darwin CFLAGS])
-  AC_MSG_RESULT([$tst_cflags]);
-
-  if test "$tst_cflags" = "yes"; then
-    old_CFLAGS=$CFLAGS
-    CFLAGS="$CFLAGS -Werror=partial-availability"
-    AC_MSG_CHECKING([whether $CC accepts -Werror=partial-availability])
-    AC_COMPILE_IFELSE([AC_LANG_PROGRAM()],
-      [AC_MSG_RESULT([yes])],
-      [AC_MSG_RESULT([no])
-      CFLAGS=$old_CFLAGS])
-  fi
-
+  old_CFLAGS=$CFLAGS
+  CFLAGS="$CFLAGS -Werror=partial-availability"
+  AC_MSG_CHECKING([whether $CC accepts -Werror=partial-availability])
+  AC_COMPILE_IFELSE([AC_LANG_PROGRAM()],
+    [AC_MSG_RESULT([yes])],
+    [AC_MSG_RESULT([no])
+    CFLAGS=$old_CFLAGS])
 ])
 
 
diff --git a/extra/curl/curl-8.12.1/aclocal.m4 b/extra/curl/curl-8.14.1/aclocal.m4
similarity index 100%
rename from extra/curl/curl-8.12.1/aclocal.m4
rename to extra/curl/curl-8.14.1/aclocal.m4
diff --git a/extra/curl/curl-8.12.1/compile b/extra/curl/curl-8.14.1/compile
similarity index 100%
rename from extra/curl/curl-8.12.1/compile
rename to extra/curl/curl-8.14.1/compile
diff --git a/extra/curl/curl-8.12.1/configure.ac b/extra/curl/curl-8.14.1/configure.ac
similarity index 94%
rename from extra/curl/curl-8.12.1/configure.ac
rename to extra/curl/curl-8.14.1/configure.ac
index 45dba219b9a5..9a5dd60f6cf2 100644
--- a/extra/curl/curl-8.12.1/configure.ac
+++ b/extra/curl/curl-8.14.1/configure.ac
@@ -39,6 +39,7 @@ terms of the curl license; see COPYING for more details])
 
 AC_CONFIG_SRCDIR([lib/urldata.h])
 AC_CONFIG_HEADERS(lib/curl_config.h)
+AH_TOP([/* !checksrc! disable COPYRIGHT all */])
 AC_CONFIG_MACRO_DIR([m4])
 AM_MAINTAINER_MODE
 m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
@@ -49,6 +50,7 @@ CURL_CHECK_OPTION_OPTIMIZE
 CURL_CHECK_OPTION_WARNINGS
 CURL_CHECK_OPTION_WERROR
 CURL_CHECK_OPTION_CURLDEBUG
+AM_CONDITIONAL(CURLDEBUG, test x$want_curldebug = xyes)
 CURL_CHECK_OPTION_SYMBOL_HIDING
 CURL_CHECK_OPTION_ARES
 CURL_CHECK_OPTION_RT
@@ -150,7 +152,7 @@ dnl initialize all the info variables
     curl_gss_msg="no      (--with-gssapi)"
   curl_gsasl_msg="no      (--with-gsasl)"
 curl_tls_srp_msg="no      (--enable-tls-srp)"
-    curl_res_msg="default (--enable-ares / --enable-threaded-resolver)"
+    curl_res_msg="blocking (--enable-ares / --enable-threaded-resolver)"
    curl_ipv6_msg="no      (--enable-ipv6)"
 curl_unix_sockets_msg="no      (--enable-unix-sockets)"
     curl_idn_msg="no      (--with-{libidn2,winidn})"
@@ -298,27 +300,39 @@ TEST_NGHTTPX=nghttpx
 AC_ARG_WITH(test-nghttpx,dnl
 AS_HELP_STRING([--with-test-nghttpx=PATH],[where to find nghttpx for testing]),
   TEST_NGHTTPX=$withval
-  if test X"$OPT_TEST_NGHTTPX" = "Xno"; then
+  if test X"$TEST_NGHTTPX" = "Xno"; then
     TEST_NGHTTPX=""
   fi
 )
 AC_SUBST(TEST_NGHTTPX)
 
-CADDY=/usr/bin/caddy
+if test -x /usr/bin/caddy; then
+  CADDY=/usr/bin/caddy
+elif test -x /usr/local/bin/caddy; then
+  CADDY=/usr/local/bin/caddy
+elif test -x "`brew --prefix 2>/dev/null`/bin/caddy"; then
+  CADDY=`brew --prefix`/bin/caddy
+fi
 AC_ARG_WITH(test-caddy,dnl
 AS_HELP_STRING([--with-test-caddy=PATH],[where to find caddy for testing]),
   CADDY=$withval
-  if test X"$OPT_CADDY" = "Xno"; then
+  if test X"$CADDY" = "Xno"; then
     CADDY=""
   fi
 )
 AC_SUBST(CADDY)
 
-VSFTPD=/usr/sbin/vsftpd
+if test -x /usr/sbin/vsftpd; then
+  VSFTPD=/usr/sbin/vsftpd
+elif test -x /usr/local/sbin/vsftpd; then
+  VSFTPD=/usr/local/sbin/vsftpd
+elif test -x "`brew --prefix 2>/dev/null`/sbin/vsftpd"; then
+  VSFTPD=`brew --prefix`/sbin/vsftpd
+fi
 AC_ARG_WITH(test-vsftpd,dnl
 AS_HELP_STRING([--with-test-vsftpd=PATH],[where to find vsftpd for testing]),
   VSFTPD=$withval
-  if test X"$OPT_VSFTPD" = "Xno"; then
+  if test X"$VSFTPD" = "Xno"; then
     VSFTPD=""
   fi
 )
@@ -440,6 +454,8 @@ XC_LIBTOOL
 
 LT_LANG([Windows Resource])
 
+AM_CONDITIONAL(NOT_CURL_CI, test -z "$CURL_CI")
+
 #
 # Automake conditionals based on libtool related checks
 #
@@ -493,8 +509,12 @@ dnl **********************************************************************
 CURL_CHECK_COMPILER
 CURL_CHECK_NATIVE_WINDOWS
 
+curl_cv_wince='no'
 curl_cv_winuwp='no'
 if test "$curl_cv_native_windows" = "yes"; then
+  case $host_os in
+    mingw32ce*) curl_cv_wince='yes';;
+  esac
   case "$CPPFLAGS" in
     *-DWINSTORECOMPAT*) curl_cv_winuwp='yes';;
   esac
@@ -567,22 +587,34 @@ case $host_os in
     ;;
 esac
 
-# In order to detect support of sendmmsg(), we need to escape the POSIX
+AM_CONDITIONAL(BUILD_UNITTESTS, test x$supports_unittests = xyes)
+
+# In order to detect support of sendmmsg() and accept4(), we need to escape the POSIX
 # jail by defining _GNU_SOURCE or <sys/socket.h> will not expose it.
 case $host_os in
-  linux*)
+  *linux*|cygwin*|msys*)
     CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE"
     ;;
 esac
 
-dnl Build unit tests when option --enable-debug is given.
-if test "x$want_debug" = "xyes" &&
-   test "x$supports_unittests" = "xyes"; then
-  want_unittests=yes
-else
-  want_unittests=no
-fi
-AM_CONDITIONAL(BUILD_UNITTESTS, test x$want_unittests = xyes)
+dnl Apply curl debug options to test servers
+OPT_SERVER_DEBUG="default"
+AC_ARG_ENABLE(server-debug,
+AS_HELP_STRING([--enable-server-debug],[Enable debug options for test servers])
+AS_HELP_STRING([--disable-server-debug],[Disable debug options for test servers]),
+OPT_SERVER_DEBUG=$enableval)
+case "$OPT_SERVER_DEBUG" in
+  no)
+    dnl --disable-server-debug option used
+    want_server_debug="no"
+    ;;
+  *)
+    dnl --enable-server-debug option used or not specified
+    want_server_debug="no"
+    ;;
+esac
+AC_MSG_RESULT([$want_server_debug])
+AM_CONDITIONAL(ENABLE_SERVER_DEBUG, test x$want_server_debug = xyes)
 
 dnl **********************************************************************
 dnl Compilation based checks should not be done before this point.
@@ -591,14 +623,16 @@ dnl **********************************************************************
 CURL_CHECK_WIN32_LARGEFILE
 CURL_CHECK_WIN32_CRYPTO
 
-CURL_DARWIN_CFLAGS
-
+curl_cv_apple='no'
 case $host in
-  *-apple-*)
-    CURL_SUPPORTS_BUILTIN_AVAILABLE
-    ;;
+  *-apple-*) curl_cv_apple='yes';;
 esac
 
+if test "$curl_cv_apple" = 'yes'; then
+  CURL_DARWIN_CFLAGS
+  CURL_SUPPORTS_BUILTIN_AVAILABLE
+fi
+
 curl_cv_cygwin='no'
 case $host_os in
   cygwin*|msys*) curl_cv_cygwin='yes';;
@@ -881,7 +915,7 @@ AS_HELP_STRING([--disable-telnet],[Disable TELNET support]),
     AC_MSG_RESULT(yes)
 )
 
-if test "$curl_cv_winuwp" = 'yes'; then
+if test "$curl_cv_winuwp" = 'yes' -o "$curl_cv_wince" = 'yes'; then
   AC_DEFINE(CURL_DISABLE_TELNET, 1, [to disable TELNET])
   CURL_DISABLE_TELNET=1
 fi
@@ -1159,11 +1193,42 @@ if test "$HAVE_GETHOSTBYNAME" != "1"; then
   ])
 fi
 
+if test "$HAVE_GETHOSTBYNAME" != "1"; then
+  if test "$curl_cv_wince" = 'yes'; then
+    dnl This is for Windows CE systems
+    winsock_LIB="-lws2"
+    if test ! -z "$winsock_LIB"; then
+      my_ac_save_LIBS=$LIBS
+      LIBS="$winsock_LIB $LIBS"
+      AC_MSG_CHECKING([for gethostbyname in $winsock_LIB])
+      AC_LINK_IFELSE([
+        AC_LANG_PROGRAM([[
+          #ifdef _WIN32
+          #ifndef WIN32_LEAN_AND_MEAN
+          #define WIN32_LEAN_AND_MEAN
+          #endif
+          #include <winsock2.h>
+          #endif
+        ]],[[
+          gethostbyname("localhost");
+        ]])
+      ],[
+        AC_MSG_RESULT([yes])
+        HAVE_GETHOSTBYNAME="1"
+      ],[
+        AC_MSG_RESULT([no])
+        winsock_LIB=""
+        LIBS=$my_ac_save_LIBS
+      ])
+    fi
+  fi
+fi
+
 # In UWP mode gethostbyname gets detected via the core libs, but some
 # code (in6addr_any) still need ws2_32, so let us detect and add it.
 if test "$HAVE_GETHOSTBYNAME" != "1" -o "$curl_cv_winuwp" = "yes"; then
-  dnl This is for Winsock systems
   if test "$curl_cv_native_windows" = "yes"; then
+    dnl This is for Winsock systems
     winsock_LIB="-lws2_32"
     if test ! -z "$winsock_LIB"; then
       my_ac_save_LIBS=$LIBS
@@ -1241,7 +1306,8 @@ if test "$HAVE_GETHOSTBYNAME" != "1" -o "${with_amissl+set}" = set; then
   struct Library *SocketBase = NULL;
   #endif
     ]],[[
-      gethostbyname("localhost");
+      unsigned char host[] = "localhost";
+      gethostbyname(host);
     ]])
   ],[
     AC_MSG_RESULT([yes])
@@ -1484,7 +1550,7 @@ if test X"$OPT_BROTLI" != Xno; then
         AC_MSG_NOTICE([Added $DIR_BROTLI to CURL_LIBRARY_PATH])
       fi
     fi
-    LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE libbrotlidec"
+    LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE libbrotlidec libbrotlicommon"
   else
     dnl no brotli, revert back to clean variables
     LDFLAGS=$CLEANLDFLAGS
@@ -1715,12 +1781,11 @@ AS_HELP_STRING([--disable-ipv6],[Disable IPv6 support]),
       #include <netinet/in6.h>
       #endif
       #endif
-
       int main(void)
       {
-        struct sockaddr_in6 s;
+        int s = (int)sizeof(struct sockaddr_in6);
         (void)s;
-        return socket(AF_INET6, SOCK_STREAM, 0) < 0;
+        return socket(AF_INET6, SOCK_STREAM, 0) > 0;
       }
     ]])
   ],
@@ -1731,6 +1796,10 @@ AS_HELP_STRING([--disable-ipv6],[Disable IPv6 support]),
   )
 )
 
+if test "$curl_cv_wince" = 'yes'; then
+  ipv6=no
+fi
+
 if test "$ipv6" = yes; then
   curl_ipv6_msg="enabled"
   AC_DEFINE(USE_IPV6, 1, [Define if you want to enable IPv6 support])
@@ -1752,6 +1821,7 @@ if test "$ipv6" = yes; then
     ]], [[
       struct sockaddr_in6 s;
       s.sin6_scope_id = 0;
+      (void)s;
     ]])
   ],[
     AC_MSG_RESULT([yes])
@@ -1788,6 +1858,9 @@ int main(int argc, char **argv)
 ],[
   curl_cv_writable_argv=cross
 ])
+if test "$curl_cv_writable_argv" = 'cross' -a "$curl_cv_apple" = 'yes'; then
+  curl_cv_writable_argv=yes
+fi
 case $curl_cv_writable_argv in
   yes)
     AC_DEFINE(HAVE_WRITABLE_ARGV, 1, [Define this symbol if your OS supports changing the contents of argv])
@@ -1933,51 +2006,48 @@ if test x"$want_gss" = xyes; then
     LIBS="-lgss $LIBS"
     link_pkgconfig=1
   elif test -z "$GSSAPI_LIB_DIR"; then
-    case $host in
-      *-apple-*)
-        LIBS="-lgssapi_krb5 -lresolv $LIBS"
-        ;;
-      *)
-        if test $GSSAPI_ROOT != "/usr"; then
-          CURL_CHECK_PKGCONFIG(mit-krb5-gssapi, $GSSAPI_ROOT/lib/pkgconfig)
-        else
-          CURL_CHECK_PKGCONFIG(mit-krb5-gssapi)
-        fi
-        if test -n "$host_alias" -a -f "$GSSAPI_ROOT/bin/$host_alias-krb5-config"; then
-          dnl krb5-config doesn't have --libs-only-L or similar, put everything
-          dnl into LIBS
-          gss_libs=`$GSSAPI_ROOT/bin/$host_alias-krb5-config --libs gssapi`
-          LIBS="$gss_libs $LIBS"
-        elif test "$PKGCONFIG" != "no"; then
-          gss_libs=`$PKGCONFIG --libs mit-krb5-gssapi`
-          LIBS="$gss_libs $LIBS"
-          link_pkgconfig=1
-        elif test -f "$KRB5CONFIG"; then
-          dnl krb5-config doesn't have --libs-only-L or similar, put everything
-          dnl into LIBS
-          gss_libs=`$KRB5CONFIG --libs gssapi`
-          LIBS="$gss_libs $LIBS"
-          link_pkgconfig=1
+    if test "$curl_cv_apple" = 'yes'; then
+      LIBS="-lgssapi_krb5 -lresolv $LIBS"
+    else
+      if test $GSSAPI_ROOT != "/usr"; then
+        CURL_CHECK_PKGCONFIG(mit-krb5-gssapi, $GSSAPI_ROOT/lib/pkgconfig)
+      else
+        CURL_CHECK_PKGCONFIG(mit-krb5-gssapi)
+      fi
+      if test -n "$host_alias" -a -f "$GSSAPI_ROOT/bin/$host_alias-krb5-config"; then
+        dnl krb5-config doesn't have --libs-only-L or similar, put everything
+        dnl into LIBS
+        gss_libs=`$GSSAPI_ROOT/bin/$host_alias-krb5-config --libs gssapi`
+        LIBS="$gss_libs $LIBS"
+      elif test "$PKGCONFIG" != "no"; then
+        gss_libs=`$PKGCONFIG --libs mit-krb5-gssapi`
+        LIBS="$gss_libs $LIBS"
+        link_pkgconfig=1
+      elif test -f "$KRB5CONFIG"; then
+        dnl krb5-config doesn't have --libs-only-L or similar, put everything
+        dnl into LIBS
+        gss_libs=`$KRB5CONFIG --libs gssapi`
+        LIBS="$gss_libs $LIBS"
+        link_pkgconfig=1
+      else
+        case $host in
+          *-hp-hpux*)
+            gss_libname="gss"
+            ;;
+          *)
+            gss_libname="gssapi"
+            ;;
+        esac
+
+        if test "$GSSAPI_ROOT" != "yes"; then
+          LDFLAGS="$LDFLAGS -L$GSSAPI_ROOT/lib$libsuff"
+          LDFLAGSPC="$LDFLAGSPC -L$GSSAPI_ROOT/lib$libsuff"
+          LIBS="-l$gss_libname $LIBS"
         else
-          case $host in
-            *-hp-hpux*)
-              gss_libname="gss"
-              ;;
-            *)
-              gss_libname="gssapi"
-              ;;
-          esac
-
-          if test "$GSSAPI_ROOT" != "yes"; then
-            LDFLAGS="$LDFLAGS -L$GSSAPI_ROOT/lib$libsuff"
-            LDFLAGSPC="$LDFLAGSPC -L$GSSAPI_ROOT/lib$libsuff"
-            LIBS="-l$gss_libname $LIBS"
-          else
-            LIBS="-l$gss_libname $LIBS"
-          fi
+          LIBS="-l$gss_libname $LIBS"
         fi
-        ;;
-    esac
+      fi
+    fi
   else
     LDFLAGS="$LDFLAGS $GSSAPI_LIB_DIR"
     LDFLAGSPC="$LDFLAGSPC $GSSAPI_LIB_DIR"
@@ -2059,11 +2129,14 @@ CURL_WITH_RUSTLS
 
 dnl link required libraries for USE_WIN32_CRYPTO or SCHANNEL_ENABLED
 if test "x$USE_WIN32_CRYPTO" = "x1" -o "x$SCHANNEL_ENABLED" = "x1"; then
-  LIBS="-ladvapi32 -lcrypt32 $LIBS"
+  LIBS="-lcrypt32 $LIBS"
+  if test "$curl_cv_wince" = 'no'; then
+    LIBS="-ladvapi32 $LIBS"
+  fi
 fi
 
 dnl link bcrypt for BCryptGenRandom() (used when building for Vista or newer)
-if test "x$curl_cv_native_windows" = "xyes"; then
+if test "x$curl_cv_native_windows" = "xyes" -a "$curl_cv_wince" = 'no'; then
   LIBS="-lbcrypt $LIBS"
 fi
 
@@ -2608,7 +2681,11 @@ dnl ----------------------------
 dnl check Windows Unicode option
 dnl ----------------------------
 
-want_winuni="no"
+if test "$curl_cv_wince" = 'yes'; then
+  want_winuni="yes"
+else
+  want_winuni="no"
+fi
 if test "$curl_cv_native_windows" = "yes"; then
   if test "$curl_cv_winuwp" = 'yes'; then
     want_winuni="yes"
@@ -2732,36 +2809,34 @@ dnl Check for the presence of AppleIDN
 dnl **********************************************************************
 
 tst_links_appleidn='no'
-case $host in
-  *-apple-*)
-    AC_MSG_CHECKING([whether to build with Apple IDN])
-    OPT_IDN="default"
-    AC_ARG_WITH(apple-idn,
+if test "$curl_cv_apple" = 'yes'; then
+  AC_MSG_CHECKING([whether to build with Apple IDN])
+  OPT_IDN="default"
+  AC_ARG_WITH(apple-idn,
 AS_HELP_STRING([--with-apple-idn],[Enable AppleIDN])
 AS_HELP_STRING([--without-apple-idn],[Disable AppleIDN]),
-      [OPT_IDN=$withval])
-    case "$OPT_IDN" in
-      yes)
-        dnl --with-apple-idn option used
-        AC_MSG_RESULT([yes, check])
-        AC_CHECK_LIB(icucore, uidna_openUTS46,
-        [
-          AC_CHECK_HEADERS(unicode/uidna.h,
-            curl_idn_msg="enabled (AppleIDN)"
-            AC_DEFINE(USE_APPLE_IDN, 1, [if AppleIDN])
-            USE_APPLE_IDN=1
-            IDN_ENABLED=1
-            LIBS="-licucore -liconv $LIBS"
-            tst_links_appleidn='yes'
-          )
-        ])
-        ;;
-      *)
-        AC_MSG_RESULT([no])
-        ;;
-    esac
-    ;;
-esac
+    [OPT_IDN=$withval])
+  case "$OPT_IDN" in
+    yes)
+      dnl --with-apple-idn option used
+      AC_MSG_RESULT([yes, check])
+      AC_CHECK_LIB(icucore, uidna_openUTS46,
+      [
+        AC_CHECK_HEADERS(unicode/uidna.h,
+          curl_idn_msg="enabled (AppleIDN)"
+          AC_DEFINE(USE_APPLE_IDN, 1, [if AppleIDN])
+          USE_APPLE_IDN=1
+          IDN_ENABLED=1
+          LIBS="-licucore -liconv $LIBS"
+          tst_links_appleidn='yes'
+        )
+      ])
+      ;;
+    *)
+      AC_MSG_RESULT([no])
+      ;;
+  esac
+fi
 
 dnl **********************************************************************
 dnl Check for the presence of libidn2
@@ -3094,7 +3169,8 @@ if test X"$want_tcp2" != Xno; then
   fi
 fi
 
-if test "x$USE_NGTCP2" = "x1" -a "x$OPENSSL_ENABLED" = "x1" -a "x$OPENSSL_IS_BORINGSSL" != "x1"; then
+if test "x$USE_NGTCP2" = "x1" -a "x$OPENSSL_ENABLED" = "x1" -a \
+   "x$OPENSSL_IS_BORINGSSL" != "x1" -a "x$OPENSSL_QUIC_API2" != "x1"; then
   dnl backup the pre-ngtcp2_crypto_quictls variables
   CLEANLDFLAGS="$LDFLAGS"
   CLEANLDFLAGSPC="$LDFLAGSPC"
@@ -3151,6 +3227,65 @@ if test "x$USE_NGTCP2" = "x1" -a "x$OPENSSL_ENABLED" = "x1" -a "x$OPENSSL_IS_BOR
   fi
 fi
 
+if test "x$USE_NGTCP2" = "x1" -a "x$OPENSSL_ENABLED" = "x1" -a \
+   "x$OPENSSL_IS_BORINGSSL" != "x1" -a "x$OPENSSL_QUIC_API2" = "x1"; then
+  dnl backup the pre-ngtcp2_crypto_ossl variables
+  CLEANLDFLAGS="$LDFLAGS"
+  CLEANLDFLAGSPC="$LDFLAGSPC"
+  CLEANCPPFLAGS="$CPPFLAGS"
+  CLEANLIBS="$LIBS"
+
+  CURL_CHECK_PKGCONFIG(libngtcp2_crypto_ossl, $want_tcp2_path)
+
+  if test "$PKGCONFIG" != "no"; then
+    LIB_NGTCP2_CRYPTO_QUICTLS=`CURL_EXPORT_PCDIR([$want_tcp2_path])
+      $PKGCONFIG --libs-only-l libngtcp2_crypto_ossl`
+    AC_MSG_NOTICE([-l is $LIB_NGTCP2_CRYPTO_QUICTLS])
+
+    CPP_NGTCP2_CRYPTO_QUICTLS=`CURL_EXPORT_PCDIR([$want_tcp2_path]) dnl
+      $PKGCONFIG --cflags-only-I libngtcp2_crypto_ossl`
+    AC_MSG_NOTICE([-I is $CPP_NGTCP2_CRYPTO_QUICTLS])
+
+    LD_NGTCP2_CRYPTO_QUICTLS=`CURL_EXPORT_PCDIR([$want_tcp2_path])
+      $PKGCONFIG --libs-only-L libngtcp2_crypto_ossl`
+    AC_MSG_NOTICE([-L is $LD_NGTCP2_CRYPTO_QUICTLS])
+
+    LDFLAGS="$LDFLAGS $LD_NGTCP2_CRYPTO_QUICTLS"
+    LDFLAGSPC="$LDFLAGSPC $LD_NGTCP2_CRYPTO_QUICTLS"
+    CPPFLAGS="$CPPFLAGS $CPP_NGTCP2_CRYPTO_QUICTLS"
+    LIBS="$LIB_NGTCP2_CRYPTO_QUICTLS $LIBS"
+
+    if test "x$cross_compiling" != "xyes"; then
+      DIR_NGTCP2_CRYPTO_QUICTLS=`echo $LD_NGTCP2_CRYPTO_QUICTLS | $SED -e 's/^-L//'`
+    fi
+    AC_CHECK_LIB(ngtcp2_crypto_ossl, ngtcp2_crypto_recv_client_initial_cb,
+      [
+        AC_CHECK_HEADERS(ngtcp2/ngtcp2_crypto.h,
+          USE_NGTCP2=1
+          CURL_LIBRARY_PATH="$CURL_LIBRARY_PATH:$DIR_NGTCP2_CRYPTO_QUICTLS"
+          export CURL_LIBRARY_PATH
+          AC_MSG_NOTICE([Added $DIR_NGTCP2_CRYPTO_QUICTLS to CURL_LIBRARY_PATH])
+          LIBCURL_PC_REQUIRES_PRIVATE="$LIBCURL_PC_REQUIRES_PRIVATE libngtcp2_crypto_ossl"
+          AC_DEFINE(OPENSSL_QUIC_API2, 1, [openssl with new QUIC API])
+        )
+      ],
+        dnl not found, revert back to clean variables
+        LDFLAGS=$CLEANLDFLAGS
+        LDFLAGSPC=$CLEANLDFLAGSPC
+        CPPFLAGS=$CLEANCPPFLAGS
+        LIBS=$CLEANLIBS
+    )
+
+  else
+    dnl no ngtcp2_crypto_ossl pkg-config found, deal with it
+    if test X"$want_tcp2" != Xdefault; then
+      dnl To avoid link errors, we do not allow --with-ngtcp2 without
+      dnl a pkgconfig file
+      AC_MSG_ERROR([--with-ngtcp2 was specified but could not find ngtcp2_crypto_ossl pkg-config file.])
+    fi
+  fi
+fi
+
 if test "x$USE_NGTCP2" = "x1" -a "x$OPENSSL_ENABLED" = "x1" -a "x$OPENSSL_IS_BORINGSSL" = "x1"; then
   dnl backup the pre-ngtcp2_crypto_boringssl variables
   CLEANLDFLAGS="$LDFLAGS"
@@ -3855,6 +3990,7 @@ AC_CHECK_HEADERS(
   libgen.h \
   locale.h \
   stdbool.h \
+  stdint.h \
   sys/filio.h \
   sys/eventfd.h,
 dnl to do if not found
@@ -4005,8 +4141,6 @@ case $host_os in
     ;;
 esac
 
-TYPE_IN_ADDR_T
-
 TYPE_SOCKADDR_STORAGE
 
 CURL_CHECK_FUNC_SELECT
@@ -4026,12 +4160,10 @@ CURL_CHECK_FUNC_FTRUNCATE
 CURL_CHECK_FUNC_GETADDRINFO
 CURL_CHECK_FUNC_GETHOSTBYNAME_R
 CURL_CHECK_FUNC_GETHOSTNAME
+CURL_CHECK_FUNC_GETIFADDRS
 CURL_CHECK_FUNC_GETPEERNAME
 CURL_CHECK_FUNC_GETSOCKNAME
-CURL_CHECK_FUNC_GETIFADDRS
 CURL_CHECK_FUNC_GMTIME_R
-CURL_CHECK_FUNC_INET_NTOP
-CURL_CHECK_FUNC_INET_PTON
 CURL_CHECK_FUNC_IOCTL
 CURL_CHECK_FUNC_IOCTLSOCKET
 CURL_CHECK_FUNC_IOCTLSOCKET_CAMEL
@@ -4044,8 +4176,6 @@ CURL_CHECK_FUNC_SOCKET
 CURL_CHECK_FUNC_SOCKETPAIR
 CURL_CHECK_FUNC_STRDUP
 CURL_CHECK_FUNC_STRERROR_R
-CURL_CHECK_FUNC_STRTOK_R
-CURL_CHECK_FUNC_STRTOLL
 
 case $host in
   *msdosdjgpp)
@@ -4056,6 +4186,7 @@ case $host in
 esac
 
 AC_CHECK_FUNCS([\
+  accept4 \
   eventfd \
   fnmatch \
   geteuid \
@@ -4067,11 +4198,11 @@ AC_CHECK_FUNCS([\
   gettimeofday \
   mach_absolute_time \
   pipe \
+  pipe2 \
   poll \
-  sendmsg \
   sendmmsg \
+  sendmsg \
   setlocale \
-  setmode \
   setrlimit \
   snprintf \
   utime \
@@ -4084,13 +4215,18 @@ if test "$curl_cv_native_windows" != 'yes'; then
     realpath \
     sched_yield \
   ])
+  CURL_CHECK_FUNC_INET_NTOP
+  CURL_CHECK_FUNC_INET_PTON
   CURL_CHECK_FUNC_STRCASECMP
   CURL_CHECK_FUNC_STRCMPI
   CURL_CHECK_FUNC_STRICMP
 fi
 
-if test "$curl_cv_native_windows" = 'yes' -o "$curl_cv_cygwin" = 'yes'; then
-  AC_CHECK_FUNCS([_setmode])
+if test "$curl_cv_wince" = 'no'; then
+  AC_CHECK_FUNCS([setmode])
+  if test "$curl_cv_native_windows" = 'yes' -o "$curl_cv_cygwin" = 'yes'; then
+    AC_CHECK_FUNCS([_setmode])
+  fi
 fi
 
 if test -z "$ssl_backends"; then
@@ -4139,15 +4275,10 @@ AM_CONDITIONAL(USE_MANUAL, test x"$USE_MANUAL" = x1)
 CURL_CHECK_LIB_ARES
 CURL_CHECK_OPTION_THREADED_RESOLVER
 
-if test "$ipv6" = yes; then
+if test "$ipv6" = yes -a "$curl_cv_apple" = 'yes'; then
   CURL_DARWIN_SYSTEMCONFIGURATION
 fi
 
-dnl turn off pthreads if rt is disabled
-if test "$want_threaded_resolver" = "yes" && test "$dontwant_rt" = "yes"; then
-  AC_MSG_ERROR([options --enable-pthreads and --disable-rt are mutually exclusive])
-fi
-
 dnl Windows threaded resolver check
 if test "$want_threaded_resolver" = "yes" && test "$curl_cv_native_windows" = "yes"; then
   USE_THREADS_WIN32=1
@@ -4470,7 +4601,7 @@ AS_HELP_STRING([--disable-unix-sockets],[Disable Unix domain sockets]),
     want_unix_sockets=auto
     ]
 )
-if test "x$want_unix_sockets" != "xno"; then
+if test "x$want_unix_sockets" != "xno" -a "$curl_cv_wince" = 'no'; then
   if test "x$curl_cv_native_windows" = "xyes"; then
     USE_UNIX_SOCKETS=1
     AC_DEFINE(USE_UNIX_SOCKETS, 1, [Use Unix domain sockets])
@@ -4826,25 +4957,35 @@ if test "x$want_ech" != "xno"; then
 
   dnl assume NOT and look for sufficient condition
   ECH_ENABLED=0
+  ECH_ENABLED_OPENSSL=0
+  ECH_ENABLED_WOLFSSL=0
+  ECH_ENABLED_RUSTLS=0
   ECH_SUPPORT=''
 
   dnl check for OpenSSL equivalent
   if test "x$OPENSSL_ENABLED" = "x1"; then
     AC_CHECK_FUNCS(SSL_set1_ech_config_list,
-      ECH_SUPPORT="ECH support available via BoringSSL with SSL_set1_ech_config_list"
-      ECH_ENABLED=1)
+      ECH_SUPPORT="$ECH_SUPPORT OpenSSL"
+      ECH_ENABLED_OPENSSL=1)
   fi
   if test "x$WOLFSSL_ENABLED" = "x1"; then
     AC_CHECK_FUNCS(wolfSSL_CTX_GenerateEchConfig,
-      ECH_SUPPORT="ECH support available via wolfSSL with wolfSSL_CTX_GenerateEchConfig"
-      ECH_ENABLED=1)
+      ECH_SUPPORT="$ECH_SUPPORT wolfSSL"
+      ECH_ENABLED_WOLFSSL=1)
+  fi
+  if test "x$RUSTLS_ENABLED" = "x1"; then
+      ECH_SUPPORT="$ECH_SUPPORT rustls-ffi"
+      ECH_ENABLED_RUSTLS=1
   fi
 
   dnl now deal with whatever we found
-  if test "x$ECH_ENABLED" = "x1"; then
+  if test "x$ECH_ENABLED_OPENSSL" = "x1" -o \
+          "x$ECH_ENABLED_WOLFSSL" = "x1" -o \
+          "x$ECH_ENABLED_RUSTLS" = "x1"; then
     AC_DEFINE(USE_ECH, 1, [if ECH support is available])
-    AC_MSG_RESULT($ECH_SUPPORT)
+    AC_MSG_RESULT(ECH support available via:$ECH_SUPPORT)
     experimental="$experimental ECH"
+    ECH_ENABLED=1
     dnl ECH wants HTTPSRR
     want_httpsrr="yes"
   else
@@ -4852,14 +4993,24 @@ if test "x$want_ech" != "xno"; then
   fi
 fi
 
+AC_MSG_CHECKING([whether to enable HTTPS-RR support])
 dnl *************************************************************
 dnl check whether HTTPSRR support if desired
 dnl
 if test "x$want_httpsrr" != "xno"; then
-  AC_MSG_RESULT([HTTPSRR support is enabled])
+  AC_MSG_RESULT([yes])
   AC_DEFINE(USE_HTTPSRR, 1, [enable HTTPS RR support])
   experimental="$experimental HTTPSRR"
   curl_httpsrr_msg="enabled (--disable-httpsrr)"
+else
+  AC_MSG_RESULT([no])
+  # no HTTPSRR wanted
+  if test "$want_threaded_resolver" = "yes"; then
+    # and using the threaded resolver
+    if test "x$USE_ARES" = "x1"; then
+      AC_MSG_ERROR([without HTTPS-RR support, asking for both threaded resolver and c-ares support is ambivalent. Please drop one of them.])
+    fi
+  fi
 fi
 
 
@@ -5066,7 +5217,7 @@ if test "x$CURL_DISABLE_NTLM" != "x1"; then
       -o "x$GNUTLS_ENABLED" = "x1" \
       -o "x$SECURETRANSPORT_ENABLED" = "x1" \
       -o "x$USE_WIN32_CRYPTO" = "x1" \
-      -o "x$WOLFSSL_NTLM" = "x1"; then
+      -o "x$HAVE_WOLFSSL_DES_ECB_ENCRYPT" = "x1"; then
     use_curl_ntlm_core=yes
   fi
 
@@ -5110,7 +5261,7 @@ if test "x$CURL_DISABLE_HTTP" != "x1"; then
         -o "x$MBEDTLS_ENABLED" = "x1"; then
       SUPPORT_FEATURES="$SUPPORT_FEATURES HTTPS-proxy"
       AC_MSG_RESULT([yes])
-    elif test "x$WOLFSSL_ENABLED" = "x1" -a "x$WOLFSSL_BIO" = "x1"; then
+    elif test "x$WOLFSSL_ENABLED" = "x1" -a "x$HAVE_WOLFSSL_BIO_NEW" = "x1"; then
       SUPPORT_FEATURES="$SUPPORT_FEATURES HTTPS-proxy"
       AC_MSG_RESULT([yes])
     else
@@ -5336,11 +5487,11 @@ AC_CONFIG_FILES([\
   tests/config \
   tests/configurehelp.pm \
   tests/certs/Makefile \
-  tests/certs/scripts/Makefile \
   tests/data/Makefile \
   tests/server/Makefile \
   tests/libtest/Makefile \
   tests/unit/Makefile \
+  tests/tunit/Makefile \
   tests/http/config.ini \
   tests/http/Makefile \
   tests/http/clients/Makefile \
diff --git a/extra/curl/curl-8.12.1/curl-config.in b/extra/curl/curl-8.14.1/curl-config.in
similarity index 100%
rename from extra/curl/curl-8.12.1/curl-config.in
rename to extra/curl/curl-8.14.1/curl-config.in
diff --git a/extra/curl/curl-8.12.1/depcomp b/extra/curl/curl-8.14.1/depcomp
similarity index 100%
rename from extra/curl/curl-8.12.1/depcomp
rename to extra/curl/curl-8.14.1/depcomp
diff --git a/extra/curl/curl-8.12.1/include/Makefile.am b/extra/curl/curl-8.14.1/include/Makefile.am
similarity index 100%
rename from extra/curl/curl-8.12.1/include/Makefile.am
rename to extra/curl/curl-8.14.1/include/Makefile.am
diff --git a/extra/curl/curl-8.12.1/include/Makefile.in b/extra/curl/curl-8.14.1/include/Makefile.in
similarity index 100%
rename from extra/curl/curl-8.12.1/include/Makefile.in
rename to extra/curl/curl-8.14.1/include/Makefile.in
diff --git a/extra/curl/curl-8.12.1/include/README.md b/extra/curl/curl-8.14.1/include/README.md
similarity index 100%
rename from extra/curl/curl-8.12.1/include/README.md
rename to extra/curl/curl-8.14.1/include/README.md
diff --git a/extra/curl/curl-8.12.1/include/curl/Makefile.am b/extra/curl/curl-8.14.1/include/curl/Makefile.am
similarity index 98%
rename from extra/curl/curl-8.12.1/include/curl/Makefile.am
rename to extra/curl/curl-8.14.1/include/curl/Makefile.am
index 8864d60e1f2a..f664f6ab7ef5 100644
--- a/extra/curl/curl-8.12.1/include/curl/Makefile.am
+++ b/extra/curl/curl-8.14.1/include/curl/Makefile.am
@@ -35,7 +35,9 @@ CS_ = $(CS_0)
 checksrc:
 	$(CHECKSRC)@PERL@ $(top_srcdir)/scripts/checksrc.pl -D$(top_srcdir)/include/curl $(pkginclude_HEADERS)
 
+if NOT_CURL_CI
 if DEBUGBUILD
 # for debug builds, we scan the sources on all regular make invokes
 all-local: checksrc
 endif
+endif
diff --git a/extra/curl/curl-8.12.1/include/curl/Makefile.in b/extra/curl/curl-8.14.1/include/curl/Makefile.in
similarity index 99%
rename from extra/curl/curl-8.12.1/include/curl/Makefile.in
rename to extra/curl/curl-8.14.1/include/curl/Makefile.in
index 8947636c72b3..dc9fd21f27d5 100644
--- a/extra/curl/curl-8.12.1/include/curl/Makefile.in
+++ b/extra/curl/curl-8.14.1/include/curl/Makefile.in
@@ -532,6 +532,7 @@ distdir-am: $(DISTFILES)
 check-am: all-am
 check: check-am
 @DEBUGBUILD_FALSE@all-local:
+@NOT_CURL_CI_FALSE@all-local:
 all-am: Makefile $(HEADERS) all-local
 installdirs:
 	for dir in "$(DESTDIR)$(pkgincludedir)"; do \
@@ -656,7 +657,7 @@ checksrc:
 	$(CHECKSRC)@PERL@ $(top_srcdir)/scripts/checksrc.pl -D$(top_srcdir)/include/curl $(pkginclude_HEADERS)
 
 # for debug builds, we scan the sources on all regular make invokes
-@DEBUGBUILD_TRUE@all-local: checksrc
+@DEBUGBUILD_TRUE@@NOT_CURL_CI_TRUE@all-local: checksrc
 
 # Tell versions [3.59,3.63) of GNU make to not export all variables.
 # Otherwise a system limit (for SysV at least) may be exceeded.
diff --git a/extra/curl/curl-8.12.1/include/curl/curl.h b/extra/curl/curl-8.14.1/include/curl/curl.h
similarity index 96%
rename from extra/curl/curl-8.12.1/include/curl/curl.h
rename to extra/curl/curl-8.14.1/include/curl/curl.h
index 84cf5f2f525a..fb225c7903c9 100644
--- a/extra/curl/curl-8.12.1/include/curl/curl.h
+++ b/extra/curl/curl-8.14.1/include/curl/curl.h
@@ -42,7 +42,7 @@
   !defined(CURL_DISABLE_DEPRECATION) && !defined(BUILDING_LIBCURL)
 #define CURL_DEPRECATED(version, message)                       \
   __attribute__((deprecated("since " # version ". " message)))
-#if defined(__IAR_SYSTEMS_ICC__)
+#ifdef __IAR_SYSTEMS_ICC__
 #define CURL_IGNORE_DEPRECATION(statements) \
       _Pragma("diag_suppress=Pe1444") \
       statements \
@@ -97,19 +97,11 @@
 #include <sys/select.h>
 #endif
 
-#if !defined(_WIN32) && !defined(_WIN32_WCE)
+#ifndef _WIN32
 #include <sys/socket.h>
-#endif
-
-#if !defined(_WIN32)
 #include <sys/time.h>
 #endif
 
-/* Compatibility for non-Clang compilers */
-#ifndef __has_declspec_attribute
-#  define __has_declspec_attribute(x) 0
-#endif
-
 #ifdef  __cplusplus
 extern "C" {
 #endif
@@ -121,11 +113,17 @@ typedef void CURLSH;
  * libcurl external API function linkage decorations.
  */
 
+#ifdef __has_declspec_attribute
+#define CURL_HAS_DECLSPEC_ATTRIBUTE(x) __has_declspec_attribute(x)
+#else
+#define CURL_HAS_DECLSPEC_ATTRIBUTE(x) 0
+#endif
+
 #ifdef CURL_STATICLIB
 #  define CURL_EXTERN
 #elif defined(_WIN32) || \
-     (__has_declspec_attribute(dllexport) && \
-      __has_declspec_attribute(dllimport))
+     (CURL_HAS_DECLSPEC_ATTRIBUTE(dllexport) && \
+      CURL_HAS_DECLSPEC_ATTRIBUTE(dllimport))
 #  if defined(BUILDING_LIBCURL)
 #    define CURL_EXTERN  __declspec(dllexport)
 #  else
@@ -177,6 +175,16 @@ typedef enum {
 #define CURLSSLBACKEND_CYASSL CURLSSLBACKEND_WOLFSSL
 #define CURLSSLBACKEND_DARWINSSL CURLSSLBACKEND_SECURETRANSPORT
 
+/* bits for the CURLOPT_FOLLOWLOCATION option */
+#define CURLFOLLOW_ALL       1L /* generic follow redirects */
+
+/* Do not use the custom method in the follow-up request if the HTTP code
+   instructs so (301, 302, 303). */
+#define CURLFOLLOW_OBEYCODE  2L
+
+/* Only use the custom method in the first request, always reset in the next */
+#define CURLFOLLOW_FIRSTONLY 3L
+
 struct curl_httppost {
   struct curl_httppost *next;       /* next entry in the list */
   char *name;                       /* pointer to allocated name */
@@ -637,7 +645,20 @@ typedef enum {
   CURLE_UNRECOVERABLE_POLL,      /* 99 - poll/select returned fatal error */
   CURLE_TOO_LARGE,               /* 100 - a value/data met its maximum */
   CURLE_ECH_REQUIRED,            /* 101 - ECH tried but failed */
-  CURL_LAST /* never use! */
+  CURL_LAST, /* never use! */
+
+  CURLE_RESERVED115 = 115,       /* 115-126 - used in tests */
+  CURLE_RESERVED116 = 116,
+  CURLE_RESERVED117 = 117,
+  CURLE_RESERVED118 = 118,
+  CURLE_RESERVED119 = 119,
+  CURLE_RESERVED120 = 120,
+  CURLE_RESERVED121 = 121,
+  CURLE_RESERVED122 = 122,
+  CURLE_RESERVED123 = 123,
+  CURLE_RESERVED124 = 124,
+  CURLE_RESERVED125 = 125,
+  CURLE_RESERVED126 = 126
 } CURLcode;
 
 #ifndef CURL_NO_OLDIES /* define this to test if your app builds with all
@@ -906,12 +927,13 @@ typedef int
 
 
 /* parameter for the CURLOPT_USE_SSL option */
+#define CURLUSESSL_NONE    0L /* do not attempt to use SSL */
+#define CURLUSESSL_TRY     1L /* try using SSL, proceed anyway otherwise */
+#define CURLUSESSL_CONTROL 2L /* SSL for the control connection or fail */
+#define CURLUSESSL_ALL     3L /* SSL for all communication or fail */
+
 typedef enum {
-  CURLUSESSL_NONE,    /* do not attempt to use SSL */
-  CURLUSESSL_TRY,     /* try using SSL, proceed anyway otherwise */
-  CURLUSESSL_CONTROL, /* SSL for the control connection or fail */
-  CURLUSESSL_ALL,     /* SSL for all communication or fail */
-  CURLUSESSL_LAST     /* not an option, never use */
+  CURLUSESSL_LAST = 4 /* not an option, never use */
 } curl_usessl;
 
 /* Definition of bits for the CURLOPT_SSL_OPTIONS argument: */
@@ -1015,6 +1037,12 @@ typedef enum {
 #define CURLALTSVC_H2           (1<<4)
 #define CURLALTSVC_H3           (1<<5)
 
+/* bitmask values for CURLOPT_UPLOAD_FLAGS */
+#define CURLULFLAG_ANSWERED (1L<<0)
+#define CURLULFLAG_DELETED  (1L<<1)
+#define CURLULFLAG_DRAFT    (1L<<2)
+#define CURLULFLAG_FLAGGED  (1L<<3)
+#define CURLULFLAG_SEEN     (1L<<4)
 
 struct curl_hstsentry {
   char *name;
@@ -2228,6 +2256,11 @@ typedef enum {
   /* maximum number of keepalive probes (Linux, *BSD, macOS, etc.) */
   CURLOPT(CURLOPT_TCP_KEEPCNT, CURLOPTTYPE_LONG, 326),
 
+  CURLOPT(CURLOPT_UPLOAD_FLAGS, CURLOPTTYPE_LONG, 327),
+
+  /* set TLS supported signature algorithms */
+  CURLOPT(CURLOPT_SSL_SIGNATURE_ALGORITHMS, CURLOPTTYPE_STRINGPOINT, 328),
+
   CURLOPT_LASTENTRY /* the last unused */
 } CURLoption;
 
@@ -2276,26 +2309,25 @@ typedef enum {
   /* Convenient "aliases" */
 #define CURLOPT_RTSPHEADER CURLOPT_HTTPHEADER
 
-  /* These enums are for use with the CURLOPT_HTTP_VERSION option. */
-enum {
-  CURL_HTTP_VERSION_NONE, /* setting this means we do not care, and that we
-                             would like the library to choose the best
-                             possible for us! */
-  CURL_HTTP_VERSION_1_0,  /* please use HTTP 1.0 in the request */
-  CURL_HTTP_VERSION_1_1,  /* please use HTTP 1.1 in the request */
-  CURL_HTTP_VERSION_2_0,  /* please use HTTP 2 in the request */
-  CURL_HTTP_VERSION_2TLS, /* use version 2 for HTTPS, version 1.1 for HTTP */
-  CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE,  /* please use HTTP 2 without HTTP/1.1
-                                           Upgrade */
-  CURL_HTTP_VERSION_3 = 30, /* Use HTTP/3, fallback to HTTP/2 or HTTP/1 if
-                               needed. For HTTPS only. For HTTP, this option
-                               makes libcurl return error. */
-  CURL_HTTP_VERSION_3ONLY = 31, /* Use HTTP/3 without fallback. For HTTPS
-                                   only. For HTTP, this makes libcurl
-                                   return error. */
-
-  CURL_HTTP_VERSION_LAST /* *ILLEGAL* http version */
-};
+/* These constants are for use with the CURLOPT_HTTP_VERSION option. */
+#define CURL_HTTP_VERSION_NONE  0L /* setting this means we do not care, and
+                                      that we would like the library to choose
+                                      the best possible for us! */
+#define CURL_HTTP_VERSION_1_0   1L /* please use HTTP 1.0 in the request */
+#define CURL_HTTP_VERSION_1_1   2L /* please use HTTP 1.1 in the request */
+#define CURL_HTTP_VERSION_2_0   3L /* please use HTTP 2 in the request */
+#define CURL_HTTP_VERSION_2TLS  4L /* use version 2 for HTTPS, version 1.1 for
+                                      HTTP */
+#define CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE 5L /* please use HTTP 2 without
+                                                  HTTP/1.1 Upgrade */
+#define CURL_HTTP_VERSION_3     30L /* Use HTTP/3, fallback to HTTP/2 or
+                                       HTTP/1 if needed. For HTTPS only. For
+                                       HTTP, this option makes libcurl
+                                       return error. */
+#define CURL_HTTP_VERSION_3ONLY 31L /* Use HTTP/3 without fallback. For
+                                       HTTPS only. For HTTP, this makes
+                                       libcurl return error. */
+#define CURL_HTTP_VERSION_LAST  32L /* *ILLEGAL* http version */
 
 /* Convenience definition simple because the name of the version is HTTP/2 and
    not 2.0. The 2_0 version of the enum name was set while the version was
@@ -2305,32 +2337,33 @@ enum {
 /*
  * Public API enums for RTSP requests
  */
-enum {
-    CURL_RTSPREQ_NONE, /* first in list */
-    CURL_RTSPREQ_OPTIONS,
-    CURL_RTSPREQ_DESCRIBE,
-    CURL_RTSPREQ_ANNOUNCE,
-    CURL_RTSPREQ_SETUP,
-    CURL_RTSPREQ_PLAY,
-    CURL_RTSPREQ_PAUSE,
-    CURL_RTSPREQ_TEARDOWN,
-    CURL_RTSPREQ_GET_PARAMETER,
-    CURL_RTSPREQ_SET_PARAMETER,
-    CURL_RTSPREQ_RECORD,
-    CURL_RTSPREQ_RECEIVE,
-    CURL_RTSPREQ_LAST /* last in list */
-};
+
+#define CURL_RTSPREQ_NONE          0L
+#define CURL_RTSPREQ_OPTIONS       1L
+#define CURL_RTSPREQ_DESCRIBE      2L
+#define CURL_RTSPREQ_ANNOUNCE      3L
+#define CURL_RTSPREQ_SETUP         4L
+#define CURL_RTSPREQ_PLAY          5L
+#define CURL_RTSPREQ_PAUSE         6L
+#define CURL_RTSPREQ_TEARDOWN      7L
+#define CURL_RTSPREQ_GET_PARAMETER 8L
+#define CURL_RTSPREQ_SET_PARAMETER 9L
+#define CURL_RTSPREQ_RECORD        10L
+#define CURL_RTSPREQ_RECEIVE       11L
+#define CURL_RTSPREQ_LAST          12L /* not used */
 
   /* These enums are for use with the CURLOPT_NETRC option. */
+#define CURL_NETRC_IGNORED  0L /* The .netrc will never be read.
+                                  This is the default. */
+#define CURL_NETRC_OPTIONAL 1L /* A user:password in the URL will be preferred
+                                  to one in the .netrc. */
+#define CURL_NETRC_REQUIRED 2L /* A user:password in the URL will be ignored.
+                                  Unless one is set programmatically, the
+                                  .netrc will be queried. */
 enum CURL_NETRC_OPTION {
-  CURL_NETRC_IGNORED,     /* The .netrc will never be read.
-                           * This is the default. */
-  CURL_NETRC_OPTIONAL,    /* A user:password in the URL will be preferred
-                           * to one in the .netrc. */
-  CURL_NETRC_REQUIRED,    /* A user:password in the URL will be ignored.
-                           * Unless one is set programmatically, the .netrc
-                           * will be queried. */
-  CURL_NETRC_LAST
+  /* we set a single member here, just to make sure we still provide the enum,
+     but the values to use are defined above with L suffixes */
+  CURL_NETRC_LAST = 3
 };
 
 #define CURL_SSLVERSION_DEFAULT 0
@@ -2354,10 +2387,13 @@ enum CURL_NETRC_OPTION {
   /* never use, keep last */
 #define CURL_SSLVERSION_MAX_LAST    (CURL_SSLVERSION_LAST    << 16)
 
+#define CURL_TLSAUTH_NONE 0L
+#define CURL_TLSAUTH_SRP  1L
+
 enum CURL_TLSAUTH {
-  CURL_TLSAUTH_NONE,
-  CURL_TLSAUTH_SRP,
-  CURL_TLSAUTH_LAST /* never use, keep last */
+  /* we set a single member here, just to make sure we still provide the enum,
+     but the values to use are defined above with L suffixes */
+  CURL_TLSAUTH_LAST = 2
 };
 
 /* symbols to use with CURLOPT_POSTREDIR.
@@ -2372,14 +2408,16 @@ enum CURL_TLSAUTH {
 #define CURL_REDIR_POST_ALL \
     (CURL_REDIR_POST_301|CURL_REDIR_POST_302|CURL_REDIR_POST_303)
 
-typedef enum {
-  CURL_TIMECOND_NONE,
-
-  CURL_TIMECOND_IFMODSINCE,
-  CURL_TIMECOND_IFUNMODSINCE,
-  CURL_TIMECOND_LASTMOD,
+#define CURL_TIMECOND_NONE         0L
+#define CURL_TIMECOND_IFMODSINCE   1L
+#define CURL_TIMECOND_IFUNMODSINCE 2L
+#define CURL_TIMECOND_LASTMOD      3L
 
-  CURL_TIMECOND_LAST
+typedef enum {
+  /* we set a single member here, just to make sure we still provide
+     the enum typedef, but the values to use are defined above with L
+     suffixes */
+  CURL_TIMECOND_LAST = 4
 } curl_TimeCond;
 
 /* Special size_t value signaling a null-terminated string. */
@@ -2775,17 +2813,17 @@ struct curl_slist {
  * *before* curl_global_init().
  *
  * The backend can be identified by the id (e.g. CURLSSLBACKEND_OPENSSL). The
- * backend can also be specified via the name parameter (passing -1 as id).
- * If both id and name are specified, the name will be ignored. If neither id
- * nor name are specified, the function will fail with
- * CURLSSLSET_UNKNOWN_BACKEND and set the "avail" pointer to the
- * NULL-terminated list of available backends.
+ * backend can also be specified via the name parameter (passing -1 as id). If
+ * both id and name are specified, the name will be ignored. If neither id nor
+ * name are specified, the function will fail with CURLSSLSET_UNKNOWN_BACKEND
+ * and set the "avail" pointer to the NULL-terminated list of available
+ * backends.
  *
  * Upon success, the function returns CURLSSLSET_OK.
  *
  * If the specified SSL backend is not available, the function returns
- * CURLSSLSET_UNKNOWN_BACKEND and sets the "avail" pointer to a NULL-terminated
- * list of available SSL backends.
+ * CURLSSLSET_UNKNOWN_BACKEND and sets the "avail" pointer to a
+ * NULL-terminated list of available SSL backends.
  *
  * The SSL backend can be set only once. If it has already been set, a
  * subsequent attempt to change it will result in a CURLSSLSET_TOO_LATE.
@@ -3288,9 +3326,7 @@ CURL_EXTERN CURLcode curl_easy_ssls_export(CURL *handle,
 #include "options.h"
 #include "header.h"
 #include "websockets.h"
-#ifndef CURL_SKIP_INCLUDE_MPRINTF
 #include "mprintf.h"
-#endif
 
 /* the typechecker does not work in C++ (yet) */
 #if defined(__GNUC__) && defined(__GNUC_MINOR__) && \
diff --git a/extra/curl/curl-8.12.1/include/curl/curlver.h b/extra/curl/curl-8.14.1/include/curl/curlver.h
similarity index 95%
rename from extra/curl/curl-8.12.1/include/curl/curlver.h
rename to extra/curl/curl-8.14.1/include/curl/curlver.h
index fc8ba4cd4b6a..2e794e63ab0d 100644
--- a/extra/curl/curl-8.12.1/include/curl/curlver.h
+++ b/extra/curl/curl-8.14.1/include/curl/curlver.h
@@ -32,12 +32,12 @@
 
 /* This is the version number of the libcurl package from which this header
    file origins: */
-#define LIBCURL_VERSION "8.12.1"
+#define LIBCURL_VERSION "8.14.1"
 
 /* The numeric version number is also available "in parts" by using these
    defines: */
 #define LIBCURL_VERSION_MAJOR 8
-#define LIBCURL_VERSION_MINOR 12
+#define LIBCURL_VERSION_MINOR 14
 #define LIBCURL_VERSION_PATCH 1
 
 /* This is the numeric version of the libcurl version number, meant for easier
@@ -59,7 +59,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 0x080c01
+#define LIBCURL_VERSION_NUM 0x080e01
 
 /*
  * This is the date and time when the full source package was created. The
@@ -70,7 +70,7 @@
  *
  * "2007-11-23"
  */
-#define LIBCURL_TIMESTAMP "2025-02-13"
+#define LIBCURL_TIMESTAMP "2025-06-04"
 
 #define CURL_VERSION_BITS(x,y,z) ((x)<<16|(y)<<8|(z))
 #define CURL_AT_LEAST_VERSION(x,y,z) \
diff --git a/extra/curl/curl-8.12.1/include/curl/easy.h b/extra/curl/curl-8.14.1/include/curl/easy.h
similarity index 100%
rename from extra/curl/curl-8.12.1/include/curl/easy.h
rename to extra/curl/curl-8.14.1/include/curl/easy.h
diff --git a/extra/curl/curl-8.12.1/include/curl/header.h b/extra/curl/curl-8.14.1/include/curl/header.h
similarity index 100%
rename from extra/curl/curl-8.12.1/include/curl/header.h
rename to extra/curl/curl-8.14.1/include/curl/header.h
diff --git a/extra/curl/curl-8.12.1/include/curl/mprintf.h b/extra/curl/curl-8.14.1/include/curl/mprintf.h
similarity index 100%
rename from extra/curl/curl-8.12.1/include/curl/mprintf.h
rename to extra/curl/curl-8.14.1/include/curl/mprintf.h
diff --git a/extra/curl/curl-8.12.1/include/curl/multi.h b/extra/curl/curl-8.14.1/include/curl/multi.h
similarity index 100%
rename from extra/curl/curl-8.12.1/include/curl/multi.h
rename to extra/curl/curl-8.14.1/include/curl/multi.h
diff --git a/extra/curl/curl-8.12.1/include/curl/options.h b/extra/curl/curl-8.14.1/include/curl/options.h
similarity index 100%
rename from extra/curl/curl-8.12.1/include/curl/options.h
rename to extra/curl/curl-8.14.1/include/curl/options.h
diff --git a/extra/curl/curl-8.12.1/include/curl/stdcheaders.h b/extra/curl/curl-8.14.1/include/curl/stdcheaders.h
similarity index 100%
rename from extra/curl/curl-8.12.1/include/curl/stdcheaders.h
rename to extra/curl/curl-8.14.1/include/curl/stdcheaders.h
diff --git a/extra/curl/curl-8.12.1/include/curl/system.h b/extra/curl/curl-8.14.1/include/curl/system.h
similarity index 94%
rename from extra/curl/curl-8.12.1/include/curl/system.h
rename to extra/curl/curl-8.14.1/include/curl/system.h
index 820fe96b39bb..f1c2719cfe5a 100644
--- a/extra/curl/curl-8.12.1/include/curl/system.h
+++ b/extra/curl/curl-8.14.1/include/curl/system.h
@@ -36,13 +36,10 @@
  * curl_off_t
  * ----------
  *
- * For any given platform/compiler curl_off_t must be typedef'ed to a 64-bit
+ * For any given platform/compiler curl_off_t MUST be typedef'ed to a 64-bit
  * wide signed integral data type. The width of this data type must remain
  * constant and independent of any possible large file support settings.
  *
- * As an exception to the above, curl_off_t shall be typedef'ed to a 32-bit
- * wide signed integral data type if there is no 64-bit type.
- *
  * As a general rule, curl_off_t shall not be mapped to off_t. This rule shall
  * only be violated if off_t is the only 64-bit data type available and the
  * size of off_t is independent of large file support settings. Keep your
@@ -52,7 +49,7 @@
  *
  */
 
-#if defined(__DJGPP__)
+#ifdef __DJGPP__
 #  define CURL_TYPEOF_CURL_OFF_T     long long
 #  define CURL_FORMAT_CURL_OFF_T     "lld"
 #  define CURL_FORMAT_CURL_OFF_TU    "llu"
@@ -137,13 +134,22 @@
 #    define CURL_TYPEOF_CURL_SOCKLEN_T unsigned int
 #  endif
 
-#elif defined(_WIN32_WCE)
-#  define CURL_TYPEOF_CURL_OFF_T     __int64
-#  define CURL_FORMAT_CURL_OFF_T     "I64d"
-#  define CURL_FORMAT_CURL_OFF_TU    "I64u"
-#  define CURL_SUFFIX_CURL_OFF_T     i64
-#  define CURL_SUFFIX_CURL_OFF_TU    ui64
-#  define CURL_TYPEOF_CURL_SOCKLEN_T int
+#elif defined(UNDER_CE)
+#  if defined(__MINGW32CE__)
+#    define CURL_TYPEOF_CURL_OFF_T     long long
+#    define CURL_FORMAT_CURL_OFF_T     "lld"
+#    define CURL_FORMAT_CURL_OFF_TU    "llu"
+#    define CURL_SUFFIX_CURL_OFF_T     LL
+#    define CURL_SUFFIX_CURL_OFF_TU    ULL
+#    define CURL_TYPEOF_CURL_SOCKLEN_T int
+#  else
+#    define CURL_TYPEOF_CURL_OFF_T     __int64
+#    define CURL_FORMAT_CURL_OFF_T     "I64d"
+#    define CURL_FORMAT_CURL_OFF_TU    "I64u"
+#    define CURL_SUFFIX_CURL_OFF_T     i64
+#    define CURL_SUFFIX_CURL_OFF_TU    ui64
+#    define CURL_TYPEOF_CURL_SOCKLEN_T int
+#  endif
 
 #elif defined(__MINGW32__)
 #  include <inttypes.h>
@@ -330,6 +336,8 @@
 #    define CURL_FORMAT_CURL_OFF_TU    "llu"
 #    define CURL_SUFFIX_CURL_OFF_T     LL
 #    define CURL_SUFFIX_CURL_OFF_TU    ULL
+#    define CURL_POPCOUNT64(x)         __builtin_popcountll(x)
+#    define CURL_CTZ64(x)              __builtin_ctzll(x)
 #  elif defined(__LP64__) || \
         defined(__x86_64__) || defined(__ppc64__) || defined(__sparc64__) || \
         defined(__e2k__) || \
@@ -340,6 +348,8 @@
 #    define CURL_FORMAT_CURL_OFF_TU    "lu"
 #    define CURL_SUFFIX_CURL_OFF_T     L
 #    define CURL_SUFFIX_CURL_OFF_TU    UL
+#    define CURL_POPCOUNT64(x)         __builtin_popcountl(x)
+#    define CURL_CTZ64(x)              __builtin_ctzl(x)
 #  endif
 #  define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t
 #  define CURL_PULL_SYS_TYPES_H      1
diff --git a/extra/curl/curl-8.12.1/include/curl/typecheck-gcc.h b/extra/curl/curl-8.14.1/include/curl/typecheck-gcc.h
similarity index 69%
rename from extra/curl/curl-8.12.1/include/curl/typecheck-gcc.h
rename to extra/curl/curl-8.14.1/include/curl/typecheck-gcc.h
index e532e6997db4..ca0c0ef9d694 100644
--- a/extra/curl/curl-8.12.1/include/curl/typecheck-gcc.h
+++ b/extra/curl/curl-8.14.1/include/curl/typecheck-gcc.h
@@ -40,115 +40,157 @@
  * To add an option that uses the same type as an existing option, you will
  * just need to extend the appropriate _curl_*_option macro
  */
+
 #define curl_easy_setopt(handle, option, value)                         \
   __extension__({                                                       \
-      CURLoption _curl_opt = (option);                                  \
-      if(__builtin_constant_p(_curl_opt)) {                             \
+      if(__builtin_constant_p(option)) {                                \
         CURL_IGNORE_DEPRECATION(                                        \
-          if(curlcheck_long_option(_curl_opt))                          \
+          if(curlcheck_long_option(option))                             \
             if(!curlcheck_long(value))                                  \
               _curl_easy_setopt_err_long();                             \
-          if(curlcheck_off_t_option(_curl_opt))                         \
+          if(curlcheck_off_t_option(option))                            \
             if(!curlcheck_off_t(value))                                 \
               _curl_easy_setopt_err_curl_off_t();                       \
-          if(curlcheck_string_option(_curl_opt))                        \
+          if(curlcheck_string_option(option))                           \
             if(!curlcheck_string(value))                                \
               _curl_easy_setopt_err_string();                           \
-          if(curlcheck_write_cb_option(_curl_opt))                      \
+          if((option) == CURLOPT_PRIVATE) { }                           \
+          if(curlcheck_write_cb_option(option))                         \
             if(!curlcheck_write_cb(value))                              \
               _curl_easy_setopt_err_write_callback();                   \
-          if((_curl_opt) == CURLOPT_RESOLVER_START_FUNCTION)            \
+          if(curlcheck_curl_option(option))                             \
+            if(!curlcheck_curl(value))                                  \
+              _curl_easy_setopt_err_curl();                             \
+          if((option) == CURLOPT_RESOLVER_START_FUNCTION)               \
             if(!curlcheck_resolver_start_callback(value))               \
               _curl_easy_setopt_err_resolver_start_callback();          \
-          if((_curl_opt) == CURLOPT_READFUNCTION)                       \
+          if((option) == CURLOPT_READFUNCTION)                          \
             if(!curlcheck_read_cb(value))                               \
               _curl_easy_setopt_err_read_cb();                          \
-          if((_curl_opt) == CURLOPT_IOCTLFUNCTION)                      \
+          if((option) == CURLOPT_IOCTLFUNCTION)                         \
             if(!curlcheck_ioctl_cb(value))                              \
               _curl_easy_setopt_err_ioctl_cb();                         \
-          if((_curl_opt) == CURLOPT_SOCKOPTFUNCTION)                    \
+          if((option) == CURLOPT_SOCKOPTFUNCTION)                       \
             if(!curlcheck_sockopt_cb(value))                            \
               _curl_easy_setopt_err_sockopt_cb();                       \
-          if((_curl_opt) == CURLOPT_OPENSOCKETFUNCTION)                 \
+          if((option) == CURLOPT_OPENSOCKETFUNCTION)                    \
             if(!curlcheck_opensocket_cb(value))                         \
               _curl_easy_setopt_err_opensocket_cb();                    \
-          if((_curl_opt) == CURLOPT_PROGRESSFUNCTION)                   \
+          if((option) == CURLOPT_PROGRESSFUNCTION)                      \
             if(!curlcheck_progress_cb(value))                           \
               _curl_easy_setopt_err_progress_cb();                      \
-          if((_curl_opt) == CURLOPT_DEBUGFUNCTION)                      \
+          if((option) == CURLOPT_XFERINFOFUNCTION)                      \
+            if(!curlcheck_xferinfo_cb(value))                           \
+              _curl_easy_setopt_err_xferinfo_cb();                      \
+          if((option) == CURLOPT_DEBUGFUNCTION)                         \
             if(!curlcheck_debug_cb(value))                              \
               _curl_easy_setopt_err_debug_cb();                         \
-          if((_curl_opt) == CURLOPT_SSL_CTX_FUNCTION)                   \
+          if((option) == CURLOPT_SSL_CTX_FUNCTION)                      \
             if(!curlcheck_ssl_ctx_cb(value))                            \
               _curl_easy_setopt_err_ssl_ctx_cb();                       \
-          if(curlcheck_conv_cb_option(_curl_opt))                       \
+          if(curlcheck_conv_cb_option(option))                          \
             if(!curlcheck_conv_cb(value))                               \
               _curl_easy_setopt_err_conv_cb();                          \
-          if((_curl_opt) == CURLOPT_SEEKFUNCTION)                       \
+          if((option) == CURLOPT_SEEKFUNCTION)                          \
             if(!curlcheck_seek_cb(value))                               \
               _curl_easy_setopt_err_seek_cb();                          \
-          if(curlcheck_cb_data_option(_curl_opt))                       \
+          if((option) == CURLOPT_CHUNK_BGN_FUNCTION)                    \
+            if(!curlcheck_chunk_bgn_cb(value))                          \
+              _curl_easy_setopt_err_chunk_bgn_cb();                     \
+          if((option) == CURLOPT_CHUNK_END_FUNCTION)                    \
+            if(!curlcheck_chunk_end_cb(value))                          \
+              _curl_easy_setopt_err_chunk_end_cb();                     \
+          if((option) == CURLOPT_CLOSESOCKETFUNCTION)                   \
+            if(!curlcheck_close_socket_cb(value))                       \
+              _curl_easy_setopt_err_close_socket_cb();                  \
+          if((option) == CURLOPT_FNMATCH_FUNCTION)                      \
+            if(!curlcheck_fnmatch_cb(value))                            \
+              _curl_easy_setopt_err_fnmatch_cb();                       \
+          if((option) == CURLOPT_HSTSREADFUNCTION)                      \
+            if(!curlcheck_hstsread_cb(value))                           \
+              _curl_easy_setopt_err_hstsread_cb();                      \
+          if((option) == CURLOPT_HSTSWRITEFUNCTION)                     \
+            if(!curlcheck_hstswrite_cb(value))                          \
+              _curl_easy_setopt_err_hstswrite_cb();                     \
+          if((option) == CURLOPT_SSH_HOSTKEYFUNCTION)                   \
+            if(!curlcheck_ssh_hostkey_cb(value))                        \
+              _curl_easy_setopt_err_ssh_hostkey_cb();                   \
+          if((option) == CURLOPT_SSH_KEYFUNCTION)                       \
+            if(!curlcheck_ssh_key_cb(value))                            \
+              _curl_easy_setopt_err_ssh_key_cb();                       \
+          if((option) == CURLOPT_INTERLEAVEFUNCTION)                    \
+            if(!curlcheck_interleave_cb(value))                         \
+              _curl_easy_setopt_err_interleave_cb();                    \
+          if((option) == CURLOPT_PREREQFUNCTION)                        \
+            if(!curlcheck_prereq_cb(value))                             \
+              _curl_easy_setopt_err_prereq_cb();                        \
+          if((option) == CURLOPT_TRAILERFUNCTION)                       \
+            if(!curlcheck_trailer_cb(value))                            \
+              _curl_easy_setopt_err_trailer_cb();                       \
+          if(curlcheck_cb_data_option(option))                          \
             if(!curlcheck_cb_data(value))                               \
               _curl_easy_setopt_err_cb_data();                          \
-          if((_curl_opt) == CURLOPT_ERRORBUFFER)                        \
+          if((option) == CURLOPT_ERRORBUFFER)                           \
             if(!curlcheck_error_buffer(value))                          \
               _curl_easy_setopt_err_error_buffer();                     \
-          if((_curl_opt) == CURLOPT_STDERR)                             \
+          if((option) == CURLOPT_CURLU)                                 \
+            if(!curlcheck_ptr((value), CURLU))                          \
+              _curl_easy_setopt_err_curlu();                    \
+          if((option) == CURLOPT_STDERR)                                \
             if(!curlcheck_FILE(value))                                  \
               _curl_easy_setopt_err_FILE();                             \
-          if(curlcheck_postfields_option(_curl_opt))                    \
+          if(curlcheck_postfields_option(option))                       \
             if(!curlcheck_postfields(value))                            \
               _curl_easy_setopt_err_postfields();                       \
-          if((_curl_opt) == CURLOPT_HTTPPOST)                           \
+          if((option) == CURLOPT_HTTPPOST)                              \
             if(!curlcheck_arr((value), struct curl_httppost))           \
               _curl_easy_setopt_err_curl_httpost();                     \
-          if((_curl_opt) == CURLOPT_MIMEPOST)                           \
+          if((option) == CURLOPT_MIMEPOST)                              \
             if(!curlcheck_ptr((value), curl_mime))                      \
               _curl_easy_setopt_err_curl_mimepost();                    \
-          if(curlcheck_slist_option(_curl_opt))                         \
+          if(curlcheck_slist_option(option))                            \
             if(!curlcheck_arr((value), struct curl_slist))              \
               _curl_easy_setopt_err_curl_slist();                       \
-          if((_curl_opt) == CURLOPT_SHARE)                              \
+          if((option) == CURLOPT_SHARE)                                 \
             if(!curlcheck_ptr((value), CURLSH))                         \
               _curl_easy_setopt_err_CURLSH();                           \
-        )                                                               \
-      }                                                                 \
-      curl_easy_setopt(handle, _curl_opt, value);                       \
+          )                                                             \
+          }                                                             \
+      curl_easy_setopt(handle, option, value);                          \
     })
 
 /* wraps curl_easy_getinfo() with typechecking */
 #define curl_easy_getinfo(handle, info, arg)                            \
   __extension__({                                                       \
-      CURLINFO _curl_info = (info);                                     \
-      if(__builtin_constant_p(_curl_info)) {                            \
+      if(__builtin_constant_p(info)) {                                  \
         CURL_IGNORE_DEPRECATION(                                        \
-          if(curlcheck_string_info(_curl_info))                         \
+          if(curlcheck_string_info(info))                               \
             if(!curlcheck_arr((arg), char *))                           \
               _curl_easy_getinfo_err_string();                          \
-          if(curlcheck_long_info(_curl_info))                           \
+          if(curlcheck_long_info(info))                                 \
             if(!curlcheck_arr((arg), long))                             \
               _curl_easy_getinfo_err_long();                            \
-          if(curlcheck_double_info(_curl_info))                         \
+          if(curlcheck_double_info(info))                               \
             if(!curlcheck_arr((arg), double))                           \
               _curl_easy_getinfo_err_double();                          \
-          if(curlcheck_slist_info(_curl_info))                          \
+          if(curlcheck_slist_info(info))                                \
             if(!curlcheck_arr((arg), struct curl_slist *))              \
               _curl_easy_getinfo_err_curl_slist();                      \
-          if(curlcheck_tlssessioninfo_info(_curl_info))                 \
+          if(curlcheck_tlssessioninfo_info(info))                       \
             if(!curlcheck_arr((arg), struct curl_tlssessioninfo *))     \
-              _curl_easy_getinfo_err_curl_tlssesssioninfo();            \
-          if(curlcheck_certinfo_info(_curl_info))                       \
+              _curl_easy_getinfo_err_curl_tlssessioninfo();            \
+          if(curlcheck_certinfo_info(info))                             \
             if(!curlcheck_arr((arg), struct curl_certinfo *))           \
               _curl_easy_getinfo_err_curl_certinfo();                   \
-          if(curlcheck_socket_info(_curl_info))                         \
+          if(curlcheck_socket_info(info))                               \
             if(!curlcheck_arr((arg), curl_socket_t))                    \
               _curl_easy_getinfo_err_curl_socket();                     \
-          if(curlcheck_off_t_info(_curl_info))                          \
+          if(curlcheck_off_t_info(info))                                \
             if(!curlcheck_arr((arg), curl_off_t))                       \
               _curl_easy_getinfo_err_curl_off_t();                      \
-        )                                                               \
-      }                                                                 \
-      curl_easy_getinfo(handle, _curl_info, arg);                       \
+          )                                                             \
+          }                                                             \
+      curl_easy_getinfo(handle, info, arg);                             \
     })
 
 /*
@@ -157,7 +199,6 @@
 #define curl_share_setopt(share,opt,param) curl_share_setopt(share,opt,param)
 #define curl_multi_setopt(handle,opt,param) curl_multi_setopt(handle,opt,param)
 
-
 /* the actual warnings, triggered by calling the _curl_easy_setopt_err*
  * functions */
 
@@ -168,187 +209,212 @@
   id(void) { __asm__(""); }
 
 CURLWARNING(_curl_easy_setopt_err_long,
-  "curl_easy_setopt expects a long argument for this option")
+            "curl_easy_setopt expects a long argument")
 CURLWARNING(_curl_easy_setopt_err_curl_off_t,
-  "curl_easy_setopt expects a curl_off_t argument for this option")
+            "curl_easy_setopt expects a curl_off_t argument")
 CURLWARNING(_curl_easy_setopt_err_string,
-              "curl_easy_setopt expects a "
-              "string ('char *' or char[]) argument for this option"
-  )
+            "curl_easy_setopt expects a "
+            "string ('char *' or char[]) argument")
 CURLWARNING(_curl_easy_setopt_err_write_callback,
-  "curl_easy_setopt expects a curl_write_callback argument for this option")
+            "curl_easy_setopt expects a curl_write_callback argument")
 CURLWARNING(_curl_easy_setopt_err_resolver_start_callback,
-              "curl_easy_setopt expects a "
-              "curl_resolver_start_callback argument for this option"
-  )
+            "curl_easy_setopt expects a "
+            "curl_resolver_start_callback argument")
 CURLWARNING(_curl_easy_setopt_err_read_cb,
-  "curl_easy_setopt expects a curl_read_callback argument for this option")
+            "curl_easy_setopt expects a curl_read_callback argument")
 CURLWARNING(_curl_easy_setopt_err_ioctl_cb,
-  "curl_easy_setopt expects a curl_ioctl_callback argument for this option")
+            "curl_easy_setopt expects a curl_ioctl_callback argument")
 CURLWARNING(_curl_easy_setopt_err_sockopt_cb,
-  "curl_easy_setopt expects a curl_sockopt_callback argument for this option")
+            "curl_easy_setopt expects a curl_sockopt_callback argument")
 CURLWARNING(_curl_easy_setopt_err_opensocket_cb,
-              "curl_easy_setopt expects a "
-              "curl_opensocket_callback argument for this option"
-  )
+            "curl_easy_setopt expects a "
+            "curl_opensocket_callback argument")
 CURLWARNING(_curl_easy_setopt_err_progress_cb,
-  "curl_easy_setopt expects a curl_progress_callback argument for this option")
+            "curl_easy_setopt expects a curl_progress_callback argument")
+CURLWARNING(_curl_easy_setopt_err_xferinfo_cb,
+            "curl_easy_setopt expects a curl_xferinfo_callback argument")
 CURLWARNING(_curl_easy_setopt_err_debug_cb,
-  "curl_easy_setopt expects a curl_debug_callback argument for this option")
+            "curl_easy_setopt expects a curl_debug_callback argument")
 CURLWARNING(_curl_easy_setopt_err_ssl_ctx_cb,
-  "curl_easy_setopt expects a curl_ssl_ctx_callback argument for this option")
+            "curl_easy_setopt expects a curl_ssl_ctx_callback argument")
 CURLWARNING(_curl_easy_setopt_err_conv_cb,
-  "curl_easy_setopt expects a curl_conv_callback argument for this option")
+            "curl_easy_setopt expects a curl_conv_callback argument")
 CURLWARNING(_curl_easy_setopt_err_seek_cb,
-  "curl_easy_setopt expects a curl_seek_callback argument for this option")
+            "curl_easy_setopt expects a curl_seek_callback argument")
 CURLWARNING(_curl_easy_setopt_err_cb_data,
-              "curl_easy_setopt expects a "
-              "private data pointer as argument for this option")
+            "curl_easy_setopt expects a "
+            "private data pointer as argument")
+CURLWARNING(_curl_easy_setopt_err_chunk_bgn_cb,
+            "curl_easy_setopt expects a curl_chunk_bgn_callback argument")
+CURLWARNING(_curl_easy_setopt_err_chunk_end_cb,
+            "curl_easy_setopt expects a curl_chunk_end_callback argument")
+CURLWARNING(_curl_easy_setopt_err_close_socket_cb,
+            "curl_easy_setopt expects a curl_closesocket_callback argument")
+CURLWARNING(_curl_easy_setopt_err_fnmatch_cb,
+            "curl_easy_setopt expects a curl_fnmatch_callback argument")
+CURLWARNING(_curl_easy_setopt_err_hstsread_cb,
+            "curl_easy_setopt expects a curl_hstsread_callback argument")
+CURLWARNING(_curl_easy_setopt_err_hstswrite_cb,
+            "curl_easy_setopt expects a curl_hstswrite_callback argument")
+CURLWARNING(_curl_easy_setopt_err_ssh_key_cb,
+            "curl_easy_setopt expects a curl_sshkeycallback argument")
+CURLWARNING(_curl_easy_setopt_err_ssh_hostkey_cb,
+            "curl_easy_setopt expects a curl_sshhostkeycallback argument")
+CURLWARNING(_curl_easy_setopt_err_interleave_cb,
+            "curl_easy_setopt expects a curl_interleave_callback argument")
+CURLWARNING(_curl_easy_setopt_err_prereq_cb,
+            "curl_easy_setopt expects a curl_prereq_callback argument")
+CURLWARNING(_curl_easy_setopt_err_trailer_cb,
+            "curl_easy_setopt expects a curl_trailerfunc_ok argument")
 CURLWARNING(_curl_easy_setopt_err_error_buffer,
-              "curl_easy_setopt expects a "
-              "char buffer of CURL_ERROR_SIZE as argument for this option")
+            "curl_easy_setopt expects a "
+            "char buffer of CURL_ERROR_SIZE as argument")
+CURLWARNING(_curl_easy_setopt_err_curlu,
+            "curl_easy_setopt expects a 'CURLU *' argument")
+CURLWARNING(_curl_easy_setopt_err_curl,
+            "curl_easy_setopt expects a 'CURL *' argument")
 CURLWARNING(_curl_easy_setopt_err_FILE,
-  "curl_easy_setopt expects a 'FILE *' argument for this option")
+            "curl_easy_setopt expects a 'FILE *' argument")
 CURLWARNING(_curl_easy_setopt_err_postfields,
-  "curl_easy_setopt expects a 'void *' or 'char *' argument for this option")
+            "curl_easy_setopt expects a 'void *' or 'char *' argument")
 CURLWARNING(_curl_easy_setopt_err_curl_httpost,
-              "curl_easy_setopt expects a 'struct curl_httppost *' "
-              "argument for this option")
+            "curl_easy_setopt expects a 'struct curl_httppost *' "
+            "argument")
 CURLWARNING(_curl_easy_setopt_err_curl_mimepost,
-              "curl_easy_setopt expects a 'curl_mime *' "
-              "argument for this option")
+            "curl_easy_setopt expects a 'curl_mime *' "
+            "argument")
 CURLWARNING(_curl_easy_setopt_err_curl_slist,
-  "curl_easy_setopt expects a 'struct curl_slist *' argument for this option")
+            "curl_easy_setopt expects a 'struct curl_slist *' argument")
 CURLWARNING(_curl_easy_setopt_err_CURLSH,
-  "curl_easy_setopt expects a CURLSH* argument for this option")
-
+            "curl_easy_setopt expects a CURLSH* argument")
 CURLWARNING(_curl_easy_getinfo_err_string,
-  "curl_easy_getinfo expects a pointer to 'char *' for this info")
+            "curl_easy_getinfo expects a pointer to 'char *'")
 CURLWARNING(_curl_easy_getinfo_err_long,
-  "curl_easy_getinfo expects a pointer to long for this info")
+            "curl_easy_getinfo expects a pointer to long")
 CURLWARNING(_curl_easy_getinfo_err_double,
-  "curl_easy_getinfo expects a pointer to double for this info")
+            "curl_easy_getinfo expects a pointer to double")
 CURLWARNING(_curl_easy_getinfo_err_curl_slist,
-  "curl_easy_getinfo expects a pointer to 'struct curl_slist *' for this info")
-CURLWARNING(_curl_easy_getinfo_err_curl_tlssesssioninfo,
-              "curl_easy_getinfo expects a pointer to "
-              "'struct curl_tlssessioninfo *' for this info")
+            "curl_easy_getinfo expects a pointer to 'struct curl_slist *'")
+CURLWARNING(_curl_easy_getinfo_err_curl_tlssessioninfo,
+            "curl_easy_getinfo expects a pointer to "
+            "'struct curl_tlssessioninfo *'")
 CURLWARNING(_curl_easy_getinfo_err_curl_certinfo,
-              "curl_easy_getinfo expects a pointer to "
-              "'struct curl_certinfo *' for this info")
+            "curl_easy_getinfo expects a pointer to "
+            "'struct curl_certinfo *'")
 CURLWARNING(_curl_easy_getinfo_err_curl_socket,
-  "curl_easy_getinfo expects a pointer to curl_socket_t for this info")
+            "curl_easy_getinfo expects a pointer to curl_socket_t")
 CURLWARNING(_curl_easy_getinfo_err_curl_off_t,
-  "curl_easy_getinfo expects a pointer to curl_off_t for this info")
+            "curl_easy_getinfo expects a pointer to curl_off_t")
 
 /* groups of curl_easy_setops options that take the same type of argument */
 
-/* To add a new option to one of the groups, just add
- *   (option) == CURLOPT_SOMETHING
- * to the or-expression. If the option takes a long or curl_off_t, you do not
- * have to do anything
- */
-
 /* evaluates to true if option takes a long argument */
 #define curlcheck_long_option(option)                   \
   (0 < (option) && (option) < CURLOPTTYPE_OBJECTPOINT)
 
-#define curlcheck_off_t_option(option)          \
+#define curlcheck_off_t_option(option)                                  \
   (((option) > CURLOPTTYPE_OFF_T) && ((option) < CURLOPTTYPE_BLOB))
 
+/* option takes a CURL * argument */
+#define curlcheck_curl_option(option)                                 \
+  ((option) == CURLOPT_STREAM_DEPENDS ||                              \
+   (option) == CURLOPT_STREAM_DEPENDS_E ||                            \
+   0)
+
 /* evaluates to true if option takes a char* argument */
-#define curlcheck_string_option(option)                                       \
-  ((option) == CURLOPT_ABSTRACT_UNIX_SOCKET ||                                \
-   (option) == CURLOPT_ACCEPT_ENCODING ||                                     \
-   (option) == CURLOPT_ALTSVC ||                                              \
-   (option) == CURLOPT_CAINFO ||                                              \
-   (option) == CURLOPT_CAPATH ||                                              \
-   (option) == CURLOPT_COOKIE ||                                              \
-   (option) == CURLOPT_COOKIEFILE ||                                          \
-   (option) == CURLOPT_COOKIEJAR ||                                           \
-   (option) == CURLOPT_COOKIELIST ||                                          \
-   (option) == CURLOPT_CRLFILE ||                                             \
-   (option) == CURLOPT_CUSTOMREQUEST ||                                       \
-   (option) == CURLOPT_DEFAULT_PROTOCOL ||                                    \
-   (option) == CURLOPT_DNS_INTERFACE ||                                       \
-   (option) == CURLOPT_DNS_LOCAL_IP4 ||                                       \
-   (option) == CURLOPT_DNS_LOCAL_IP6 ||                                       \
-   (option) == CURLOPT_DNS_SERVERS ||                                         \
-   (option) == CURLOPT_DOH_URL ||                                             \
-   (option) == CURLOPT_ECH        ||                                          \
-   (option) == CURLOPT_EGDSOCKET ||                                           \
-   (option) == CURLOPT_FTP_ACCOUNT ||                                         \
-   (option) == CURLOPT_FTP_ALTERNATIVE_TO_USER ||                             \
-   (option) == CURLOPT_FTPPORT ||                                             \
-   (option) == CURLOPT_HSTS ||                                                \
-   (option) == CURLOPT_HAPROXY_CLIENT_IP ||                                   \
-   (option) == CURLOPT_INTERFACE ||                                           \
-   (option) == CURLOPT_ISSUERCERT ||                                          \
-   (option) == CURLOPT_KEYPASSWD ||                                           \
-   (option) == CURLOPT_KRBLEVEL ||                                            \
-   (option) == CURLOPT_LOGIN_OPTIONS ||                                       \
-   (option) == CURLOPT_MAIL_AUTH ||                                           \
-   (option) == CURLOPT_MAIL_FROM ||                                           \
-   (option) == CURLOPT_NETRC_FILE ||                                          \
-   (option) == CURLOPT_NOPROXY ||                                             \
-   (option) == CURLOPT_PASSWORD ||                                            \
-   (option) == CURLOPT_PINNEDPUBLICKEY ||                                     \
-   (option) == CURLOPT_PRE_PROXY ||                                           \
-   (option) == CURLOPT_PROTOCOLS_STR ||                                       \
-   (option) == CURLOPT_PROXY ||                                               \
-   (option) == CURLOPT_PROXY_CAINFO ||                                        \
-   (option) == CURLOPT_PROXY_CAPATH ||                                        \
-   (option) == CURLOPT_PROXY_CRLFILE ||                                       \
-   (option) == CURLOPT_PROXY_ISSUERCERT ||                                    \
-   (option) == CURLOPT_PROXY_KEYPASSWD ||                                     \
-   (option) == CURLOPT_PROXY_PINNEDPUBLICKEY ||                               \
-   (option) == CURLOPT_PROXY_SERVICE_NAME ||                                  \
-   (option) == CURLOPT_PROXY_SSL_CIPHER_LIST ||                               \
-   (option) == CURLOPT_PROXY_SSLCERT ||                                       \
-   (option) == CURLOPT_PROXY_SSLCERTTYPE ||                                   \
-   (option) == CURLOPT_PROXY_SSLKEY ||                                        \
-   (option) == CURLOPT_PROXY_SSLKEYTYPE ||                                    \
-   (option) == CURLOPT_PROXY_TLS13_CIPHERS ||                                 \
-   (option) == CURLOPT_PROXY_TLSAUTH_PASSWORD ||                              \
-   (option) == CURLOPT_PROXY_TLSAUTH_TYPE ||                                  \
-   (option) == CURLOPT_PROXY_TLSAUTH_USERNAME ||                              \
-   (option) == CURLOPT_PROXYPASSWORD ||                                       \
-   (option) == CURLOPT_PROXYUSERNAME ||                                       \
-   (option) == CURLOPT_PROXYUSERPWD ||                                        \
-   (option) == CURLOPT_RANDOM_FILE ||                                         \
-   (option) == CURLOPT_RANGE ||                                               \
-   (option) == CURLOPT_REDIR_PROTOCOLS_STR ||                                 \
-   (option) == CURLOPT_REFERER ||                                             \
-   (option) == CURLOPT_REQUEST_TARGET ||                                      \
-   (option) == CURLOPT_RTSP_SESSION_ID ||                                     \
-   (option) == CURLOPT_RTSP_STREAM_URI ||                                     \
-   (option) == CURLOPT_RTSP_TRANSPORT ||                                      \
-   (option) == CURLOPT_SASL_AUTHZID ||                                        \
-   (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 ||                                  \
-   (option) == CURLOPT_SSLCERT ||                                             \
-   (option) == CURLOPT_SSLCERTTYPE ||                                         \
-   (option) == CURLOPT_SSLENGINE ||                                           \
-   (option) == CURLOPT_SSLKEY ||                                              \
-   (option) == CURLOPT_SSLKEYTYPE ||                                          \
-   (option) == CURLOPT_SSL_CIPHER_LIST ||                                     \
-   (option) == CURLOPT_TLS13_CIPHERS ||                                       \
-   (option) == CURLOPT_TLSAUTH_PASSWORD ||                                    \
-   (option) == CURLOPT_TLSAUTH_TYPE ||                                        \
-   (option) == CURLOPT_TLSAUTH_USERNAME ||                                    \
-   (option) == CURLOPT_UNIX_SOCKET_PATH ||                                    \
-   (option) == CURLOPT_URL ||                                                 \
-   (option) == CURLOPT_USERAGENT ||                                           \
-   (option) == CURLOPT_USERNAME ||                                            \
-   (option) == CURLOPT_AWS_SIGV4 ||                                           \
-   (option) == CURLOPT_USERPWD ||                                             \
-   (option) == CURLOPT_XOAUTH2_BEARER ||                                      \
-   (option) == CURLOPT_SSL_EC_CURVES ||                                       \
+#define curlcheck_string_option(option)                                 \
+  ((option) == CURLOPT_ABSTRACT_UNIX_SOCKET ||                          \
+   (option) == CURLOPT_ACCEPT_ENCODING ||                               \
+   (option) == CURLOPT_ALTSVC ||                                        \
+   (option) == CURLOPT_CAINFO ||                                        \
+   (option) == CURLOPT_CAPATH ||                                        \
+   (option) == CURLOPT_COOKIE ||                                        \
+   (option) == CURLOPT_COOKIEFILE ||                                    \
+   (option) == CURLOPT_COOKIEJAR ||                                     \
+   (option) == CURLOPT_COOKIELIST ||                                    \
+   (option) == CURLOPT_CRLFILE ||                                       \
+   (option) == CURLOPT_CUSTOMREQUEST ||                                 \
+   (option) == CURLOPT_DEFAULT_PROTOCOL ||                              \
+   (option) == CURLOPT_DNS_INTERFACE ||                                 \
+   (option) == CURLOPT_DNS_LOCAL_IP4 ||                                 \
+   (option) == CURLOPT_DNS_LOCAL_IP6 ||                                 \
+   (option) == CURLOPT_DNS_SERVERS ||                                   \
+   (option) == CURLOPT_DOH_URL ||                                       \
+   (option) == CURLOPT_ECH ||                                           \
+   (option) == CURLOPT_EGDSOCKET ||                                     \
+   (option) == CURLOPT_FTP_ACCOUNT ||                                   \
+   (option) == CURLOPT_FTP_ALTERNATIVE_TO_USER ||                       \
+   (option) == CURLOPT_FTPPORT ||                                       \
+   (option) == CURLOPT_HAPROXY_CLIENT_IP ||                             \
+   (option) == CURLOPT_HSTS ||                                          \
+   (option) == CURLOPT_INTERFACE ||                                     \
+   (option) == CURLOPT_ISSUERCERT ||                                    \
+   (option) == CURLOPT_KEYPASSWD ||                                     \
+   (option) == CURLOPT_KRBLEVEL ||                                      \
+   (option) == CURLOPT_LOGIN_OPTIONS ||                                 \
+   (option) == CURLOPT_MAIL_AUTH ||                                     \
+   (option) == CURLOPT_MAIL_FROM ||                                     \
+   (option) == CURLOPT_NETRC_FILE ||                                    \
+   (option) == CURLOPT_NOPROXY ||                                       \
+   (option) == CURLOPT_PASSWORD ||                                      \
+   (option) == CURLOPT_PINNEDPUBLICKEY ||                               \
+   (option) == CURLOPT_PRE_PROXY ||                                     \
+   (option) == CURLOPT_PROTOCOLS_STR ||                                 \
+   (option) == CURLOPT_PROXY ||                                         \
+   (option) == CURLOPT_PROXY_CAINFO ||                                  \
+   (option) == CURLOPT_PROXY_CAPATH ||                                  \
+   (option) == CURLOPT_PROXY_CRLFILE ||                                 \
+   (option) == CURLOPT_PROXY_ISSUERCERT ||                              \
+   (option) == CURLOPT_PROXY_KEYPASSWD ||                               \
+   (option) == CURLOPT_PROXY_PINNEDPUBLICKEY ||                         \
+   (option) == CURLOPT_PROXY_SERVICE_NAME ||                            \
+   (option) == CURLOPT_PROXY_SSL_CIPHER_LIST ||                         \
+   (option) == CURLOPT_PROXY_SSLCERT ||                                 \
+   (option) == CURLOPT_PROXY_SSLCERTTYPE ||                             \
+   (option) == CURLOPT_PROXY_SSLKEY ||                                  \
+   (option) == CURLOPT_PROXY_SSLKEYTYPE ||                              \
+   (option) == CURLOPT_PROXY_TLS13_CIPHERS ||                           \
+   (option) == CURLOPT_PROXY_TLSAUTH_PASSWORD ||                        \
+   (option) == CURLOPT_PROXY_TLSAUTH_TYPE ||                            \
+   (option) == CURLOPT_PROXY_TLSAUTH_USERNAME ||                        \
+   (option) == CURLOPT_PROXYPASSWORD ||                                 \
+   (option) == CURLOPT_PROXYUSERNAME ||                                 \
+   (option) == CURLOPT_PROXYUSERPWD ||                                  \
+   (option) == CURLOPT_RANDOM_FILE ||                                   \
+   (option) == CURLOPT_RANGE ||                                         \
+   (option) == CURLOPT_REDIR_PROTOCOLS_STR ||                           \
+   (option) == CURLOPT_REFERER ||                                       \
+   (option) == CURLOPT_REQUEST_TARGET ||                                \
+   (option) == CURLOPT_RTSP_SESSION_ID ||                               \
+   (option) == CURLOPT_RTSP_STREAM_URI ||                               \
+   (option) == CURLOPT_RTSP_TRANSPORT ||                                \
+   (option) == CURLOPT_SASL_AUTHZID ||                                  \
+   (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 ||                            \
+   (option) == CURLOPT_SSLCERT ||                                       \
+   (option) == CURLOPT_SSLCERTTYPE ||                                   \
+   (option) == CURLOPT_SSLENGINE ||                                     \
+   (option) == CURLOPT_SSLKEY ||                                        \
+   (option) == CURLOPT_SSLKEYTYPE ||                                    \
+   (option) == CURLOPT_SSL_CIPHER_LIST ||                               \
+   (option) == CURLOPT_SSL_EC_CURVES ||                                 \
+   (option) == CURLOPT_SSL_SIGNATURE_ALGORITHMS ||                      \
+   (option) == CURLOPT_TLS13_CIPHERS ||                                 \
+   (option) == CURLOPT_TLSAUTH_PASSWORD ||                              \
+   (option) == CURLOPT_TLSAUTH_TYPE ||                                  \
+   (option) == CURLOPT_TLSAUTH_USERNAME ||                              \
+   (option) == CURLOPT_UNIX_SOCKET_PATH ||                              \
+   (option) == CURLOPT_URL ||                                           \
+   (option) == CURLOPT_USERAGENT ||                                     \
+   (option) == CURLOPT_USERNAME ||                                      \
+   (option) == CURLOPT_AWS_SIGV4 ||                                     \
+   (option) == CURLOPT_USERPWD ||                                       \
+   (option) == CURLOPT_XOAUTH2_BEARER ||                                \
    0)
 
 /* evaluates to true if option takes a curl_write_callback argument */
@@ -375,7 +441,7 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t,
    (option) == CURLOPT_IOCTLDATA ||                                           \
    (option) == CURLOPT_OPENSOCKETDATA ||                                      \
    (option) == CURLOPT_PREREQDATA ||                                          \
-   (option) == CURLOPT_PROGRESSDATA ||                                        \
+   (option) == CURLOPT_XFERINFODATA ||                                        \
    (option) == CURLOPT_READDATA ||                                            \
    (option) == CURLOPT_SEEKDATA ||                                            \
    (option) == CURLOPT_SOCKOPTDATA ||                                         \
@@ -479,22 +545,36 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t,
    curlcheck_arr((expr), signed char) ||                                \
    curlcheck_arr((expr), unsigned char))
 
+/* evaluates to true if expr is a CURL * */
+#define curlcheck_curl(expr)                                          \
+  (curlcheck_NULL(expr) ||                                              \
+   __builtin_types_compatible_p(__typeof__(expr), CURL *))
+
+
 /* evaluates to true if expr is a long (no matter the signedness)
  * XXX: for now, int is also accepted (and therefore short and char, which
  * are promoted to int when passed to a variadic function) */
-#define curlcheck_long(expr)                                                  \
-  (__builtin_types_compatible_p(__typeof__(expr), long) ||                    \
-   __builtin_types_compatible_p(__typeof__(expr), signed long) ||             \
-   __builtin_types_compatible_p(__typeof__(expr), unsigned long) ||           \
-   __builtin_types_compatible_p(__typeof__(expr), int) ||                     \
-   __builtin_types_compatible_p(__typeof__(expr), signed int) ||              \
-   __builtin_types_compatible_p(__typeof__(expr), unsigned int) ||            \
-   __builtin_types_compatible_p(__typeof__(expr), short) ||                   \
-   __builtin_types_compatible_p(__typeof__(expr), signed short) ||            \
-   __builtin_types_compatible_p(__typeof__(expr), unsigned short) ||          \
-   __builtin_types_compatible_p(__typeof__(expr), char) ||                    \
-   __builtin_types_compatible_p(__typeof__(expr), signed char) ||             \
-   __builtin_types_compatible_p(__typeof__(expr), unsigned char))
+#define curlcheck_long(expr)                                            \
+  (                                                                     \
+  ((sizeof(long) != sizeof(int)) &&                                     \
+   (__builtin_types_compatible_p(__typeof__(expr), long) ||             \
+    __builtin_types_compatible_p(__typeof__(expr), signed long) ||      \
+    __builtin_types_compatible_p(__typeof__(expr), unsigned long)))     \
+  ||                                                                    \
+  ((sizeof(long) == sizeof(int)) &&                                     \
+  (__builtin_types_compatible_p(__typeof__(expr), long) ||              \
+   __builtin_types_compatible_p(__typeof__(expr), signed long) ||       \
+   __builtin_types_compatible_p(__typeof__(expr), unsigned long) ||     \
+   __builtin_types_compatible_p(__typeof__(expr), int) ||               \
+   __builtin_types_compatible_p(__typeof__(expr), signed int) ||        \
+   __builtin_types_compatible_p(__typeof__(expr), unsigned int) ||      \
+   __builtin_types_compatible_p(__typeof__(expr), short) ||             \
+   __builtin_types_compatible_p(__typeof__(expr), signed short) ||      \
+   __builtin_types_compatible_p(__typeof__(expr), unsigned short) ||    \
+   __builtin_types_compatible_p(__typeof__(expr), char) ||              \
+   __builtin_types_compatible_p(__typeof__(expr), signed char) ||       \
+   __builtin_types_compatible_p(__typeof__(expr), unsigned char)))      \
+                                                                  )
 
 /* evaluates to true if expr is of type curl_off_t */
 #define curlcheck_off_t(expr)                                   \
@@ -629,6 +709,11 @@ typedef int (*_curl_progress_callback1)(void *,
 typedef int (*_curl_progress_callback2)(const void *,
     double, double, double, double);
 
+/* evaluates to true if expr is of type curl_xferinfo_callback */
+#define curlcheck_xferinfo_cb(expr)                                     \
+  (curlcheck_NULL(expr) ||                                              \
+   curlcheck_cb_compatible((expr), curl_xferinfo_callback))
+
 /* evaluates to true if expr is of type curl_debug_callback or "similar" */
 #define curlcheck_debug_cb(expr)                                        \
   (curlcheck_NULL(expr) ||                                              \
@@ -714,5 +799,69 @@ typedef CURLcode (*_curl_conv_callback4)(const void *, size_t length);
 typedef CURLcode (*_curl_seek_callback1)(void *, curl_off_t, int);
 typedef CURLcode (*_curl_seek_callback2)(const void *, curl_off_t, int);
 
+/* evaluates to true if expr is of type curl_chunk_bgn_callback */
+#define curlcheck_chunk_bgn_cb(expr)                                    \
+  (curlcheck_NULL(expr) ||                                              \
+   curlcheck_cb_compatible((expr), curl_chunk_bgn_callback) ||          \
+   curlcheck_cb_compatible((expr), _curl_chunk_bgn_callback1) ||        \
+   curlcheck_cb_compatible((expr), _curl_chunk_bgn_callback2))
+typedef long (*_curl_chunk_bgn_callback1)(struct curl_fileinfo *,
+                                          void *, int);
+typedef long (*_curl_chunk_bgn_callback2)(void *, void *, int);
+
+/* evaluates to true if expr is of type curl_chunk_end_callback */
+#define curlcheck_chunk_end_cb(expr)                                    \
+  (curlcheck_NULL(expr) ||                                              \
+   curlcheck_cb_compatible((expr), curl_chunk_end_callback))
+
+/* evaluates to true if expr is of type curl_closesocket_callback */
+#define curlcheck_close_socket_cb(expr)                                 \
+  (curlcheck_NULL(expr) ||                                              \
+   curlcheck_cb_compatible((expr), curl_closesocket_callback))
+
+/* evaluates to true if expr is of type curl_fnmatch_callback */
+#define curlcheck_fnmatch_cb(expr)                                      \
+  (curlcheck_NULL(expr) ||                                              \
+   curlcheck_cb_compatible((expr), curl_fnmatch_callback))
+
+/* evaluates to true if expr is of type curl_hstsread_callback */
+#define curlcheck_hstsread_cb(expr)                                     \
+  (curlcheck_NULL(expr) ||                                              \
+   curlcheck_cb_compatible((expr), curl_hstsread_callback))
+
+/* evaluates to true if expr is of type curl_hstswrite_callback */
+#define curlcheck_hstswrite_cb(expr)                                    \
+  (curlcheck_NULL(expr) ||                                              \
+   curlcheck_cb_compatible((expr), curl_hstswrite_callback))
+
+/* evaluates to true if expr is of type curl_sshhostkeycallback */
+#define curlcheck_ssh_hostkey_cb(expr)                                  \
+  (curlcheck_NULL(expr) ||                                              \
+   curlcheck_cb_compatible((expr), curl_sshhostkeycallback))
+
+/* evaluates to true if expr is of type curl_sshkeycallback */
+#define curlcheck_ssh_key_cb(expr)                                  \
+  (curlcheck_NULL(expr) ||                                          \
+   curlcheck_cb_compatible((expr), curl_sshkeycallback))
+
+/* evaluates to true if expr is of type curl_interleave_callback */
+#define curlcheck_interleave_cb(expr)                                   \
+  (curlcheck_NULL(expr) ||                                              \
+   curlcheck_cb_compatible((expr), _curl_interleave_callback1) ||       \
+   curlcheck_cb_compatible((expr), _curl_interleave_callback2))
+typedef size_t (*_curl_interleave_callback1)(void *p, size_t s,
+                                             size_t n, void *u);
+typedef size_t (*_curl_interleave_callback2)(char *p, size_t s,
+                                             size_t n, void *u);
+
+/* evaluates to true if expr is of type curl_prereq_callback */
+#define curlcheck_prereq_cb(expr)                                    \
+  (curlcheck_NULL(expr) ||                                           \
+   curlcheck_cb_compatible((expr), curl_prereq_callback))
+
+/* evaluates to true if expr is of type curl_trailer_callback */
+#define curlcheck_trailer_cb(expr)                                    \
+  (curlcheck_NULL(expr) ||                                            \
+   curlcheck_cb_compatible((expr), curl_trailer_callback))
 
 #endif /* CURLINC_TYPECHECK_GCC_H */
diff --git a/extra/curl/curl-8.12.1/include/curl/urlapi.h b/extra/curl/curl-8.14.1/include/curl/urlapi.h
similarity index 100%
rename from extra/curl/curl-8.12.1/include/curl/urlapi.h
rename to extra/curl/curl-8.14.1/include/curl/urlapi.h
diff --git a/extra/curl/curl-8.12.1/include/curl/websockets.h b/extra/curl/curl-8.14.1/include/curl/websockets.h
similarity index 97%
rename from extra/curl/curl-8.12.1/include/curl/websockets.h
rename to extra/curl/curl-8.14.1/include/curl/websockets.h
index 6ef6a2bc92ec..afb86b4ebcf9 100644
--- a/extra/curl/curl-8.12.1/include/curl/websockets.h
+++ b/extra/curl/curl-8.14.1/include/curl/websockets.h
@@ -73,7 +73,8 @@ CURL_EXTERN CURLcode curl_ws_send(CURL *curl, const void *buffer,
                                   unsigned int flags);
 
 /* bits for the CURLOPT_WS_OPTIONS bitmask: */
-#define CURLWS_RAW_MODE (1<<0)
+#define CURLWS_RAW_MODE   (1<<0)
+#define CURLWS_NOAUTOPONG (1<<1)
 
 CURL_EXTERN const struct curl_ws_frame *curl_ws_meta(CURL *curl);
 
diff --git a/extra/curl/curl-8.12.1/install-sh b/extra/curl/curl-8.14.1/install-sh
similarity index 100%
rename from extra/curl/curl-8.12.1/install-sh
rename to extra/curl/curl-8.14.1/install-sh
diff --git a/extra/curl/curl-8.12.1/lib/.checksrc b/extra/curl/curl-8.14.1/lib/.checksrc
similarity index 63%
rename from extra/curl/curl-8.12.1/lib/.checksrc
rename to extra/curl/curl-8.14.1/lib/.checksrc
index 3985707fd9e4..3c6aa6920513 100644
--- a/extra/curl/curl-8.12.1/lib/.checksrc
+++ b/extra/curl/curl-8.14.1/lib/.checksrc
@@ -3,3 +3,6 @@ banfunc strncpy
 banfunc sscanf
 banfunc snprintf
 banfunc vsnprint
+banfunc strtoul
+banfunc strtol
+banfunc strtok_r
diff --git a/extra/curl/curl-8.12.1/lib/CMakeLists.txt b/extra/curl/curl-8.14.1/lib/CMakeLists.txt
similarity index 77%
rename from extra/curl/curl-8.12.1/lib/CMakeLists.txt
rename to extra/curl/curl-8.14.1/lib/CMakeLists.txt
index 5aa58368a136..29e5ae400d62 100644
--- a/extra/curl/curl-8.12.1/lib/CMakeLists.txt
+++ b/extra/curl/curl-8.14.1/lib/CMakeLists.txt
@@ -21,9 +21,11 @@
 # SPDX-License-Identifier: curl
 #
 ###########################################################################
-set(LIB_NAME "libcurl")
-set(LIBCURL_OUTPUT_NAME "libcurl" CACHE STRING "Basename of the curl library")
-add_definitions("-DBUILDING_LIBCURL")
+
+set(LIBCURL_OUTPUT_NAME "${LIB_NAME}" CACHE STRING "Basename of the curl library")
+
+set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "BUILDING_LIBCURL")
+set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "${CURL_DEBUG_MACROS}")
 
 configure_file("curl_config.h.cmake" "${CMAKE_CURRENT_BINARY_DIR}/curl_config.h")
 
@@ -31,17 +33,14 @@ configure_file("curl_config.h.cmake" "${CMAKE_CURRENT_BINARY_DIR}/curl_config.h"
 curl_transform_makefile_inc("Makefile.inc" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake")
 include("${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake")
 
-# DllMain is added later for DLL builds only.
-list(REMOVE_ITEM CSOURCES "dllmain.c")
-
 list(APPEND HHEADERS "${CMAKE_CURRENT_BINARY_DIR}/curl_config.h")
 
 # The rest of the build
 
-include_directories(
-  "${PROJECT_BINARY_DIR}/lib"  # for "curl_config.h"
-  "${PROJECT_SOURCE_DIR}/lib"  # for "curl_setup.h"
+set_property(DIRECTORY APPEND PROPERTY INCLUDE_DIRECTORIES
+  "${PROJECT_BINARY_DIR}/lib"        # for "curl_config.h"
 )
+
 if(USE_ARES)
   include_directories(SYSTEM ${CARES_INCLUDE_DIRS})
 endif()
@@ -53,7 +52,7 @@ if(CURL_BUILD_TESTING)
     EXCLUDE_FROM_ALL
     ${HHEADERS} ${CSOURCES}
   )
-  target_compile_definitions(curlu PUBLIC "UNITTESTS" "CURL_STATICLIB")
+  target_compile_definitions(curlu PUBLIC "CURL_STATICLIB" "UNITTESTS")
   target_link_libraries(curlu PRIVATE ${CURL_LIBS})
   # There is plenty of parallelism when building the testdeps target.
   # Override the curlu batch size with the maximum to optimize performance.
@@ -61,26 +60,35 @@ if(CURL_BUILD_TESTING)
 endif()
 
 if(ENABLE_CURLDEBUG)
-  # We must compile these sources separately to avoid memdebug.h redefinitions
-  # applying to them.
-  set_source_files_properties("memdebug.c" "curl_multibyte.c" PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON)
+  # We must compile this source separately to avoid memdebug.h redefinitions
+  # applying to it.
+  set_source_files_properties("memdebug.c" PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON)
 endif()
 
 ## Library definition
 
 if(NOT DEFINED IMPORT_LIB_SUFFIX)
-  set(IMPORT_LIB_SUFFIX "")
+  # Suffix implib name with "_imp" by default, to avoid conflicting with
+  # the generated static "libcurl.lib" (typically with MSVC).
+  if(WIN32 AND BUILD_SHARED_LIBS AND
+     CMAKE_IMPORT_LIBRARY_SUFFIX STREQUAL CMAKE_STATIC_LIBRARY_SUFFIX)
+    set(IMPORT_LIB_SUFFIX "_imp")
+  else()
+    set(IMPORT_LIB_SUFFIX "")
+  endif()
 endif()
 if(NOT DEFINED STATIC_LIB_SUFFIX)
   set(STATIC_LIB_SUFFIX "")
 endif()
 
-# Add "_imp" as a suffix before the extension to avoid conflicting with
-# the statically linked "libcurl.lib" (typically with MSVC)
-if(WIN32 AND
-   NOT IMPORT_LIB_SUFFIX AND
-   CMAKE_STATIC_LIBRARY_SUFFIX STREQUAL CMAKE_IMPORT_LIBRARY_SUFFIX)
-  set(IMPORT_LIB_SUFFIX "_imp")
+# Detect implib static lib filename collision
+if(WIN32 AND BUILD_STATIC_LIBS AND BUILD_SHARED_LIBS AND
+   "${IMPORT_LIB_SUFFIX}${CMAKE_IMPORT_LIBRARY_SUFFIX}" STREQUAL
+   "${STATIC_LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}")
+  message(FATAL_ERROR "Library suffix is the same ('${STATIC_LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}') "
+    "for the import and static '${LIBCURL_OUTPUT_NAME}' library. "
+    "Set IMPORT_LIB_SUFFIX and/or STATIC_LIB_SUFFIX to different values, "
+    "or disable building either the shared or static library to avoid the filename collision.")
 endif()
 
 # Whether to do a single compilation pass for libcurl sources and reuse these
@@ -97,9 +105,9 @@ if(NOT DEFINED SHARE_LIB_OBJECT)
   endif()
 endif()
 
-if(SHARE_LIB_OBJECT)
+if(SHARE_LIB_OBJECT AND CMAKE_VERSION VERSION_GREATER_EQUAL 3.12)
   set(LIB_OBJECT "libcurl_object")
-  add_library(${LIB_OBJECT} OBJECT ${HHEADERS} ${CSOURCES})
+  add_library(${LIB_OBJECT} OBJECT ${HHEADERS} ${CSOURCES})  # Requires CMake 3.12
   if(WIN32)
     # Define CURL_STATICLIB always, to disable __declspec(dllexport) for
     # exported libcurl symbols. We handle exports via libcurl.def instead.
@@ -115,7 +123,13 @@ if(SHARE_LIB_OBJECT)
     set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS")
   endif()
   if(CURL_HAS_LTO)
-    set_target_properties(${LIB_OBJECT} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
+    if(CMAKE_CONFIGURATION_TYPES)
+      set_target_properties(${LIB_OBJECT} PROPERTIES
+        INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE
+        INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE)
+    else()
+      set_target_properties(${LIB_OBJECT} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
+    endif()
   endif()
 
   target_include_directories(${LIB_OBJECT} INTERFACE
@@ -140,13 +154,20 @@ if(BUILD_STATIC_LIBS)
   set_target_properties(${LIB_STATIC} PROPERTIES
     PREFIX "" OUTPUT_NAME "${LIBCURL_OUTPUT_NAME}"
     SUFFIX "${STATIC_LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}"
-    INTERFACE_COMPILE_DEFINITIONS "CURL_STATICLIB")
+    INTERFACE_COMPILE_DEFINITIONS "CURL_STATICLIB"
+    INTERFACE_LINK_DIRECTORIES "${CURL_LIBDIRS}")
   if(CURL_HIDES_PRIVATE_SYMBOLS)
     set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_FLAGS "${CURL_CFLAG_SYMBOLS_HIDE}")
     set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS")
   endif()
   if(CURL_HAS_LTO)
-    set_target_properties(${LIB_STATIC} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
+    if(CMAKE_CONFIGURATION_TYPES)
+      set_target_properties(${LIB_OBJECT} PROPERTIES
+        INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE
+        INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE)
+    else()
+      set_target_properties(${LIB_OBJECT} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
+    endif()
   endif()
 
   target_include_directories(${LIB_STATIC} INTERFACE
@@ -158,15 +179,8 @@ if(BUILD_SHARED_LIBS)
   list(APPEND libcurl_export ${LIB_SHARED})
   add_library(${LIB_SHARED} SHARED ${LIB_SOURCE})
   add_library(${PROJECT_NAME}::${LIB_SHARED} ALIAS ${LIB_SHARED})
-  if(WIN32 OR CYGWIN)
-    if(CYGWIN)
-      # For Cygwin always compile dllmain.c as a separate unit since it
-      # includes windows.h, which should not be included in other units.
-      set_source_files_properties("dllmain.c" PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON)
-    endif()
-    set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES "dllmain.c")
-  endif()
   if(WIN32)
+    set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES "dllmain.c")
     set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES "libcurl.rc")
     if(CURL_HIDES_PRIVATE_SYMBOLS)
       set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES "${PROJECT_SOURCE_DIR}/lib/libcurl.def")
@@ -183,7 +197,13 @@ if(BUILD_SHARED_LIBS)
     set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS")
   endif()
   if(CURL_HAS_LTO)
-    set_target_properties(${LIB_SHARED} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
+    if(CMAKE_CONFIGURATION_TYPES)
+      set_target_properties(${LIB_OBJECT} PROPERTIES
+        INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE
+        INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE)
+    else()
+      set_target_properties(${LIB_OBJECT} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
+    endif()
   endif()
 
   target_include_directories(${LIB_SHARED} INTERFACE
@@ -259,8 +279,11 @@ if(BUILD_SHARED_LIBS)
     set(CMAKE_REQUIRED_LINK_OPTIONS "-Wl,--version-script=${CMAKE_CURRENT_BINARY_DIR}/libcurl.vers")
     check_c_source_compiles("int main(void) { return 0; }" HAVE_VERSIONED_SYMBOLS)
     if(HAVE_VERSIONED_SYMBOLS)
-      # Superseded by LINK_OPTIONS in CMake 3.13 and later.
-      set_target_properties(${LIB_SHARED} PROPERTIES LINK_FLAGS "${CMAKE_REQUIRED_LINK_OPTIONS}")
+      if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13)
+        set_target_properties(${LIB_SHARED} PROPERTIES LINK_OPTIONS "${CMAKE_REQUIRED_LINK_OPTIONS}")
+      else()
+        set_target_properties(${LIB_SHARED} PROPERTIES LINK_FLAGS "${CMAKE_REQUIRED_LINK_OPTIONS}")
+      endif()
     else()
       message(WARNING "Versioned symbols requested, but not supported by the toolchain.")
     endif()
diff --git a/extra/curl/curl-8.12.1/lib/Makefile.am b/extra/curl/curl-8.14.1/lib/Makefile.am
similarity index 86%
rename from extra/curl/curl-8.12.1/lib/Makefile.am
rename to extra/curl/curl-8.14.1/lib/Makefile.am
index b77d10bb22b3..463b72ed8e45 100644
--- a/extra/curl/curl-8.12.1/lib/Makefile.am
+++ b/extra/curl/curl-8.14.1/lib/Makefile.am
@@ -50,11 +50,9 @@ CFLAGS += @CURL_CFLAG_EXTRAS@
 #
 # $(top_srcdir)/include is for libcurl's external include files
 # $(top_builddir)/lib is for libcurl's generated lib/curl_config.h file
-# $(top_srcdir)/lib for libcurl's lib/curl_setup.h and other "private" files
 
-AM_CPPFLAGS = -I$(top_srcdir)/include        \
-              -I$(top_builddir)/lib          \
-              -I$(top_srcdir)/lib
+AM_CPPFLAGS = -I$(top_srcdir)/include   \
+              -I$(top_builddir)/lib
 
 # Prevent LIBS from being used for all link targets
 LIBS = $(BLANK_AT_MAKETIME)
@@ -64,18 +62,28 @@ include Makefile.soname
 AM_CPPFLAGS += -DBUILDING_LIBCURL
 AM_LDFLAGS =
 AM_CFLAGS =
+if DEBUGBUILD
+AM_CPPFLAGS += -DDEBUGBUILD
+endif
+if CURLDEBUG
+AM_CPPFLAGS += -DCURLDEBUG
+endif
 
 # Makefile.inc provides the CSOURCES and HHEADERS defines
 include Makefile.inc
 
+if DOING_NATIVE_WINDOWS
+CSOURCES += dllmain.c
+endif
+
 if USE_UNITY
 # Keep these separate to avoid duplicate definitions when linking libtests
 # in static mode.
-curl_EXCLUDE = curl_threads.c timediff.c warnless.c
-if DEBUGBUILD
-# We must compile these sources separately to avoid memdebug.h redefinitions
-# applying to them.
-curl_EXCLUDE += memdebug.c curl_multibyte.c
+curl_EXCLUDE = curl_threads.c curlx/timediff.c curlx/warnless.c
+if CURLDEBUG
+# We must compile this source separately to avoid memdebug.h redefinitions
+# applying to it.
+curl_EXCLUDE += memdebug.c
 endif
 libcurl_unity.c: $(top_srcdir)/scripts/mk-unity.pl $(CSOURCES)
 	@PERL@ $(top_srcdir)/scripts/mk-unity.pl $(srcdir) $(CSOURCES) --exclude $(curl_EXCLUDE) > libcurl_unity.c
@@ -143,15 +151,15 @@ CS_1 =
 CS_ = $(CS_0)
 
 checksrc:
-	$(CHECKSRC)(@PERL@ $(top_srcdir)/scripts/checksrc.pl -D$(srcdir)         \
-	  -W$(srcdir)/libcurl_unity.c -W$(srcdir)/curl_config.h $(srcdir)/*.[ch] \
-	  $(srcdir)/vauth/*.[ch] $(srcdir)/vtls/*.[ch] $(srcdir)/vquic/*.[ch]    \
-	  $(srcdir)/vssh/*.[ch])
+	$(CHECKSRC)(@PERL@ $(top_srcdir)/scripts/checksrc.pl -D$(srcdir) \
+	$(CSOURCES) $(HHEADERS))
 
+if NOT_CURL_CI
 if DEBUGBUILD
 # for debug builds, we scan the sources on all regular make invokes
 all-local: checksrc
 endif
+endif
 
 # disable the tests that are mostly causing false positives
 TIDYFLAGS := -checks=-clang-analyzer-security.insecureAPI.strcpy,-clang-analyzer-optin.performance.Padding,-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling -quiet
@@ -162,10 +170,11 @@ endif
 TIDY := clang-tidy
 
 tidy:
-	$(TIDY) $(CSOURCES) $(TIDYFLAGS) $(CURL_CLANG_TIDYFLAGS) -- $(AM_CPPFLAGS) $(CPPFLAGS) -DHAVE_CONFIG_H
+	(_csources=`echo ' $(CSOURCES)' | sed -E -e 's/ +$$//' -e 's/ +/ /g' -e 's| | $(srcdir)/|g'`; \
+	$(TIDY) $$_csources $(TIDYFLAGS) $(CURL_CLANG_TIDYFLAGS) -- $(AM_CPPFLAGS) $(CPPFLAGS) -DHAVE_CONFIG_H)
 
 optiontable:
-	perl optiontable.pl < $(top_srcdir)/include/curl/curl.h > easyoptions.c
+	@PERL@ $(srcdir)/optiontable.pl < $(top_srcdir)/include/curl/curl.h > $(srcdir)/easyoptions.c
 
 if HAVE_WINDRES
 .rc.lo:
diff --git a/extra/curl/curl-8.12.1/lib/Makefile.in b/extra/curl/curl-8.14.1/lib/Makefile.in
similarity index 88%
rename from extra/curl/curl-8.12.1/lib/Makefile.in
rename to extra/curl/curl-8.14.1/lib/Makefile.in
index 2d3706eaeed3..8262d5aa36fa 100644
--- a/extra/curl/curl-8.12.1/lib/Makefile.in
+++ b/extra/curl/curl-8.14.1/lib/Makefile.in
@@ -136,20 +136,25 @@ PRE_UNINSTALL = :
 POST_UNINSTALL = :
 build_triplet = @build@
 host_triplet = @host@
-# We must compile these sources separately to avoid memdebug.h redefinitions
-# applying to them.
-@DEBUGBUILD_TRUE@@USE_UNITY_TRUE@am__append_1 = memdebug.c curl_multibyte.c
-@CURL_LT_SHLIB_USE_VERSION_INFO_TRUE@am__append_2 = $(VERSIONINFO)
-@CURL_LT_SHLIB_USE_NO_UNDEFINED_TRUE@am__append_3 = -no-undefined
-@CURL_LT_SHLIB_USE_MIMPURE_TEXT_TRUE@am__append_4 = -mimpure-text
-@CURL_LT_SHLIB_USE_VERSIONED_SYMBOLS_TRUE@am__append_5 = -Wl,--version-script=libcurl.vers
+@DEBUGBUILD_TRUE@am__append_1 = -DDEBUGBUILD
+@CURLDEBUG_TRUE@am__append_2 = -DCURLDEBUG
+
+# Makefile.inc provides the CSOURCES and HHEADERS defines
+@DOING_NATIVE_WINDOWS_TRUE@am__append_3 = dllmain.c
+# We must compile this source separately to avoid memdebug.h redefinitions
+# applying to it.
+@CURLDEBUG_TRUE@@USE_UNITY_TRUE@am__append_4 = memdebug.c
+@CURL_LT_SHLIB_USE_VERSION_INFO_TRUE@am__append_5 = $(VERSIONINFO)
+@CURL_LT_SHLIB_USE_NO_UNDEFINED_TRUE@am__append_6 = -no-undefined
+@CURL_LT_SHLIB_USE_MIMPURE_TEXT_TRUE@am__append_7 = -mimpure-text
+@CURL_LT_SHLIB_USE_VERSIONED_SYMBOLS_TRUE@am__append_8 = -Wl,--version-script=libcurl.vers
 # if symbol-hiding is enabled, hide them!
-@CURL_LT_SHLIB_USE_VERSIONED_SYMBOLS_FALSE@@DOING_CURL_SYMBOL_HIDING_TRUE@am__append_6 = -export-symbols-regex '^curl_.*'
-@USE_CPPFLAG_CURL_STATICLIB_TRUE@am__append_7 = -DCURL_STATICLIB
-@HAVE_WINDRES_TRUE@@USE_CPPFLAG_CURL_STATICLIB_FALSE@am__append_8 = $(LIB_RCFILES)
-@DOING_CURL_SYMBOL_HIDING_TRUE@am__append_9 = -DCURL_HIDDEN_SYMBOLS
-@DOING_CURL_SYMBOL_HIDING_TRUE@am__append_10 = $(CFLAG_CURL_SYMBOL_HIDING)
-@CURL_WERROR_TRUE@am__append_11 = --warnings-as-errors=*
+@CURL_LT_SHLIB_USE_VERSIONED_SYMBOLS_FALSE@@DOING_CURL_SYMBOL_HIDING_TRUE@am__append_9 = -export-symbols-regex '^curl_.*'
+@USE_CPPFLAG_CURL_STATICLIB_TRUE@am__append_10 = -DCURL_STATICLIB
+@HAVE_WINDRES_TRUE@@USE_CPPFLAG_CURL_STATICLIB_FALSE@am__append_11 = $(LIB_RCFILES)
+@DOING_CURL_SYMBOL_HIDING_TRUE@am__append_12 = -DCURL_HIDDEN_SYMBOLS
+@DOING_CURL_SYMBOL_HIDING_TRUE@am__append_13 = $(CFLAG_CURL_SYMBOL_HIDING)
+@CURL_WERROR_TRUE@am__append_14 = --warnings-as-errors=*
 subdir = lib
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
 am__aclocal_m4_deps = $(top_srcdir)/m4/curl-amissl.m4 \
@@ -215,30 +220,30 @@ am__installdirs = "$(DESTDIR)$(libdir)"
 LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES)
 libcurl_la_LIBADD =
 am__libcurl_la_SOURCES_DIST = altsvc.c amigaos.c asyn-ares.c \
-	asyn-thread.c base64.c bufq.c bufref.c cf-h1-proxy.c \
+	asyn-base.c asyn-thrdd.c bufq.c bufref.c cf-h1-proxy.c \
 	cf-h2-proxy.c cf-haproxy.c cf-https-connect.c cf-socket.c \
 	cfilters.c conncache.c connect.c content_encoding.c cookie.c \
-	curl_addrinfo.c curl_des.c curl_endian.c curl_fnmatch.c \
-	curl_get_line.c curl_gethostname.c curl_gssapi.c \
-	curl_memrchr.c curl_multibyte.c curl_ntlm_core.c curl_range.c \
+	cshutdn.c curl_addrinfo.c curl_des.c curl_endian.c \
+	curl_fnmatch.c curl_get_line.c curl_gethostname.c \
+	curl_gssapi.c curl_memrchr.c curl_ntlm_core.c curl_range.c \
 	curl_rtmp.c curl_sasl.c curl_sha512_256.c curl_sspi.c \
-	curl_threads.c curl_trc.c cw-out.c dict.c dllmain.c doh.c \
-	dynbuf.c dynhds.c easy.c easygetopt.c easyoptions.c escape.c \
-	file.c fileinfo.c fopen.c formdata.c ftp.c ftplistparser.c \
-	getenv.c getinfo.c gopher.c hash.c headers.c hmac.c hostasyn.c \
-	hostip.c hostip4.c hostip6.c hostsyn.c hsts.c http.c http1.c \
+	curl_threads.c curl_trc.c cw-out.c cw-pause.c dict.c doh.c \
+	dynhds.c easy.c easygetopt.c easyoptions.c escape.c \
+	fake_addrinfo.c file.c fileinfo.c fopen.c formdata.c ftp.c \
+	ftplistparser.c getenv.c getinfo.c gopher.c hash.c headers.c \
+	hmac.c hostip.c hostip4.c hostip6.c hsts.c http.c http1.c \
 	http2.c http_aws_sigv4.c http_chunks.c http_digest.c \
 	http_negotiate.c http_ntlm.c http_proxy.c httpsrr.c idn.c \
-	if2ip.c imap.c inet_ntop.c inet_pton.c krb5.c ldap.c llist.c \
-	macos.c md4.c md5.c memdebug.c mime.c mprintf.c mqtt.c multi.c \
-	netrc.c nonblock.c noproxy.c openldap.c parsedate.c pingpong.c \
-	pop3.c progress.c psl.c rand.c rename.c request.c rtsp.c \
-	select.c sendf.c setopt.c sha256.c share.c slist.c smb.c \
-	smtp.c socketpair.c socks.c socks_gssapi.c socks_sspi.c \
-	speedcheck.c splay.c strcase.c strdup.c strerror.c strparse.c \
-	strtok.c strtoofft.c system_win32.c telnet.c tftp.c timediff.c \
-	timeval.c transfer.c url.c urlapi.c version.c version_win32.c \
-	warnless.c ws.c vauth/cleartext.c vauth/cram.c vauth/digest.c \
+	if2ip.c imap.c inet_ntop.c krb5.c ldap.c llist.c macos.c md4.c \
+	md5.c memdebug.c mime.c mprintf.c mqtt.c multi.c multi_ev.c \
+	netrc.c noproxy.c openldap.c parsedate.c pingpong.c pop3.c \
+	progress.c psl.c rand.c rename.c request.c rtsp.c select.c \
+	sendf.c setopt.c sha256.c share.c slist.c smb.c smtp.c \
+	socketpair.c socks.c socks_gssapi.c socks_sspi.c speedcheck.c \
+	splay.c strcase.c strdup.c strequal.c strerror.c \
+	system_win32.c telnet.c tftp.c transfer.c uint-bset.c \
+	uint-hash.c uint-spbset.c uint-table.c url.c urlapi.c \
+	version.c ws.c vauth/cleartext.c vauth/cram.c vauth/digest.c \
 	vauth/digest_sspi.c vauth/gsasl.c vauth/krb5_gssapi.c \
 	vauth/krb5_sspi.c vauth/ntlm.c vauth/ntlm_sspi.c \
 	vauth/oauth2.c vauth/spnego_gssapi.c vauth/spnego_sspi.c \
@@ -250,34 +255,37 @@ am__libcurl_la_SOURCES_DIST = altsvc.c amigaos.c asyn-ares.c \
 	vtls/wolfssl.c vtls/x509asn1.c vquic/curl_msh3.c \
 	vquic/curl_ngtcp2.c vquic/curl_osslq.c vquic/curl_quiche.c \
 	vquic/vquic.c vquic/vquic-tls.c vssh/libssh.c vssh/libssh2.c \
-	vssh/curl_path.c vssh/wolfssh.c altsvc.h amigaos.h \
-	arpa_telnet.h asyn.h bufq.h bufref.h cf-h1-proxy.h \
-	cf-h2-proxy.h cf-haproxy.h cf-https-connect.h cf-socket.h \
-	cfilters.h conncache.h connect.h content_encoding.h cookie.h \
-	curl_addrinfo.h curl_base64.h curl_ctype.h curl_des.h \
-	curl_endian.h curl_fnmatch.h curl_get_line.h \
-	curl_gethostname.h curl_gssapi.h curl_hmac.h curl_krb5.h \
-	curl_ldap.h curl_md4.h curl_md5.h curl_memory.h curl_memrchr.h \
-	curl_multibyte.h curl_ntlm_core.h curl_printf.h curl_range.h \
-	curl_rtmp.h curl_sasl.h curl_setup.h curl_setup_once.h \
-	curl_sha256.h curl_sha512_256.h curl_sspi.h curl_threads.h \
-	curl_trc.h curlx.h cw-out.h dict.h doh.h dynbuf.h dynhds.h \
-	easy_lock.h easyif.h easyoptions.h escape.h file.h fileinfo.h \
-	fopen.h formdata.h ftp.h ftplistparser.h functypes.h getinfo.h \
-	gopher.h hash.h headers.h hostip.h hsts.h http.h http1.h \
-	http2.h http_aws_sigv4.h http_chunks.h http_digest.h \
-	http_negotiate.h http_ntlm.h http_proxy.h httpsrr.h idn.h \
-	if2ip.h imap.h inet_ntop.h inet_pton.h llist.h macos.h \
-	memdebug.h mime.h mqtt.h multihandle.h multiif.h netrc.h \
-	nonblock.h noproxy.h parsedate.h pingpong.h pop3.h progress.h \
-	psl.h rand.h rename.h request.h rtsp.h select.h sendf.h \
-	setopt.h setup-os400.h setup-vms.h setup-win32.h share.h \
-	sigpipe.h slist.h smb.h smtp.h sockaddr.h socketpair.h socks.h \
-	speedcheck.h splay.h strcase.h strdup.h strerror.h strparse.h \
-	strtok.h strtoofft.h system_win32.h telnet.h tftp.h timediff.h \
-	timeval.h transfer.h url.h urlapi-int.h urldata.h \
-	version_win32.h warnless.h ws.h vauth/digest.h vauth/ntlm.h \
-	vauth/vauth.h vtls/bearssl.h vtls/cipher_suite.h vtls/gtls.h \
+	vssh/curl_path.c vssh/wolfssh.c curlx/base64.c curlx/dynbuf.c \
+	curlx/inet_pton.c curlx/multibyte.c curlx/nonblock.c \
+	curlx/strparse.c curlx/timediff.c curlx/timeval.c \
+	curlx/version_win32.c curlx/warnless.c curlx/winapi.c \
+	dllmain.c altsvc.h amigaos.h arpa_telnet.h asyn.h bufq.h \
+	bufref.h cf-h1-proxy.h cf-h2-proxy.h cf-haproxy.h \
+	cf-https-connect.h cf-socket.h cfilters.h conncache.h \
+	cshutdn.h connect.h content_encoding.h cookie.h \
+	curl_addrinfo.h curl_ctype.h curl_des.h curl_endian.h \
+	curl_fnmatch.h curl_get_line.h curl_gethostname.h \
+	curl_gssapi.h curl_hmac.h curl_krb5.h curl_ldap.h curl_md4.h \
+	curl_md5.h curl_memory.h curl_memrchr.h curl_ntlm_core.h \
+	curl_printf.h curl_range.h curl_rtmp.h curl_sasl.h \
+	curl_setup.h curl_setup_once.h curl_sha256.h curl_sha512_256.h \
+	curl_sspi.h curl_threads.h curl_trc.h cw-out.h cw-pause.h \
+	dict.h doh.h dynhds.h easy_lock.h easyif.h easyoptions.h \
+	escape.h fake_addrinfo.h file.h fileinfo.h fopen.h formdata.h \
+	ftp.h ftplistparser.h functypes.h getinfo.h gopher.h hash.h \
+	headers.h hostip.h hsts.h http.h http1.h http2.h \
+	http_aws_sigv4.h http_chunks.h http_digest.h http_negotiate.h \
+	http_ntlm.h http_proxy.h httpsrr.h idn.h if2ip.h imap.h \
+	inet_ntop.h llist.h macos.h memdebug.h mime.h mqtt.h \
+	multihandle.h multi_ev.h multiif.h netrc.h noproxy.h \
+	parsedate.h pingpong.h pop3.h progress.h psl.h rand.h rename.h \
+	request.h rtsp.h select.h sendf.h setopt.h setup-os400.h \
+	setup-vms.h setup-win32.h share.h sigpipe.h slist.h smb.h \
+	smtp.h sockaddr.h socketpair.h socks.h speedcheck.h splay.h \
+	strcase.h strdup.h strerror.h system_win32.h telnet.h tftp.h \
+	transfer.h uint-bset.h uint-hash.h uint-spbset.h uint-table.h \
+	url.h urlapi-int.h urldata.h ws.h vauth/digest.h vauth/vauth.h \
+	vtls/bearssl.h vtls/cipher_suite.h vtls/gtls.h \
 	vtls/hostcheck.h vtls/keylog.h vtls/mbedtls.h \
 	vtls/mbedtls_threadlock.h vtls/openssl.h vtls/rustls.h \
 	vtls/schannel.h vtls/schannel_int.h vtls/sectransp.h \
@@ -285,46 +293,48 @@ am__libcurl_la_SOURCES_DIST = altsvc.c amigaos.c asyn-ares.c \
 	vtls/vtls_spack.h vtls/wolfssl.h vtls/x509asn1.h \
 	vquic/curl_msh3.h vquic/curl_ngtcp2.h vquic/curl_osslq.h \
 	vquic/curl_quiche.h vquic/vquic.h vquic/vquic_int.h \
-	vquic/vquic-tls.h vssh/curl_path.h vssh/ssh.h libcurl.rc
+	vquic/vquic-tls.h vssh/curl_path.h vssh/ssh.h curlx/base64.h \
+	curlx/curlx.h curlx/dynbuf.h curlx/inet_pton.h \
+	curlx/multibyte.h curlx/nonblock.h curlx/strparse.h \
+	curlx/timediff.h curlx/timeval.h curlx/version_win32.h \
+	curlx/warnless.h curlx/winapi.h libcurl.rc
 am__objects_1 = libcurl_la-altsvc.lo libcurl_la-amigaos.lo \
-	libcurl_la-asyn-ares.lo libcurl_la-asyn-thread.lo \
-	libcurl_la-base64.lo libcurl_la-bufq.lo libcurl_la-bufref.lo \
-	libcurl_la-cf-h1-proxy.lo libcurl_la-cf-h2-proxy.lo \
-	libcurl_la-cf-haproxy.lo libcurl_la-cf-https-connect.lo \
-	libcurl_la-cf-socket.lo libcurl_la-cfilters.lo \
-	libcurl_la-conncache.lo libcurl_la-connect.lo \
-	libcurl_la-content_encoding.lo libcurl_la-cookie.lo \
+	libcurl_la-asyn-ares.lo libcurl_la-asyn-base.lo \
+	libcurl_la-asyn-thrdd.lo libcurl_la-bufq.lo \
+	libcurl_la-bufref.lo libcurl_la-cf-h1-proxy.lo \
+	libcurl_la-cf-h2-proxy.lo libcurl_la-cf-haproxy.lo \
+	libcurl_la-cf-https-connect.lo libcurl_la-cf-socket.lo \
+	libcurl_la-cfilters.lo libcurl_la-conncache.lo \
+	libcurl_la-connect.lo libcurl_la-content_encoding.lo \
+	libcurl_la-cookie.lo libcurl_la-cshutdn.lo \
 	libcurl_la-curl_addrinfo.lo libcurl_la-curl_des.lo \
 	libcurl_la-curl_endian.lo libcurl_la-curl_fnmatch.lo \
 	libcurl_la-curl_get_line.lo libcurl_la-curl_gethostname.lo \
 	libcurl_la-curl_gssapi.lo libcurl_la-curl_memrchr.lo \
-	libcurl_la-curl_multibyte.lo libcurl_la-curl_ntlm_core.lo \
-	libcurl_la-curl_range.lo libcurl_la-curl_rtmp.lo \
-	libcurl_la-curl_sasl.lo libcurl_la-curl_sha512_256.lo \
-	libcurl_la-curl_sspi.lo libcurl_la-curl_threads.lo \
-	libcurl_la-curl_trc.lo libcurl_la-cw-out.lo libcurl_la-dict.lo \
-	libcurl_la-dllmain.lo libcurl_la-doh.lo libcurl_la-dynbuf.lo \
-	libcurl_la-dynhds.lo libcurl_la-easy.lo \
+	libcurl_la-curl_ntlm_core.lo libcurl_la-curl_range.lo \
+	libcurl_la-curl_rtmp.lo libcurl_la-curl_sasl.lo \
+	libcurl_la-curl_sha512_256.lo libcurl_la-curl_sspi.lo \
+	libcurl_la-curl_threads.lo libcurl_la-curl_trc.lo \
+	libcurl_la-cw-out.lo libcurl_la-cw-pause.lo libcurl_la-dict.lo \
+	libcurl_la-doh.lo libcurl_la-dynhds.lo libcurl_la-easy.lo \
 	libcurl_la-easygetopt.lo libcurl_la-easyoptions.lo \
-	libcurl_la-escape.lo libcurl_la-file.lo libcurl_la-fileinfo.lo \
-	libcurl_la-fopen.lo libcurl_la-formdata.lo libcurl_la-ftp.lo \
+	libcurl_la-escape.lo libcurl_la-fake_addrinfo.lo \
+	libcurl_la-file.lo libcurl_la-fileinfo.lo libcurl_la-fopen.lo \
+	libcurl_la-formdata.lo libcurl_la-ftp.lo \
 	libcurl_la-ftplistparser.lo libcurl_la-getenv.lo \
 	libcurl_la-getinfo.lo libcurl_la-gopher.lo libcurl_la-hash.lo \
-	libcurl_la-headers.lo libcurl_la-hmac.lo \
-	libcurl_la-hostasyn.lo libcurl_la-hostip.lo \
-	libcurl_la-hostip4.lo libcurl_la-hostip6.lo \
-	libcurl_la-hostsyn.lo libcurl_la-hsts.lo libcurl_la-http.lo \
-	libcurl_la-http1.lo libcurl_la-http2.lo \
+	libcurl_la-headers.lo libcurl_la-hmac.lo libcurl_la-hostip.lo \
+	libcurl_la-hostip4.lo libcurl_la-hostip6.lo libcurl_la-hsts.lo \
+	libcurl_la-http.lo libcurl_la-http1.lo libcurl_la-http2.lo \
 	libcurl_la-http_aws_sigv4.lo libcurl_la-http_chunks.lo \
 	libcurl_la-http_digest.lo libcurl_la-http_negotiate.lo \
 	libcurl_la-http_ntlm.lo libcurl_la-http_proxy.lo \
 	libcurl_la-httpsrr.lo libcurl_la-idn.lo libcurl_la-if2ip.lo \
-	libcurl_la-imap.lo libcurl_la-inet_ntop.lo \
-	libcurl_la-inet_pton.lo libcurl_la-krb5.lo libcurl_la-ldap.lo \
-	libcurl_la-llist.lo libcurl_la-macos.lo libcurl_la-md4.lo \
-	libcurl_la-md5.lo libcurl_la-memdebug.lo libcurl_la-mime.lo \
-	libcurl_la-mprintf.lo libcurl_la-mqtt.lo libcurl_la-multi.lo \
-	libcurl_la-netrc.lo libcurl_la-nonblock.lo \
+	libcurl_la-imap.lo libcurl_la-inet_ntop.lo libcurl_la-krb5.lo \
+	libcurl_la-ldap.lo libcurl_la-llist.lo libcurl_la-macos.lo \
+	libcurl_la-md4.lo libcurl_la-md5.lo libcurl_la-memdebug.lo \
+	libcurl_la-mime.lo libcurl_la-mprintf.lo libcurl_la-mqtt.lo \
+	libcurl_la-multi.lo libcurl_la-multi_ev.lo libcurl_la-netrc.lo \
 	libcurl_la-noproxy.lo libcurl_la-openldap.lo \
 	libcurl_la-parsedate.lo libcurl_la-pingpong.lo \
 	libcurl_la-pop3.lo libcurl_la-progress.lo libcurl_la-psl.lo \
@@ -336,13 +346,12 @@ am__objects_1 = libcurl_la-altsvc.lo libcurl_la-amigaos.lo \
 	libcurl_la-socks_gssapi.lo libcurl_la-socks_sspi.lo \
 	libcurl_la-speedcheck.lo libcurl_la-splay.lo \
 	libcurl_la-strcase.lo libcurl_la-strdup.lo \
-	libcurl_la-strerror.lo libcurl_la-strparse.lo \
-	libcurl_la-strtok.lo libcurl_la-strtoofft.lo \
+	libcurl_la-strequal.lo libcurl_la-strerror.lo \
 	libcurl_la-system_win32.lo libcurl_la-telnet.lo \
-	libcurl_la-tftp.lo libcurl_la-timediff.lo \
-	libcurl_la-timeval.lo libcurl_la-transfer.lo libcurl_la-url.lo \
-	libcurl_la-urlapi.lo libcurl_la-version.lo \
-	libcurl_la-version_win32.lo libcurl_la-warnless.lo \
+	libcurl_la-tftp.lo libcurl_la-transfer.lo \
+	libcurl_la-uint-bset.lo libcurl_la-uint-hash.lo \
+	libcurl_la-uint-spbset.lo libcurl_la-uint-table.lo \
+	libcurl_la-url.lo libcurl_la-urlapi.lo libcurl_la-version.lo \
 	libcurl_la-ws.lo
 am__dirstamp = $(am__leading_dot)dirstamp
 am__objects_2 = vauth/libcurl_la-cleartext.lo vauth/libcurl_la-cram.lo \
@@ -368,23 +377,30 @@ am__objects_4 = vquic/libcurl_la-curl_msh3.lo \
 	vquic/libcurl_la-vquic-tls.lo
 am__objects_5 = vssh/libcurl_la-libssh.lo vssh/libcurl_la-libssh2.lo \
 	vssh/libcurl_la-curl_path.lo vssh/libcurl_la-wolfssh.lo
-am__objects_6 = $(am__objects_1) $(am__objects_2) $(am__objects_3) \
-	$(am__objects_4) $(am__objects_5)
-am__objects_7 =
-am__objects_8 = $(am__objects_7) $(am__objects_7) $(am__objects_7) \
-	$(am__objects_7) $(am__objects_7)
-am__objects_9 = libcurl.lo
-@HAVE_WINDRES_TRUE@@USE_CPPFLAG_CURL_STATICLIB_FALSE@am__objects_10 = $(am__objects_9)
-@DEBUGBUILD_TRUE@@USE_UNITY_TRUE@am__objects_11 =  \
-@DEBUGBUILD_TRUE@@USE_UNITY_TRUE@	libcurl_la-memdebug.lo \
-@DEBUGBUILD_TRUE@@USE_UNITY_TRUE@	libcurl_la-curl_multibyte.lo
-@USE_UNITY_TRUE@am__objects_12 = libcurl_la-curl_threads.lo \
-@USE_UNITY_TRUE@	libcurl_la-timediff.lo libcurl_la-warnless.lo \
-@USE_UNITY_TRUE@	$(am__objects_11)
-@USE_UNITY_FALSE@am_libcurl_la_OBJECTS = $(am__objects_6) \
-@USE_UNITY_FALSE@	$(am__objects_8) $(am__objects_10)
-@USE_UNITY_TRUE@am_libcurl_la_OBJECTS = $(am__objects_12) \
-@USE_UNITY_TRUE@	$(am__objects_10)
+am__objects_6 = curlx/libcurl_la-base64.lo curlx/libcurl_la-dynbuf.lo \
+	curlx/libcurl_la-inet_pton.lo curlx/libcurl_la-multibyte.lo \
+	curlx/libcurl_la-nonblock.lo curlx/libcurl_la-strparse.lo \
+	curlx/libcurl_la-timediff.lo curlx/libcurl_la-timeval.lo \
+	curlx/libcurl_la-version_win32.lo curlx/libcurl_la-warnless.lo \
+	curlx/libcurl_la-winapi.lo
+@DOING_NATIVE_WINDOWS_TRUE@am__objects_7 = libcurl_la-dllmain.lo
+am__objects_8 = $(am__objects_1) $(am__objects_2) $(am__objects_3) \
+	$(am__objects_4) $(am__objects_5) $(am__objects_6) \
+	$(am__objects_7)
+am__objects_9 =
+am__objects_10 = $(am__objects_9) $(am__objects_9) $(am__objects_9) \
+	$(am__objects_9) $(am__objects_9) $(am__objects_9)
+am__objects_11 = libcurl.lo
+@HAVE_WINDRES_TRUE@@USE_CPPFLAG_CURL_STATICLIB_FALSE@am__objects_12 = $(am__objects_11)
+@CURLDEBUG_TRUE@@USE_UNITY_TRUE@am__objects_13 =  \
+@CURLDEBUG_TRUE@@USE_UNITY_TRUE@	libcurl_la-memdebug.lo
+@USE_UNITY_TRUE@am__objects_14 = libcurl_la-curl_threads.lo \
+@USE_UNITY_TRUE@	curlx/libcurl_la-timediff.lo \
+@USE_UNITY_TRUE@	curlx/libcurl_la-warnless.lo $(am__objects_13)
+@USE_UNITY_FALSE@am_libcurl_la_OBJECTS = $(am__objects_8) \
+@USE_UNITY_FALSE@	$(am__objects_10) $(am__objects_12)
+@USE_UNITY_TRUE@am_libcurl_la_OBJECTS = $(am__objects_14) \
+@USE_UNITY_TRUE@	$(am__objects_12)
 @USE_UNITY_TRUE@nodist_libcurl_la_OBJECTS =  \
 @USE_UNITY_TRUE@	libcurl_la-libcurl_unity.lo
 libcurl_la_OBJECTS = $(am_libcurl_la_OBJECTS) \
@@ -398,30 +414,30 @@ libcurl_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
 	$(CFLAGS) $(libcurl_la_LDFLAGS) $(LDFLAGS) -o $@
 libcurlu_la_LIBADD =
 am__libcurlu_la_SOURCES_DIST = altsvc.c amigaos.c asyn-ares.c \
-	asyn-thread.c base64.c bufq.c bufref.c cf-h1-proxy.c \
+	asyn-base.c asyn-thrdd.c bufq.c bufref.c cf-h1-proxy.c \
 	cf-h2-proxy.c cf-haproxy.c cf-https-connect.c cf-socket.c \
 	cfilters.c conncache.c connect.c content_encoding.c cookie.c \
-	curl_addrinfo.c curl_des.c curl_endian.c curl_fnmatch.c \
-	curl_get_line.c curl_gethostname.c curl_gssapi.c \
-	curl_memrchr.c curl_multibyte.c curl_ntlm_core.c curl_range.c \
+	cshutdn.c curl_addrinfo.c curl_des.c curl_endian.c \
+	curl_fnmatch.c curl_get_line.c curl_gethostname.c \
+	curl_gssapi.c curl_memrchr.c curl_ntlm_core.c curl_range.c \
 	curl_rtmp.c curl_sasl.c curl_sha512_256.c curl_sspi.c \
-	curl_threads.c curl_trc.c cw-out.c dict.c dllmain.c doh.c \
-	dynbuf.c dynhds.c easy.c easygetopt.c easyoptions.c escape.c \
-	file.c fileinfo.c fopen.c formdata.c ftp.c ftplistparser.c \
-	getenv.c getinfo.c gopher.c hash.c headers.c hmac.c hostasyn.c \
-	hostip.c hostip4.c hostip6.c hostsyn.c hsts.c http.c http1.c \
+	curl_threads.c curl_trc.c cw-out.c cw-pause.c dict.c doh.c \
+	dynhds.c easy.c easygetopt.c easyoptions.c escape.c \
+	fake_addrinfo.c file.c fileinfo.c fopen.c formdata.c ftp.c \
+	ftplistparser.c getenv.c getinfo.c gopher.c hash.c headers.c \
+	hmac.c hostip.c hostip4.c hostip6.c hsts.c http.c http1.c \
 	http2.c http_aws_sigv4.c http_chunks.c http_digest.c \
 	http_negotiate.c http_ntlm.c http_proxy.c httpsrr.c idn.c \
-	if2ip.c imap.c inet_ntop.c inet_pton.c krb5.c ldap.c llist.c \
-	macos.c md4.c md5.c memdebug.c mime.c mprintf.c mqtt.c multi.c \
-	netrc.c nonblock.c noproxy.c openldap.c parsedate.c pingpong.c \
-	pop3.c progress.c psl.c rand.c rename.c request.c rtsp.c \
-	select.c sendf.c setopt.c sha256.c share.c slist.c smb.c \
-	smtp.c socketpair.c socks.c socks_gssapi.c socks_sspi.c \
-	speedcheck.c splay.c strcase.c strdup.c strerror.c strparse.c \
-	strtok.c strtoofft.c system_win32.c telnet.c tftp.c timediff.c \
-	timeval.c transfer.c url.c urlapi.c version.c version_win32.c \
-	warnless.c ws.c vauth/cleartext.c vauth/cram.c vauth/digest.c \
+	if2ip.c imap.c inet_ntop.c krb5.c ldap.c llist.c macos.c md4.c \
+	md5.c memdebug.c mime.c mprintf.c mqtt.c multi.c multi_ev.c \
+	netrc.c noproxy.c openldap.c parsedate.c pingpong.c pop3.c \
+	progress.c psl.c rand.c rename.c request.c rtsp.c select.c \
+	sendf.c setopt.c sha256.c share.c slist.c smb.c smtp.c \
+	socketpair.c socks.c socks_gssapi.c socks_sspi.c speedcheck.c \
+	splay.c strcase.c strdup.c strequal.c strerror.c \
+	system_win32.c telnet.c tftp.c transfer.c uint-bset.c \
+	uint-hash.c uint-spbset.c uint-table.c url.c urlapi.c \
+	version.c ws.c vauth/cleartext.c vauth/cram.c vauth/digest.c \
 	vauth/digest_sspi.c vauth/gsasl.c vauth/krb5_gssapi.c \
 	vauth/krb5_sspi.c vauth/ntlm.c vauth/ntlm_sspi.c \
 	vauth/oauth2.c vauth/spnego_gssapi.c vauth/spnego_sspi.c \
@@ -433,34 +449,37 @@ am__libcurlu_la_SOURCES_DIST = altsvc.c amigaos.c asyn-ares.c \
 	vtls/wolfssl.c vtls/x509asn1.c vquic/curl_msh3.c \
 	vquic/curl_ngtcp2.c vquic/curl_osslq.c vquic/curl_quiche.c \
 	vquic/vquic.c vquic/vquic-tls.c vssh/libssh.c vssh/libssh2.c \
-	vssh/curl_path.c vssh/wolfssh.c altsvc.h amigaos.h \
-	arpa_telnet.h asyn.h bufq.h bufref.h cf-h1-proxy.h \
-	cf-h2-proxy.h cf-haproxy.h cf-https-connect.h cf-socket.h \
-	cfilters.h conncache.h connect.h content_encoding.h cookie.h \
-	curl_addrinfo.h curl_base64.h curl_ctype.h curl_des.h \
-	curl_endian.h curl_fnmatch.h curl_get_line.h \
-	curl_gethostname.h curl_gssapi.h curl_hmac.h curl_krb5.h \
-	curl_ldap.h curl_md4.h curl_md5.h curl_memory.h curl_memrchr.h \
-	curl_multibyte.h curl_ntlm_core.h curl_printf.h curl_range.h \
-	curl_rtmp.h curl_sasl.h curl_setup.h curl_setup_once.h \
-	curl_sha256.h curl_sha512_256.h curl_sspi.h curl_threads.h \
-	curl_trc.h curlx.h cw-out.h dict.h doh.h dynbuf.h dynhds.h \
-	easy_lock.h easyif.h easyoptions.h escape.h file.h fileinfo.h \
-	fopen.h formdata.h ftp.h ftplistparser.h functypes.h getinfo.h \
-	gopher.h hash.h headers.h hostip.h hsts.h http.h http1.h \
-	http2.h http_aws_sigv4.h http_chunks.h http_digest.h \
-	http_negotiate.h http_ntlm.h http_proxy.h httpsrr.h idn.h \
-	if2ip.h imap.h inet_ntop.h inet_pton.h llist.h macos.h \
-	memdebug.h mime.h mqtt.h multihandle.h multiif.h netrc.h \
-	nonblock.h noproxy.h parsedate.h pingpong.h pop3.h progress.h \
-	psl.h rand.h rename.h request.h rtsp.h select.h sendf.h \
-	setopt.h setup-os400.h setup-vms.h setup-win32.h share.h \
-	sigpipe.h slist.h smb.h smtp.h sockaddr.h socketpair.h socks.h \
-	speedcheck.h splay.h strcase.h strdup.h strerror.h strparse.h \
-	strtok.h strtoofft.h system_win32.h telnet.h tftp.h timediff.h \
-	timeval.h transfer.h url.h urlapi-int.h urldata.h \
-	version_win32.h warnless.h ws.h vauth/digest.h vauth/ntlm.h \
-	vauth/vauth.h vtls/bearssl.h vtls/cipher_suite.h vtls/gtls.h \
+	vssh/curl_path.c vssh/wolfssh.c curlx/base64.c curlx/dynbuf.c \
+	curlx/inet_pton.c curlx/multibyte.c curlx/nonblock.c \
+	curlx/strparse.c curlx/timediff.c curlx/timeval.c \
+	curlx/version_win32.c curlx/warnless.c curlx/winapi.c \
+	dllmain.c altsvc.h amigaos.h arpa_telnet.h asyn.h bufq.h \
+	bufref.h cf-h1-proxy.h cf-h2-proxy.h cf-haproxy.h \
+	cf-https-connect.h cf-socket.h cfilters.h conncache.h \
+	cshutdn.h connect.h content_encoding.h cookie.h \
+	curl_addrinfo.h curl_ctype.h curl_des.h curl_endian.h \
+	curl_fnmatch.h curl_get_line.h curl_gethostname.h \
+	curl_gssapi.h curl_hmac.h curl_krb5.h curl_ldap.h curl_md4.h \
+	curl_md5.h curl_memory.h curl_memrchr.h curl_ntlm_core.h \
+	curl_printf.h curl_range.h curl_rtmp.h curl_sasl.h \
+	curl_setup.h curl_setup_once.h curl_sha256.h curl_sha512_256.h \
+	curl_sspi.h curl_threads.h curl_trc.h cw-out.h cw-pause.h \
+	dict.h doh.h dynhds.h easy_lock.h easyif.h easyoptions.h \
+	escape.h fake_addrinfo.h file.h fileinfo.h fopen.h formdata.h \
+	ftp.h ftplistparser.h functypes.h getinfo.h gopher.h hash.h \
+	headers.h hostip.h hsts.h http.h http1.h http2.h \
+	http_aws_sigv4.h http_chunks.h http_digest.h http_negotiate.h \
+	http_ntlm.h http_proxy.h httpsrr.h idn.h if2ip.h imap.h \
+	inet_ntop.h llist.h macos.h memdebug.h mime.h mqtt.h \
+	multihandle.h multi_ev.h multiif.h netrc.h noproxy.h \
+	parsedate.h pingpong.h pop3.h progress.h psl.h rand.h rename.h \
+	request.h rtsp.h select.h sendf.h setopt.h setup-os400.h \
+	setup-vms.h setup-win32.h share.h sigpipe.h slist.h smb.h \
+	smtp.h sockaddr.h socketpair.h socks.h speedcheck.h splay.h \
+	strcase.h strdup.h strerror.h system_win32.h telnet.h tftp.h \
+	transfer.h uint-bset.h uint-hash.h uint-spbset.h uint-table.h \
+	url.h urlapi-int.h urldata.h ws.h vauth/digest.h vauth/vauth.h \
+	vtls/bearssl.h vtls/cipher_suite.h vtls/gtls.h \
 	vtls/hostcheck.h vtls/keylog.h vtls/mbedtls.h \
 	vtls/mbedtls_threadlock.h vtls/openssl.h vtls/rustls.h \
 	vtls/schannel.h vtls/schannel_int.h vtls/sectransp.h \
@@ -468,50 +487,52 @@ am__libcurlu_la_SOURCES_DIST = altsvc.c amigaos.c asyn-ares.c \
 	vtls/vtls_spack.h vtls/wolfssl.h vtls/x509asn1.h \
 	vquic/curl_msh3.h vquic/curl_ngtcp2.h vquic/curl_osslq.h \
 	vquic/curl_quiche.h vquic/vquic.h vquic/vquic_int.h \
-	vquic/vquic-tls.h vssh/curl_path.h vssh/ssh.h
-am__objects_13 = libcurlu_la-altsvc.lo libcurlu_la-amigaos.lo \
-	libcurlu_la-asyn-ares.lo libcurlu_la-asyn-thread.lo \
-	libcurlu_la-base64.lo libcurlu_la-bufq.lo \
+	vquic/vquic-tls.h vssh/curl_path.h vssh/ssh.h curlx/base64.h \
+	curlx/curlx.h curlx/dynbuf.h curlx/inet_pton.h \
+	curlx/multibyte.h curlx/nonblock.h curlx/strparse.h \
+	curlx/timediff.h curlx/timeval.h curlx/version_win32.h \
+	curlx/warnless.h curlx/winapi.h
+am__objects_15 = libcurlu_la-altsvc.lo libcurlu_la-amigaos.lo \
+	libcurlu_la-asyn-ares.lo libcurlu_la-asyn-base.lo \
+	libcurlu_la-asyn-thrdd.lo libcurlu_la-bufq.lo \
 	libcurlu_la-bufref.lo libcurlu_la-cf-h1-proxy.lo \
 	libcurlu_la-cf-h2-proxy.lo libcurlu_la-cf-haproxy.lo \
 	libcurlu_la-cf-https-connect.lo libcurlu_la-cf-socket.lo \
 	libcurlu_la-cfilters.lo libcurlu_la-conncache.lo \
 	libcurlu_la-connect.lo libcurlu_la-content_encoding.lo \
-	libcurlu_la-cookie.lo libcurlu_la-curl_addrinfo.lo \
-	libcurlu_la-curl_des.lo libcurlu_la-curl_endian.lo \
-	libcurlu_la-curl_fnmatch.lo libcurlu_la-curl_get_line.lo \
-	libcurlu_la-curl_gethostname.lo libcurlu_la-curl_gssapi.lo \
-	libcurlu_la-curl_memrchr.lo libcurlu_la-curl_multibyte.lo \
+	libcurlu_la-cookie.lo libcurlu_la-cshutdn.lo \
+	libcurlu_la-curl_addrinfo.lo libcurlu_la-curl_des.lo \
+	libcurlu_la-curl_endian.lo libcurlu_la-curl_fnmatch.lo \
+	libcurlu_la-curl_get_line.lo libcurlu_la-curl_gethostname.lo \
+	libcurlu_la-curl_gssapi.lo libcurlu_la-curl_memrchr.lo \
 	libcurlu_la-curl_ntlm_core.lo libcurlu_la-curl_range.lo \
 	libcurlu_la-curl_rtmp.lo libcurlu_la-curl_sasl.lo \
 	libcurlu_la-curl_sha512_256.lo libcurlu_la-curl_sspi.lo \
 	libcurlu_la-curl_threads.lo libcurlu_la-curl_trc.lo \
-	libcurlu_la-cw-out.lo libcurlu_la-dict.lo \
-	libcurlu_la-dllmain.lo libcurlu_la-doh.lo \
-	libcurlu_la-dynbuf.lo libcurlu_la-dynhds.lo \
+	libcurlu_la-cw-out.lo libcurlu_la-cw-pause.lo \
+	libcurlu_la-dict.lo libcurlu_la-doh.lo libcurlu_la-dynhds.lo \
 	libcurlu_la-easy.lo libcurlu_la-easygetopt.lo \
 	libcurlu_la-easyoptions.lo libcurlu_la-escape.lo \
-	libcurlu_la-file.lo libcurlu_la-fileinfo.lo \
-	libcurlu_la-fopen.lo libcurlu_la-formdata.lo \
-	libcurlu_la-ftp.lo libcurlu_la-ftplistparser.lo \
-	libcurlu_la-getenv.lo libcurlu_la-getinfo.lo \
-	libcurlu_la-gopher.lo libcurlu_la-hash.lo \
-	libcurlu_la-headers.lo libcurlu_la-hmac.lo \
-	libcurlu_la-hostasyn.lo libcurlu_la-hostip.lo \
-	libcurlu_la-hostip4.lo libcurlu_la-hostip6.lo \
-	libcurlu_la-hostsyn.lo libcurlu_la-hsts.lo libcurlu_la-http.lo \
+	libcurlu_la-fake_addrinfo.lo libcurlu_la-file.lo \
+	libcurlu_la-fileinfo.lo libcurlu_la-fopen.lo \
+	libcurlu_la-formdata.lo libcurlu_la-ftp.lo \
+	libcurlu_la-ftplistparser.lo libcurlu_la-getenv.lo \
+	libcurlu_la-getinfo.lo libcurlu_la-gopher.lo \
+	libcurlu_la-hash.lo libcurlu_la-headers.lo libcurlu_la-hmac.lo \
+	libcurlu_la-hostip.lo libcurlu_la-hostip4.lo \
+	libcurlu_la-hostip6.lo libcurlu_la-hsts.lo libcurlu_la-http.lo \
 	libcurlu_la-http1.lo libcurlu_la-http2.lo \
 	libcurlu_la-http_aws_sigv4.lo libcurlu_la-http_chunks.lo \
 	libcurlu_la-http_digest.lo libcurlu_la-http_negotiate.lo \
 	libcurlu_la-http_ntlm.lo libcurlu_la-http_proxy.lo \
 	libcurlu_la-httpsrr.lo libcurlu_la-idn.lo libcurlu_la-if2ip.lo \
 	libcurlu_la-imap.lo libcurlu_la-inet_ntop.lo \
-	libcurlu_la-inet_pton.lo libcurlu_la-krb5.lo \
-	libcurlu_la-ldap.lo libcurlu_la-llist.lo libcurlu_la-macos.lo \
-	libcurlu_la-md4.lo libcurlu_la-md5.lo libcurlu_la-memdebug.lo \
-	libcurlu_la-mime.lo libcurlu_la-mprintf.lo libcurlu_la-mqtt.lo \
-	libcurlu_la-multi.lo libcurlu_la-netrc.lo \
-	libcurlu_la-nonblock.lo libcurlu_la-noproxy.lo \
+	libcurlu_la-krb5.lo libcurlu_la-ldap.lo libcurlu_la-llist.lo \
+	libcurlu_la-macos.lo libcurlu_la-md4.lo libcurlu_la-md5.lo \
+	libcurlu_la-memdebug.lo libcurlu_la-mime.lo \
+	libcurlu_la-mprintf.lo libcurlu_la-mqtt.lo \
+	libcurlu_la-multi.lo libcurlu_la-multi_ev.lo \
+	libcurlu_la-netrc.lo libcurlu_la-noproxy.lo \
 	libcurlu_la-openldap.lo libcurlu_la-parsedate.lo \
 	libcurlu_la-pingpong.lo libcurlu_la-pop3.lo \
 	libcurlu_la-progress.lo libcurlu_la-psl.lo libcurlu_la-rand.lo \
@@ -523,16 +544,14 @@ am__objects_13 = libcurlu_la-altsvc.lo libcurlu_la-amigaos.lo \
 	libcurlu_la-socks.lo libcurlu_la-socks_gssapi.lo \
 	libcurlu_la-socks_sspi.lo libcurlu_la-speedcheck.lo \
 	libcurlu_la-splay.lo libcurlu_la-strcase.lo \
-	libcurlu_la-strdup.lo libcurlu_la-strerror.lo \
-	libcurlu_la-strparse.lo libcurlu_la-strtok.lo \
-	libcurlu_la-strtoofft.lo libcurlu_la-system_win32.lo \
+	libcurlu_la-strdup.lo libcurlu_la-strequal.lo \
+	libcurlu_la-strerror.lo libcurlu_la-system_win32.lo \
 	libcurlu_la-telnet.lo libcurlu_la-tftp.lo \
-	libcurlu_la-timediff.lo libcurlu_la-timeval.lo \
-	libcurlu_la-transfer.lo libcurlu_la-url.lo \
-	libcurlu_la-urlapi.lo libcurlu_la-version.lo \
-	libcurlu_la-version_win32.lo libcurlu_la-warnless.lo \
-	libcurlu_la-ws.lo
-am__objects_14 = vauth/libcurlu_la-cleartext.lo \
+	libcurlu_la-transfer.lo libcurlu_la-uint-bset.lo \
+	libcurlu_la-uint-hash.lo libcurlu_la-uint-spbset.lo \
+	libcurlu_la-uint-table.lo libcurlu_la-url.lo \
+	libcurlu_la-urlapi.lo libcurlu_la-version.lo libcurlu_la-ws.lo
+am__objects_16 = vauth/libcurlu_la-cleartext.lo \
 	vauth/libcurlu_la-cram.lo vauth/libcurlu_la-digest.lo \
 	vauth/libcurlu_la-digest_sspi.lo vauth/libcurlu_la-gsasl.lo \
 	vauth/libcurlu_la-krb5_gssapi.lo \
@@ -540,7 +559,7 @@ am__objects_14 = vauth/libcurlu_la-cleartext.lo \
 	vauth/libcurlu_la-ntlm_sspi.lo vauth/libcurlu_la-oauth2.lo \
 	vauth/libcurlu_la-spnego_gssapi.lo \
 	vauth/libcurlu_la-spnego_sspi.lo vauth/libcurlu_la-vauth.lo
-am__objects_15 = vtls/libcurlu_la-bearssl.lo \
+am__objects_17 = vtls/libcurlu_la-bearssl.lo \
 	vtls/libcurlu_la-cipher_suite.lo vtls/libcurlu_la-gtls.lo \
 	vtls/libcurlu_la-hostcheck.lo vtls/libcurlu_la-keylog.lo \
 	vtls/libcurlu_la-mbedtls.lo \
@@ -551,25 +570,34 @@ am__objects_15 = vtls/libcurlu_la-bearssl.lo \
 	vtls/libcurlu_la-sectransp.lo vtls/libcurlu_la-vtls.lo \
 	vtls/libcurlu_la-vtls_scache.lo vtls/libcurlu_la-vtls_spack.lo \
 	vtls/libcurlu_la-wolfssl.lo vtls/libcurlu_la-x509asn1.lo
-am__objects_16 = vquic/libcurlu_la-curl_msh3.lo \
+am__objects_18 = vquic/libcurlu_la-curl_msh3.lo \
 	vquic/libcurlu_la-curl_ngtcp2.lo \
 	vquic/libcurlu_la-curl_osslq.lo \
 	vquic/libcurlu_la-curl_quiche.lo vquic/libcurlu_la-vquic.lo \
 	vquic/libcurlu_la-vquic-tls.lo
-am__objects_17 = vssh/libcurlu_la-libssh.lo \
+am__objects_19 = vssh/libcurlu_la-libssh.lo \
 	vssh/libcurlu_la-libssh2.lo vssh/libcurlu_la-curl_path.lo \
 	vssh/libcurlu_la-wolfssh.lo
-am__objects_18 = $(am__objects_13) $(am__objects_14) $(am__objects_15) \
-	$(am__objects_16) $(am__objects_17)
-@DEBUGBUILD_TRUE@@USE_UNITY_TRUE@am__objects_19 =  \
-@DEBUGBUILD_TRUE@@USE_UNITY_TRUE@	libcurlu_la-memdebug.lo \
-@DEBUGBUILD_TRUE@@USE_UNITY_TRUE@	libcurlu_la-curl_multibyte.lo
-@USE_UNITY_TRUE@am__objects_20 = libcurlu_la-curl_threads.lo \
-@USE_UNITY_TRUE@	libcurlu_la-timediff.lo \
-@USE_UNITY_TRUE@	libcurlu_la-warnless.lo $(am__objects_19)
-@USE_UNITY_FALSE@am_libcurlu_la_OBJECTS = $(am__objects_18) \
-@USE_UNITY_FALSE@	$(am__objects_8)
-@USE_UNITY_TRUE@am_libcurlu_la_OBJECTS = $(am__objects_20)
+am__objects_20 = curlx/libcurlu_la-base64.lo \
+	curlx/libcurlu_la-dynbuf.lo curlx/libcurlu_la-inet_pton.lo \
+	curlx/libcurlu_la-multibyte.lo curlx/libcurlu_la-nonblock.lo \
+	curlx/libcurlu_la-strparse.lo curlx/libcurlu_la-timediff.lo \
+	curlx/libcurlu_la-timeval.lo \
+	curlx/libcurlu_la-version_win32.lo \
+	curlx/libcurlu_la-warnless.lo curlx/libcurlu_la-winapi.lo
+@DOING_NATIVE_WINDOWS_TRUE@am__objects_21 = libcurlu_la-dllmain.lo
+am__objects_22 = $(am__objects_15) $(am__objects_16) $(am__objects_17) \
+	$(am__objects_18) $(am__objects_19) $(am__objects_20) \
+	$(am__objects_21)
+@CURLDEBUG_TRUE@@USE_UNITY_TRUE@am__objects_23 =  \
+@CURLDEBUG_TRUE@@USE_UNITY_TRUE@	libcurlu_la-memdebug.lo
+@USE_UNITY_TRUE@am__objects_24 = libcurlu_la-curl_threads.lo \
+@USE_UNITY_TRUE@	curlx/libcurlu_la-timediff.lo \
+@USE_UNITY_TRUE@	curlx/libcurlu_la-warnless.lo \
+@USE_UNITY_TRUE@	$(am__objects_23)
+@USE_UNITY_FALSE@am_libcurlu_la_OBJECTS = $(am__objects_22) \
+@USE_UNITY_FALSE@	$(am__objects_10)
+@USE_UNITY_TRUE@am_libcurlu_la_OBJECTS = $(am__objects_24)
 @USE_UNITY_TRUE@nodist_libcurlu_la_OBJECTS =  \
 @USE_UNITY_TRUE@	libcurlu_la-libcurl_unity.lo
 libcurlu_la_OBJECTS = $(am_libcurlu_la_OBJECTS) \
@@ -596,8 +624,8 @@ am__maybe_remake_depfiles = depfiles
 am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \
 	./$(DEPDIR)/libcurl_la-amigaos.Plo \
 	./$(DEPDIR)/libcurl_la-asyn-ares.Plo \
-	./$(DEPDIR)/libcurl_la-asyn-thread.Plo \
-	./$(DEPDIR)/libcurl_la-base64.Plo \
+	./$(DEPDIR)/libcurl_la-asyn-base.Plo \
+	./$(DEPDIR)/libcurl_la-asyn-thrdd.Plo \
 	./$(DEPDIR)/libcurl_la-bufq.Plo \
 	./$(DEPDIR)/libcurl_la-bufref.Plo \
 	./$(DEPDIR)/libcurl_la-cf-h1-proxy.Plo \
@@ -610,6 +638,7 @@ am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \
 	./$(DEPDIR)/libcurl_la-connect.Plo \
 	./$(DEPDIR)/libcurl_la-content_encoding.Plo \
 	./$(DEPDIR)/libcurl_la-cookie.Plo \
+	./$(DEPDIR)/libcurl_la-cshutdn.Plo \
 	./$(DEPDIR)/libcurl_la-curl_addrinfo.Plo \
 	./$(DEPDIR)/libcurl_la-curl_des.Plo \
 	./$(DEPDIR)/libcurl_la-curl_endian.Plo \
@@ -618,7 +647,6 @@ am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \
 	./$(DEPDIR)/libcurl_la-curl_gethostname.Plo \
 	./$(DEPDIR)/libcurl_la-curl_gssapi.Plo \
 	./$(DEPDIR)/libcurl_la-curl_memrchr.Plo \
-	./$(DEPDIR)/libcurl_la-curl_multibyte.Plo \
 	./$(DEPDIR)/libcurl_la-curl_ntlm_core.Plo \
 	./$(DEPDIR)/libcurl_la-curl_range.Plo \
 	./$(DEPDIR)/libcurl_la-curl_rtmp.Plo \
@@ -628,15 +656,16 @@ am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \
 	./$(DEPDIR)/libcurl_la-curl_threads.Plo \
 	./$(DEPDIR)/libcurl_la-curl_trc.Plo \
 	./$(DEPDIR)/libcurl_la-cw-out.Plo \
+	./$(DEPDIR)/libcurl_la-cw-pause.Plo \
 	./$(DEPDIR)/libcurl_la-dict.Plo \
 	./$(DEPDIR)/libcurl_la-dllmain.Plo \
 	./$(DEPDIR)/libcurl_la-doh.Plo \
-	./$(DEPDIR)/libcurl_la-dynbuf.Plo \
 	./$(DEPDIR)/libcurl_la-dynhds.Plo \
 	./$(DEPDIR)/libcurl_la-easy.Plo \
 	./$(DEPDIR)/libcurl_la-easygetopt.Plo \
 	./$(DEPDIR)/libcurl_la-easyoptions.Plo \
 	./$(DEPDIR)/libcurl_la-escape.Plo \
+	./$(DEPDIR)/libcurl_la-fake_addrinfo.Plo \
 	./$(DEPDIR)/libcurl_la-file.Plo \
 	./$(DEPDIR)/libcurl_la-fileinfo.Plo \
 	./$(DEPDIR)/libcurl_la-fopen.Plo \
@@ -649,11 +678,9 @@ am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \
 	./$(DEPDIR)/libcurl_la-hash.Plo \
 	./$(DEPDIR)/libcurl_la-headers.Plo \
 	./$(DEPDIR)/libcurl_la-hmac.Plo \
-	./$(DEPDIR)/libcurl_la-hostasyn.Plo \
 	./$(DEPDIR)/libcurl_la-hostip.Plo \
 	./$(DEPDIR)/libcurl_la-hostip4.Plo \
 	./$(DEPDIR)/libcurl_la-hostip6.Plo \
-	./$(DEPDIR)/libcurl_la-hostsyn.Plo \
 	./$(DEPDIR)/libcurl_la-hsts.Plo \
 	./$(DEPDIR)/libcurl_la-http.Plo \
 	./$(DEPDIR)/libcurl_la-http1.Plo \
@@ -669,7 +696,6 @@ am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \
 	./$(DEPDIR)/libcurl_la-if2ip.Plo \
 	./$(DEPDIR)/libcurl_la-imap.Plo \
 	./$(DEPDIR)/libcurl_la-inet_ntop.Plo \
-	./$(DEPDIR)/libcurl_la-inet_pton.Plo \
 	./$(DEPDIR)/libcurl_la-krb5.Plo \
 	./$(DEPDIR)/libcurl_la-ldap.Plo \
 	./$(DEPDIR)/libcurl_la-libcurl_unity.Plo \
@@ -681,8 +707,8 @@ am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \
 	./$(DEPDIR)/libcurl_la-mprintf.Plo \
 	./$(DEPDIR)/libcurl_la-mqtt.Plo \
 	./$(DEPDIR)/libcurl_la-multi.Plo \
+	./$(DEPDIR)/libcurl_la-multi_ev.Plo \
 	./$(DEPDIR)/libcurl_la-netrc.Plo \
-	./$(DEPDIR)/libcurl_la-nonblock.Plo \
 	./$(DEPDIR)/libcurl_la-noproxy.Plo \
 	./$(DEPDIR)/libcurl_la-openldap.Plo \
 	./$(DEPDIR)/libcurl_la-parsedate.Plo \
@@ -708,27 +734,25 @@ am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \
 	./$(DEPDIR)/libcurl_la-splay.Plo \
 	./$(DEPDIR)/libcurl_la-strcase.Plo \
 	./$(DEPDIR)/libcurl_la-strdup.Plo \
+	./$(DEPDIR)/libcurl_la-strequal.Plo \
 	./$(DEPDIR)/libcurl_la-strerror.Plo \
-	./$(DEPDIR)/libcurl_la-strparse.Plo \
-	./$(DEPDIR)/libcurl_la-strtok.Plo \
-	./$(DEPDIR)/libcurl_la-strtoofft.Plo \
 	./$(DEPDIR)/libcurl_la-system_win32.Plo \
 	./$(DEPDIR)/libcurl_la-telnet.Plo \
 	./$(DEPDIR)/libcurl_la-tftp.Plo \
-	./$(DEPDIR)/libcurl_la-timediff.Plo \
-	./$(DEPDIR)/libcurl_la-timeval.Plo \
 	./$(DEPDIR)/libcurl_la-transfer.Plo \
+	./$(DEPDIR)/libcurl_la-uint-bset.Plo \
+	./$(DEPDIR)/libcurl_la-uint-hash.Plo \
+	./$(DEPDIR)/libcurl_la-uint-spbset.Plo \
+	./$(DEPDIR)/libcurl_la-uint-table.Plo \
 	./$(DEPDIR)/libcurl_la-url.Plo \
 	./$(DEPDIR)/libcurl_la-urlapi.Plo \
 	./$(DEPDIR)/libcurl_la-version.Plo \
-	./$(DEPDIR)/libcurl_la-version_win32.Plo \
-	./$(DEPDIR)/libcurl_la-warnless.Plo \
 	./$(DEPDIR)/libcurl_la-ws.Plo \
 	./$(DEPDIR)/libcurlu_la-altsvc.Plo \
 	./$(DEPDIR)/libcurlu_la-amigaos.Plo \
 	./$(DEPDIR)/libcurlu_la-asyn-ares.Plo \
-	./$(DEPDIR)/libcurlu_la-asyn-thread.Plo \
-	./$(DEPDIR)/libcurlu_la-base64.Plo \
+	./$(DEPDIR)/libcurlu_la-asyn-base.Plo \
+	./$(DEPDIR)/libcurlu_la-asyn-thrdd.Plo \
 	./$(DEPDIR)/libcurlu_la-bufq.Plo \
 	./$(DEPDIR)/libcurlu_la-bufref.Plo \
 	./$(DEPDIR)/libcurlu_la-cf-h1-proxy.Plo \
@@ -741,6 +765,7 @@ am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \
 	./$(DEPDIR)/libcurlu_la-connect.Plo \
 	./$(DEPDIR)/libcurlu_la-content_encoding.Plo \
 	./$(DEPDIR)/libcurlu_la-cookie.Plo \
+	./$(DEPDIR)/libcurlu_la-cshutdn.Plo \
 	./$(DEPDIR)/libcurlu_la-curl_addrinfo.Plo \
 	./$(DEPDIR)/libcurlu_la-curl_des.Plo \
 	./$(DEPDIR)/libcurlu_la-curl_endian.Plo \
@@ -749,7 +774,6 @@ am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \
 	./$(DEPDIR)/libcurlu_la-curl_gethostname.Plo \
 	./$(DEPDIR)/libcurlu_la-curl_gssapi.Plo \
 	./$(DEPDIR)/libcurlu_la-curl_memrchr.Plo \
-	./$(DEPDIR)/libcurlu_la-curl_multibyte.Plo \
 	./$(DEPDIR)/libcurlu_la-curl_ntlm_core.Plo \
 	./$(DEPDIR)/libcurlu_la-curl_range.Plo \
 	./$(DEPDIR)/libcurlu_la-curl_rtmp.Plo \
@@ -759,15 +783,16 @@ am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \
 	./$(DEPDIR)/libcurlu_la-curl_threads.Plo \
 	./$(DEPDIR)/libcurlu_la-curl_trc.Plo \
 	./$(DEPDIR)/libcurlu_la-cw-out.Plo \
+	./$(DEPDIR)/libcurlu_la-cw-pause.Plo \
 	./$(DEPDIR)/libcurlu_la-dict.Plo \
 	./$(DEPDIR)/libcurlu_la-dllmain.Plo \
 	./$(DEPDIR)/libcurlu_la-doh.Plo \
-	./$(DEPDIR)/libcurlu_la-dynbuf.Plo \
 	./$(DEPDIR)/libcurlu_la-dynhds.Plo \
 	./$(DEPDIR)/libcurlu_la-easy.Plo \
 	./$(DEPDIR)/libcurlu_la-easygetopt.Plo \
 	./$(DEPDIR)/libcurlu_la-easyoptions.Plo \
 	./$(DEPDIR)/libcurlu_la-escape.Plo \
+	./$(DEPDIR)/libcurlu_la-fake_addrinfo.Plo \
 	./$(DEPDIR)/libcurlu_la-file.Plo \
 	./$(DEPDIR)/libcurlu_la-fileinfo.Plo \
 	./$(DEPDIR)/libcurlu_la-fopen.Plo \
@@ -780,11 +805,9 @@ am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \
 	./$(DEPDIR)/libcurlu_la-hash.Plo \
 	./$(DEPDIR)/libcurlu_la-headers.Plo \
 	./$(DEPDIR)/libcurlu_la-hmac.Plo \
-	./$(DEPDIR)/libcurlu_la-hostasyn.Plo \
 	./$(DEPDIR)/libcurlu_la-hostip.Plo \
 	./$(DEPDIR)/libcurlu_la-hostip4.Plo \
 	./$(DEPDIR)/libcurlu_la-hostip6.Plo \
-	./$(DEPDIR)/libcurlu_la-hostsyn.Plo \
 	./$(DEPDIR)/libcurlu_la-hsts.Plo \
 	./$(DEPDIR)/libcurlu_la-http.Plo \
 	./$(DEPDIR)/libcurlu_la-http1.Plo \
@@ -800,7 +823,6 @@ am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \
 	./$(DEPDIR)/libcurlu_la-if2ip.Plo \
 	./$(DEPDIR)/libcurlu_la-imap.Plo \
 	./$(DEPDIR)/libcurlu_la-inet_ntop.Plo \
-	./$(DEPDIR)/libcurlu_la-inet_pton.Plo \
 	./$(DEPDIR)/libcurlu_la-krb5.Plo \
 	./$(DEPDIR)/libcurlu_la-ldap.Plo \
 	./$(DEPDIR)/libcurlu_la-libcurl_unity.Plo \
@@ -813,8 +835,8 @@ am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \
 	./$(DEPDIR)/libcurlu_la-mprintf.Plo \
 	./$(DEPDIR)/libcurlu_la-mqtt.Plo \
 	./$(DEPDIR)/libcurlu_la-multi.Plo \
+	./$(DEPDIR)/libcurlu_la-multi_ev.Plo \
 	./$(DEPDIR)/libcurlu_la-netrc.Plo \
-	./$(DEPDIR)/libcurlu_la-nonblock.Plo \
 	./$(DEPDIR)/libcurlu_la-noproxy.Plo \
 	./$(DEPDIR)/libcurlu_la-openldap.Plo \
 	./$(DEPDIR)/libcurlu_la-parsedate.Plo \
@@ -842,22 +864,42 @@ am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \
 	./$(DEPDIR)/libcurlu_la-splay.Plo \
 	./$(DEPDIR)/libcurlu_la-strcase.Plo \
 	./$(DEPDIR)/libcurlu_la-strdup.Plo \
+	./$(DEPDIR)/libcurlu_la-strequal.Plo \
 	./$(DEPDIR)/libcurlu_la-strerror.Plo \
-	./$(DEPDIR)/libcurlu_la-strparse.Plo \
-	./$(DEPDIR)/libcurlu_la-strtok.Plo \
-	./$(DEPDIR)/libcurlu_la-strtoofft.Plo \
 	./$(DEPDIR)/libcurlu_la-system_win32.Plo \
 	./$(DEPDIR)/libcurlu_la-telnet.Plo \
 	./$(DEPDIR)/libcurlu_la-tftp.Plo \
-	./$(DEPDIR)/libcurlu_la-timediff.Plo \
-	./$(DEPDIR)/libcurlu_la-timeval.Plo \
 	./$(DEPDIR)/libcurlu_la-transfer.Plo \
+	./$(DEPDIR)/libcurlu_la-uint-bset.Plo \
+	./$(DEPDIR)/libcurlu_la-uint-hash.Plo \
+	./$(DEPDIR)/libcurlu_la-uint-spbset.Plo \
+	./$(DEPDIR)/libcurlu_la-uint-table.Plo \
 	./$(DEPDIR)/libcurlu_la-url.Plo \
 	./$(DEPDIR)/libcurlu_la-urlapi.Plo \
 	./$(DEPDIR)/libcurlu_la-version.Plo \
-	./$(DEPDIR)/libcurlu_la-version_win32.Plo \
-	./$(DEPDIR)/libcurlu_la-warnless.Plo \
 	./$(DEPDIR)/libcurlu_la-ws.Plo \
+	curlx/$(DEPDIR)/libcurl_la-base64.Plo \
+	curlx/$(DEPDIR)/libcurl_la-dynbuf.Plo \
+	curlx/$(DEPDIR)/libcurl_la-inet_pton.Plo \
+	curlx/$(DEPDIR)/libcurl_la-multibyte.Plo \
+	curlx/$(DEPDIR)/libcurl_la-nonblock.Plo \
+	curlx/$(DEPDIR)/libcurl_la-strparse.Plo \
+	curlx/$(DEPDIR)/libcurl_la-timediff.Plo \
+	curlx/$(DEPDIR)/libcurl_la-timeval.Plo \
+	curlx/$(DEPDIR)/libcurl_la-version_win32.Plo \
+	curlx/$(DEPDIR)/libcurl_la-warnless.Plo \
+	curlx/$(DEPDIR)/libcurl_la-winapi.Plo \
+	curlx/$(DEPDIR)/libcurlu_la-base64.Plo \
+	curlx/$(DEPDIR)/libcurlu_la-dynbuf.Plo \
+	curlx/$(DEPDIR)/libcurlu_la-inet_pton.Plo \
+	curlx/$(DEPDIR)/libcurlu_la-multibyte.Plo \
+	curlx/$(DEPDIR)/libcurlu_la-nonblock.Plo \
+	curlx/$(DEPDIR)/libcurlu_la-strparse.Plo \
+	curlx/$(DEPDIR)/libcurlu_la-timediff.Plo \
+	curlx/$(DEPDIR)/libcurlu_la-timeval.Plo \
+	curlx/$(DEPDIR)/libcurlu_la-version_win32.Plo \
+	curlx/$(DEPDIR)/libcurlu_la-warnless.Plo \
+	curlx/$(DEPDIR)/libcurlu_la-winapi.Plo \
 	vauth/$(DEPDIR)/libcurl_la-cleartext.Plo \
 	vauth/$(DEPDIR)/libcurl_la-cram.Plo \
 	vauth/$(DEPDIR)/libcurl_la-digest.Plo \
@@ -1199,9 +1241,8 @@ lib_LTLIBRARIES = libcurl.la
 #
 # $(top_srcdir)/include is for libcurl's external include files
 # $(top_builddir)/lib is for libcurl's generated lib/curl_config.h file
-# $(top_srcdir)/lib for libcurl's lib/curl_setup.h and other "private" files
 AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/lib \
-	-I$(top_srcdir)/lib -DBUILDING_LIBCURL
+	-DBUILDING_LIBCURL $(am__append_1) $(am__append_2)
 
 # Keep in sync with CURL_LIBCURL_VERSIONED_SYMBOLS_SONAME in configure.ac
 VERSIONCHANGE = 12
@@ -1212,6 +1253,33 @@ VERSIONDEL = 8
 VERSIONINFO = -version-info $(VERSIONCHANGE):$(VERSIONADD):$(VERSIONDEL)
 AM_LDFLAGS = 
 AM_CFLAGS = 
+LIB_CURLX_CFILES = \
+  curlx/base64.c   \
+  curlx/dynbuf.c   \
+  curlx/inet_pton.c \
+  curlx/multibyte.c \
+  curlx/nonblock.c \
+  curlx/strparse.c \
+  curlx/timediff.c \
+  curlx/timeval.c  \
+  curlx/version_win32.c \
+  curlx/warnless.c \
+  curlx/winapi.c
+
+LIB_CURLX_HFILES = \
+  curlx/base64.h   \
+  curlx/curlx.h    \
+  curlx/dynbuf.h   \
+  curlx/inet_pton.h \
+  curlx/multibyte.h \
+  curlx/nonblock.h \
+  curlx/strparse.h \
+  curlx/timediff.h \
+  curlx/timeval.h  \
+  curlx/version_win32.h \
+  curlx/warnless.h \
+  curlx/winapi.h
+
 LIB_VAUTH_CFILES = \
   vauth/cleartext.c     \
   vauth/cram.c          \
@@ -1229,7 +1297,6 @@ LIB_VAUTH_CFILES = \
 
 LIB_VAUTH_HFILES = \
   vauth/digest.h        \
-  vauth/ntlm.h          \
   vauth/vauth.h
 
 LIB_VTLS_CFILES = \
@@ -1302,8 +1369,8 @@ LIB_CFILES = \
   altsvc.c           \
   amigaos.c          \
   asyn-ares.c        \
-  asyn-thread.c      \
-  base64.c           \
+  asyn-base.c        \
+  asyn-thrdd.c       \
   bufq.c             \
   bufref.c           \
   cf-h1-proxy.c      \
@@ -1316,6 +1383,7 @@ LIB_CFILES = \
   connect.c          \
   content_encoding.c \
   cookie.c           \
+  cshutdn.c          \
   curl_addrinfo.c    \
   curl_des.c         \
   curl_endian.c      \
@@ -1324,7 +1392,6 @@ LIB_CFILES = \
   curl_gethostname.c \
   curl_gssapi.c      \
   curl_memrchr.c     \
-  curl_multibyte.c   \
   curl_ntlm_core.c   \
   curl_range.c       \
   curl_rtmp.c        \
@@ -1334,15 +1401,15 @@ LIB_CFILES = \
   curl_threads.c     \
   curl_trc.c         \
   cw-out.c           \
+  cw-pause.c         \
   dict.c             \
-  dllmain.c          \
   doh.c              \
-  dynbuf.c           \
   dynhds.c           \
   easy.c             \
   easygetopt.c       \
   easyoptions.c      \
   escape.c           \
+  fake_addrinfo.c    \
   file.c             \
   fileinfo.c         \
   fopen.c            \
@@ -1355,11 +1422,9 @@ LIB_CFILES = \
   hash.c             \
   headers.c          \
   hmac.c             \
-  hostasyn.c         \
   hostip.c           \
   hostip4.c          \
   hostip6.c          \
-  hostsyn.c          \
   hsts.c             \
   http.c             \
   http1.c            \
@@ -1375,7 +1440,6 @@ LIB_CFILES = \
   if2ip.c            \
   imap.c             \
   inet_ntop.c        \
-  inet_pton.c        \
   krb5.c             \
   ldap.c             \
   llist.c            \
@@ -1387,8 +1451,8 @@ LIB_CFILES = \
   mprintf.c          \
   mqtt.c             \
   multi.c            \
+  multi_ev.c         \
   netrc.c            \
-  nonblock.c         \
   noproxy.c          \
   openldap.c         \
   parsedate.c        \
@@ -1416,21 +1480,19 @@ LIB_CFILES = \
   splay.c            \
   strcase.c          \
   strdup.c           \
+  strequal.c         \
   strerror.c         \
-  strparse.c         \
-  strtok.c           \
-  strtoofft.c        \
   system_win32.c     \
   telnet.c           \
   tftp.c             \
-  timediff.c         \
-  timeval.c          \
   transfer.c         \
+  uint-bset.c        \
+  uint-hash.c        \
+  uint-spbset.c      \
+  uint-table.c       \
   url.c              \
   urlapi.c           \
   version.c          \
-  version_win32.c    \
-  warnless.c         \
   ws.c
 
 LIB_HFILES = \
@@ -1447,11 +1509,11 @@ LIB_HFILES = \
   cf-socket.h        \
   cfilters.h         \
   conncache.h        \
+  cshutdn.h          \
   connect.h          \
   content_encoding.h \
   cookie.h           \
   curl_addrinfo.h    \
-  curl_base64.h      \
   curl_ctype.h       \
   curl_des.h         \
   curl_endian.h      \
@@ -1466,7 +1528,6 @@ LIB_HFILES = \
   curl_md5.h         \
   curl_memory.h      \
   curl_memrchr.h     \
-  curl_multibyte.h   \
   curl_ntlm_core.h   \
   curl_printf.h      \
   curl_range.h       \
@@ -1479,16 +1540,16 @@ LIB_HFILES = \
   curl_sspi.h        \
   curl_threads.h     \
   curl_trc.h         \
-  curlx.h            \
   cw-out.h           \
+  cw-pause.h         \
   dict.h             \
   doh.h              \
-  dynbuf.h           \
   dynhds.h           \
   easy_lock.h        \
   easyif.h           \
   easyoptions.h      \
   escape.h           \
+  fake_addrinfo.h    \
   file.h             \
   fileinfo.h         \
   fopen.h            \
@@ -1516,16 +1577,15 @@ LIB_HFILES = \
   if2ip.h            \
   imap.h             \
   inet_ntop.h        \
-  inet_pton.h        \
   llist.h            \
   macos.h            \
   memdebug.h         \
   mime.h             \
   mqtt.h             \
   multihandle.h      \
+  multi_ev.h         \
   multiif.h          \
   netrc.h            \
-  nonblock.h         \
   noproxy.h          \
   parsedate.h        \
   pingpong.h         \
@@ -1555,48 +1615,43 @@ LIB_HFILES = \
   strcase.h          \
   strdup.h           \
   strerror.h         \
-  strparse.h         \
-  strtok.h           \
-  strtoofft.h        \
   system_win32.h     \
   telnet.h           \
   tftp.h             \
-  timediff.h         \
-  timeval.h          \
   transfer.h         \
+  uint-bset.h        \
+  uint-hash.h        \
+  uint-spbset.h      \
+  uint-table.h       \
   url.h              \
   urlapi-int.h       \
   urldata.h          \
-  version_win32.h    \
-  warnless.h         \
   ws.h
 
 LIB_RCFILES = libcurl.rc
 CSOURCES = $(LIB_CFILES) $(LIB_VAUTH_CFILES) $(LIB_VTLS_CFILES) \
-  $(LIB_VQUIC_CFILES) $(LIB_VSSH_CFILES)
-
+	$(LIB_VQUIC_CFILES) $(LIB_VSSH_CFILES) $(LIB_CURLX_CFILES) \
+	$(am__append_3)
 HHEADERS = $(LIB_HFILES) $(LIB_VAUTH_HFILES) $(LIB_VTLS_HFILES) \
-  $(LIB_VQUIC_HFILES) $(LIB_VSSH_HFILES)
-
+  $(LIB_VQUIC_HFILES) $(LIB_VSSH_HFILES) $(LIB_CURLX_HFILES)
 
-# Makefile.inc provides the CSOURCES and HHEADERS defines
 
 # Keep these separate to avoid duplicate definitions when linking libtests
 # in static mode.
-@USE_UNITY_TRUE@curl_EXCLUDE = curl_threads.c timediff.c warnless.c \
-@USE_UNITY_TRUE@	$(am__append_1)
+@USE_UNITY_TRUE@curl_EXCLUDE = curl_threads.c curlx/timediff.c \
+@USE_UNITY_TRUE@	curlx/warnless.c $(am__append_4)
 @USE_UNITY_TRUE@nodist_libcurl_la_SOURCES = libcurl_unity.c
 @USE_UNITY_FALSE@libcurl_la_SOURCES = $(CSOURCES) $(HHEADERS) \
-@USE_UNITY_FALSE@	$(am__append_8)
-@USE_UNITY_TRUE@libcurl_la_SOURCES = $(curl_EXCLUDE) $(am__append_8)
+@USE_UNITY_FALSE@	$(am__append_11)
+@USE_UNITY_TRUE@libcurl_la_SOURCES = $(curl_EXCLUDE) $(am__append_11)
 @USE_UNITY_TRUE@nodist_libcurlu_la_SOURCES = libcurl_unity.c
 @USE_UNITY_FALSE@libcurlu_la_SOURCES = $(CSOURCES) $(HHEADERS)
 @USE_UNITY_TRUE@libcurlu_la_SOURCES = $(curl_EXCLUDE)
 @USE_UNITY_TRUE@CLEANFILES = libcurl_unity.c
-libcurl_la_CPPFLAGS_EXTRA = $(am__append_7) $(am__append_9)
-libcurl_la_LDFLAGS_EXTRA = $(am__append_2) $(am__append_3) \
-	$(am__append_4) $(am__append_5) $(am__append_6)
-libcurl_la_CFLAGS_EXTRA = $(am__append_10)
+libcurl_la_CPPFLAGS_EXTRA = $(am__append_10) $(am__append_12)
+libcurl_la_LDFLAGS_EXTRA = $(am__append_5) $(am__append_6) \
+	$(am__append_7) $(am__append_8) $(am__append_9)
+libcurl_la_CFLAGS_EXTRA = $(am__append_13)
 libcurl_la_CPPFLAGS = $(AM_CPPFLAGS) $(libcurl_la_CPPFLAGS_EXTRA)
 libcurl_la_LDFLAGS = $(AM_LDFLAGS) $(libcurl_la_LDFLAGS_EXTRA) $(CURL_LDFLAGS_LIB) $(LIBCURL_PC_LIBS_PRIVATE)
 libcurl_la_CFLAGS = $(AM_CFLAGS) $(libcurl_la_CFLAGS_EXTRA)
@@ -1610,7 +1665,7 @@ CS_ = $(CS_0)
 
 # disable the tests that are mostly causing false positives
 TIDYFLAGS := -checks=-clang-analyzer-security.insecureAPI.strcpy,-clang-analyzer-optin.performance.Padding,-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling \
-	-quiet $(am__append_11)
+	-quiet $(am__append_14)
 TIDY := clang-tidy
 all: curl_config.h
 	$(MAKE) $(AM_MAKEFLAGS) all-am
@@ -1814,6 +1869,34 @@ vssh/libcurl_la-curl_path.lo: vssh/$(am__dirstamp) \
 	vssh/$(DEPDIR)/$(am__dirstamp)
 vssh/libcurl_la-wolfssh.lo: vssh/$(am__dirstamp) \
 	vssh/$(DEPDIR)/$(am__dirstamp)
+curlx/$(am__dirstamp):
+	@$(MKDIR_P) curlx
+	@: > curlx/$(am__dirstamp)
+curlx/$(DEPDIR)/$(am__dirstamp):
+	@$(MKDIR_P) curlx/$(DEPDIR)
+	@: > curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurl_la-base64.lo: curlx/$(am__dirstamp) \
+	curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurl_la-dynbuf.lo: curlx/$(am__dirstamp) \
+	curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurl_la-inet_pton.lo: curlx/$(am__dirstamp) \
+	curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurl_la-multibyte.lo: curlx/$(am__dirstamp) \
+	curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurl_la-nonblock.lo: curlx/$(am__dirstamp) \
+	curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurl_la-strparse.lo: curlx/$(am__dirstamp) \
+	curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurl_la-timediff.lo: curlx/$(am__dirstamp) \
+	curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurl_la-timeval.lo: curlx/$(am__dirstamp) \
+	curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurl_la-version_win32.lo: curlx/$(am__dirstamp) \
+	curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurl_la-warnless.lo: curlx/$(am__dirstamp) \
+	curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurl_la-winapi.lo: curlx/$(am__dirstamp) \
+	curlx/$(DEPDIR)/$(am__dirstamp)
 
 libcurl.la: $(libcurl_la_OBJECTS) $(libcurl_la_DEPENDENCIES) $(EXTRA_libcurl_la_DEPENDENCIES) 
 	$(AM_V_CCLD)$(libcurl_la_LINK) -rpath $(libdir) $(libcurl_la_OBJECTS) $(libcurl_la_LIBADD) $(LIBS)
@@ -1897,12 +1980,36 @@ vssh/libcurlu_la-curl_path.lo: vssh/$(am__dirstamp) \
 	vssh/$(DEPDIR)/$(am__dirstamp)
 vssh/libcurlu_la-wolfssh.lo: vssh/$(am__dirstamp) \
 	vssh/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurlu_la-base64.lo: curlx/$(am__dirstamp) \
+	curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurlu_la-dynbuf.lo: curlx/$(am__dirstamp) \
+	curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurlu_la-inet_pton.lo: curlx/$(am__dirstamp) \
+	curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurlu_la-multibyte.lo: curlx/$(am__dirstamp) \
+	curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurlu_la-nonblock.lo: curlx/$(am__dirstamp) \
+	curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurlu_la-strparse.lo: curlx/$(am__dirstamp) \
+	curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurlu_la-timediff.lo: curlx/$(am__dirstamp) \
+	curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurlu_la-timeval.lo: curlx/$(am__dirstamp) \
+	curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurlu_la-version_win32.lo: curlx/$(am__dirstamp) \
+	curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurlu_la-warnless.lo: curlx/$(am__dirstamp) \
+	curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurlu_la-winapi.lo: curlx/$(am__dirstamp) \
+	curlx/$(DEPDIR)/$(am__dirstamp)
 
 libcurlu.la: $(libcurlu_la_OBJECTS) $(libcurlu_la_DEPENDENCIES) $(EXTRA_libcurlu_la_DEPENDENCIES) 
 	$(AM_V_CCLD)$(libcurlu_la_LINK) $(am_libcurlu_la_rpath) $(libcurlu_la_OBJECTS) $(libcurlu_la_LIBADD) $(LIBS)
 
 mostlyclean-compile:
 	-rm -f *.$(OBJEXT)
+	-rm -f curlx/*.$(OBJEXT)
+	-rm -f curlx/*.lo
 	-rm -f vauth/*.$(OBJEXT)
 	-rm -f vauth/*.lo
 	-rm -f vquic/*.$(OBJEXT)
@@ -1918,8 +2025,8 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-altsvc.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-amigaos.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-asyn-ares.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-asyn-thread.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-base64.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-asyn-base.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-asyn-thrdd.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-bufq.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-bufref.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-cf-h1-proxy.Plo@am__quote@ # am--include-marker
@@ -1932,6 +2039,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-connect.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-content_encoding.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-cookie.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-cshutdn.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_addrinfo.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_des.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_endian.Plo@am__quote@ # am--include-marker
@@ -1940,7 +2048,6 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_gethostname.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_gssapi.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_memrchr.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_multibyte.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_ntlm_core.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_range.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_rtmp.Plo@am__quote@ # am--include-marker
@@ -1950,15 +2057,16 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_threads.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_trc.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-cw-out.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-cw-pause.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-dict.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-dllmain.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-doh.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-dynbuf.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-dynhds.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-easy.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-easygetopt.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-easyoptions.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-escape.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-fake_addrinfo.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-file.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-fileinfo.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-fopen.Plo@am__quote@ # am--include-marker
@@ -1971,11 +2079,9 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-hash.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-headers.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-hmac.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-hostasyn.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-hostip.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-hostip4.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-hostip6.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-hostsyn.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-hsts.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-http.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-http1.Plo@am__quote@ # am--include-marker
@@ -1991,7 +2097,6 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-if2ip.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-imap.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-inet_ntop.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-inet_pton.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-krb5.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-ldap.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-libcurl_unity.Plo@am__quote@ # am--include-marker
@@ -2004,8 +2109,8 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-mprintf.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-mqtt.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-multi.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-multi_ev.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-netrc.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-nonblock.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-noproxy.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-openldap.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-parsedate.Plo@am__quote@ # am--include-marker
@@ -2033,27 +2138,25 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-splay.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-strcase.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-strdup.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-strequal.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-strerror.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-strparse.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-strtok.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-strtoofft.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-system_win32.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-telnet.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-tftp.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-timediff.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-timeval.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-transfer.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-uint-bset.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-uint-hash.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-uint-spbset.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-uint-table.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-url.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-urlapi.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-version.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-version_win32.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-warnless.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-ws.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-altsvc.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-amigaos.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-asyn-ares.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-asyn-thread.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-base64.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-asyn-base.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-asyn-thrdd.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-bufq.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-bufref.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-cf-h1-proxy.Plo@am__quote@ # am--include-marker
@@ -2066,6 +2169,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-connect.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-content_encoding.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-cookie.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-cshutdn.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_addrinfo.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_des.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_endian.Plo@am__quote@ # am--include-marker
@@ -2074,7 +2178,6 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_gethostname.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_gssapi.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_memrchr.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_multibyte.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_ntlm_core.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_range.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_rtmp.Plo@am__quote@ # am--include-marker
@@ -2084,15 +2187,16 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_threads.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_trc.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-cw-out.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-cw-pause.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-dict.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-dllmain.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-doh.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-dynbuf.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-dynhds.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-easy.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-easygetopt.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-easyoptions.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-escape.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-fake_addrinfo.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-file.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-fileinfo.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-fopen.Plo@am__quote@ # am--include-marker
@@ -2105,11 +2209,9 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-hash.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-headers.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-hmac.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-hostasyn.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-hostip.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-hostip4.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-hostip6.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-hostsyn.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-hsts.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-http.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-http1.Plo@am__quote@ # am--include-marker
@@ -2125,7 +2227,6 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-if2ip.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-imap.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-inet_ntop.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-inet_pton.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-krb5.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-ldap.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-libcurl_unity.Plo@am__quote@ # am--include-marker
@@ -2138,8 +2239,8 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-mprintf.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-mqtt.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-multi.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-multi_ev.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-netrc.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-nonblock.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-noproxy.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-openldap.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-parsedate.Plo@am__quote@ # am--include-marker
@@ -2167,22 +2268,42 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-splay.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-strcase.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-strdup.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-strequal.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-strerror.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-strparse.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-strtok.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-strtoofft.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-system_win32.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-telnet.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-tftp.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-timediff.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-timeval.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-transfer.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-uint-bset.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-uint-hash.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-uint-spbset.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-uint-table.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-url.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-urlapi.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-version.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-version_win32.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-warnless.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-ws.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurl_la-base64.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurl_la-dynbuf.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurl_la-inet_pton.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurl_la-multibyte.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurl_la-nonblock.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurl_la-strparse.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurl_la-timediff.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurl_la-timeval.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurl_la-version_win32.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurl_la-warnless.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurl_la-winapi.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurlu_la-base64.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurlu_la-dynbuf.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurlu_la-inet_pton.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurlu_la-multibyte.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurlu_la-nonblock.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurlu_la-strparse.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurlu_la-timediff.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurlu_la-timeval.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurlu_la-version_win32.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurlu_la-warnless.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurlu_la-winapi.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-cleartext.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-cram.Plo@am__quote@ # am--include-marker
 @AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-digest.Plo@am__quote@ # am--include-marker
@@ -2315,19 +2436,19 @@ libcurl_la-asyn-ares.lo: asyn-ares.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-asyn-ares.lo `test -f 'asyn-ares.c' || echo '$(srcdir)/'`asyn-ares.c
 
-libcurl_la-asyn-thread.lo: asyn-thread.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-asyn-thread.lo -MD -MP -MF $(DEPDIR)/libcurl_la-asyn-thread.Tpo -c -o libcurl_la-asyn-thread.lo `test -f 'asyn-thread.c' || echo '$(srcdir)/'`asyn-thread.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-asyn-thread.Tpo $(DEPDIR)/libcurl_la-asyn-thread.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='asyn-thread.c' object='libcurl_la-asyn-thread.lo' libtool=yes @AMDEPBACKSLASH@
+libcurl_la-asyn-base.lo: asyn-base.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-asyn-base.lo -MD -MP -MF $(DEPDIR)/libcurl_la-asyn-base.Tpo -c -o libcurl_la-asyn-base.lo `test -f 'asyn-base.c' || echo '$(srcdir)/'`asyn-base.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-asyn-base.Tpo $(DEPDIR)/libcurl_la-asyn-base.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='asyn-base.c' object='libcurl_la-asyn-base.lo' libtool=yes @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-asyn-thread.lo `test -f 'asyn-thread.c' || echo '$(srcdir)/'`asyn-thread.c
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-asyn-base.lo `test -f 'asyn-base.c' || echo '$(srcdir)/'`asyn-base.c
 
-libcurl_la-base64.lo: base64.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-base64.lo -MD -MP -MF $(DEPDIR)/libcurl_la-base64.Tpo -c -o libcurl_la-base64.lo `test -f 'base64.c' || echo '$(srcdir)/'`base64.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-base64.Tpo $(DEPDIR)/libcurl_la-base64.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='base64.c' object='libcurl_la-base64.lo' libtool=yes @AMDEPBACKSLASH@
+libcurl_la-asyn-thrdd.lo: asyn-thrdd.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-asyn-thrdd.lo -MD -MP -MF $(DEPDIR)/libcurl_la-asyn-thrdd.Tpo -c -o libcurl_la-asyn-thrdd.lo `test -f 'asyn-thrdd.c' || echo '$(srcdir)/'`asyn-thrdd.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-asyn-thrdd.Tpo $(DEPDIR)/libcurl_la-asyn-thrdd.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='asyn-thrdd.c' object='libcurl_la-asyn-thrdd.lo' libtool=yes @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-base64.lo `test -f 'base64.c' || echo '$(srcdir)/'`base64.c
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-asyn-thrdd.lo `test -f 'asyn-thrdd.c' || echo '$(srcdir)/'`asyn-thrdd.c
 
 libcurl_la-bufq.lo: bufq.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-bufq.lo -MD -MP -MF $(DEPDIR)/libcurl_la-bufq.Tpo -c -o libcurl_la-bufq.lo `test -f 'bufq.c' || echo '$(srcdir)/'`bufq.c
@@ -2413,6 +2534,13 @@ libcurl_la-cookie.lo: cookie.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-cookie.lo `test -f 'cookie.c' || echo '$(srcdir)/'`cookie.c
 
+libcurl_la-cshutdn.lo: cshutdn.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-cshutdn.lo -MD -MP -MF $(DEPDIR)/libcurl_la-cshutdn.Tpo -c -o libcurl_la-cshutdn.lo `test -f 'cshutdn.c' || echo '$(srcdir)/'`cshutdn.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-cshutdn.Tpo $(DEPDIR)/libcurl_la-cshutdn.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='cshutdn.c' object='libcurl_la-cshutdn.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-cshutdn.lo `test -f 'cshutdn.c' || echo '$(srcdir)/'`cshutdn.c
+
 libcurl_la-curl_addrinfo.lo: curl_addrinfo.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_addrinfo.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_addrinfo.Tpo -c -o libcurl_la-curl_addrinfo.lo `test -f 'curl_addrinfo.c' || echo '$(srcdir)/'`curl_addrinfo.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_addrinfo.Tpo $(DEPDIR)/libcurl_la-curl_addrinfo.Plo
@@ -2469,13 +2597,6 @@ libcurl_la-curl_memrchr.lo: curl_memrchr.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_memrchr.lo `test -f 'curl_memrchr.c' || echo '$(srcdir)/'`curl_memrchr.c
 
-libcurl_la-curl_multibyte.lo: curl_multibyte.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_multibyte.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_multibyte.Tpo -c -o libcurl_la-curl_multibyte.lo `test -f 'curl_multibyte.c' || echo '$(srcdir)/'`curl_multibyte.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_multibyte.Tpo $(DEPDIR)/libcurl_la-curl_multibyte.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='curl_multibyte.c' object='libcurl_la-curl_multibyte.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_multibyte.lo `test -f 'curl_multibyte.c' || echo '$(srcdir)/'`curl_multibyte.c
-
 libcurl_la-curl_ntlm_core.lo: curl_ntlm_core.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_ntlm_core.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_ntlm_core.Tpo -c -o libcurl_la-curl_ntlm_core.lo `test -f 'curl_ntlm_core.c' || echo '$(srcdir)/'`curl_ntlm_core.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_ntlm_core.Tpo $(DEPDIR)/libcurl_la-curl_ntlm_core.Plo
@@ -2539,6 +2660,13 @@ libcurl_la-cw-out.lo: cw-out.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-cw-out.lo `test -f 'cw-out.c' || echo '$(srcdir)/'`cw-out.c
 
+libcurl_la-cw-pause.lo: cw-pause.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-cw-pause.lo -MD -MP -MF $(DEPDIR)/libcurl_la-cw-pause.Tpo -c -o libcurl_la-cw-pause.lo `test -f 'cw-pause.c' || echo '$(srcdir)/'`cw-pause.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-cw-pause.Tpo $(DEPDIR)/libcurl_la-cw-pause.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='cw-pause.c' object='libcurl_la-cw-pause.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-cw-pause.lo `test -f 'cw-pause.c' || echo '$(srcdir)/'`cw-pause.c
+
 libcurl_la-dict.lo: dict.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-dict.lo -MD -MP -MF $(DEPDIR)/libcurl_la-dict.Tpo -c -o libcurl_la-dict.lo `test -f 'dict.c' || echo '$(srcdir)/'`dict.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-dict.Tpo $(DEPDIR)/libcurl_la-dict.Plo
@@ -2546,13 +2674,6 @@ libcurl_la-dict.lo: dict.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-dict.lo `test -f 'dict.c' || echo '$(srcdir)/'`dict.c
 
-libcurl_la-dllmain.lo: dllmain.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-dllmain.lo -MD -MP -MF $(DEPDIR)/libcurl_la-dllmain.Tpo -c -o libcurl_la-dllmain.lo `test -f 'dllmain.c' || echo '$(srcdir)/'`dllmain.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-dllmain.Tpo $(DEPDIR)/libcurl_la-dllmain.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='dllmain.c' object='libcurl_la-dllmain.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-dllmain.lo `test -f 'dllmain.c' || echo '$(srcdir)/'`dllmain.c
-
 libcurl_la-doh.lo: doh.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-doh.lo -MD -MP -MF $(DEPDIR)/libcurl_la-doh.Tpo -c -o libcurl_la-doh.lo `test -f 'doh.c' || echo '$(srcdir)/'`doh.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-doh.Tpo $(DEPDIR)/libcurl_la-doh.Plo
@@ -2560,13 +2681,6 @@ libcurl_la-doh.lo: doh.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-doh.lo `test -f 'doh.c' || echo '$(srcdir)/'`doh.c
 
-libcurl_la-dynbuf.lo: dynbuf.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-dynbuf.lo -MD -MP -MF $(DEPDIR)/libcurl_la-dynbuf.Tpo -c -o libcurl_la-dynbuf.lo `test -f 'dynbuf.c' || echo '$(srcdir)/'`dynbuf.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-dynbuf.Tpo $(DEPDIR)/libcurl_la-dynbuf.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='dynbuf.c' object='libcurl_la-dynbuf.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-dynbuf.lo `test -f 'dynbuf.c' || echo '$(srcdir)/'`dynbuf.c
-
 libcurl_la-dynhds.lo: dynhds.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-dynhds.lo -MD -MP -MF $(DEPDIR)/libcurl_la-dynhds.Tpo -c -o libcurl_la-dynhds.lo `test -f 'dynhds.c' || echo '$(srcdir)/'`dynhds.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-dynhds.Tpo $(DEPDIR)/libcurl_la-dynhds.Plo
@@ -2602,6 +2716,13 @@ libcurl_la-escape.lo: escape.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-escape.lo `test -f 'escape.c' || echo '$(srcdir)/'`escape.c
 
+libcurl_la-fake_addrinfo.lo: fake_addrinfo.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-fake_addrinfo.lo -MD -MP -MF $(DEPDIR)/libcurl_la-fake_addrinfo.Tpo -c -o libcurl_la-fake_addrinfo.lo `test -f 'fake_addrinfo.c' || echo '$(srcdir)/'`fake_addrinfo.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-fake_addrinfo.Tpo $(DEPDIR)/libcurl_la-fake_addrinfo.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='fake_addrinfo.c' object='libcurl_la-fake_addrinfo.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-fake_addrinfo.lo `test -f 'fake_addrinfo.c' || echo '$(srcdir)/'`fake_addrinfo.c
+
 libcurl_la-file.lo: file.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-file.lo -MD -MP -MF $(DEPDIR)/libcurl_la-file.Tpo -c -o libcurl_la-file.lo `test -f 'file.c' || echo '$(srcdir)/'`file.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-file.Tpo $(DEPDIR)/libcurl_la-file.Plo
@@ -2686,13 +2807,6 @@ libcurl_la-hmac.lo: hmac.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-hmac.lo `test -f 'hmac.c' || echo '$(srcdir)/'`hmac.c
 
-libcurl_la-hostasyn.lo: hostasyn.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-hostasyn.lo -MD -MP -MF $(DEPDIR)/libcurl_la-hostasyn.Tpo -c -o libcurl_la-hostasyn.lo `test -f 'hostasyn.c' || echo '$(srcdir)/'`hostasyn.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-hostasyn.Tpo $(DEPDIR)/libcurl_la-hostasyn.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='hostasyn.c' object='libcurl_la-hostasyn.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-hostasyn.lo `test -f 'hostasyn.c' || echo '$(srcdir)/'`hostasyn.c
-
 libcurl_la-hostip.lo: hostip.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-hostip.lo -MD -MP -MF $(DEPDIR)/libcurl_la-hostip.Tpo -c -o libcurl_la-hostip.lo `test -f 'hostip.c' || echo '$(srcdir)/'`hostip.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-hostip.Tpo $(DEPDIR)/libcurl_la-hostip.Plo
@@ -2714,13 +2828,6 @@ libcurl_la-hostip6.lo: hostip6.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-hostip6.lo `test -f 'hostip6.c' || echo '$(srcdir)/'`hostip6.c
 
-libcurl_la-hostsyn.lo: hostsyn.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-hostsyn.lo -MD -MP -MF $(DEPDIR)/libcurl_la-hostsyn.Tpo -c -o libcurl_la-hostsyn.lo `test -f 'hostsyn.c' || echo '$(srcdir)/'`hostsyn.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-hostsyn.Tpo $(DEPDIR)/libcurl_la-hostsyn.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='hostsyn.c' object='libcurl_la-hostsyn.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-hostsyn.lo `test -f 'hostsyn.c' || echo '$(srcdir)/'`hostsyn.c
-
 libcurl_la-hsts.lo: hsts.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-hsts.lo -MD -MP -MF $(DEPDIR)/libcurl_la-hsts.Tpo -c -o libcurl_la-hsts.lo `test -f 'hsts.c' || echo '$(srcdir)/'`hsts.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-hsts.Tpo $(DEPDIR)/libcurl_la-hsts.Plo
@@ -2826,13 +2933,6 @@ libcurl_la-inet_ntop.lo: inet_ntop.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-inet_ntop.lo `test -f 'inet_ntop.c' || echo '$(srcdir)/'`inet_ntop.c
 
-libcurl_la-inet_pton.lo: inet_pton.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-inet_pton.lo -MD -MP -MF $(DEPDIR)/libcurl_la-inet_pton.Tpo -c -o libcurl_la-inet_pton.lo `test -f 'inet_pton.c' || echo '$(srcdir)/'`inet_pton.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-inet_pton.Tpo $(DEPDIR)/libcurl_la-inet_pton.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='inet_pton.c' object='libcurl_la-inet_pton.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-inet_pton.lo `test -f 'inet_pton.c' || echo '$(srcdir)/'`inet_pton.c
-
 libcurl_la-krb5.lo: krb5.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-krb5.lo -MD -MP -MF $(DEPDIR)/libcurl_la-krb5.Tpo -c -o libcurl_la-krb5.lo `test -f 'krb5.c' || echo '$(srcdir)/'`krb5.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-krb5.Tpo $(DEPDIR)/libcurl_la-krb5.Plo
@@ -2910,6 +3010,13 @@ libcurl_la-multi.lo: multi.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-multi.lo `test -f 'multi.c' || echo '$(srcdir)/'`multi.c
 
+libcurl_la-multi_ev.lo: multi_ev.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-multi_ev.lo -MD -MP -MF $(DEPDIR)/libcurl_la-multi_ev.Tpo -c -o libcurl_la-multi_ev.lo `test -f 'multi_ev.c' || echo '$(srcdir)/'`multi_ev.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-multi_ev.Tpo $(DEPDIR)/libcurl_la-multi_ev.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='multi_ev.c' object='libcurl_la-multi_ev.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-multi_ev.lo `test -f 'multi_ev.c' || echo '$(srcdir)/'`multi_ev.c
+
 libcurl_la-netrc.lo: netrc.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-netrc.lo -MD -MP -MF $(DEPDIR)/libcurl_la-netrc.Tpo -c -o libcurl_la-netrc.lo `test -f 'netrc.c' || echo '$(srcdir)/'`netrc.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-netrc.Tpo $(DEPDIR)/libcurl_la-netrc.Plo
@@ -2917,13 +3024,6 @@ libcurl_la-netrc.lo: netrc.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-netrc.lo `test -f 'netrc.c' || echo '$(srcdir)/'`netrc.c
 
-libcurl_la-nonblock.lo: nonblock.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-nonblock.lo -MD -MP -MF $(DEPDIR)/libcurl_la-nonblock.Tpo -c -o libcurl_la-nonblock.lo `test -f 'nonblock.c' || echo '$(srcdir)/'`nonblock.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-nonblock.Tpo $(DEPDIR)/libcurl_la-nonblock.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='nonblock.c' object='libcurl_la-nonblock.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-nonblock.lo `test -f 'nonblock.c' || echo '$(srcdir)/'`nonblock.c
-
 libcurl_la-noproxy.lo: noproxy.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-noproxy.lo -MD -MP -MF $(DEPDIR)/libcurl_la-noproxy.Tpo -c -o libcurl_la-noproxy.lo `test -f 'noproxy.c' || echo '$(srcdir)/'`noproxy.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-noproxy.Tpo $(DEPDIR)/libcurl_la-noproxy.Plo
@@ -3113,6 +3213,13 @@ libcurl_la-strdup.lo: strdup.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-strdup.lo `test -f 'strdup.c' || echo '$(srcdir)/'`strdup.c
 
+libcurl_la-strequal.lo: strequal.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-strequal.lo -MD -MP -MF $(DEPDIR)/libcurl_la-strequal.Tpo -c -o libcurl_la-strequal.lo `test -f 'strequal.c' || echo '$(srcdir)/'`strequal.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-strequal.Tpo $(DEPDIR)/libcurl_la-strequal.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='strequal.c' object='libcurl_la-strequal.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-strequal.lo `test -f 'strequal.c' || echo '$(srcdir)/'`strequal.c
+
 libcurl_la-strerror.lo: strerror.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-strerror.lo -MD -MP -MF $(DEPDIR)/libcurl_la-strerror.Tpo -c -o libcurl_la-strerror.lo `test -f 'strerror.c' || echo '$(srcdir)/'`strerror.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-strerror.Tpo $(DEPDIR)/libcurl_la-strerror.Plo
@@ -3120,27 +3227,6 @@ libcurl_la-strerror.lo: strerror.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-strerror.lo `test -f 'strerror.c' || echo '$(srcdir)/'`strerror.c
 
-libcurl_la-strparse.lo: strparse.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-strparse.lo -MD -MP -MF $(DEPDIR)/libcurl_la-strparse.Tpo -c -o libcurl_la-strparse.lo `test -f 'strparse.c' || echo '$(srcdir)/'`strparse.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-strparse.Tpo $(DEPDIR)/libcurl_la-strparse.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='strparse.c' object='libcurl_la-strparse.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-strparse.lo `test -f 'strparse.c' || echo '$(srcdir)/'`strparse.c
-
-libcurl_la-strtok.lo: strtok.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-strtok.lo -MD -MP -MF $(DEPDIR)/libcurl_la-strtok.Tpo -c -o libcurl_la-strtok.lo `test -f 'strtok.c' || echo '$(srcdir)/'`strtok.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-strtok.Tpo $(DEPDIR)/libcurl_la-strtok.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='strtok.c' object='libcurl_la-strtok.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-strtok.lo `test -f 'strtok.c' || echo '$(srcdir)/'`strtok.c
-
-libcurl_la-strtoofft.lo: strtoofft.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-strtoofft.lo -MD -MP -MF $(DEPDIR)/libcurl_la-strtoofft.Tpo -c -o libcurl_la-strtoofft.lo `test -f 'strtoofft.c' || echo '$(srcdir)/'`strtoofft.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-strtoofft.Tpo $(DEPDIR)/libcurl_la-strtoofft.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='strtoofft.c' object='libcurl_la-strtoofft.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-strtoofft.lo `test -f 'strtoofft.c' || echo '$(srcdir)/'`strtoofft.c
-
 libcurl_la-system_win32.lo: system_win32.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-system_win32.lo -MD -MP -MF $(DEPDIR)/libcurl_la-system_win32.Tpo -c -o libcurl_la-system_win32.lo `test -f 'system_win32.c' || echo '$(srcdir)/'`system_win32.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-system_win32.Tpo $(DEPDIR)/libcurl_la-system_win32.Plo
@@ -3162,20 +3248,6 @@ libcurl_la-tftp.lo: tftp.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-tftp.lo `test -f 'tftp.c' || echo '$(srcdir)/'`tftp.c
 
-libcurl_la-timediff.lo: timediff.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-timediff.lo -MD -MP -MF $(DEPDIR)/libcurl_la-timediff.Tpo -c -o libcurl_la-timediff.lo `test -f 'timediff.c' || echo '$(srcdir)/'`timediff.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-timediff.Tpo $(DEPDIR)/libcurl_la-timediff.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='timediff.c' object='libcurl_la-timediff.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-timediff.lo `test -f 'timediff.c' || echo '$(srcdir)/'`timediff.c
-
-libcurl_la-timeval.lo: timeval.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-timeval.lo -MD -MP -MF $(DEPDIR)/libcurl_la-timeval.Tpo -c -o libcurl_la-timeval.lo `test -f 'timeval.c' || echo '$(srcdir)/'`timeval.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-timeval.Tpo $(DEPDIR)/libcurl_la-timeval.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='timeval.c' object='libcurl_la-timeval.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-timeval.lo `test -f 'timeval.c' || echo '$(srcdir)/'`timeval.c
-
 libcurl_la-transfer.lo: transfer.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-transfer.lo -MD -MP -MF $(DEPDIR)/libcurl_la-transfer.Tpo -c -o libcurl_la-transfer.lo `test -f 'transfer.c' || echo '$(srcdir)/'`transfer.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-transfer.Tpo $(DEPDIR)/libcurl_la-transfer.Plo
@@ -3183,6 +3255,34 @@ libcurl_la-transfer.lo: transfer.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-transfer.lo `test -f 'transfer.c' || echo '$(srcdir)/'`transfer.c
 
+libcurl_la-uint-bset.lo: uint-bset.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-uint-bset.lo -MD -MP -MF $(DEPDIR)/libcurl_la-uint-bset.Tpo -c -o libcurl_la-uint-bset.lo `test -f 'uint-bset.c' || echo '$(srcdir)/'`uint-bset.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-uint-bset.Tpo $(DEPDIR)/libcurl_la-uint-bset.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='uint-bset.c' object='libcurl_la-uint-bset.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-uint-bset.lo `test -f 'uint-bset.c' || echo '$(srcdir)/'`uint-bset.c
+
+libcurl_la-uint-hash.lo: uint-hash.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-uint-hash.lo -MD -MP -MF $(DEPDIR)/libcurl_la-uint-hash.Tpo -c -o libcurl_la-uint-hash.lo `test -f 'uint-hash.c' || echo '$(srcdir)/'`uint-hash.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-uint-hash.Tpo $(DEPDIR)/libcurl_la-uint-hash.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='uint-hash.c' object='libcurl_la-uint-hash.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-uint-hash.lo `test -f 'uint-hash.c' || echo '$(srcdir)/'`uint-hash.c
+
+libcurl_la-uint-spbset.lo: uint-spbset.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-uint-spbset.lo -MD -MP -MF $(DEPDIR)/libcurl_la-uint-spbset.Tpo -c -o libcurl_la-uint-spbset.lo `test -f 'uint-spbset.c' || echo '$(srcdir)/'`uint-spbset.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-uint-spbset.Tpo $(DEPDIR)/libcurl_la-uint-spbset.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='uint-spbset.c' object='libcurl_la-uint-spbset.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-uint-spbset.lo `test -f 'uint-spbset.c' || echo '$(srcdir)/'`uint-spbset.c
+
+libcurl_la-uint-table.lo: uint-table.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-uint-table.lo -MD -MP -MF $(DEPDIR)/libcurl_la-uint-table.Tpo -c -o libcurl_la-uint-table.lo `test -f 'uint-table.c' || echo '$(srcdir)/'`uint-table.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-uint-table.Tpo $(DEPDIR)/libcurl_la-uint-table.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='uint-table.c' object='libcurl_la-uint-table.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-uint-table.lo `test -f 'uint-table.c' || echo '$(srcdir)/'`uint-table.c
+
 libcurl_la-url.lo: url.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-url.lo -MD -MP -MF $(DEPDIR)/libcurl_la-url.Tpo -c -o libcurl_la-url.lo `test -f 'url.c' || echo '$(srcdir)/'`url.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-url.Tpo $(DEPDIR)/libcurl_la-url.Plo
@@ -3204,20 +3304,6 @@ libcurl_la-version.lo: version.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-version.lo `test -f 'version.c' || echo '$(srcdir)/'`version.c
 
-libcurl_la-version_win32.lo: version_win32.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-version_win32.lo -MD -MP -MF $(DEPDIR)/libcurl_la-version_win32.Tpo -c -o libcurl_la-version_win32.lo `test -f 'version_win32.c' || echo '$(srcdir)/'`version_win32.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-version_win32.Tpo $(DEPDIR)/libcurl_la-version_win32.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='version_win32.c' object='libcurl_la-version_win32.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-version_win32.lo `test -f 'version_win32.c' || echo '$(srcdir)/'`version_win32.c
-
-libcurl_la-warnless.lo: warnless.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-warnless.lo -MD -MP -MF $(DEPDIR)/libcurl_la-warnless.Tpo -c -o libcurl_la-warnless.lo `test -f 'warnless.c' || echo '$(srcdir)/'`warnless.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-warnless.Tpo $(DEPDIR)/libcurl_la-warnless.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='warnless.c' object='libcurl_la-warnless.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-warnless.lo `test -f 'warnless.c' || echo '$(srcdir)/'`warnless.c
-
 libcurl_la-ws.lo: ws.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-ws.lo -MD -MP -MF $(DEPDIR)/libcurl_la-ws.Tpo -c -o libcurl_la-ws.lo `test -f 'ws.c' || echo '$(srcdir)/'`ws.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-ws.Tpo $(DEPDIR)/libcurl_la-ws.Plo
@@ -3505,6 +3591,90 @@ vssh/libcurl_la-wolfssh.lo: vssh/wolfssh.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vssh/libcurl_la-wolfssh.lo `test -f 'vssh/wolfssh.c' || echo '$(srcdir)/'`vssh/wolfssh.c
 
+curlx/libcurl_la-base64.lo: curlx/base64.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT curlx/libcurl_la-base64.lo -MD -MP -MF curlx/$(DEPDIR)/libcurl_la-base64.Tpo -c -o curlx/libcurl_la-base64.lo `test -f 'curlx/base64.c' || echo '$(srcdir)/'`curlx/base64.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurl_la-base64.Tpo curlx/$(DEPDIR)/libcurl_la-base64.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='curlx/base64.c' object='curlx/libcurl_la-base64.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o curlx/libcurl_la-base64.lo `test -f 'curlx/base64.c' || echo '$(srcdir)/'`curlx/base64.c
+
+curlx/libcurl_la-dynbuf.lo: curlx/dynbuf.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT curlx/libcurl_la-dynbuf.lo -MD -MP -MF curlx/$(DEPDIR)/libcurl_la-dynbuf.Tpo -c -o curlx/libcurl_la-dynbuf.lo `test -f 'curlx/dynbuf.c' || echo '$(srcdir)/'`curlx/dynbuf.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurl_la-dynbuf.Tpo curlx/$(DEPDIR)/libcurl_la-dynbuf.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='curlx/dynbuf.c' object='curlx/libcurl_la-dynbuf.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o curlx/libcurl_la-dynbuf.lo `test -f 'curlx/dynbuf.c' || echo '$(srcdir)/'`curlx/dynbuf.c
+
+curlx/libcurl_la-inet_pton.lo: curlx/inet_pton.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT curlx/libcurl_la-inet_pton.lo -MD -MP -MF curlx/$(DEPDIR)/libcurl_la-inet_pton.Tpo -c -o curlx/libcurl_la-inet_pton.lo `test -f 'curlx/inet_pton.c' || echo '$(srcdir)/'`curlx/inet_pton.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurl_la-inet_pton.Tpo curlx/$(DEPDIR)/libcurl_la-inet_pton.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='curlx/inet_pton.c' object='curlx/libcurl_la-inet_pton.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o curlx/libcurl_la-inet_pton.lo `test -f 'curlx/inet_pton.c' || echo '$(srcdir)/'`curlx/inet_pton.c
+
+curlx/libcurl_la-multibyte.lo: curlx/multibyte.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT curlx/libcurl_la-multibyte.lo -MD -MP -MF curlx/$(DEPDIR)/libcurl_la-multibyte.Tpo -c -o curlx/libcurl_la-multibyte.lo `test -f 'curlx/multibyte.c' || echo '$(srcdir)/'`curlx/multibyte.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurl_la-multibyte.Tpo curlx/$(DEPDIR)/libcurl_la-multibyte.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='curlx/multibyte.c' object='curlx/libcurl_la-multibyte.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o curlx/libcurl_la-multibyte.lo `test -f 'curlx/multibyte.c' || echo '$(srcdir)/'`curlx/multibyte.c
+
+curlx/libcurl_la-nonblock.lo: curlx/nonblock.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT curlx/libcurl_la-nonblock.lo -MD -MP -MF curlx/$(DEPDIR)/libcurl_la-nonblock.Tpo -c -o curlx/libcurl_la-nonblock.lo `test -f 'curlx/nonblock.c' || echo '$(srcdir)/'`curlx/nonblock.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurl_la-nonblock.Tpo curlx/$(DEPDIR)/libcurl_la-nonblock.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='curlx/nonblock.c' object='curlx/libcurl_la-nonblock.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o curlx/libcurl_la-nonblock.lo `test -f 'curlx/nonblock.c' || echo '$(srcdir)/'`curlx/nonblock.c
+
+curlx/libcurl_la-strparse.lo: curlx/strparse.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT curlx/libcurl_la-strparse.lo -MD -MP -MF curlx/$(DEPDIR)/libcurl_la-strparse.Tpo -c -o curlx/libcurl_la-strparse.lo `test -f 'curlx/strparse.c' || echo '$(srcdir)/'`curlx/strparse.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurl_la-strparse.Tpo curlx/$(DEPDIR)/libcurl_la-strparse.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='curlx/strparse.c' object='curlx/libcurl_la-strparse.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o curlx/libcurl_la-strparse.lo `test -f 'curlx/strparse.c' || echo '$(srcdir)/'`curlx/strparse.c
+
+curlx/libcurl_la-timediff.lo: curlx/timediff.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT curlx/libcurl_la-timediff.lo -MD -MP -MF curlx/$(DEPDIR)/libcurl_la-timediff.Tpo -c -o curlx/libcurl_la-timediff.lo `test -f 'curlx/timediff.c' || echo '$(srcdir)/'`curlx/timediff.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurl_la-timediff.Tpo curlx/$(DEPDIR)/libcurl_la-timediff.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='curlx/timediff.c' object='curlx/libcurl_la-timediff.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o curlx/libcurl_la-timediff.lo `test -f 'curlx/timediff.c' || echo '$(srcdir)/'`curlx/timediff.c
+
+curlx/libcurl_la-timeval.lo: curlx/timeval.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT curlx/libcurl_la-timeval.lo -MD -MP -MF curlx/$(DEPDIR)/libcurl_la-timeval.Tpo -c -o curlx/libcurl_la-timeval.lo `test -f 'curlx/timeval.c' || echo '$(srcdir)/'`curlx/timeval.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurl_la-timeval.Tpo curlx/$(DEPDIR)/libcurl_la-timeval.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='curlx/timeval.c' object='curlx/libcurl_la-timeval.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o curlx/libcurl_la-timeval.lo `test -f 'curlx/timeval.c' || echo '$(srcdir)/'`curlx/timeval.c
+
+curlx/libcurl_la-version_win32.lo: curlx/version_win32.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT curlx/libcurl_la-version_win32.lo -MD -MP -MF curlx/$(DEPDIR)/libcurl_la-version_win32.Tpo -c -o curlx/libcurl_la-version_win32.lo `test -f 'curlx/version_win32.c' || echo '$(srcdir)/'`curlx/version_win32.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurl_la-version_win32.Tpo curlx/$(DEPDIR)/libcurl_la-version_win32.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='curlx/version_win32.c' object='curlx/libcurl_la-version_win32.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o curlx/libcurl_la-version_win32.lo `test -f 'curlx/version_win32.c' || echo '$(srcdir)/'`curlx/version_win32.c
+
+curlx/libcurl_la-warnless.lo: curlx/warnless.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT curlx/libcurl_la-warnless.lo -MD -MP -MF curlx/$(DEPDIR)/libcurl_la-warnless.Tpo -c -o curlx/libcurl_la-warnless.lo `test -f 'curlx/warnless.c' || echo '$(srcdir)/'`curlx/warnless.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurl_la-warnless.Tpo curlx/$(DEPDIR)/libcurl_la-warnless.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='curlx/warnless.c' object='curlx/libcurl_la-warnless.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o curlx/libcurl_la-warnless.lo `test -f 'curlx/warnless.c' || echo '$(srcdir)/'`curlx/warnless.c
+
+curlx/libcurl_la-winapi.lo: curlx/winapi.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT curlx/libcurl_la-winapi.lo -MD -MP -MF curlx/$(DEPDIR)/libcurl_la-winapi.Tpo -c -o curlx/libcurl_la-winapi.lo `test -f 'curlx/winapi.c' || echo '$(srcdir)/'`curlx/winapi.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurl_la-winapi.Tpo curlx/$(DEPDIR)/libcurl_la-winapi.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='curlx/winapi.c' object='curlx/libcurl_la-winapi.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o curlx/libcurl_la-winapi.lo `test -f 'curlx/winapi.c' || echo '$(srcdir)/'`curlx/winapi.c
+
+libcurl_la-dllmain.lo: dllmain.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-dllmain.lo -MD -MP -MF $(DEPDIR)/libcurl_la-dllmain.Tpo -c -o libcurl_la-dllmain.lo `test -f 'dllmain.c' || echo '$(srcdir)/'`dllmain.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-dllmain.Tpo $(DEPDIR)/libcurl_la-dllmain.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='dllmain.c' object='libcurl_la-dllmain.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-dllmain.lo `test -f 'dllmain.c' || echo '$(srcdir)/'`dllmain.c
+
 libcurl_la-libcurl_unity.lo: libcurl_unity.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-libcurl_unity.lo -MD -MP -MF $(DEPDIR)/libcurl_la-libcurl_unity.Tpo -c -o libcurl_la-libcurl_unity.lo `test -f 'libcurl_unity.c' || echo '$(srcdir)/'`libcurl_unity.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-libcurl_unity.Tpo $(DEPDIR)/libcurl_la-libcurl_unity.Plo
@@ -3533,19 +3703,19 @@ libcurlu_la-asyn-ares.lo: asyn-ares.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-asyn-ares.lo `test -f 'asyn-ares.c' || echo '$(srcdir)/'`asyn-ares.c
 
-libcurlu_la-asyn-thread.lo: asyn-thread.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-asyn-thread.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-asyn-thread.Tpo -c -o libcurlu_la-asyn-thread.lo `test -f 'asyn-thread.c' || echo '$(srcdir)/'`asyn-thread.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-asyn-thread.Tpo $(DEPDIR)/libcurlu_la-asyn-thread.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='asyn-thread.c' object='libcurlu_la-asyn-thread.lo' libtool=yes @AMDEPBACKSLASH@
+libcurlu_la-asyn-base.lo: asyn-base.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-asyn-base.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-asyn-base.Tpo -c -o libcurlu_la-asyn-base.lo `test -f 'asyn-base.c' || echo '$(srcdir)/'`asyn-base.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-asyn-base.Tpo $(DEPDIR)/libcurlu_la-asyn-base.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='asyn-base.c' object='libcurlu_la-asyn-base.lo' libtool=yes @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-asyn-thread.lo `test -f 'asyn-thread.c' || echo '$(srcdir)/'`asyn-thread.c
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-asyn-base.lo `test -f 'asyn-base.c' || echo '$(srcdir)/'`asyn-base.c
 
-libcurlu_la-base64.lo: base64.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-base64.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-base64.Tpo -c -o libcurlu_la-base64.lo `test -f 'base64.c' || echo '$(srcdir)/'`base64.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-base64.Tpo $(DEPDIR)/libcurlu_la-base64.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='base64.c' object='libcurlu_la-base64.lo' libtool=yes @AMDEPBACKSLASH@
+libcurlu_la-asyn-thrdd.lo: asyn-thrdd.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-asyn-thrdd.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-asyn-thrdd.Tpo -c -o libcurlu_la-asyn-thrdd.lo `test -f 'asyn-thrdd.c' || echo '$(srcdir)/'`asyn-thrdd.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-asyn-thrdd.Tpo $(DEPDIR)/libcurlu_la-asyn-thrdd.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='asyn-thrdd.c' object='libcurlu_la-asyn-thrdd.lo' libtool=yes @AMDEPBACKSLASH@
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-base64.lo `test -f 'base64.c' || echo '$(srcdir)/'`base64.c
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-asyn-thrdd.lo `test -f 'asyn-thrdd.c' || echo '$(srcdir)/'`asyn-thrdd.c
 
 libcurlu_la-bufq.lo: bufq.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-bufq.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-bufq.Tpo -c -o libcurlu_la-bufq.lo `test -f 'bufq.c' || echo '$(srcdir)/'`bufq.c
@@ -3631,6 +3801,13 @@ libcurlu_la-cookie.lo: cookie.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-cookie.lo `test -f 'cookie.c' || echo '$(srcdir)/'`cookie.c
 
+libcurlu_la-cshutdn.lo: cshutdn.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-cshutdn.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-cshutdn.Tpo -c -o libcurlu_la-cshutdn.lo `test -f 'cshutdn.c' || echo '$(srcdir)/'`cshutdn.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-cshutdn.Tpo $(DEPDIR)/libcurlu_la-cshutdn.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='cshutdn.c' object='libcurlu_la-cshutdn.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-cshutdn.lo `test -f 'cshutdn.c' || echo '$(srcdir)/'`cshutdn.c
+
 libcurlu_la-curl_addrinfo.lo: curl_addrinfo.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_addrinfo.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_addrinfo.Tpo -c -o libcurlu_la-curl_addrinfo.lo `test -f 'curl_addrinfo.c' || echo '$(srcdir)/'`curl_addrinfo.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_addrinfo.Tpo $(DEPDIR)/libcurlu_la-curl_addrinfo.Plo
@@ -3687,13 +3864,6 @@ libcurlu_la-curl_memrchr.lo: curl_memrchr.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_memrchr.lo `test -f 'curl_memrchr.c' || echo '$(srcdir)/'`curl_memrchr.c
 
-libcurlu_la-curl_multibyte.lo: curl_multibyte.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_multibyte.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_multibyte.Tpo -c -o libcurlu_la-curl_multibyte.lo `test -f 'curl_multibyte.c' || echo '$(srcdir)/'`curl_multibyte.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_multibyte.Tpo $(DEPDIR)/libcurlu_la-curl_multibyte.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='curl_multibyte.c' object='libcurlu_la-curl_multibyte.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_multibyte.lo `test -f 'curl_multibyte.c' || echo '$(srcdir)/'`curl_multibyte.c
-
 libcurlu_la-curl_ntlm_core.lo: curl_ntlm_core.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_ntlm_core.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_ntlm_core.Tpo -c -o libcurlu_la-curl_ntlm_core.lo `test -f 'curl_ntlm_core.c' || echo '$(srcdir)/'`curl_ntlm_core.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_ntlm_core.Tpo $(DEPDIR)/libcurlu_la-curl_ntlm_core.Plo
@@ -3757,6 +3927,13 @@ libcurlu_la-cw-out.lo: cw-out.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-cw-out.lo `test -f 'cw-out.c' || echo '$(srcdir)/'`cw-out.c
 
+libcurlu_la-cw-pause.lo: cw-pause.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-cw-pause.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-cw-pause.Tpo -c -o libcurlu_la-cw-pause.lo `test -f 'cw-pause.c' || echo '$(srcdir)/'`cw-pause.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-cw-pause.Tpo $(DEPDIR)/libcurlu_la-cw-pause.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='cw-pause.c' object='libcurlu_la-cw-pause.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-cw-pause.lo `test -f 'cw-pause.c' || echo '$(srcdir)/'`cw-pause.c
+
 libcurlu_la-dict.lo: dict.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-dict.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-dict.Tpo -c -o libcurlu_la-dict.lo `test -f 'dict.c' || echo '$(srcdir)/'`dict.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-dict.Tpo $(DEPDIR)/libcurlu_la-dict.Plo
@@ -3764,13 +3941,6 @@ libcurlu_la-dict.lo: dict.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-dict.lo `test -f 'dict.c' || echo '$(srcdir)/'`dict.c
 
-libcurlu_la-dllmain.lo: dllmain.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-dllmain.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-dllmain.Tpo -c -o libcurlu_la-dllmain.lo `test -f 'dllmain.c' || echo '$(srcdir)/'`dllmain.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-dllmain.Tpo $(DEPDIR)/libcurlu_la-dllmain.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='dllmain.c' object='libcurlu_la-dllmain.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-dllmain.lo `test -f 'dllmain.c' || echo '$(srcdir)/'`dllmain.c
-
 libcurlu_la-doh.lo: doh.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-doh.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-doh.Tpo -c -o libcurlu_la-doh.lo `test -f 'doh.c' || echo '$(srcdir)/'`doh.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-doh.Tpo $(DEPDIR)/libcurlu_la-doh.Plo
@@ -3778,13 +3948,6 @@ libcurlu_la-doh.lo: doh.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-doh.lo `test -f 'doh.c' || echo '$(srcdir)/'`doh.c
 
-libcurlu_la-dynbuf.lo: dynbuf.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-dynbuf.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-dynbuf.Tpo -c -o libcurlu_la-dynbuf.lo `test -f 'dynbuf.c' || echo '$(srcdir)/'`dynbuf.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-dynbuf.Tpo $(DEPDIR)/libcurlu_la-dynbuf.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='dynbuf.c' object='libcurlu_la-dynbuf.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-dynbuf.lo `test -f 'dynbuf.c' || echo '$(srcdir)/'`dynbuf.c
-
 libcurlu_la-dynhds.lo: dynhds.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-dynhds.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-dynhds.Tpo -c -o libcurlu_la-dynhds.lo `test -f 'dynhds.c' || echo '$(srcdir)/'`dynhds.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-dynhds.Tpo $(DEPDIR)/libcurlu_la-dynhds.Plo
@@ -3820,6 +3983,13 @@ libcurlu_la-escape.lo: escape.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-escape.lo `test -f 'escape.c' || echo '$(srcdir)/'`escape.c
 
+libcurlu_la-fake_addrinfo.lo: fake_addrinfo.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-fake_addrinfo.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-fake_addrinfo.Tpo -c -o libcurlu_la-fake_addrinfo.lo `test -f 'fake_addrinfo.c' || echo '$(srcdir)/'`fake_addrinfo.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-fake_addrinfo.Tpo $(DEPDIR)/libcurlu_la-fake_addrinfo.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='fake_addrinfo.c' object='libcurlu_la-fake_addrinfo.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-fake_addrinfo.lo `test -f 'fake_addrinfo.c' || echo '$(srcdir)/'`fake_addrinfo.c
+
 libcurlu_la-file.lo: file.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-file.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-file.Tpo -c -o libcurlu_la-file.lo `test -f 'file.c' || echo '$(srcdir)/'`file.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-file.Tpo $(DEPDIR)/libcurlu_la-file.Plo
@@ -3904,13 +4074,6 @@ libcurlu_la-hmac.lo: hmac.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-hmac.lo `test -f 'hmac.c' || echo '$(srcdir)/'`hmac.c
 
-libcurlu_la-hostasyn.lo: hostasyn.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-hostasyn.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-hostasyn.Tpo -c -o libcurlu_la-hostasyn.lo `test -f 'hostasyn.c' || echo '$(srcdir)/'`hostasyn.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-hostasyn.Tpo $(DEPDIR)/libcurlu_la-hostasyn.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='hostasyn.c' object='libcurlu_la-hostasyn.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-hostasyn.lo `test -f 'hostasyn.c' || echo '$(srcdir)/'`hostasyn.c
-
 libcurlu_la-hostip.lo: hostip.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-hostip.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-hostip.Tpo -c -o libcurlu_la-hostip.lo `test -f 'hostip.c' || echo '$(srcdir)/'`hostip.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-hostip.Tpo $(DEPDIR)/libcurlu_la-hostip.Plo
@@ -3932,13 +4095,6 @@ libcurlu_la-hostip6.lo: hostip6.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-hostip6.lo `test -f 'hostip6.c' || echo '$(srcdir)/'`hostip6.c
 
-libcurlu_la-hostsyn.lo: hostsyn.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-hostsyn.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-hostsyn.Tpo -c -o libcurlu_la-hostsyn.lo `test -f 'hostsyn.c' || echo '$(srcdir)/'`hostsyn.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-hostsyn.Tpo $(DEPDIR)/libcurlu_la-hostsyn.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='hostsyn.c' object='libcurlu_la-hostsyn.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-hostsyn.lo `test -f 'hostsyn.c' || echo '$(srcdir)/'`hostsyn.c
-
 libcurlu_la-hsts.lo: hsts.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-hsts.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-hsts.Tpo -c -o libcurlu_la-hsts.lo `test -f 'hsts.c' || echo '$(srcdir)/'`hsts.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-hsts.Tpo $(DEPDIR)/libcurlu_la-hsts.Plo
@@ -4044,13 +4200,6 @@ libcurlu_la-inet_ntop.lo: inet_ntop.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-inet_ntop.lo `test -f 'inet_ntop.c' || echo '$(srcdir)/'`inet_ntop.c
 
-libcurlu_la-inet_pton.lo: inet_pton.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-inet_pton.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-inet_pton.Tpo -c -o libcurlu_la-inet_pton.lo `test -f 'inet_pton.c' || echo '$(srcdir)/'`inet_pton.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-inet_pton.Tpo $(DEPDIR)/libcurlu_la-inet_pton.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='inet_pton.c' object='libcurlu_la-inet_pton.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-inet_pton.lo `test -f 'inet_pton.c' || echo '$(srcdir)/'`inet_pton.c
-
 libcurlu_la-krb5.lo: krb5.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-krb5.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-krb5.Tpo -c -o libcurlu_la-krb5.lo `test -f 'krb5.c' || echo '$(srcdir)/'`krb5.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-krb5.Tpo $(DEPDIR)/libcurlu_la-krb5.Plo
@@ -4128,6 +4277,13 @@ libcurlu_la-multi.lo: multi.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-multi.lo `test -f 'multi.c' || echo '$(srcdir)/'`multi.c
 
+libcurlu_la-multi_ev.lo: multi_ev.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-multi_ev.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-multi_ev.Tpo -c -o libcurlu_la-multi_ev.lo `test -f 'multi_ev.c' || echo '$(srcdir)/'`multi_ev.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-multi_ev.Tpo $(DEPDIR)/libcurlu_la-multi_ev.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='multi_ev.c' object='libcurlu_la-multi_ev.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-multi_ev.lo `test -f 'multi_ev.c' || echo '$(srcdir)/'`multi_ev.c
+
 libcurlu_la-netrc.lo: netrc.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-netrc.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-netrc.Tpo -c -o libcurlu_la-netrc.lo `test -f 'netrc.c' || echo '$(srcdir)/'`netrc.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-netrc.Tpo $(DEPDIR)/libcurlu_la-netrc.Plo
@@ -4135,13 +4291,6 @@ libcurlu_la-netrc.lo: netrc.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-netrc.lo `test -f 'netrc.c' || echo '$(srcdir)/'`netrc.c
 
-libcurlu_la-nonblock.lo: nonblock.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-nonblock.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-nonblock.Tpo -c -o libcurlu_la-nonblock.lo `test -f 'nonblock.c' || echo '$(srcdir)/'`nonblock.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-nonblock.Tpo $(DEPDIR)/libcurlu_la-nonblock.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='nonblock.c' object='libcurlu_la-nonblock.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-nonblock.lo `test -f 'nonblock.c' || echo '$(srcdir)/'`nonblock.c
-
 libcurlu_la-noproxy.lo: noproxy.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-noproxy.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-noproxy.Tpo -c -o libcurlu_la-noproxy.lo `test -f 'noproxy.c' || echo '$(srcdir)/'`noproxy.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-noproxy.Tpo $(DEPDIR)/libcurlu_la-noproxy.Plo
@@ -4331,6 +4480,13 @@ libcurlu_la-strdup.lo: strdup.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-strdup.lo `test -f 'strdup.c' || echo '$(srcdir)/'`strdup.c
 
+libcurlu_la-strequal.lo: strequal.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-strequal.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-strequal.Tpo -c -o libcurlu_la-strequal.lo `test -f 'strequal.c' || echo '$(srcdir)/'`strequal.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-strequal.Tpo $(DEPDIR)/libcurlu_la-strequal.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='strequal.c' object='libcurlu_la-strequal.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-strequal.lo `test -f 'strequal.c' || echo '$(srcdir)/'`strequal.c
+
 libcurlu_la-strerror.lo: strerror.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-strerror.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-strerror.Tpo -c -o libcurlu_la-strerror.lo `test -f 'strerror.c' || echo '$(srcdir)/'`strerror.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-strerror.Tpo $(DEPDIR)/libcurlu_la-strerror.Plo
@@ -4338,27 +4494,6 @@ libcurlu_la-strerror.lo: strerror.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-strerror.lo `test -f 'strerror.c' || echo '$(srcdir)/'`strerror.c
 
-libcurlu_la-strparse.lo: strparse.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-strparse.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-strparse.Tpo -c -o libcurlu_la-strparse.lo `test -f 'strparse.c' || echo '$(srcdir)/'`strparse.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-strparse.Tpo $(DEPDIR)/libcurlu_la-strparse.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='strparse.c' object='libcurlu_la-strparse.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-strparse.lo `test -f 'strparse.c' || echo '$(srcdir)/'`strparse.c
-
-libcurlu_la-strtok.lo: strtok.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-strtok.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-strtok.Tpo -c -o libcurlu_la-strtok.lo `test -f 'strtok.c' || echo '$(srcdir)/'`strtok.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-strtok.Tpo $(DEPDIR)/libcurlu_la-strtok.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='strtok.c' object='libcurlu_la-strtok.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-strtok.lo `test -f 'strtok.c' || echo '$(srcdir)/'`strtok.c
-
-libcurlu_la-strtoofft.lo: strtoofft.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-strtoofft.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-strtoofft.Tpo -c -o libcurlu_la-strtoofft.lo `test -f 'strtoofft.c' || echo '$(srcdir)/'`strtoofft.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-strtoofft.Tpo $(DEPDIR)/libcurlu_la-strtoofft.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='strtoofft.c' object='libcurlu_la-strtoofft.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-strtoofft.lo `test -f 'strtoofft.c' || echo '$(srcdir)/'`strtoofft.c
-
 libcurlu_la-system_win32.lo: system_win32.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-system_win32.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-system_win32.Tpo -c -o libcurlu_la-system_win32.lo `test -f 'system_win32.c' || echo '$(srcdir)/'`system_win32.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-system_win32.Tpo $(DEPDIR)/libcurlu_la-system_win32.Plo
@@ -4380,20 +4515,6 @@ libcurlu_la-tftp.lo: tftp.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-tftp.lo `test -f 'tftp.c' || echo '$(srcdir)/'`tftp.c
 
-libcurlu_la-timediff.lo: timediff.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-timediff.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-timediff.Tpo -c -o libcurlu_la-timediff.lo `test -f 'timediff.c' || echo '$(srcdir)/'`timediff.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-timediff.Tpo $(DEPDIR)/libcurlu_la-timediff.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='timediff.c' object='libcurlu_la-timediff.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-timediff.lo `test -f 'timediff.c' || echo '$(srcdir)/'`timediff.c
-
-libcurlu_la-timeval.lo: timeval.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-timeval.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-timeval.Tpo -c -o libcurlu_la-timeval.lo `test -f 'timeval.c' || echo '$(srcdir)/'`timeval.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-timeval.Tpo $(DEPDIR)/libcurlu_la-timeval.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='timeval.c' object='libcurlu_la-timeval.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-timeval.lo `test -f 'timeval.c' || echo '$(srcdir)/'`timeval.c
-
 libcurlu_la-transfer.lo: transfer.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-transfer.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-transfer.Tpo -c -o libcurlu_la-transfer.lo `test -f 'transfer.c' || echo '$(srcdir)/'`transfer.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-transfer.Tpo $(DEPDIR)/libcurlu_la-transfer.Plo
@@ -4401,6 +4522,34 @@ libcurlu_la-transfer.lo: transfer.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-transfer.lo `test -f 'transfer.c' || echo '$(srcdir)/'`transfer.c
 
+libcurlu_la-uint-bset.lo: uint-bset.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-uint-bset.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-uint-bset.Tpo -c -o libcurlu_la-uint-bset.lo `test -f 'uint-bset.c' || echo '$(srcdir)/'`uint-bset.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-uint-bset.Tpo $(DEPDIR)/libcurlu_la-uint-bset.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='uint-bset.c' object='libcurlu_la-uint-bset.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-uint-bset.lo `test -f 'uint-bset.c' || echo '$(srcdir)/'`uint-bset.c
+
+libcurlu_la-uint-hash.lo: uint-hash.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-uint-hash.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-uint-hash.Tpo -c -o libcurlu_la-uint-hash.lo `test -f 'uint-hash.c' || echo '$(srcdir)/'`uint-hash.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-uint-hash.Tpo $(DEPDIR)/libcurlu_la-uint-hash.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='uint-hash.c' object='libcurlu_la-uint-hash.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-uint-hash.lo `test -f 'uint-hash.c' || echo '$(srcdir)/'`uint-hash.c
+
+libcurlu_la-uint-spbset.lo: uint-spbset.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-uint-spbset.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-uint-spbset.Tpo -c -o libcurlu_la-uint-spbset.lo `test -f 'uint-spbset.c' || echo '$(srcdir)/'`uint-spbset.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-uint-spbset.Tpo $(DEPDIR)/libcurlu_la-uint-spbset.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='uint-spbset.c' object='libcurlu_la-uint-spbset.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-uint-spbset.lo `test -f 'uint-spbset.c' || echo '$(srcdir)/'`uint-spbset.c
+
+libcurlu_la-uint-table.lo: uint-table.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-uint-table.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-uint-table.Tpo -c -o libcurlu_la-uint-table.lo `test -f 'uint-table.c' || echo '$(srcdir)/'`uint-table.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-uint-table.Tpo $(DEPDIR)/libcurlu_la-uint-table.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='uint-table.c' object='libcurlu_la-uint-table.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-uint-table.lo `test -f 'uint-table.c' || echo '$(srcdir)/'`uint-table.c
+
 libcurlu_la-url.lo: url.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-url.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-url.Tpo -c -o libcurlu_la-url.lo `test -f 'url.c' || echo '$(srcdir)/'`url.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-url.Tpo $(DEPDIR)/libcurlu_la-url.Plo
@@ -4422,20 +4571,6 @@ libcurlu_la-version.lo: version.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-version.lo `test -f 'version.c' || echo '$(srcdir)/'`version.c
 
-libcurlu_la-version_win32.lo: version_win32.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-version_win32.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-version_win32.Tpo -c -o libcurlu_la-version_win32.lo `test -f 'version_win32.c' || echo '$(srcdir)/'`version_win32.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-version_win32.Tpo $(DEPDIR)/libcurlu_la-version_win32.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='version_win32.c' object='libcurlu_la-version_win32.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-version_win32.lo `test -f 'version_win32.c' || echo '$(srcdir)/'`version_win32.c
-
-libcurlu_la-warnless.lo: warnless.c
-@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-warnless.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-warnless.Tpo -c -o libcurlu_la-warnless.lo `test -f 'warnless.c' || echo '$(srcdir)/'`warnless.c
-@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-warnless.Tpo $(DEPDIR)/libcurlu_la-warnless.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='warnless.c' object='libcurlu_la-warnless.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-warnless.lo `test -f 'warnless.c' || echo '$(srcdir)/'`warnless.c
-
 libcurlu_la-ws.lo: ws.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-ws.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-ws.Tpo -c -o libcurlu_la-ws.lo `test -f 'ws.c' || echo '$(srcdir)/'`ws.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-ws.Tpo $(DEPDIR)/libcurlu_la-ws.Plo
@@ -4723,6 +4858,90 @@ vssh/libcurlu_la-wolfssh.lo: vssh/wolfssh.c
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
 @am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vssh/libcurlu_la-wolfssh.lo `test -f 'vssh/wolfssh.c' || echo '$(srcdir)/'`vssh/wolfssh.c
 
+curlx/libcurlu_la-base64.lo: curlx/base64.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT curlx/libcurlu_la-base64.lo -MD -MP -MF curlx/$(DEPDIR)/libcurlu_la-base64.Tpo -c -o curlx/libcurlu_la-base64.lo `test -f 'curlx/base64.c' || echo '$(srcdir)/'`curlx/base64.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurlu_la-base64.Tpo curlx/$(DEPDIR)/libcurlu_la-base64.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='curlx/base64.c' object='curlx/libcurlu_la-base64.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o curlx/libcurlu_la-base64.lo `test -f 'curlx/base64.c' || echo '$(srcdir)/'`curlx/base64.c
+
+curlx/libcurlu_la-dynbuf.lo: curlx/dynbuf.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT curlx/libcurlu_la-dynbuf.lo -MD -MP -MF curlx/$(DEPDIR)/libcurlu_la-dynbuf.Tpo -c -o curlx/libcurlu_la-dynbuf.lo `test -f 'curlx/dynbuf.c' || echo '$(srcdir)/'`curlx/dynbuf.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurlu_la-dynbuf.Tpo curlx/$(DEPDIR)/libcurlu_la-dynbuf.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='curlx/dynbuf.c' object='curlx/libcurlu_la-dynbuf.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o curlx/libcurlu_la-dynbuf.lo `test -f 'curlx/dynbuf.c' || echo '$(srcdir)/'`curlx/dynbuf.c
+
+curlx/libcurlu_la-inet_pton.lo: curlx/inet_pton.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT curlx/libcurlu_la-inet_pton.lo -MD -MP -MF curlx/$(DEPDIR)/libcurlu_la-inet_pton.Tpo -c -o curlx/libcurlu_la-inet_pton.lo `test -f 'curlx/inet_pton.c' || echo '$(srcdir)/'`curlx/inet_pton.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurlu_la-inet_pton.Tpo curlx/$(DEPDIR)/libcurlu_la-inet_pton.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='curlx/inet_pton.c' object='curlx/libcurlu_la-inet_pton.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o curlx/libcurlu_la-inet_pton.lo `test -f 'curlx/inet_pton.c' || echo '$(srcdir)/'`curlx/inet_pton.c
+
+curlx/libcurlu_la-multibyte.lo: curlx/multibyte.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT curlx/libcurlu_la-multibyte.lo -MD -MP -MF curlx/$(DEPDIR)/libcurlu_la-multibyte.Tpo -c -o curlx/libcurlu_la-multibyte.lo `test -f 'curlx/multibyte.c' || echo '$(srcdir)/'`curlx/multibyte.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurlu_la-multibyte.Tpo curlx/$(DEPDIR)/libcurlu_la-multibyte.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='curlx/multibyte.c' object='curlx/libcurlu_la-multibyte.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o curlx/libcurlu_la-multibyte.lo `test -f 'curlx/multibyte.c' || echo '$(srcdir)/'`curlx/multibyte.c
+
+curlx/libcurlu_la-nonblock.lo: curlx/nonblock.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT curlx/libcurlu_la-nonblock.lo -MD -MP -MF curlx/$(DEPDIR)/libcurlu_la-nonblock.Tpo -c -o curlx/libcurlu_la-nonblock.lo `test -f 'curlx/nonblock.c' || echo '$(srcdir)/'`curlx/nonblock.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurlu_la-nonblock.Tpo curlx/$(DEPDIR)/libcurlu_la-nonblock.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='curlx/nonblock.c' object='curlx/libcurlu_la-nonblock.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o curlx/libcurlu_la-nonblock.lo `test -f 'curlx/nonblock.c' || echo '$(srcdir)/'`curlx/nonblock.c
+
+curlx/libcurlu_la-strparse.lo: curlx/strparse.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT curlx/libcurlu_la-strparse.lo -MD -MP -MF curlx/$(DEPDIR)/libcurlu_la-strparse.Tpo -c -o curlx/libcurlu_la-strparse.lo `test -f 'curlx/strparse.c' || echo '$(srcdir)/'`curlx/strparse.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurlu_la-strparse.Tpo curlx/$(DEPDIR)/libcurlu_la-strparse.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='curlx/strparse.c' object='curlx/libcurlu_la-strparse.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o curlx/libcurlu_la-strparse.lo `test -f 'curlx/strparse.c' || echo '$(srcdir)/'`curlx/strparse.c
+
+curlx/libcurlu_la-timediff.lo: curlx/timediff.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT curlx/libcurlu_la-timediff.lo -MD -MP -MF curlx/$(DEPDIR)/libcurlu_la-timediff.Tpo -c -o curlx/libcurlu_la-timediff.lo `test -f 'curlx/timediff.c' || echo '$(srcdir)/'`curlx/timediff.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurlu_la-timediff.Tpo curlx/$(DEPDIR)/libcurlu_la-timediff.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='curlx/timediff.c' object='curlx/libcurlu_la-timediff.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o curlx/libcurlu_la-timediff.lo `test -f 'curlx/timediff.c' || echo '$(srcdir)/'`curlx/timediff.c
+
+curlx/libcurlu_la-timeval.lo: curlx/timeval.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT curlx/libcurlu_la-timeval.lo -MD -MP -MF curlx/$(DEPDIR)/libcurlu_la-timeval.Tpo -c -o curlx/libcurlu_la-timeval.lo `test -f 'curlx/timeval.c' || echo '$(srcdir)/'`curlx/timeval.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurlu_la-timeval.Tpo curlx/$(DEPDIR)/libcurlu_la-timeval.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='curlx/timeval.c' object='curlx/libcurlu_la-timeval.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o curlx/libcurlu_la-timeval.lo `test -f 'curlx/timeval.c' || echo '$(srcdir)/'`curlx/timeval.c
+
+curlx/libcurlu_la-version_win32.lo: curlx/version_win32.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT curlx/libcurlu_la-version_win32.lo -MD -MP -MF curlx/$(DEPDIR)/libcurlu_la-version_win32.Tpo -c -o curlx/libcurlu_la-version_win32.lo `test -f 'curlx/version_win32.c' || echo '$(srcdir)/'`curlx/version_win32.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurlu_la-version_win32.Tpo curlx/$(DEPDIR)/libcurlu_la-version_win32.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='curlx/version_win32.c' object='curlx/libcurlu_la-version_win32.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o curlx/libcurlu_la-version_win32.lo `test -f 'curlx/version_win32.c' || echo '$(srcdir)/'`curlx/version_win32.c
+
+curlx/libcurlu_la-warnless.lo: curlx/warnless.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT curlx/libcurlu_la-warnless.lo -MD -MP -MF curlx/$(DEPDIR)/libcurlu_la-warnless.Tpo -c -o curlx/libcurlu_la-warnless.lo `test -f 'curlx/warnless.c' || echo '$(srcdir)/'`curlx/warnless.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurlu_la-warnless.Tpo curlx/$(DEPDIR)/libcurlu_la-warnless.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='curlx/warnless.c' object='curlx/libcurlu_la-warnless.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o curlx/libcurlu_la-warnless.lo `test -f 'curlx/warnless.c' || echo '$(srcdir)/'`curlx/warnless.c
+
+curlx/libcurlu_la-winapi.lo: curlx/winapi.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT curlx/libcurlu_la-winapi.lo -MD -MP -MF curlx/$(DEPDIR)/libcurlu_la-winapi.Tpo -c -o curlx/libcurlu_la-winapi.lo `test -f 'curlx/winapi.c' || echo '$(srcdir)/'`curlx/winapi.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurlu_la-winapi.Tpo curlx/$(DEPDIR)/libcurlu_la-winapi.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='curlx/winapi.c' object='curlx/libcurlu_la-winapi.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o curlx/libcurlu_la-winapi.lo `test -f 'curlx/winapi.c' || echo '$(srcdir)/'`curlx/winapi.c
+
+libcurlu_la-dllmain.lo: dllmain.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-dllmain.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-dllmain.Tpo -c -o libcurlu_la-dllmain.lo `test -f 'dllmain.c' || echo '$(srcdir)/'`dllmain.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-dllmain.Tpo $(DEPDIR)/libcurlu_la-dllmain.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='dllmain.c' object='libcurlu_la-dllmain.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-dllmain.lo `test -f 'dllmain.c' || echo '$(srcdir)/'`dllmain.c
+
 libcurlu_la-libcurl_unity.lo: libcurl_unity.c
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-libcurl_unity.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-libcurl_unity.Tpo -c -o libcurlu_la-libcurl_unity.lo `test -f 'libcurl_unity.c' || echo '$(srcdir)/'`libcurl_unity.c
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-libcurl_unity.Tpo $(DEPDIR)/libcurlu_la-libcurl_unity.Plo
@@ -4735,6 +4954,7 @@ mostlyclean-libtool:
 
 clean-libtool:
 	-rm -rf .libs _libs
+	-rm -rf curlx/.libs curlx/_libs
 	-rm -rf vauth/.libs vauth/_libs
 	-rm -rf vquic/.libs vquic/_libs
 	-rm -rf vssh/.libs vssh/_libs
@@ -4827,6 +5047,7 @@ distdir-am: $(DISTFILES)
 check-am: all-am
 check: check-am
 @DEBUGBUILD_FALSE@all-local:
+@NOT_CURL_CI_FALSE@all-local:
 all-am: Makefile $(LTLIBRARIES) curl_config.h all-local
 installdirs:
 	for dir in "$(DESTDIR)$(libdir)"; do \
@@ -4859,6 +5080,8 @@ clean-generic:
 distclean-generic:
 	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
 	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+	-rm -f curlx/$(DEPDIR)/$(am__dirstamp)
+	-rm -f curlx/$(am__dirstamp)
 	-rm -f vauth/$(DEPDIR)/$(am__dirstamp)
 	-rm -f vauth/$(am__dirstamp)
 	-rm -f vquic/$(DEPDIR)/$(am__dirstamp)
@@ -4880,8 +5103,8 @@ distclean: distclean-am
 		-rm -f ./$(DEPDIR)/libcurl_la-altsvc.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-amigaos.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-asyn-ares.Plo
-	-rm -f ./$(DEPDIR)/libcurl_la-asyn-thread.Plo
-	-rm -f ./$(DEPDIR)/libcurl_la-base64.Plo
+	-rm -f ./$(DEPDIR)/libcurl_la-asyn-base.Plo
+	-rm -f ./$(DEPDIR)/libcurl_la-asyn-thrdd.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-bufq.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-bufref.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-cf-h1-proxy.Plo
@@ -4894,6 +5117,7 @@ distclean: distclean-am
 	-rm -f ./$(DEPDIR)/libcurl_la-connect.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-content_encoding.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-cookie.Plo
+	-rm -f ./$(DEPDIR)/libcurl_la-cshutdn.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-curl_addrinfo.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-curl_des.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-curl_endian.Plo
@@ -4902,7 +5126,6 @@ distclean: distclean-am
 	-rm -f ./$(DEPDIR)/libcurl_la-curl_gethostname.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-curl_gssapi.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-curl_memrchr.Plo
-	-rm -f ./$(DEPDIR)/libcurl_la-curl_multibyte.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-curl_ntlm_core.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-curl_range.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-curl_rtmp.Plo
@@ -4912,15 +5135,16 @@ distclean: distclean-am
 	-rm -f ./$(DEPDIR)/libcurl_la-curl_threads.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-curl_trc.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-cw-out.Plo
+	-rm -f ./$(DEPDIR)/libcurl_la-cw-pause.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-dict.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-dllmain.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-doh.Plo
-	-rm -f ./$(DEPDIR)/libcurl_la-dynbuf.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-dynhds.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-easy.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-easygetopt.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-easyoptions.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-escape.Plo
+	-rm -f ./$(DEPDIR)/libcurl_la-fake_addrinfo.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-file.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-fileinfo.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-fopen.Plo
@@ -4933,11 +5157,9 @@ distclean: distclean-am
 	-rm -f ./$(DEPDIR)/libcurl_la-hash.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-headers.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-hmac.Plo
-	-rm -f ./$(DEPDIR)/libcurl_la-hostasyn.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-hostip.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-hostip4.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-hostip6.Plo
-	-rm -f ./$(DEPDIR)/libcurl_la-hostsyn.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-hsts.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-http.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-http1.Plo
@@ -4953,7 +5175,6 @@ distclean: distclean-am
 	-rm -f ./$(DEPDIR)/libcurl_la-if2ip.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-imap.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-inet_ntop.Plo
-	-rm -f ./$(DEPDIR)/libcurl_la-inet_pton.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-krb5.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-ldap.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-libcurl_unity.Plo
@@ -4966,8 +5187,8 @@ distclean: distclean-am
 	-rm -f ./$(DEPDIR)/libcurl_la-mprintf.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-mqtt.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-multi.Plo
+	-rm -f ./$(DEPDIR)/libcurl_la-multi_ev.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-netrc.Plo
-	-rm -f ./$(DEPDIR)/libcurl_la-nonblock.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-noproxy.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-openldap.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-parsedate.Plo
@@ -4995,27 +5216,25 @@ distclean: distclean-am
 	-rm -f ./$(DEPDIR)/libcurl_la-splay.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-strcase.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-strdup.Plo
+	-rm -f ./$(DEPDIR)/libcurl_la-strequal.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-strerror.Plo
-	-rm -f ./$(DEPDIR)/libcurl_la-strparse.Plo
-	-rm -f ./$(DEPDIR)/libcurl_la-strtok.Plo
-	-rm -f ./$(DEPDIR)/libcurl_la-strtoofft.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-system_win32.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-telnet.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-tftp.Plo
-	-rm -f ./$(DEPDIR)/libcurl_la-timediff.Plo
-	-rm -f ./$(DEPDIR)/libcurl_la-timeval.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-transfer.Plo
+	-rm -f ./$(DEPDIR)/libcurl_la-uint-bset.Plo
+	-rm -f ./$(DEPDIR)/libcurl_la-uint-hash.Plo
+	-rm -f ./$(DEPDIR)/libcurl_la-uint-spbset.Plo
+	-rm -f ./$(DEPDIR)/libcurl_la-uint-table.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-url.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-urlapi.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-version.Plo
-	-rm -f ./$(DEPDIR)/libcurl_la-version_win32.Plo
-	-rm -f ./$(DEPDIR)/libcurl_la-warnless.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-ws.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-altsvc.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-amigaos.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-asyn-ares.Plo
-	-rm -f ./$(DEPDIR)/libcurlu_la-asyn-thread.Plo
-	-rm -f ./$(DEPDIR)/libcurlu_la-base64.Plo
+	-rm -f ./$(DEPDIR)/libcurlu_la-asyn-base.Plo
+	-rm -f ./$(DEPDIR)/libcurlu_la-asyn-thrdd.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-bufq.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-bufref.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-cf-h1-proxy.Plo
@@ -5028,6 +5247,7 @@ distclean: distclean-am
 	-rm -f ./$(DEPDIR)/libcurlu_la-connect.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-content_encoding.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-cookie.Plo
+	-rm -f ./$(DEPDIR)/libcurlu_la-cshutdn.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-curl_addrinfo.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-curl_des.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-curl_endian.Plo
@@ -5036,7 +5256,6 @@ distclean: distclean-am
 	-rm -f ./$(DEPDIR)/libcurlu_la-curl_gethostname.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-curl_gssapi.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-curl_memrchr.Plo
-	-rm -f ./$(DEPDIR)/libcurlu_la-curl_multibyte.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-curl_ntlm_core.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-curl_range.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-curl_rtmp.Plo
@@ -5046,15 +5265,16 @@ distclean: distclean-am
 	-rm -f ./$(DEPDIR)/libcurlu_la-curl_threads.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-curl_trc.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-cw-out.Plo
+	-rm -f ./$(DEPDIR)/libcurlu_la-cw-pause.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-dict.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-dllmain.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-doh.Plo
-	-rm -f ./$(DEPDIR)/libcurlu_la-dynbuf.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-dynhds.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-easy.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-easygetopt.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-easyoptions.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-escape.Plo
+	-rm -f ./$(DEPDIR)/libcurlu_la-fake_addrinfo.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-file.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-fileinfo.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-fopen.Plo
@@ -5067,11 +5287,9 @@ distclean: distclean-am
 	-rm -f ./$(DEPDIR)/libcurlu_la-hash.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-headers.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-hmac.Plo
-	-rm -f ./$(DEPDIR)/libcurlu_la-hostasyn.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-hostip.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-hostip4.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-hostip6.Plo
-	-rm -f ./$(DEPDIR)/libcurlu_la-hostsyn.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-hsts.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-http.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-http1.Plo
@@ -5087,7 +5305,6 @@ distclean: distclean-am
 	-rm -f ./$(DEPDIR)/libcurlu_la-if2ip.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-imap.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-inet_ntop.Plo
-	-rm -f ./$(DEPDIR)/libcurlu_la-inet_pton.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-krb5.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-ldap.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-libcurl_unity.Plo
@@ -5100,8 +5317,8 @@ distclean: distclean-am
 	-rm -f ./$(DEPDIR)/libcurlu_la-mprintf.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-mqtt.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-multi.Plo
+	-rm -f ./$(DEPDIR)/libcurlu_la-multi_ev.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-netrc.Plo
-	-rm -f ./$(DEPDIR)/libcurlu_la-nonblock.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-noproxy.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-openldap.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-parsedate.Plo
@@ -5129,22 +5346,42 @@ distclean: distclean-am
 	-rm -f ./$(DEPDIR)/libcurlu_la-splay.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-strcase.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-strdup.Plo
+	-rm -f ./$(DEPDIR)/libcurlu_la-strequal.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-strerror.Plo
-	-rm -f ./$(DEPDIR)/libcurlu_la-strparse.Plo
-	-rm -f ./$(DEPDIR)/libcurlu_la-strtok.Plo
-	-rm -f ./$(DEPDIR)/libcurlu_la-strtoofft.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-system_win32.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-telnet.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-tftp.Plo
-	-rm -f ./$(DEPDIR)/libcurlu_la-timediff.Plo
-	-rm -f ./$(DEPDIR)/libcurlu_la-timeval.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-transfer.Plo
+	-rm -f ./$(DEPDIR)/libcurlu_la-uint-bset.Plo
+	-rm -f ./$(DEPDIR)/libcurlu_la-uint-hash.Plo
+	-rm -f ./$(DEPDIR)/libcurlu_la-uint-spbset.Plo
+	-rm -f ./$(DEPDIR)/libcurlu_la-uint-table.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-url.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-urlapi.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-version.Plo
-	-rm -f ./$(DEPDIR)/libcurlu_la-version_win32.Plo
-	-rm -f ./$(DEPDIR)/libcurlu_la-warnless.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-ws.Plo
+	-rm -f curlx/$(DEPDIR)/libcurl_la-base64.Plo
+	-rm -f curlx/$(DEPDIR)/libcurl_la-dynbuf.Plo
+	-rm -f curlx/$(DEPDIR)/libcurl_la-inet_pton.Plo
+	-rm -f curlx/$(DEPDIR)/libcurl_la-multibyte.Plo
+	-rm -f curlx/$(DEPDIR)/libcurl_la-nonblock.Plo
+	-rm -f curlx/$(DEPDIR)/libcurl_la-strparse.Plo
+	-rm -f curlx/$(DEPDIR)/libcurl_la-timediff.Plo
+	-rm -f curlx/$(DEPDIR)/libcurl_la-timeval.Plo
+	-rm -f curlx/$(DEPDIR)/libcurl_la-version_win32.Plo
+	-rm -f curlx/$(DEPDIR)/libcurl_la-warnless.Plo
+	-rm -f curlx/$(DEPDIR)/libcurl_la-winapi.Plo
+	-rm -f curlx/$(DEPDIR)/libcurlu_la-base64.Plo
+	-rm -f curlx/$(DEPDIR)/libcurlu_la-dynbuf.Plo
+	-rm -f curlx/$(DEPDIR)/libcurlu_la-inet_pton.Plo
+	-rm -f curlx/$(DEPDIR)/libcurlu_la-multibyte.Plo
+	-rm -f curlx/$(DEPDIR)/libcurlu_la-nonblock.Plo
+	-rm -f curlx/$(DEPDIR)/libcurlu_la-strparse.Plo
+	-rm -f curlx/$(DEPDIR)/libcurlu_la-timediff.Plo
+	-rm -f curlx/$(DEPDIR)/libcurlu_la-timeval.Plo
+	-rm -f curlx/$(DEPDIR)/libcurlu_la-version_win32.Plo
+	-rm -f curlx/$(DEPDIR)/libcurlu_la-warnless.Plo
+	-rm -f curlx/$(DEPDIR)/libcurlu_la-winapi.Plo
 	-rm -f vauth/$(DEPDIR)/libcurl_la-cleartext.Plo
 	-rm -f vauth/$(DEPDIR)/libcurl_la-cram.Plo
 	-rm -f vauth/$(DEPDIR)/libcurl_la-digest.Plo
@@ -5273,8 +5510,8 @@ maintainer-clean: maintainer-clean-am
 		-rm -f ./$(DEPDIR)/libcurl_la-altsvc.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-amigaos.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-asyn-ares.Plo
-	-rm -f ./$(DEPDIR)/libcurl_la-asyn-thread.Plo
-	-rm -f ./$(DEPDIR)/libcurl_la-base64.Plo
+	-rm -f ./$(DEPDIR)/libcurl_la-asyn-base.Plo
+	-rm -f ./$(DEPDIR)/libcurl_la-asyn-thrdd.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-bufq.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-bufref.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-cf-h1-proxy.Plo
@@ -5287,6 +5524,7 @@ maintainer-clean: maintainer-clean-am
 	-rm -f ./$(DEPDIR)/libcurl_la-connect.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-content_encoding.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-cookie.Plo
+	-rm -f ./$(DEPDIR)/libcurl_la-cshutdn.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-curl_addrinfo.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-curl_des.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-curl_endian.Plo
@@ -5295,7 +5533,6 @@ maintainer-clean: maintainer-clean-am
 	-rm -f ./$(DEPDIR)/libcurl_la-curl_gethostname.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-curl_gssapi.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-curl_memrchr.Plo
-	-rm -f ./$(DEPDIR)/libcurl_la-curl_multibyte.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-curl_ntlm_core.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-curl_range.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-curl_rtmp.Plo
@@ -5305,15 +5542,16 @@ maintainer-clean: maintainer-clean-am
 	-rm -f ./$(DEPDIR)/libcurl_la-curl_threads.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-curl_trc.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-cw-out.Plo
+	-rm -f ./$(DEPDIR)/libcurl_la-cw-pause.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-dict.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-dllmain.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-doh.Plo
-	-rm -f ./$(DEPDIR)/libcurl_la-dynbuf.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-dynhds.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-easy.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-easygetopt.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-easyoptions.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-escape.Plo
+	-rm -f ./$(DEPDIR)/libcurl_la-fake_addrinfo.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-file.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-fileinfo.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-fopen.Plo
@@ -5326,11 +5564,9 @@ maintainer-clean: maintainer-clean-am
 	-rm -f ./$(DEPDIR)/libcurl_la-hash.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-headers.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-hmac.Plo
-	-rm -f ./$(DEPDIR)/libcurl_la-hostasyn.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-hostip.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-hostip4.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-hostip6.Plo
-	-rm -f ./$(DEPDIR)/libcurl_la-hostsyn.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-hsts.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-http.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-http1.Plo
@@ -5346,7 +5582,6 @@ maintainer-clean: maintainer-clean-am
 	-rm -f ./$(DEPDIR)/libcurl_la-if2ip.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-imap.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-inet_ntop.Plo
-	-rm -f ./$(DEPDIR)/libcurl_la-inet_pton.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-krb5.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-ldap.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-libcurl_unity.Plo
@@ -5359,8 +5594,8 @@ maintainer-clean: maintainer-clean-am
 	-rm -f ./$(DEPDIR)/libcurl_la-mprintf.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-mqtt.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-multi.Plo
+	-rm -f ./$(DEPDIR)/libcurl_la-multi_ev.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-netrc.Plo
-	-rm -f ./$(DEPDIR)/libcurl_la-nonblock.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-noproxy.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-openldap.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-parsedate.Plo
@@ -5388,27 +5623,25 @@ maintainer-clean: maintainer-clean-am
 	-rm -f ./$(DEPDIR)/libcurl_la-splay.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-strcase.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-strdup.Plo
+	-rm -f ./$(DEPDIR)/libcurl_la-strequal.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-strerror.Plo
-	-rm -f ./$(DEPDIR)/libcurl_la-strparse.Plo
-	-rm -f ./$(DEPDIR)/libcurl_la-strtok.Plo
-	-rm -f ./$(DEPDIR)/libcurl_la-strtoofft.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-system_win32.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-telnet.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-tftp.Plo
-	-rm -f ./$(DEPDIR)/libcurl_la-timediff.Plo
-	-rm -f ./$(DEPDIR)/libcurl_la-timeval.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-transfer.Plo
+	-rm -f ./$(DEPDIR)/libcurl_la-uint-bset.Plo
+	-rm -f ./$(DEPDIR)/libcurl_la-uint-hash.Plo
+	-rm -f ./$(DEPDIR)/libcurl_la-uint-spbset.Plo
+	-rm -f ./$(DEPDIR)/libcurl_la-uint-table.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-url.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-urlapi.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-version.Plo
-	-rm -f ./$(DEPDIR)/libcurl_la-version_win32.Plo
-	-rm -f ./$(DEPDIR)/libcurl_la-warnless.Plo
 	-rm -f ./$(DEPDIR)/libcurl_la-ws.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-altsvc.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-amigaos.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-asyn-ares.Plo
-	-rm -f ./$(DEPDIR)/libcurlu_la-asyn-thread.Plo
-	-rm -f ./$(DEPDIR)/libcurlu_la-base64.Plo
+	-rm -f ./$(DEPDIR)/libcurlu_la-asyn-base.Plo
+	-rm -f ./$(DEPDIR)/libcurlu_la-asyn-thrdd.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-bufq.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-bufref.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-cf-h1-proxy.Plo
@@ -5421,6 +5654,7 @@ maintainer-clean: maintainer-clean-am
 	-rm -f ./$(DEPDIR)/libcurlu_la-connect.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-content_encoding.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-cookie.Plo
+	-rm -f ./$(DEPDIR)/libcurlu_la-cshutdn.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-curl_addrinfo.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-curl_des.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-curl_endian.Plo
@@ -5429,7 +5663,6 @@ maintainer-clean: maintainer-clean-am
 	-rm -f ./$(DEPDIR)/libcurlu_la-curl_gethostname.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-curl_gssapi.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-curl_memrchr.Plo
-	-rm -f ./$(DEPDIR)/libcurlu_la-curl_multibyte.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-curl_ntlm_core.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-curl_range.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-curl_rtmp.Plo
@@ -5439,15 +5672,16 @@ maintainer-clean: maintainer-clean-am
 	-rm -f ./$(DEPDIR)/libcurlu_la-curl_threads.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-curl_trc.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-cw-out.Plo
+	-rm -f ./$(DEPDIR)/libcurlu_la-cw-pause.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-dict.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-dllmain.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-doh.Plo
-	-rm -f ./$(DEPDIR)/libcurlu_la-dynbuf.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-dynhds.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-easy.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-easygetopt.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-easyoptions.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-escape.Plo
+	-rm -f ./$(DEPDIR)/libcurlu_la-fake_addrinfo.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-file.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-fileinfo.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-fopen.Plo
@@ -5460,11 +5694,9 @@ maintainer-clean: maintainer-clean-am
 	-rm -f ./$(DEPDIR)/libcurlu_la-hash.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-headers.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-hmac.Plo
-	-rm -f ./$(DEPDIR)/libcurlu_la-hostasyn.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-hostip.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-hostip4.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-hostip6.Plo
-	-rm -f ./$(DEPDIR)/libcurlu_la-hostsyn.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-hsts.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-http.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-http1.Plo
@@ -5480,7 +5712,6 @@ maintainer-clean: maintainer-clean-am
 	-rm -f ./$(DEPDIR)/libcurlu_la-if2ip.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-imap.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-inet_ntop.Plo
-	-rm -f ./$(DEPDIR)/libcurlu_la-inet_pton.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-krb5.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-ldap.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-libcurl_unity.Plo
@@ -5493,8 +5724,8 @@ maintainer-clean: maintainer-clean-am
 	-rm -f ./$(DEPDIR)/libcurlu_la-mprintf.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-mqtt.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-multi.Plo
+	-rm -f ./$(DEPDIR)/libcurlu_la-multi_ev.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-netrc.Plo
-	-rm -f ./$(DEPDIR)/libcurlu_la-nonblock.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-noproxy.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-openldap.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-parsedate.Plo
@@ -5522,22 +5753,42 @@ maintainer-clean: maintainer-clean-am
 	-rm -f ./$(DEPDIR)/libcurlu_la-splay.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-strcase.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-strdup.Plo
+	-rm -f ./$(DEPDIR)/libcurlu_la-strequal.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-strerror.Plo
-	-rm -f ./$(DEPDIR)/libcurlu_la-strparse.Plo
-	-rm -f ./$(DEPDIR)/libcurlu_la-strtok.Plo
-	-rm -f ./$(DEPDIR)/libcurlu_la-strtoofft.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-system_win32.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-telnet.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-tftp.Plo
-	-rm -f ./$(DEPDIR)/libcurlu_la-timediff.Plo
-	-rm -f ./$(DEPDIR)/libcurlu_la-timeval.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-transfer.Plo
+	-rm -f ./$(DEPDIR)/libcurlu_la-uint-bset.Plo
+	-rm -f ./$(DEPDIR)/libcurlu_la-uint-hash.Plo
+	-rm -f ./$(DEPDIR)/libcurlu_la-uint-spbset.Plo
+	-rm -f ./$(DEPDIR)/libcurlu_la-uint-table.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-url.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-urlapi.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-version.Plo
-	-rm -f ./$(DEPDIR)/libcurlu_la-version_win32.Plo
-	-rm -f ./$(DEPDIR)/libcurlu_la-warnless.Plo
 	-rm -f ./$(DEPDIR)/libcurlu_la-ws.Plo
+	-rm -f curlx/$(DEPDIR)/libcurl_la-base64.Plo
+	-rm -f curlx/$(DEPDIR)/libcurl_la-dynbuf.Plo
+	-rm -f curlx/$(DEPDIR)/libcurl_la-inet_pton.Plo
+	-rm -f curlx/$(DEPDIR)/libcurl_la-multibyte.Plo
+	-rm -f curlx/$(DEPDIR)/libcurl_la-nonblock.Plo
+	-rm -f curlx/$(DEPDIR)/libcurl_la-strparse.Plo
+	-rm -f curlx/$(DEPDIR)/libcurl_la-timediff.Plo
+	-rm -f curlx/$(DEPDIR)/libcurl_la-timeval.Plo
+	-rm -f curlx/$(DEPDIR)/libcurl_la-version_win32.Plo
+	-rm -f curlx/$(DEPDIR)/libcurl_la-warnless.Plo
+	-rm -f curlx/$(DEPDIR)/libcurl_la-winapi.Plo
+	-rm -f curlx/$(DEPDIR)/libcurlu_la-base64.Plo
+	-rm -f curlx/$(DEPDIR)/libcurlu_la-dynbuf.Plo
+	-rm -f curlx/$(DEPDIR)/libcurlu_la-inet_pton.Plo
+	-rm -f curlx/$(DEPDIR)/libcurlu_la-multibyte.Plo
+	-rm -f curlx/$(DEPDIR)/libcurlu_la-nonblock.Plo
+	-rm -f curlx/$(DEPDIR)/libcurlu_la-strparse.Plo
+	-rm -f curlx/$(DEPDIR)/libcurlu_la-timediff.Plo
+	-rm -f curlx/$(DEPDIR)/libcurlu_la-timeval.Plo
+	-rm -f curlx/$(DEPDIR)/libcurlu_la-version_win32.Plo
+	-rm -f curlx/$(DEPDIR)/libcurlu_la-warnless.Plo
+	-rm -f curlx/$(DEPDIR)/libcurlu_la-winapi.Plo
 	-rm -f vauth/$(DEPDIR)/libcurl_la-cleartext.Plo
 	-rm -f vauth/$(DEPDIR)/libcurl_la-cram.Plo
 	-rm -f vauth/$(DEPDIR)/libcurl_la-digest.Plo
@@ -5673,19 +5924,18 @@ uninstall-am: uninstall-libLTLIBRARIES
 @HAVE_WINDRES_TRUE@@USE_CPPFLAG_CURL_STATICLIB_FALSE@$(LIB_RCFILES): $(top_srcdir)/include/curl/curlver.h
 
 checksrc:
-	$(CHECKSRC)(@PERL@ $(top_srcdir)/scripts/checksrc.pl -D$(srcdir)         \
-	  -W$(srcdir)/libcurl_unity.c -W$(srcdir)/curl_config.h $(srcdir)/*.[ch] \
-	  $(srcdir)/vauth/*.[ch] $(srcdir)/vtls/*.[ch] $(srcdir)/vquic/*.[ch]    \
-	  $(srcdir)/vssh/*.[ch])
+	$(CHECKSRC)(@PERL@ $(top_srcdir)/scripts/checksrc.pl -D$(srcdir) \
+	$(CSOURCES) $(HHEADERS))
 
 # for debug builds, we scan the sources on all regular make invokes
-@DEBUGBUILD_TRUE@all-local: checksrc
+@DEBUGBUILD_TRUE@@NOT_CURL_CI_TRUE@all-local: checksrc
 
 tidy:
-	$(TIDY) $(CSOURCES) $(TIDYFLAGS) $(CURL_CLANG_TIDYFLAGS) -- $(AM_CPPFLAGS) $(CPPFLAGS) -DHAVE_CONFIG_H
+	(_csources=`echo ' $(CSOURCES)' | sed -E -e 's/ +$$//' -e 's/ +/ /g' -e 's| | $(srcdir)/|g'`; \
+	$(TIDY) $$_csources $(TIDYFLAGS) $(CURL_CLANG_TIDYFLAGS) -- $(AM_CPPFLAGS) $(CPPFLAGS) -DHAVE_CONFIG_H)
 
 optiontable:
-	perl optiontable.pl < $(top_srcdir)/include/curl/curl.h > easyoptions.c
+	@PERL@ $(srcdir)/optiontable.pl < $(top_srcdir)/include/curl/curl.h > $(srcdir)/easyoptions.c
 
 @HAVE_WINDRES_TRUE@.rc.lo:
 @HAVE_WINDRES_TRUE@	$(LIBTOOL) --tag=RC --mode=compile $(RC) -I$(top_srcdir)/include $(RCFLAGS) -i $< -o $@
diff --git a/extra/curl/curl-8.12.1/lib/Makefile.inc b/extra/curl/curl-8.14.1/lib/Makefile.inc
similarity index 88%
rename from extra/curl/curl-8.12.1/lib/Makefile.inc
rename to extra/curl/curl-8.14.1/lib/Makefile.inc
index 25c6df84ecb9..b384e09623d9 100644
--- a/extra/curl/curl-8.12.1/lib/Makefile.inc
+++ b/extra/curl/curl-8.14.1/lib/Makefile.inc
@@ -22,6 +22,33 @@
 #
 ###########################################################################
 
+LIB_CURLX_CFILES = \
+  curlx/base64.c   \
+  curlx/dynbuf.c   \
+  curlx/inet_pton.c \
+  curlx/multibyte.c \
+  curlx/nonblock.c \
+  curlx/strparse.c \
+  curlx/timediff.c \
+  curlx/timeval.c  \
+  curlx/version_win32.c \
+  curlx/warnless.c \
+  curlx/winapi.c
+
+LIB_CURLX_HFILES = \
+  curlx/base64.h   \
+  curlx/curlx.h    \
+  curlx/dynbuf.h   \
+  curlx/inet_pton.h \
+  curlx/multibyte.h \
+  curlx/nonblock.h \
+  curlx/strparse.h \
+  curlx/timediff.h \
+  curlx/timeval.h  \
+  curlx/version_win32.h \
+  curlx/warnless.h \
+  curlx/winapi.h
+
 LIB_VAUTH_CFILES =      \
   vauth/cleartext.c     \
   vauth/cram.c          \
@@ -39,7 +66,6 @@ LIB_VAUTH_CFILES =      \
 
 LIB_VAUTH_HFILES =      \
   vauth/digest.h        \
-  vauth/ntlm.h          \
   vauth/vauth.h
 
 LIB_VTLS_CFILES =           \
@@ -112,8 +138,8 @@ LIB_CFILES =         \
   altsvc.c           \
   amigaos.c          \
   asyn-ares.c        \
-  asyn-thread.c      \
-  base64.c           \
+  asyn-base.c        \
+  asyn-thrdd.c       \
   bufq.c             \
   bufref.c           \
   cf-h1-proxy.c      \
@@ -126,6 +152,7 @@ LIB_CFILES =         \
   connect.c          \
   content_encoding.c \
   cookie.c           \
+  cshutdn.c          \
   curl_addrinfo.c    \
   curl_des.c         \
   curl_endian.c      \
@@ -134,7 +161,6 @@ LIB_CFILES =         \
   curl_gethostname.c \
   curl_gssapi.c      \
   curl_memrchr.c     \
-  curl_multibyte.c   \
   curl_ntlm_core.c   \
   curl_range.c       \
   curl_rtmp.c        \
@@ -144,15 +170,15 @@ LIB_CFILES =         \
   curl_threads.c     \
   curl_trc.c         \
   cw-out.c           \
+  cw-pause.c         \
   dict.c             \
-  dllmain.c          \
   doh.c              \
-  dynbuf.c           \
   dynhds.c           \
   easy.c             \
   easygetopt.c       \
   easyoptions.c      \
   escape.c           \
+  fake_addrinfo.c    \
   file.c             \
   fileinfo.c         \
   fopen.c            \
@@ -165,11 +191,9 @@ LIB_CFILES =         \
   hash.c             \
   headers.c          \
   hmac.c             \
-  hostasyn.c         \
   hostip.c           \
   hostip4.c          \
   hostip6.c          \
-  hostsyn.c          \
   hsts.c             \
   http.c             \
   http1.c            \
@@ -185,7 +209,6 @@ LIB_CFILES =         \
   if2ip.c            \
   imap.c             \
   inet_ntop.c        \
-  inet_pton.c        \
   krb5.c             \
   ldap.c             \
   llist.c            \
@@ -197,8 +220,8 @@ LIB_CFILES =         \
   mprintf.c          \
   mqtt.c             \
   multi.c            \
+  multi_ev.c         \
   netrc.c            \
-  nonblock.c         \
   noproxy.c          \
   openldap.c         \
   parsedate.c        \
@@ -226,21 +249,19 @@ LIB_CFILES =         \
   splay.c            \
   strcase.c          \
   strdup.c           \
+  strequal.c         \
   strerror.c         \
-  strparse.c         \
-  strtok.c           \
-  strtoofft.c        \
   system_win32.c     \
   telnet.c           \
   tftp.c             \
-  timediff.c         \
-  timeval.c          \
   transfer.c         \
+  uint-bset.c        \
+  uint-hash.c        \
+  uint-spbset.c      \
+  uint-table.c       \
   url.c              \
   urlapi.c           \
   version.c          \
-  version_win32.c    \
-  warnless.c         \
   ws.c
 
 LIB_HFILES =         \
@@ -257,11 +278,11 @@ LIB_HFILES =         \
   cf-socket.h        \
   cfilters.h         \
   conncache.h        \
+  cshutdn.h          \
   connect.h          \
   content_encoding.h \
   cookie.h           \
   curl_addrinfo.h    \
-  curl_base64.h      \
   curl_ctype.h       \
   curl_des.h         \
   curl_endian.h      \
@@ -276,7 +297,6 @@ LIB_HFILES =         \
   curl_md5.h         \
   curl_memory.h      \
   curl_memrchr.h     \
-  curl_multibyte.h   \
   curl_ntlm_core.h   \
   curl_printf.h      \
   curl_range.h       \
@@ -289,16 +309,16 @@ LIB_HFILES =         \
   curl_sspi.h        \
   curl_threads.h     \
   curl_trc.h         \
-  curlx.h            \
   cw-out.h           \
+  cw-pause.h         \
   dict.h             \
   doh.h              \
-  dynbuf.h           \
   dynhds.h           \
   easy_lock.h        \
   easyif.h           \
   easyoptions.h      \
   escape.h           \
+  fake_addrinfo.h    \
   file.h             \
   fileinfo.h         \
   fopen.h            \
@@ -326,16 +346,15 @@ LIB_HFILES =         \
   if2ip.h            \
   imap.h             \
   inet_ntop.h        \
-  inet_pton.h        \
   llist.h            \
   macos.h            \
   memdebug.h         \
   mime.h             \
   mqtt.h             \
   multihandle.h      \
+  multi_ev.h         \
   multiif.h          \
   netrc.h            \
-  nonblock.h         \
   noproxy.h          \
   parsedate.h        \
   pingpong.h         \
@@ -365,25 +384,22 @@ LIB_HFILES =         \
   strcase.h          \
   strdup.h           \
   strerror.h         \
-  strparse.h         \
-  strtok.h           \
-  strtoofft.h        \
   system_win32.h     \
   telnet.h           \
   tftp.h             \
-  timediff.h         \
-  timeval.h          \
   transfer.h         \
+  uint-bset.h        \
+  uint-hash.h        \
+  uint-spbset.h      \
+  uint-table.h       \
   url.h              \
   urlapi-int.h       \
   urldata.h          \
-  version_win32.h    \
-  warnless.h         \
   ws.h
 
 LIB_RCFILES = libcurl.rc
 
 CSOURCES = $(LIB_CFILES) $(LIB_VAUTH_CFILES) $(LIB_VTLS_CFILES) \
-  $(LIB_VQUIC_CFILES) $(LIB_VSSH_CFILES)
+  $(LIB_VQUIC_CFILES) $(LIB_VSSH_CFILES) $(LIB_CURLX_CFILES)
 HHEADERS = $(LIB_HFILES) $(LIB_VAUTH_HFILES) $(LIB_VTLS_HFILES) \
-  $(LIB_VQUIC_HFILES) $(LIB_VSSH_HFILES)
+  $(LIB_VQUIC_HFILES) $(LIB_VSSH_HFILES) $(LIB_CURLX_HFILES)
diff --git a/extra/curl/curl-8.12.1/lib/Makefile.soname b/extra/curl/curl-8.14.1/lib/Makefile.soname
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/Makefile.soname
rename to extra/curl/curl-8.14.1/lib/Makefile.soname
diff --git a/extra/curl/curl-8.12.1/lib/altsvc.c b/extra/curl/curl-8.14.1/lib/altsvc.c
similarity index 66%
rename from extra/curl/curl-8.12.1/lib/altsvc.c
rename to extra/curl/curl-8.14.1/lib/altsvc.c
index 095b3c168a0e..602ef61defff 100644
--- a/extra/curl/curl-8.12.1/lib/altsvc.c
+++ b/extra/curl/curl-8.14.1/lib/altsvc.c
@@ -35,12 +35,12 @@
 #include "strcase.h"
 #include "parsedate.h"
 #include "sendf.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
 #include "fopen.h"
 #include "rename.h"
 #include "strdup.h"
-#include "inet_pton.h"
-#include "strparse.h"
+#include "curlx/inet_pton.h"
+#include "curlx/strparse.h"
 #include "connect.h"
 
 /* The last 3 #include files should be in this order */
@@ -138,18 +138,20 @@ static struct altsvc *altsvc_create(struct Curl_str *srchost,
                                     size_t srcport,
                                     size_t dstport)
 {
-  enum alpnid dstalpnid = Curl_alpn2alpnid(dstalpn->str, dstalpn->len);
-  enum alpnid srcalpnid = Curl_alpn2alpnid(srcalpn->str, srcalpn->len);
+  enum alpnid dstalpnid =
+    Curl_alpn2alpnid(curlx_str(dstalpn), curlx_strlen(dstalpn));
+  enum alpnid srcalpnid =
+    Curl_alpn2alpnid(curlx_str(srcalpn), curlx_strlen(srcalpn));
   if(!srcalpnid || !dstalpnid)
     return NULL;
-  return altsvc_createid(srchost->str, srchost->len,
-                         dsthost->str, dsthost->len,
+  return altsvc_createid(curlx_str(srchost), curlx_strlen(srchost),
+                         curlx_str(dsthost), curlx_strlen(dsthost),
                          srcalpnid, dstalpnid,
                          srcport, dstport);
 }
 
 /* only returns SERIOUS errors */
-static CURLcode altsvc_add(struct altsvcinfo *asi, char *line)
+static CURLcode altsvc_add(struct altsvcinfo *asi, const char *line)
 {
   /* Example line:
      h2 example.com 443 h3 shiny.example.com 8443 "20191231 10:00:00" 1
@@ -159,42 +161,42 @@ static CURLcode altsvc_add(struct altsvcinfo *asi, char *line)
   struct Curl_str srcalpn;
   struct Curl_str dstalpn;
   struct Curl_str date;
-  size_t srcport;
-  size_t dstport;
-  size_t persist;
-  size_t prio;
-
-  if(Curl_str_word(&line, &srcalpn, MAX_ALTSVC_ALPNLEN) ||
-     Curl_str_singlespace(&line) ||
-     Curl_str_word(&line, &srchost, MAX_ALTSVC_HOSTLEN) ||
-     Curl_str_singlespace(&line) ||
-     Curl_str_number(&line, &srcport, 65535) ||
-     Curl_str_singlespace(&line) ||
-     Curl_str_word(&line, &dstalpn, MAX_ALTSVC_ALPNLEN) ||
-     Curl_str_singlespace(&line) ||
-     Curl_str_word(&line, &dsthost, MAX_ALTSVC_HOSTLEN) ||
-     Curl_str_singlespace(&line) ||
-     Curl_str_number(&line, &dstport, 65535) ||
-     Curl_str_singlespace(&line) ||
-     Curl_str_quotedword(&line, &date, MAX_ALTSVC_DATELEN) ||
-     Curl_str_singlespace(&line) ||
-     Curl_str_number(&line, &persist, 1) ||
-     Curl_str_singlespace(&line) ||
-     Curl_str_number(&line, &prio, 0) ||
-     Curl_str_newline(&line))
+  curl_off_t srcport;
+  curl_off_t dstport;
+  curl_off_t persist;
+  curl_off_t prio;
+
+  if(curlx_str_word(&line, &srcalpn, MAX_ALTSVC_ALPNLEN) ||
+     curlx_str_singlespace(&line) ||
+     curlx_str_word(&line, &srchost, MAX_ALTSVC_HOSTLEN) ||
+     curlx_str_singlespace(&line) ||
+     curlx_str_number(&line, &srcport, 65535) ||
+     curlx_str_singlespace(&line) ||
+     curlx_str_word(&line, &dstalpn, MAX_ALTSVC_ALPNLEN) ||
+     curlx_str_singlespace(&line) ||
+     curlx_str_word(&line, &dsthost, MAX_ALTSVC_HOSTLEN) ||
+     curlx_str_singlespace(&line) ||
+     curlx_str_number(&line, &dstport, 65535) ||
+     curlx_str_singlespace(&line) ||
+     curlx_str_quotedword(&line, &date, MAX_ALTSVC_DATELEN) ||
+     curlx_str_singlespace(&line) ||
+     curlx_str_number(&line, &persist, 1) ||
+     curlx_str_singlespace(&line) ||
+     curlx_str_number(&line, &prio, 0) ||
+     curlx_str_newline(&line))
     ;
   else {
     struct altsvc *as;
     char dbuf[MAX_ALTSVC_DATELEN + 1];
     time_t expires;
 
-    /* The date parser works on a null terminated string. The maximum length
-       is upheld by Curl_str_quotedword(). */
-    memcpy(dbuf, date.str, date.len);
-    dbuf[date.len] = 0;
+    /* The date parser works on a null-terminated string. The maximum length
+       is upheld by curlx_str_quotedword(). */
+    memcpy(dbuf, curlx_str(&date), curlx_strlen(&date));
+    dbuf[curlx_strlen(&date)] = 0;
     expires = Curl_getdate_capped(dbuf);
-    as = altsvc_create(&srchost, &dsthost, &srcalpn, &dstalpn, srcport,
-                       dstport);
+    as = altsvc_create(&srchost, &dsthost, &srcalpn, &dstalpn,
+                       (size_t)srcport, (size_t)dstport);
     if(as) {
       as->expires = expires;
       as->prio = 0; /* not supported to just set zero */
@@ -229,18 +231,14 @@ static CURLcode altsvc_load(struct altsvcinfo *asi, const char *file)
   fp = fopen(file, FOPEN_READTEXT);
   if(fp) {
     struct dynbuf buf;
-    Curl_dyn_init(&buf, MAX_ALTSVC_LINE);
+    curlx_dyn_init(&buf, MAX_ALTSVC_LINE);
     while(Curl_get_line(&buf, fp)) {
-      char *lineptr = Curl_dyn_ptr(&buf);
-      while(*lineptr && ISBLANK(*lineptr))
-        lineptr++;
-      if(*lineptr == '#')
-        /* skip commented lines */
-        continue;
-
-      altsvc_add(asi, lineptr);
+      const char *lineptr = curlx_dyn_ptr(&buf);
+      curlx_str_passblanks(&lineptr);
+      if(curlx_str_single(&lineptr, '#'))
+        altsvc_add(asi, lineptr);
     }
-    Curl_dyn_free(&buf); /* free the line buffer */
+    curlx_dyn_free(&buf); /* free the line buffer */
     fclose(fp);
   }
   return result;
@@ -263,11 +261,11 @@ static CURLcode altsvc_out(struct altsvc *as, FILE *fp)
 #ifdef USE_IPV6
   else {
     char ipv6_unused[16];
-    if(1 == Curl_inet_pton(AF_INET6, as->dst.host, ipv6_unused)) {
+    if(1 == curlx_inet_pton(AF_INET6, as->dst.host, ipv6_unused)) {
       dst6_pre = "[";
       dst6_post = "]";
     }
-    if(1 == Curl_inet_pton(AF_INET6, as->src.host, ipv6_unused)) {
+    if(1 == curlx_inet_pton(AF_INET6, as->src.host, ipv6_unused)) {
       src6_pre = "[";
       src6_post = "]";
     }
@@ -405,26 +403,6 @@ CURLcode Curl_altsvc_save(struct Curl_easy *data,
   return result;
 }
 
-static CURLcode getalnum(const char **ptr, char *alpnbuf, size_t buflen)
-{
-  size_t len;
-  const char *protop;
-  const char *p = *ptr;
-  while(*p && ISBLANK(*p))
-    p++;
-  protop = p;
-  while(*p && !ISBLANK(*p) && (*p != ';') && (*p != '='))
-    p++;
-  len = p - protop;
-  *ptr = p;
-
-  if(!len || (len >= buflen))
-    return CURLE_BAD_FUNCTION_ARGUMENT;
-  memcpy(alpnbuf, protop, len);
-  alpnbuf[len] = 0;
-  return CURLE_OK;
-}
-
 /* hostcompare() returns true if 'host' matches 'check'. The first host
  * argument may have a trailing dot present that will be ignored.
  */
@@ -460,15 +438,16 @@ static void altsvc_flush(struct altsvcinfo *asi, enum alpnid srcalpnid,
   }
 }
 
-#ifdef DEBUGBUILD
+#if defined(DEBUGBUILD) || defined(UNITTESTS)
 /* to play well with debug builds, we can *set* a fixed time this will
    return */
 static time_t altsvc_debugtime(void *unused)
 {
-  char *timestr = getenv("CURL_TIME");
+  const char *timestr = getenv("CURL_TIME");
   (void)unused;
   if(timestr) {
-    long val = strtol(timestr, NULL, 10);
+    curl_off_t val;
+    curlx_str_number(&timestr, &val, TIME_T_MAX);
     return (time_t)val;
   }
   return time(NULL);
@@ -494,153 +473,124 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data,
                            unsigned short srcport)
 {
   const char *p = value;
-  char alpnbuf[MAX_ALTSVC_ALPNLEN] = "";
   struct altsvc *as;
   unsigned short dstport = srcport; /* the same by default */
-  CURLcode result = getalnum(&p, alpnbuf, sizeof(alpnbuf));
   size_t entries = 0;
-  size_t alpnlen = strlen(alpnbuf);
-  size_t srchostlen = strlen(srchost);
+  struct Curl_str alpn;
+  const char *sp;
+  time_t maxage = 24 * 3600; /* default is 24 hours */
+  bool persist = FALSE;
 #ifdef CURL_DISABLE_VERBOSE_STRINGS
   (void)data;
 #endif
-  if(result) {
-    infof(data, "Excessive alt-svc header, ignoring.");
-    return CURLE_OK;
-  }
 
   DEBUGASSERT(asi);
 
-  /* "clear" is a magic keyword */
-  if(strcasecompare(alpnbuf, "clear")) {
-    /* Flush cached alternatives for this source origin */
-    altsvc_flush(asi, srcalpnid, srchost, srcport);
-    return CURLE_OK;
+  /* initial check for "clear" */
+  if(!curlx_str_until(&p, &alpn, MAX_ALTSVC_LINE, ';') &&
+     !curlx_str_single(&p, ';')) {
+    curlx_str_trimblanks(&alpn);
+    /* "clear" is a magic keyword */
+    if(curlx_str_casecompare(&alpn, "clear")) {
+      /* Flush cached alternatives for this source origin */
+      altsvc_flush(asi, srcalpnid, srchost, srcport);
+      return CURLE_OK;
+    }
+  }
+
+  p = value;
+
+  if(curlx_str_until(&p, &alpn, MAX_ALTSVC_LINE, '='))
+    return CURLE_OK; /* strange line */
+
+  curlx_str_trimblanks(&alpn);
+
+  /* Handle the optional 'ma' and 'persist' flags once first, as they need to
+     be known for each alternative service. Unknown flags are skipped. */
+  sp = strchr(p, ';');
+  if(sp) {
+    sp++; /* pass the semicolon */
+    for(;;) {
+      struct Curl_str name;
+      struct Curl_str val;
+      const char *vp;
+      curl_off_t num;
+      bool quoted;
+      /* allow some extra whitespaces around name and value */
+      if(curlx_str_until(&sp, &name, 20, '=') ||
+         curlx_str_single(&sp, '=') ||
+         curlx_str_until(&sp, &val, 80, ';'))
+        break;
+      curlx_str_trimblanks(&name);
+      curlx_str_trimblanks(&val);
+      /* the value might be quoted */
+      vp = curlx_str(&val);
+      quoted = (*vp == '\"');
+      if(quoted)
+        vp++;
+      if(!curlx_str_number(&vp, &num, TIME_T_MAX)) {
+        if(curlx_str_casecompare(&name, "ma"))
+          maxage = (time_t)num;
+        else if(curlx_str_casecompare(&name, "persist") && (num == 1))
+          persist = TRUE;
+      }
+      if(quoted && curlx_str_single(&sp, '\"'))
+        break;
+      if(curlx_str_single(&sp, ';'))
+        break;
+    }
   }
 
   do {
-    if(*p == '=') {
-      /* [protocol]="[host][:port]" */
-      enum alpnid dstalpnid = Curl_alpn2alpnid(alpnbuf, alpnlen);
-      p++;
-      if(*p == '\"') {
-        const char *dsthost = "";
-        size_t dstlen = 0; /* destination hostname length */
-        const char *value_ptr;
-        char option[32];
-        unsigned long num;
-        char *end_ptr;
-        bool quoted = FALSE;
-        time_t maxage = 24 * 3600; /* default is 24 hours */
-        bool persist = FALSE;
-        bool valid = TRUE;
-        p++;
-        if(*p != ':') {
+    if(!curlx_str_single(&p, '=')) {
+      /* [protocol]="[host][:port], [protocol]="[host][:port]" */
+      enum alpnid dstalpnid =
+        Curl_alpn2alpnid(curlx_str(&alpn), curlx_strlen(&alpn));
+      if(!curlx_str_single(&p, '\"')) {
+        struct Curl_str dsthost;
+        curl_off_t port = 0;
+        if(curlx_str_single(&p, ':')) {
           /* hostname starts here */
-          const char *hostp = p;
-          if(*p == '[') {
-            /* pass all valid IPv6 letters - does not handle zone id */
-            dstlen = strspn(++p, "0123456789abcdefABCDEF:.");
-            if(p[dstlen] != ']')
-              /* invalid host syntax, bail out */
+          if(curlx_str_single(&p, '[')) {
+            if(curlx_str_until(&p, &dsthost, MAX_ALTSVC_HOSTLEN, ':')) {
+              infof(data, "Bad alt-svc hostname, ignoring.");
               break;
-            /* we store the IPv6 numerical address *with* brackets */
-            dstlen += 2;
-            p = &p[dstlen-1];
+            }
           }
           else {
-            while(*p && (ISALNUM(*p) || (*p == '.') || (*p == '-')))
-              p++;
-            dstlen = p - hostp;
-          }
-          if(!dstlen || (dstlen >= MAX_ALTSVC_HOSTLEN)) {
-            infof(data, "Excessive alt-svc hostname, ignoring.");
-            valid = FALSE;
-          }
-          else {
-            dsthost = hostp;
+            /* IPv6 host name */
+            if(curlx_str_until(&p, &dsthost, MAX_IPADR_LEN, ']') ||
+               curlx_str_single(&p, ']')) {
+              infof(data, "Bad alt-svc IPv6 hostname, ignoring.");
+              break;
+            }
           }
+          if(curlx_str_single(&p, ':'))
+            break;
         }
-        else {
+        else
           /* no destination name, use source host */
-          dsthost = srchost;
-          dstlen = strlen(srchost);
-        }
-        if(*p == ':') {
-          unsigned long port = 0;
-          p++;
-          if(ISDIGIT(*p))
-            /* a port number */
-            port = strtoul(p, &end_ptr, 10);
-          else
-            end_ptr = (char *)p; /* not left uninitialized */
-          if(!port || port > USHRT_MAX || end_ptr == p || *end_ptr != '\"') {
-            infof(data, "Unknown alt-svc port number, ignoring.");
-            valid = FALSE;
-          }
-          else {
-            dstport = curlx_ultous(port);
-            p = end_ptr;
-          }
-        }
-        if(*p++ != '\"')
+          curlx_str_assign(&dsthost, srchost, strlen(srchost));
+
+        if(curlx_str_number(&p, &port, 0xffff)) {
+          infof(data, "Unknown alt-svc port number, ignoring.");
           break;
-        /* Handle the optional 'ma' and 'persist' flags. Unknown flags
-           are skipped. */
-        for(;;) {
-          while(ISBLANK(*p))
-            p++;
-          if(*p != ';')
-            break;
-          p++; /* pass the semicolon */
-          if(!*p || ISNEWLINE(*p))
-            break;
-          result = getalnum(&p, option, sizeof(option));
-          if(result) {
-            /* skip option if name is too long */
-            option[0] = '\0';
-          }
-          while(*p && ISBLANK(*p))
-            p++;
-          if(*p != '=')
-            return CURLE_OK;
-          p++;
-          while(*p && ISBLANK(*p))
-            p++;
-          if(!*p)
-            return CURLE_OK;
-          if(*p == '\"') {
-            /* quoted value */
-            p++;
-            quoted = TRUE;
-          }
-          value_ptr = p;
-          if(quoted) {
-            while(*p && *p != '\"')
-              p++;
-            if(!*p++)
-              return CURLE_OK;
-          }
-          else {
-            while(*p && !ISBLANK(*p) && *p!= ';' && *p != ',')
-              p++;
-          }
-          num = strtoul(value_ptr, &end_ptr, 10);
-          if((end_ptr != value_ptr) && (num < ULONG_MAX)) {
-            if(strcasecompare("ma", option))
-              maxage = (time_t)num;
-            else if(strcasecompare("persist", option) && (num == 1))
-              persist = TRUE;
-          }
         }
-        if(dstalpnid && valid) {
+
+        dstport = (unsigned short)port;
+
+        if(curlx_str_single(&p, '\"'))
+          break;
+
+        if(dstalpnid) {
           if(!entries++)
             /* Flush cached alternatives for this source origin, if any - when
                this is the first entry of the line. */
             altsvc_flush(asi, srcalpnid, srchost, srcport);
 
-          as = altsvc_createid(srchost, srchostlen,
-                               dsthost, dstlen,
+          as = altsvc_createid(srchost, strlen(srchost),
+                               curlx_str(&dsthost),
+                               curlx_strlen(&dsthost),
                                srcalpnid, dstalpnid,
                                srcport, dstport);
           if(as) {
@@ -653,26 +603,28 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data,
               as->expires = maxage + secs;
             as->persist = persist;
             Curl_llist_append(&asi->list, as, &as->node);
-            infof(data, "Added alt-svc: %s:%d over %s", dsthost, dstport,
-                  Curl_alpnid2str(dstalpnid));
+            infof(data, "Added alt-svc: %.*s:%d over %s",
+                  (int)curlx_strlen(&dsthost), curlx_str(&dsthost),
+                  dstport, Curl_alpnid2str(dstalpnid));
           }
         }
       }
       else
         break;
+
       /* after the double quote there can be a comma if there is another
          string or a semicolon if no more */
-      if(*p == ',') {
-        /* comma means another alternative is presented */
-        p++;
-        result = getalnum(&p, alpnbuf, sizeof(alpnbuf));
-        if(result)
-          break;
-      }
+      if(curlx_str_single(&p, ','))
+        break;
+
+      /* comma means another alternative is present */
+      if(curlx_str_until(&p, &alpn, MAX_ALTSVC_LINE, '='))
+        break;
+      curlx_str_trimblanks(&alpn);
     }
     else
       break;
-  } while(*p && (*p != ';') && (*p != '\n') && (*p != '\r'));
+  } while(1);
 
   return CURLE_OK;
 }
diff --git a/extra/curl/curl-8.12.1/lib/altsvc.h b/extra/curl/curl-8.14.1/lib/altsvc.h
similarity index 99%
rename from extra/curl/curl-8.12.1/lib/altsvc.h
rename to extra/curl/curl-8.14.1/lib/altsvc.h
index 5f94f832b4e1..831cd097436a 100644
--- a/extra/curl/curl-8.12.1/lib/altsvc.h
+++ b/extra/curl/curl-8.14.1/lib/altsvc.h
@@ -39,9 +39,9 @@ struct altsvc {
   struct althost src;
   struct althost dst;
   time_t expires;
-  bool persist;
-  unsigned int prio;
   struct Curl_llist_node node;
+  unsigned int prio;
+  BIT(persist);
 };
 
 struct altsvcinfo {
diff --git a/extra/curl/curl-8.12.1/lib/amigaos.c b/extra/curl/curl-8.14.1/lib/amigaos.c
similarity index 99%
rename from extra/curl/curl-8.12.1/lib/amigaos.c
rename to extra/curl/curl-8.14.1/lib/amigaos.c
index c4872f248d92..ac6d6b41937d 100644
--- a/extra/curl/curl-8.12.1/lib/amigaos.c
+++ b/extra/curl/curl-8.14.1/lib/amigaos.c
@@ -184,7 +184,7 @@ int Curl_amiga_select(int nfds, fd_set *readfds, fd_set *writefds,
 {
   int r = WaitSelect(nfds, readfds, writefds, errorfds, timeout, 0);
   /* Ensure Ctrl-C signal is actioned */
-  if((r == -1) && (SOCKERRNO == EINTR))
+  if((r == -1) && (SOCKERRNO == SOCKEINTR))
     raise(SIGINT);
   return r;
 }
diff --git a/extra/curl/curl-8.12.1/lib/amigaos.h b/extra/curl/curl-8.14.1/lib/amigaos.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/amigaos.h
rename to extra/curl/curl-8.14.1/lib/amigaos.h
diff --git a/extra/curl/curl-8.12.1/lib/arpa_telnet.h b/extra/curl/curl-8.14.1/lib/arpa_telnet.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/arpa_telnet.h
rename to extra/curl/curl-8.14.1/lib/arpa_telnet.h
diff --git a/extra/curl/curl-8.14.1/lib/asyn-ares.c b/extra/curl/curl-8.14.1/lib/asyn-ares.c
new file mode 100644
index 000000000000..10f870a15af6
--- /dev/null
+++ b/extra/curl/curl-8.14.1/lib/asyn-ares.c
@@ -0,0 +1,952 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef CURLRES_ARES
+
+/***********************************************************************
+ * Only for ares-enabled builds
+ * And only for functions that fulfill the asynch resolver backend API
+ * as defined in asyn.h, nothing else belongs in this file!
+ **********************************************************************/
+
+#include <limits.h>
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef __VMS
+#include <in.h>
+#include <inet.h>
+#endif
+
+#include "urldata.h"
+#include "sendf.h"
+#include "hostip.h"
+#include "hash.h"
+#include "share.h"
+#include "url.h"
+#include "multiif.h"
+#include "curlx/inet_pton.h"
+#include "connect.h"
+#include "select.h"
+#include "progress.h"
+#include "curlx/timediff.h"
+#include "httpsrr.h"
+#include "strdup.h"
+
+#include <ares.h>
+#include <ares_version.h> /* really old c-ares did not include this by
+                             itself */
+
+#if ARES_VERSION >= 0x010601
+/* IPv6 supported since 1.6.1 */
+#define HAVE_CARES_IPV6 1
+#endif
+
+#if ARES_VERSION >= 0x010704
+#define HAVE_CARES_SERVERS_CSV 1
+#define HAVE_CARES_LOCAL_DEV 1
+#define HAVE_CARES_SET_LOCAL 1
+#endif
+
+#if ARES_VERSION >= 0x010b00
+#define HAVE_CARES_PORTS_CSV 1
+#endif
+
+#if ARES_VERSION >= 0x011000
+/* 1.16.0 or later has ares_getaddrinfo */
+#define HAVE_CARES_GETADDRINFO 1
+#endif
+
+#ifdef USE_HTTPSRR
+#if ARES_VERSION < 0x011c00
+#error "requires c-ares 1.28.0 or newer for HTTPSRR"
+#endif
+#define HTTPSRR_WORKS
+#endif
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/* How long we are willing to wait for additional parallel responses after
+   obtaining a "definitive" one. For old c-ares without getaddrinfo.
+
+   This is intended to equal the c-ares default timeout. cURL always uses that
+   default value. Unfortunately, c-ares does not expose its default timeout in
+   its API, but it is officially documented as 5 seconds.
+
+   See query_completed_cb() for an explanation of how this is used.
+ */
+#define HAPPY_EYEBALLS_DNS_TIMEOUT 5000
+
+#define CARES_TIMEOUT_PER_ATTEMPT 2000
+
+static int ares_ver = 0;
+
+static CURLcode async_ares_set_dns_servers(struct Curl_easy *data,
+                                           bool reset_on_null);
+
+/*
+ * Curl_async_global_init() - the generic low-level asynchronous name
+ * resolve API. Called from curl_global_init() to initialize global resolver
+ * environment. Initializes ares library.
+ */
+int Curl_async_global_init(void)
+{
+#ifdef CARES_HAVE_ARES_LIBRARY_INIT
+  if(ares_library_init(ARES_LIB_INIT_ALL)) {
+    return CURLE_FAILED_INIT;
+  }
+#endif
+  ares_version(&ares_ver);
+  return CURLE_OK;
+}
+
+/*
+ * Curl_async_global_cleanup()
+ *
+ * Called from curl_global_cleanup() to destroy global resolver environment.
+ * Deinitializes ares library.
+ */
+void Curl_async_global_cleanup(void)
+{
+#ifdef CARES_HAVE_ARES_LIBRARY_CLEANUP
+  ares_library_cleanup();
+#endif
+}
+
+
+static void sock_state_cb(void *data, ares_socket_t socket_fd,
+                          int readable, int writable)
+{
+  struct Curl_easy *easy = data;
+  if(!readable && !writable) {
+    DEBUGASSERT(easy);
+    Curl_multi_will_close(easy, socket_fd);
+  }
+}
+
+static CURLcode async_ares_init(struct Curl_easy *data)
+{
+  struct async_ares_ctx *ares = &data->state.async.ares;
+  int status;
+  struct ares_options options;
+  int optmask = ARES_OPT_SOCK_STATE_CB;
+  CURLcode rc = CURLE_OK;
+
+  options.sock_state_cb = sock_state_cb;
+  options.sock_state_cb_data = data;
+
+  DEBUGASSERT(!ares->channel);
+  /*
+     if c ares < 1.20.0: curl set timeout to CARES_TIMEOUT_PER_ATTEMPT (2s)
+
+     if c-ares >= 1.20.0 it already has the timeout to 2s, curl does not need
+     to set the timeout value;
+
+     if c-ares >= 1.24.0, user can set the timeout via /etc/resolv.conf to
+     overwrite c-ares' timeout.
+  */
+  DEBUGASSERT(ares_ver);
+  if(ares_ver < 0x011400) {
+    options.timeout = CARES_TIMEOUT_PER_ATTEMPT;
+    optmask |= ARES_OPT_TIMEOUTMS;
+  }
+
+  status = ares_init_options(&ares->channel, &options, optmask);
+  if(status != ARES_SUCCESS) {
+    ares->channel = NULL;
+    rc = (status == ARES_ENOMEM) ?
+         CURLE_OUT_OF_MEMORY : CURLE_FAILED_INIT;
+    goto out;
+  }
+
+  rc = async_ares_set_dns_servers(data, FALSE);
+  if(rc && rc != CURLE_NOT_BUILT_IN)
+    goto out;
+
+  rc = Curl_async_ares_set_dns_interface(data);
+  if(rc && rc != CURLE_NOT_BUILT_IN)
+    goto out;
+
+  rc = Curl_async_ares_set_dns_local_ip4(data);
+  if(rc && rc != CURLE_NOT_BUILT_IN)
+    goto out;
+
+  rc = Curl_async_ares_set_dns_local_ip6(data);
+  if(rc && rc != CURLE_NOT_BUILT_IN)
+    goto out;
+
+  rc = CURLE_OK;
+
+out:
+  if(rc && ares->channel) {
+    ares_destroy(ares->channel);
+    ares->channel = NULL;
+  }
+  return rc;
+}
+
+static CURLcode async_ares_init_lazy(struct Curl_easy *data)
+{
+  struct async_ares_ctx *ares = &data->state.async.ares;
+  if(!ares->channel)
+    return async_ares_init(data);
+  return CURLE_OK;
+}
+
+CURLcode Curl_async_get_impl(struct Curl_easy *data, void **impl)
+{
+  struct async_ares_ctx *ares = &data->state.async.ares;
+  CURLcode result = CURLE_OK;
+  if(!ares->channel) {
+    result = async_ares_init(data);
+  }
+  *impl = ares->channel;
+  return result;
+}
+
+static void async_ares_cleanup(struct Curl_easy *data);
+
+void Curl_async_ares_shutdown(struct Curl_easy *data)
+{
+  struct async_ares_ctx *ares = &data->state.async.ares;
+  if(ares->channel)
+    ares_cancel(ares->channel);
+  async_ares_cleanup(data);
+}
+
+void Curl_async_ares_destroy(struct Curl_easy *data)
+{
+  struct async_ares_ctx *ares = &data->state.async.ares;
+  Curl_async_ares_shutdown(data);
+  if(ares->channel) {
+    ares_destroy(ares->channel);
+    ares->channel = NULL;
+  }
+}
+
+/*
+ * async_ares_cleanup() cleans up async resolver data.
+ */
+static void async_ares_cleanup(struct Curl_easy *data)
+{
+  struct async_ares_ctx *ares = &data->state.async.ares;
+  if(ares->temp_ai) {
+    Curl_freeaddrinfo(ares->temp_ai);
+    ares->temp_ai = NULL;
+  }
+#ifdef USE_HTTPSRR
+  Curl_httpsrr_cleanup(&ares->hinfo);
+#endif
+}
+
+/*
+ * Curl_async_getsock() is called when someone from the outside world
+ * (using curl_multi_fdset()) wants to get our fd_set setup.
+ */
+
+int Curl_async_getsock(struct Curl_easy *data, curl_socket_t *socks)
+{
+  struct async_ares_ctx *ares = &data->state.async.ares;
+  DEBUGASSERT(ares->channel);
+  return Curl_ares_getsock(data, ares->channel, socks);
+}
+
+/*
+ * Curl_async_is_resolved() is called repeatedly to check if a previous
+ * name resolve request has completed. It should also make sure to time-out if
+ * the operation seems to take too long.
+ *
+ * Returns normal CURLcode errors.
+ */
+CURLcode Curl_async_is_resolved(struct Curl_easy *data,
+                                struct Curl_dns_entry **dns)
+{
+  struct async_ares_ctx *ares = &data->state.async.ares;
+  CURLcode result = CURLE_OK;
+
+  DEBUGASSERT(dns);
+  *dns = NULL;
+
+  if(data->state.async.done) {
+    *dns = data->state.async.dns;
+    return CURLE_OK;
+  }
+
+  if(Curl_ares_perform(ares->channel, 0) < 0)
+    return CURLE_UNRECOVERABLE_POLL;
+
+#ifndef HAVE_CARES_GETADDRINFO
+  /* Now that we have checked for any last minute results above, see if there
+     are any responses still pending when the EXPIRE_HAPPY_EYEBALLS_DNS timer
+     expires. */
+  if(ares->num_pending
+     /* This is only set to non-zero if the timer was started. */
+     && (ares->happy_eyeballs_dns_time.tv_sec
+         || ares->happy_eyeballs_dns_time.tv_usec)
+     && (curlx_timediff(curlx_now(), ares->happy_eyeballs_dns_time)
+         >= HAPPY_EYEBALLS_DNS_TIMEOUT)) {
+    /* Remember that the EXPIRE_HAPPY_EYEBALLS_DNS timer is no longer
+       running. */
+    memset(&ares->happy_eyeballs_dns_time, 0,
+           sizeof(ares->happy_eyeballs_dns_time));
+
+    /* Cancel the raw c-ares request, which will fire query_completed_cb() with
+       ARES_ECANCELLED synchronously for all pending responses. This will
+       leave us with res->num_pending == 0, which is perfect for the next
+       block. */
+    ares_cancel(ares->channel);
+    DEBUGASSERT(ares->num_pending == 0);
+  }
+#endif
+
+  if(!ares->num_pending) {
+    /* all c-ares operations done, what is the result to report? */
+    Curl_resolv_unlink(data, &data->state.async.dns);
+    data->state.async.done = TRUE;
+    result = ares->result;
+    if(ares->last_status == CURL_ASYNC_SUCCESS && !result) {
+      data->state.async.dns =
+        Curl_dnscache_mk_entry(data, ares->temp_ai,
+                               data->state.async.hostname, 0,
+                               data->state.async.port, FALSE);
+      ares->temp_ai = NULL; /* temp_ai now owned by entry */
+#ifdef HTTPSRR_WORKS
+        if(data->state.async.dns) {
+          struct Curl_https_rrinfo *lhrr = Curl_httpsrr_dup_move(&ares->hinfo);
+          if(!lhrr)
+            result = CURLE_OUT_OF_MEMORY;
+          else
+            data->state.async.dns->hinfo = lhrr;
+        }
+#endif
+      if(!result && data->state.async.dns)
+        result = Curl_dnscache_add(data, data->state.async.dns);
+    }
+    /* if we have not found anything, report the proper
+     * CURLE_COULDNT_RESOLVE_* code */
+    if(!result && !data->state.async.dns)
+      result = Curl_resolver_error(data);
+    if(result)
+      Curl_resolv_unlink(data, &data->state.async.dns);
+    *dns = data->state.async.dns;
+    CURL_TRC_DNS(data, "is_resolved() result=%d, dns=%sfound",
+                 result, *dns ? "" : "not ");
+    async_ares_cleanup(data);
+  }
+  return result;
+}
+
+/*
+ * Curl_async_await()
+ *
+ * Waits for a resolve to finish. This function should be avoided since using
+ * this risk getting the multi interface to "hang".
+ *
+ * 'entry' MUST be non-NULL.
+ *
+ * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
+ * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
+ */
+CURLcode Curl_async_await(struct Curl_easy *data,
+                          struct Curl_dns_entry **entry)
+{
+  struct async_ares_ctx *ares = &data->state.async.ares;
+  CURLcode result = CURLE_OK;
+  timediff_t timeout;
+  struct curltime now = curlx_now();
+
+  DEBUGASSERT(entry);
+  *entry = NULL; /* clear on entry */
+
+  timeout = Curl_timeleft(data, &now, TRUE);
+  if(timeout < 0) {
+    /* already expired! */
+    connclose(data->conn, "Timed out before name resolve started");
+    return CURLE_OPERATION_TIMEDOUT;
+  }
+  if(!timeout)
+    timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */
+
+  /* Wait for the name resolve query to complete. */
+  while(!result) {
+    struct timeval *tvp, tv, store;
+    int itimeout;
+    timediff_t timeout_ms;
+
+#if TIMEDIFF_T_MAX > INT_MAX
+    itimeout = (timeout > INT_MAX) ? INT_MAX : (int)timeout;
+#else
+    itimeout = (int)timeout;
+#endif
+
+    store.tv_sec = itimeout/1000;
+    store.tv_usec = (itimeout%1000)*1000;
+
+    tvp = ares_timeout(ares->channel, &store, &tv);
+
+    /* use the timeout period ares returned to us above if less than one
+       second is left, otherwise just use 1000ms to make sure the progress
+       callback gets called frequent enough */
+    if(!tvp->tv_sec)
+      timeout_ms = (timediff_t)(tvp->tv_usec/1000);
+    else
+      timeout_ms = 1000;
+
+    if(Curl_ares_perform(ares->channel, timeout_ms) < 0)
+      return CURLE_UNRECOVERABLE_POLL;
+
+    result = Curl_async_is_resolved(data, entry);
+    if(result || data->state.async.done)
+      break;
+
+    if(Curl_pgrsUpdate(data))
+      result = CURLE_ABORTED_BY_CALLBACK;
+    else {
+      struct curltime now2 = curlx_now();
+      timediff_t timediff = curlx_timediff(now2, now); /* spent time */
+      if(timediff <= 0)
+        timeout -= 1; /* always deduct at least 1 */
+      else if(timediff > timeout)
+        timeout = -1;
+      else
+        timeout -= timediff;
+      now = now2; /* for next loop */
+    }
+    if(timeout < 0)
+      result = CURLE_OPERATION_TIMEDOUT;
+  }
+
+  /* Operation complete, if the lookup was successful we now have the entry
+     in the cache. */
+  data->state.async.done = TRUE;
+  if(entry)
+    *entry = data->state.async.dns;
+
+  if(result)
+    ares_cancel(ares->channel);
+  return result;
+}
+
+#ifndef HAVE_CARES_GETADDRINFO
+
+/* Connects results to the list */
+static void async_addr_concat(struct Curl_addrinfo **pbase,
+                              struct Curl_addrinfo *ai)
+{
+  if(!ai)
+    return;
+
+  /* When adding `ai` to an existing address list, we prefer ipv6
+   * to be in front. */
+#ifdef USE_IPV6 /* CURLRES_IPV6 */
+  if(*pbase && (*pbase)->ai_family == PF_INET6) {
+    /* ipv6 already in front, append `ai` */
+    struct Curl_addrinfo *tail = *pbase;
+    while(tail->ai_next)
+      tail = tail->ai_next;
+    tail->ai_next = ai;
+  }
+  else
+#endif /* CURLRES_IPV6 */
+  {
+    /* prepend to the (possibly) existing list. */
+    struct Curl_addrinfo *tail = ai;
+    while(tail->ai_next)
+      tail = tail->ai_next;
+    tail->ai_next = *pbase;
+    *pbase = ai;
+  }
+}
+
+/*
+ * ares_query_completed_cb() is the callback that ares will call when
+ * the host query initiated by ares_gethostbyname() from
+ * Curl_async_getaddrinfo(), when using ares, is completed either
+ * successfully or with failure.
+ */
+static void async_ares_hostbyname_cb(void *user_data,
+                                     int status,
+                                     int timeouts,
+                                     struct hostent *hostent)
+{
+  struct Curl_easy *data = (struct Curl_easy *)user_data;
+  struct async_ares_ctx *ares = &data->state.async.ares;
+
+  (void)timeouts; /* ignored */
+
+  if(ARES_EDESTRUCTION == status)
+    /* when this ares handle is getting destroyed, the 'arg' pointer may not
+       be valid so only defer it when we know the 'status' says its fine! */
+    return;
+
+  if(CURL_ASYNC_SUCCESS == status) {
+    ares->last_status = status; /* one success overrules any error */
+    async_addr_concat(&ares->temp_ai,
+      Curl_he2ai(hostent, data->state.async.port));
+  }
+  else if(ares->last_status != ARES_SUCCESS) {
+    /* no success so far, remember error */
+    ares->last_status = status;
+  }
+
+  ares->num_pending--;
+
+  CURL_TRC_DNS(data, "ares: hostbyname done, status=%d, pending=%d, "
+               "addr=%sfound",
+               status, ares->num_pending, ares->temp_ai ? "" : "not ");
+  /* If there are responses still pending, we presume they must be the
+     complementary IPv4 or IPv6 lookups that we started in parallel in
+     Curl_async_getaddrinfo() (for Happy Eyeballs). If we have got a
+     "definitive" response from one of a set of parallel queries, we need to
+     think about how long we are willing to wait for more responses. */
+  if(ares->num_pending
+     /* Only these c-ares status values count as "definitive" for these
+        purposes. For example, ARES_ENODATA is what we expect when there is
+        no IPv6 entry for a domain name, and that is not a reason to get more
+        aggressive in our timeouts for the other response. Other errors are
+        either a result of bad input (which should affect all parallel
+        requests), local or network conditions, non-definitive server
+        responses, or us cancelling the request. */
+     && (status == ARES_SUCCESS || status == ARES_ENOTFOUND)) {
+    /* Right now, there can only be up to two parallel queries, so do not
+       bother handling any other cases. */
+    DEBUGASSERT(ares->num_pending == 1);
+
+    /* it is possible that one of these parallel queries could succeed
+       quickly, but the other could always fail or timeout (when we are
+       talking to a pool of DNS servers that can only successfully resolve
+       IPv4 address, for example).
+
+       it is also possible that the other request could always just take
+       longer because it needs more time or only the second DNS server can
+       fulfill it successfully. But, to align with the philosophy of Happy
+       Eyeballs, we do not want to wait _too_ long or users will think
+       requests are slow when IPv6 lookups do not actually work (but IPv4
+       ones do).
+
+       So, now that we have a usable answer (some IPv4 addresses, some IPv6
+       addresses, or "no such domain"), we start a timeout for the remaining
+       pending responses. Even though it is typical that this resolved
+       request came back quickly, that needn't be the case. It might be that
+       this completing request did not get a result from the first DNS
+       server or even the first round of the whole DNS server pool. So it
+       could already be quite some time after we issued the DNS queries in
+       the first place. Without modifying c-ares, we cannot know exactly
+       where in its retry cycle we are. We could guess based on how much
+       time has gone by, but it does not really matter. Happy Eyeballs tells
+       us that, given usable information in hand, we simply do not want to
+       wait "too much longer" after we get a result.
+
+       We simply wait an additional amount of time equal to the default
+       c-ares query timeout. That is enough time for a typical parallel
+       response to arrive without being "too long". Even on a network
+       where one of the two types of queries is failing or timing out
+       constantly, this will usually mean we wait a total of the default
+       c-ares timeout (5 seconds) plus the round trip time for the successful
+       request, which seems bearable. The downside is that c-ares might race
+       with us to issue one more retry just before we give up, but it seems
+       better to "waste" that request instead of trying to guess the perfect
+       timeout to prevent it. After all, we do not even know where in the
+       c-ares retry cycle each request is.
+    */
+    ares->happy_eyeballs_dns_time = curlx_now();
+    Curl_expire(data, HAPPY_EYEBALLS_DNS_TIMEOUT,
+                EXPIRE_HAPPY_EYEBALLS_DNS);
+  }
+}
+
+#else
+/* c-ares 1.16.0 or later */
+
+/*
+ * async_ares_node2addr() converts an address list provided by c-ares
+ * to an internal libcurl compatible list.
+ */
+static struct Curl_addrinfo *
+async_ares_node2addr(struct ares_addrinfo_node *node)
+{
+  /* traverse the ares_addrinfo_node list */
+  struct ares_addrinfo_node *ai;
+  struct Curl_addrinfo *cafirst = NULL;
+  struct Curl_addrinfo *calast = NULL;
+  int error = 0;
+
+  for(ai = node; ai != NULL; ai = ai->ai_next) {
+    size_t ss_size;
+    struct Curl_addrinfo *ca;
+    /* ignore elements with unsupported address family, */
+    /* settle family-specific sockaddr structure size.  */
+    if(ai->ai_family == AF_INET)
+      ss_size = sizeof(struct sockaddr_in);
+#ifdef USE_IPV6
+    else if(ai->ai_family == AF_INET6)
+      ss_size = sizeof(struct sockaddr_in6);
+#endif
+    else
+      continue;
+
+    /* ignore elements without required address info */
+    if(!ai->ai_addr || !(ai->ai_addrlen > 0))
+      continue;
+
+    /* ignore elements with bogus address size */
+    if((size_t)ai->ai_addrlen < ss_size)
+      continue;
+
+    ca = malloc(sizeof(struct Curl_addrinfo) + ss_size);
+    if(!ca) {
+      error = EAI_MEMORY;
+      break;
+    }
+
+    /* copy each structure member individually, member ordering, */
+    /* size, or padding might be different for each platform.    */
+
+    ca->ai_flags     = ai->ai_flags;
+    ca->ai_family    = ai->ai_family;
+    ca->ai_socktype  = ai->ai_socktype;
+    ca->ai_protocol  = ai->ai_protocol;
+    ca->ai_addrlen   = (curl_socklen_t)ss_size;
+    ca->ai_addr      = NULL;
+    ca->ai_canonname = NULL;
+    ca->ai_next      = NULL;
+
+    ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
+    memcpy(ca->ai_addr, ai->ai_addr, ss_size);
+
+    /* if the return list is empty, this becomes the first element */
+    if(!cafirst)
+      cafirst = ca;
+
+    /* add this element last in the return list */
+    if(calast)
+      calast->ai_next = ca;
+    calast = ca;
+  }
+
+  /* if we failed, destroy the Curl_addrinfo list */
+  if(error) {
+    Curl_freeaddrinfo(cafirst);
+    cafirst = NULL;
+  }
+
+  return cafirst;
+}
+
+static void async_ares_addrinfo_cb(void *user_data, int status, int timeouts,
+                                   struct ares_addrinfo *result)
+{
+  struct Curl_easy *data = (struct Curl_easy *)user_data;
+  struct async_ares_ctx *ares = &data->state.async.ares;
+  (void)timeouts;
+  CURL_TRC_DNS(data, "asyn-ares: addrinfo callback, status=%d", status);
+  if(ARES_SUCCESS == status) {
+    ares->temp_ai = async_ares_node2addr(result->nodes);
+    ares->last_status = CURL_ASYNC_SUCCESS;
+    ares_freeaddrinfo(result);
+  }
+  ares->num_pending--;
+  CURL_TRC_DNS(data, "ares: addrinfo done, status=%d, pending=%d, "
+               "addr=%sfound",
+               status, ares->num_pending, ares->temp_ai ? "" : "not ");
+}
+
+#endif
+
+#ifdef USE_HTTPSRR
+static void async_ares_rr_done(void *user_data, ares_status_t status,
+                               size_t timeouts,
+                               const ares_dns_record_t *dnsrec)
+{
+  struct Curl_easy *data = user_data;
+  struct async_ares_ctx *ares = &data->state.async.ares;
+
+  (void)timeouts;
+  --ares->num_pending;
+  CURL_TRC_DNS(data, "ares: httpsrr done, status=%d, pending=%d, "
+               "dnsres=%sfound",
+               status, ares->num_pending,
+               (dnsrec &&
+                ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ANSWER)) ?
+                "" : "not ");
+  if((ARES_SUCCESS != status) || !dnsrec)
+    return;
+  ares->result = Curl_httpsrr_from_ares(data, dnsrec, &ares->hinfo);
+}
+#endif /* USE_HTTPSRR */
+
+/*
+ * Curl_async_getaddrinfo() - when using ares
+ *
+ * Returns name information about the given hostname and port number. If
+ * successful, the 'hostent' is returned and the fourth argument will point to
+ * memory we need to free after use. That memory *MUST* be freed with
+ * Curl_freeaddrinfo(), nothing else.
+ */
+struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data,
+                                             const char *hostname,
+                                             int port,
+                                             int ip_version,
+                                             int *waitp)
+{
+  struct async_ares_ctx *ares = &data->state.async.ares;
+  *waitp = 0; /* default to synchronous response */
+
+  if(async_ares_init_lazy(data))
+    return NULL;
+
+  data->state.async.done = FALSE;   /* not done */
+  data->state.async.dns = NULL;     /* clear */
+  data->state.async.port = port;
+  data->state.async.ip_version = ip_version;
+  data->state.async.hostname = strdup(hostname);
+  if(!data->state.async.hostname)
+    return NULL;
+
+  /* initial status - failed */
+  ares->last_status = ARES_ENOTFOUND;
+
+#ifdef HAVE_CARES_GETADDRINFO
+  {
+    struct ares_addrinfo_hints hints;
+    char service[12];
+    int pf = PF_INET;
+    memset(&hints, 0, sizeof(hints));
+#ifdef CURLRES_IPV6
+    if((ip_version != CURL_IPRESOLVE_V4) &&
+       Curl_ipv6works(data)) {
+      /* The stack seems to be IPv6-enabled */
+      if(ip_version == CURL_IPRESOLVE_V6)
+        pf = PF_INET6;
+      else
+        pf = PF_UNSPEC;
+    }
+#endif /* CURLRES_IPV6 */
+    CURL_TRC_DNS(data, "asyn-ares: fire off getaddrinfo for %s",
+                 (pf == PF_UNSPEC) ? "A+AAAA" :
+                 ((pf == PF_INET) ? "A" : "AAAA"));
+    hints.ai_family = pf;
+    hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP) ?
+      SOCK_STREAM : SOCK_DGRAM;
+    /* Since the service is a numerical one, set the hint flags
+     * accordingly to save a call to getservbyname in inside C-Ares
+     */
+    hints.ai_flags = ARES_AI_NUMERICSERV;
+    msnprintf(service, sizeof(service), "%d", port);
+    ares->num_pending = 1;
+    ares_getaddrinfo(ares->channel, data->state.async.hostname,
+                     service, &hints, async_ares_addrinfo_cb, data);
+  }
+#else
+
+#ifdef HAVE_CARES_IPV6
+  if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
+    /* The stack seems to be IPv6-enabled */
+    /* areschannel is already setup in the Curl_open() function */
+    CURL_TRC_DNS(data, "asyn-ares: fire off query for A");
+    ares_gethostbyname(ares->channel, hostname, PF_INET,
+                       async_ares_hostbyname_cb, data);
+    CURL_TRC_DNS(data, "asyn-ares: fire off query for AAAA");
+    ares->num_pending = 2;
+    ares_gethostbyname(ares->channel, data->state.async.hostname, PF_INET6,
+                       async_ares_hostbyname_cb, data);
+  }
+  else
+#endif
+  {
+    /* areschannel is already setup in the Curl_open() function */
+    CURL_TRC_DNS(data, "asyn-ares: fire off query for A");
+    ares->num_pending = 1;
+    ares_gethostbyname(ares->channel, data->state.async.hostname, PF_INET,
+                       async_ares_hostbyname_cb, data);
+  }
+#endif
+#ifdef USE_HTTPSRR
+  {
+    CURL_TRC_DNS(data, "asyn-ares: fire off query for HTTPSRR");
+    memset(&ares->hinfo, 0, sizeof(ares->hinfo));
+    ares->hinfo.port = -1;
+    ares->num_pending++; /* one more */
+    ares_query_dnsrec(ares->channel, data->state.async.hostname,
+                      ARES_CLASS_IN, ARES_REC_TYPE_HTTPS,
+                      async_ares_rr_done, data, NULL);
+  }
+#endif
+  *waitp = 1; /* expect asynchronous response */
+
+  return NULL; /* no struct yet */
+}
+
+/* Set what DNS server are is to use. This is called in 2 situations:
+ * 1. when the application does 'CURLOPT_DNS_SERVERS' and passing NULL
+ *    means any previous set value should be unset. Which means
+ *    we need to destroy and create the are channel anew, if there is one.
+ * 2. When we lazy init the ares channel and NULL means that there
+ *    are no preferences and we do not reset any existing channel. */
+static CURLcode async_ares_set_dns_servers(struct Curl_easy *data,
+                                           bool reset_on_null)
+{
+  struct async_ares_ctx *ares = &data->state.async.ares;
+  CURLcode result = CURLE_NOT_BUILT_IN;
+  const char *servers = data->set.str[STRING_DNS_SERVERS];
+  int ares_result = ARES_SUCCESS;
+
+#if defined(CURLDEBUG) && defined(HAVE_CARES_SERVERS_CSV)
+  if(getenv("CURL_DNS_SERVER"))
+    servers = getenv("CURL_DNS_SERVER");
+#endif
+
+  if(!servers) {
+    if(reset_on_null) {
+      Curl_async_destroy(data);
+    }
+    return CURLE_OK;
+  }
+
+#ifdef HAVE_CARES_SERVERS_CSV
+  /* if channel is not there, this is just a parameter check */
+  if(ares->channel)
+#ifdef HAVE_CARES_PORTS_CSV
+    ares_result = ares_set_servers_ports_csv(ares->channel, servers);
+#else
+    ares_result = ares_set_servers_csv(ares->channel, servers);
+#endif
+  switch(ares_result) {
+  case ARES_SUCCESS:
+    result = CURLE_OK;
+    break;
+  case ARES_ENOMEM:
+    result = CURLE_OUT_OF_MEMORY;
+    break;
+  case ARES_ENOTINITIALIZED:
+  case ARES_ENODATA:
+  case ARES_EBADSTR:
+  default:
+    DEBUGF(infof(data, "bad servers set"));
+    result = CURLE_BAD_FUNCTION_ARGUMENT;
+    break;
+  }
+#else /* too old c-ares version! */
+  (void)data;
+  (void)(ares_result);
+#endif
+  return result;
+}
+
+CURLcode Curl_async_ares_set_dns_servers(struct Curl_easy *data)
+{
+  return async_ares_set_dns_servers(data, TRUE);
+}
+
+CURLcode Curl_async_ares_set_dns_interface(struct Curl_easy *data)
+{
+#ifdef HAVE_CARES_LOCAL_DEV
+  struct async_ares_ctx *ares = &data->state.async.ares;
+  const char *interf = data->set.str[STRING_DNS_INTERFACE];
+
+  if(!interf)
+    interf = "";
+
+  /* if channel is not there, this is just a parameter check */
+  if(ares->channel)
+    ares_set_local_dev(ares->channel, interf);
+
+  return CURLE_OK;
+#else /* c-ares version too old! */
+  (void)data;
+  (void)interf;
+  return CURLE_NOT_BUILT_IN;
+#endif
+}
+
+CURLcode Curl_async_ares_set_dns_local_ip4(struct Curl_easy *data)
+{
+#ifdef HAVE_CARES_SET_LOCAL
+  struct async_ares_ctx *ares = &data->state.async.ares;
+  struct in_addr a4;
+  const char *local_ip4 = data->set.str[STRING_DNS_LOCAL_IP4];
+
+  if((!local_ip4) || (local_ip4[0] == 0)) {
+    a4.s_addr = 0; /* disabled: do not bind to a specific address */
+  }
+  else {
+    if(curlx_inet_pton(AF_INET, local_ip4, &a4) != 1) {
+      DEBUGF(infof(data, "bad DNS IPv4 address"));
+      return CURLE_BAD_FUNCTION_ARGUMENT;
+    }
+  }
+
+  /* if channel is not there yet, this is just a parameter check */
+  if(ares->channel)
+    ares_set_local_ip4(ares->channel, ntohl(a4.s_addr));
+
+  return CURLE_OK;
+#else /* c-ares version too old! */
+  (void)data;
+  (void)local_ip4;
+  return CURLE_NOT_BUILT_IN;
+#endif
+}
+
+CURLcode Curl_async_ares_set_dns_local_ip6(struct Curl_easy *data)
+{
+#if defined(HAVE_CARES_SET_LOCAL) && defined(USE_IPV6)
+  struct async_ares_ctx *ares = &data->state.async.ares;
+  unsigned char a6[INET6_ADDRSTRLEN];
+  const char *local_ip6 = data->set.str[STRING_DNS_LOCAL_IP6];
+
+  if((!local_ip6) || (local_ip6[0] == 0)) {
+    /* disabled: do not bind to a specific address */
+    memset(a6, 0, sizeof(a6));
+  }
+  else {
+    if(curlx_inet_pton(AF_INET6, local_ip6, a6) != 1) {
+      DEBUGF(infof(data, "bad DNS IPv6 address"));
+      return CURLE_BAD_FUNCTION_ARGUMENT;
+    }
+  }
+
+  /* if channel is not there, this is just a parameter check */
+  if(ares->channel)
+    ares_set_local_ip6(ares->channel, a6);
+
+  return CURLE_OK;
+#else /* c-ares version too old! */
+  (void)data;
+  return CURLE_NOT_BUILT_IN;
+#endif
+}
+
+#endif /* CURLRES_ARES */
diff --git a/extra/curl/curl-8.14.1/lib/asyn-base.c b/extra/curl/curl-8.14.1/lib/asyn-base.c
new file mode 100644
index 000000000000..ea89fba15c32
--- /dev/null
+++ b/extra/curl/curl-8.14.1/lib/asyn-base.c
@@ -0,0 +1,196 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef __VMS
+#include <in.h>
+#include <inet.h>
+#endif
+
+#ifdef USE_ARES
+#include <ares.h>
+#include <ares_version.h> /* really old c-ares did not include this by
+                             itself */
+#endif
+
+#include "urldata.h"
+#include "asyn.h"
+#include "sendf.h"
+#include "hostip.h"
+#include "hash.h"
+#include "multiif.h"
+#include "select.h"
+#include "share.h"
+#include "url.h"
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/***********************************************************************
+ * Only for builds using asynchronous name resolves
+ **********************************************************************/
+#ifdef CURLRES_ASYNCH
+
+
+#ifdef USE_ARES
+
+#if ARES_VERSION < 0x010600
+#error "requires c-ares 1.6.0 or newer"
+#endif
+
+/*
+ * Curl_ares_getsock() is called when the outside world (using
+ * curl_multi_fdset()) wants to get our fd_set setup and we are talking with
+ * ares. The caller must make sure that this function is only called when we
+ * have a working ares channel.
+ *
+ * Returns: sockets-in-use-bitmap
+ */
+
+int Curl_ares_getsock(struct Curl_easy *data,
+                      ares_channel channel,
+                      curl_socket_t *socks)
+{
+  struct timeval maxtime = { CURL_TIMEOUT_RESOLVE, 0 };
+  struct timeval timebuf;
+  int max = ares_getsock(channel,
+                         (ares_socket_t *)socks, MAX_SOCKSPEREASYHANDLE);
+  struct timeval *timeout = ares_timeout(channel, &maxtime, &timebuf);
+  timediff_t milli = curlx_tvtoms(timeout);
+  Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
+  return max;
+}
+
+/*
+ * Curl_ares_perform()
+ *
+ * 1) Ask ares what sockets it currently plays with, then
+ * 2) wait for the timeout period to check for action on ares' sockets.
+ * 3) tell ares to act on all the sockets marked as "with action"
+ *
+ * return number of sockets it worked on, or -1 on error
+ */
+int Curl_ares_perform(ares_channel channel,
+                      timediff_t timeout_ms)
+{
+  int nfds;
+  int bitmask;
+  ares_socket_t socks[ARES_GETSOCK_MAXNUM];
+  struct pollfd pfd[ARES_GETSOCK_MAXNUM];
+  int i;
+  int num = 0;
+
+  if(!channel)
+    return 0;
+
+  bitmask = ares_getsock(channel, socks, ARES_GETSOCK_MAXNUM);
+
+  for(i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
+    pfd[i].events = 0;
+    pfd[i].revents = 0;
+    if(ARES_GETSOCK_READABLE(bitmask, i)) {
+      pfd[i].fd = socks[i];
+      pfd[i].events |= POLLRDNORM|POLLIN;
+    }
+    if(ARES_GETSOCK_WRITABLE(bitmask, i)) {
+      pfd[i].fd = socks[i];
+      pfd[i].events |= POLLWRNORM|POLLOUT;
+    }
+    if(pfd[i].events)
+      num++;
+    else
+      break;
+  }
+
+  if(num) {
+    nfds = Curl_poll(pfd, (unsigned int)num, timeout_ms);
+    if(nfds < 0)
+      return -1;
+  }
+  else
+    nfds = 0;
+
+  if(!nfds)
+    /* Call ares_process() unconditionally here, even if we simply timed out
+       above, as otherwise the ares name resolve will not timeout! */
+    ares_process_fd(channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
+  else {
+    /* move through the descriptors and ask for processing on them */
+    for(i = 0; i < num; i++)
+      ares_process_fd(channel,
+                      (pfd[i].revents & (POLLRDNORM|POLLIN)) ?
+                      pfd[i].fd : ARES_SOCKET_BAD,
+                      (pfd[i].revents & (POLLWRNORM|POLLOUT)) ?
+                      pfd[i].fd : ARES_SOCKET_BAD);
+  }
+  return nfds;
+}
+
+#endif
+
+#endif /* CURLRES_ASYNCH */
+
+#ifdef USE_CURL_ASYNC
+
+#include "doh.h"
+
+void Curl_async_shutdown(struct Curl_easy *data)
+{
+#ifdef CURLRES_ARES
+  Curl_async_ares_shutdown(data);
+#endif
+#ifdef CURLRES_THREADED
+  Curl_async_thrdd_shutdown(data);
+#endif
+#ifndef CURL_DISABLE_DOH
+  Curl_doh_cleanup(data);
+#endif
+  Curl_safefree(data->state.async.hostname);
+}
+
+void Curl_async_destroy(struct Curl_easy *data)
+{
+#ifdef CURLRES_ARES
+  Curl_async_ares_destroy(data);
+#endif
+#ifdef CURLRES_THREADED
+  Curl_async_thrdd_destroy(data);
+#endif
+#ifndef CURL_DISABLE_DOH
+  Curl_doh_cleanup(data);
+#endif
+  Curl_safefree(data->state.async.hostname);
+}
+
+#endif /* USE_CURL_ASYNC */
diff --git a/extra/curl/curl-8.14.1/lib/asyn-thrdd.c b/extra/curl/curl-8.14.1/lib/asyn-thrdd.c
new file mode 100644
index 000000000000..9cd25dcd209c
--- /dev/null
+++ b/extra/curl/curl-8.14.1/lib/asyn-thrdd.c
@@ -0,0 +1,760 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+#include "socketpair.h"
+
+/***********************************************************************
+ * Only for threaded name resolves builds
+ **********************************************************************/
+#ifdef CURLRES_THREADED
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef __VMS
+#include <in.h>
+#include <inet.h>
+#endif
+
+#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)
+#  include <pthread.h>
+#endif
+
+#ifdef HAVE_GETADDRINFO
+#  define RESOLVER_ENOMEM  EAI_MEMORY  /* = WSA_NOT_ENOUGH_MEMORY on Windows */
+#else
+#  define RESOLVER_ENOMEM  SOCKENOMEM
+#endif
+
+#include "urldata.h"
+#include "sendf.h"
+#include "hostip.h"
+#include "hash.h"
+#include "share.h"
+#include "url.h"
+#include "multiif.h"
+#include "inet_ntop.h"
+#include "curl_threads.h"
+#include "strdup.h"
+
+#ifdef USE_ARES
+#include <ares.h>
+#ifdef USE_HTTPSRR
+#define USE_HTTPSRR_ARES  /* the combo */
+#endif
+#endif
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+
+/*
+ * Curl_async_global_init()
+ * Called from curl_global_init() to initialize global resolver environment.
+ * Does nothing here.
+ */
+int Curl_async_global_init(void)
+{
+#if defined(USE_ARES) && defined(CARES_HAVE_ARES_LIBRARY_INIT)
+  if(ares_library_init(ARES_LIB_INIT_ALL)) {
+    return CURLE_FAILED_INIT;
+  }
+#endif
+  return CURLE_OK;
+}
+
+/*
+ * Curl_async_global_cleanup()
+ * Called from curl_global_cleanup() to destroy global resolver environment.
+ * Does nothing here.
+ */
+void Curl_async_global_cleanup(void)
+{
+#if defined(USE_ARES) && defined(CARES_HAVE_ARES_LIBRARY_INIT)
+  ares_library_cleanup();
+#endif
+}
+
+static void async_thrdd_destroy(struct Curl_easy *);
+
+CURLcode Curl_async_get_impl(struct Curl_easy *data, void **impl)
+{
+  (void)data;
+  *impl = NULL;
+  return CURLE_OK;
+}
+
+/* Destroy context of threaded resolver */
+static void addr_ctx_destroy(struct async_thrdd_addr_ctx *addr_ctx)
+{
+  if(addr_ctx) {
+    DEBUGASSERT(!addr_ctx->ref_count);
+    Curl_mutex_destroy(&addr_ctx->mutx);
+    free(addr_ctx->hostname);
+    if(addr_ctx->res)
+      Curl_freeaddrinfo(addr_ctx->res);
+#ifndef CURL_DISABLE_SOCKETPAIR
+  /*
+   * close one end of the socket pair (may be done in resolver thread);
+   * the other end (for reading) is always closed in the parent thread.
+   */
+#ifndef USE_EVENTFD
+  if(addr_ctx->sock_pair[1] != CURL_SOCKET_BAD) {
+    wakeup_close(addr_ctx->sock_pair[1]);
+  }
+#endif
+#endif
+    free(addr_ctx);
+  }
+}
+
+/* Initialize context for threaded resolver */
+static struct async_thrdd_addr_ctx *
+addr_ctx_create(const char *hostname, int port,
+                const struct addrinfo *hints)
+{
+  struct async_thrdd_addr_ctx *addr_ctx = calloc(1, sizeof(*addr_ctx));
+  if(!addr_ctx)
+    return NULL;
+
+  addr_ctx->thread_hnd = curl_thread_t_null;
+  addr_ctx->port = port;
+#ifndef CURL_DISABLE_SOCKETPAIR
+  addr_ctx->sock_pair[0] = CURL_SOCKET_BAD;
+  addr_ctx->sock_pair[1] = CURL_SOCKET_BAD;
+#endif
+  addr_ctx->ref_count = 0;
+
+#ifdef HAVE_GETADDRINFO
+  DEBUGASSERT(hints);
+  addr_ctx->hints = *hints;
+#else
+  (void) hints;
+#endif
+
+  Curl_mutex_init(&addr_ctx->mutx);
+
+#ifndef CURL_DISABLE_SOCKETPAIR
+  /* create socket pair or pipe */
+  if(wakeup_create(addr_ctx->sock_pair, FALSE) < 0) {
+    addr_ctx->sock_pair[0] = CURL_SOCKET_BAD;
+    addr_ctx->sock_pair[1] = CURL_SOCKET_BAD;
+    goto err_exit;
+  }
+#endif
+  addr_ctx->sock_error = CURL_ASYNC_SUCCESS;
+
+  /* Copying hostname string because original can be destroyed by parent
+   * thread during gethostbyname execution.
+   */
+  addr_ctx->hostname = strdup(hostname);
+  if(!addr_ctx->hostname)
+    goto err_exit;
+
+  addr_ctx->ref_count = 1;
+  return addr_ctx;
+
+err_exit:
+#ifndef CURL_DISABLE_SOCKETPAIR
+  if(addr_ctx->sock_pair[0] != CURL_SOCKET_BAD) {
+    wakeup_close(addr_ctx->sock_pair[0]);
+    addr_ctx->sock_pair[0] = CURL_SOCKET_BAD;
+  }
+#endif
+  addr_ctx_destroy(addr_ctx);
+  return NULL;
+}
+
+#ifdef HAVE_GETADDRINFO
+
+/*
+ * getaddrinfo_thread() resolves a name and then exits.
+ *
+ * For builds without ARES, but with USE_IPV6, create a resolver thread
+ * and wait on it.
+ */
+static
+#if defined(CURL_WINDOWS_UWP) || defined(UNDER_CE)
+DWORD
+#else
+unsigned int
+#endif
+CURL_STDCALL getaddrinfo_thread(void *arg)
+{
+  struct async_thrdd_addr_ctx *addr_ctx = arg;
+  char service[12];
+  int rc;
+  bool all_gone;
+
+  msnprintf(service, sizeof(service), "%d", addr_ctx->port);
+
+  rc = Curl_getaddrinfo_ex(addr_ctx->hostname, service,
+                           &addr_ctx->hints, &addr_ctx->res);
+
+  if(rc) {
+    addr_ctx->sock_error = SOCKERRNO ? SOCKERRNO : rc;
+    if(addr_ctx->sock_error == 0)
+      addr_ctx->sock_error = RESOLVER_ENOMEM;
+  }
+  else {
+    Curl_addrinfo_set_port(addr_ctx->res, addr_ctx->port);
+  }
+
+  Curl_mutex_acquire(&addr_ctx->mutx);
+  if(addr_ctx->ref_count > 1) {
+    /* Someone still waiting on our results. */
+#ifndef CURL_DISABLE_SOCKETPAIR
+    if(addr_ctx->sock_pair[1] != CURL_SOCKET_BAD) {
+#ifdef USE_EVENTFD
+      const uint64_t buf[1] = { 1 };
+#else
+      const char buf[1] = { 1 };
+#endif
+      /* DNS has been resolved, signal client task */
+      if(wakeup_write(addr_ctx->sock_pair[1], buf, sizeof(buf)) < 0) {
+        /* update sock_erro to errno */
+        addr_ctx->sock_error = SOCKERRNO;
+      }
+    }
+#endif
+  }
+  /* thread gives up its reference to the shared data now. */
+  --addr_ctx->ref_count;
+  all_gone = !addr_ctx->ref_count;
+  Curl_mutex_release(&addr_ctx->mutx);
+  if(all_gone)
+    addr_ctx_destroy(addr_ctx);
+
+  return 0;
+}
+
+#else /* HAVE_GETADDRINFO */
+
+/*
+ * gethostbyname_thread() resolves a name and then exits.
+ */
+static
+#if defined(CURL_WINDOWS_UWP) || defined(UNDER_CE)
+DWORD
+#else
+unsigned int
+#endif
+CURL_STDCALL gethostbyname_thread(void *arg)
+{
+  struct async_thrdd_addr_ctx *addr_ctx = arg;
+  bool all_gone;
+
+  addr_ctx->res = Curl_ipv4_resolve_r(addr_ctx->hostname, addr_ctx->port);
+
+  if(!addr_ctx->res) {
+    addr_ctx->sock_error = SOCKERRNO;
+    if(addr_ctx->sock_error == 0)
+      addr_ctx->sock_error = RESOLVER_ENOMEM;
+  }
+
+  Curl_mutex_acquire(&addr_ctx->mutx);
+  /* thread gives up its reference to the shared data now. */
+  --addr_ctx->ref_count;
+  all_gone = !addr_ctx->ref_count;;
+  Curl_mutex_release(&addr_ctx->mutx);
+  if(all_gone)
+    addr_ctx_destroy(addr_ctx);
+
+  return 0;
+}
+
+#endif /* HAVE_GETADDRINFO */
+
+/*
+ * async_thrdd_destroy() cleans up async resolver data and thread handle.
+ */
+static void async_thrdd_destroy(struct Curl_easy *data)
+{
+  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
+  struct async_thrdd_addr_ctx *addr = thrdd->addr;
+#ifdef USE_HTTPSRR_ARES
+  if(thrdd->rr.channel) {
+    ares_destroy(thrdd->rr.channel);
+    thrdd->rr.channel = NULL;
+  }
+  Curl_httpsrr_cleanup(&thrdd->rr.hinfo);
+#endif
+
+  if(addr) {
+#ifndef CURL_DISABLE_SOCKETPAIR
+    curl_socket_t sock_rd = addr->sock_pair[0];
+#endif
+    bool done;
+
+    /* Release our reference to the data shared with the thread. */
+    Curl_mutex_acquire(&addr->mutx);
+    --addr->ref_count;
+    CURL_TRC_DNS(data, "resolve, destroy async data, shared ref=%d",
+                 addr->ref_count);
+    done = !addr->ref_count;
+    /* we give up our reference to `addr`, so NULL our pointer.
+     * coverity analyses this as being a potential unsynched write,
+     * assuming two calls to this function could be invoked concurrently.
+     * Which they never are, as the transfer's side runs single-threaded. */
+    thrdd->addr = NULL;
+    if(!done) {
+      /* thread is still running. Detach the thread while mutexed, it will
+       * trigger the cleanup when it releases its reference. */
+      Curl_thread_destroy(&addr->thread_hnd);
+    }
+    Curl_mutex_release(&addr->mutx);
+
+    if(done) {
+      /* thread has released its reference, join it and
+       * release the memory we shared with it. */
+      if(addr->thread_hnd != curl_thread_t_null)
+        Curl_thread_join(&addr->thread_hnd);
+      addr_ctx_destroy(addr);
+    }
+#ifndef CURL_DISABLE_SOCKETPAIR
+    /*
+     * ensure CURLMOPT_SOCKETFUNCTION fires CURL_POLL_REMOVE
+     * before the FD is invalidated to avoid EBADF on EPOLL_CTL_DEL
+     */
+    Curl_multi_will_close(data, sock_rd);
+    wakeup_close(sock_rd);
+#endif
+  }
+}
+
+#ifdef USE_HTTPSRR_ARES
+
+static void async_thrdd_rr_done(void *user_data, ares_status_t status,
+                                size_t timeouts,
+                                const ares_dns_record_t *dnsrec)
+{
+  struct Curl_easy *data = user_data;
+  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
+
+  (void)timeouts;
+  thrdd->rr.done = TRUE;
+  if((ARES_SUCCESS != status) || !dnsrec)
+    return;
+  thrdd->rr.result = Curl_httpsrr_from_ares(data, dnsrec, &thrdd->rr.hinfo);
+}
+
+static CURLcode async_rr_start(struct Curl_easy *data)
+{
+  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
+  int status;
+
+  DEBUGASSERT(!thrdd->rr.channel);
+  status = ares_init_options(&thrdd->rr.channel, NULL, 0);
+  if(status != ARES_SUCCESS) {
+    thrdd->rr.channel = NULL;
+    return CURLE_FAILED_INIT;
+  }
+
+  memset(&thrdd->rr.hinfo, 0, sizeof(thrdd->rr.hinfo));
+  thrdd->rr.hinfo.port = -1;
+  ares_query_dnsrec(thrdd->rr.channel,
+                    data->conn->host.name, ARES_CLASS_IN,
+                    ARES_REC_TYPE_HTTPS,
+                    async_thrdd_rr_done, data, NULL);
+  return CURLE_OK;
+}
+#endif
+
+/*
+ * async_thrdd_init() starts a new thread that performs the actual
+ * resolve. This function returns before the resolve is done.
+ *
+ * Returns FALSE in case of failure, otherwise TRUE.
+ */
+static bool async_thrdd_init(struct Curl_easy *data,
+                             const char *hostname, int port, int ip_version,
+                             const struct addrinfo *hints)
+{
+  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
+  struct async_thrdd_addr_ctx *addr_ctx;
+
+  /* !checksrc! disable ERRNOVAR 1 */
+  int err = ENOMEM;
+
+  if(thrdd->addr
+#ifdef USE_HTTPSRR_ARES
+     || thrdd->rr.channel
+#endif
+     ) {
+    CURL_TRC_DNS(data, "starting new resolve, with previous not cleaned up");
+    async_thrdd_destroy(data);
+    DEBUGASSERT(!thrdd->addr);
+#ifdef USE_HTTPSRR_ARES
+    DEBUGASSERT(!thrdd->rr.channel);
+#endif
+  }
+
+  data->state.async.dns = NULL;
+  data->state.async.done = FALSE;
+  data->state.async.port = port;
+  data->state.async.ip_version = ip_version;
+  data->state.async.hostname = strdup(hostname);
+  if(!data->state.async.hostname)
+    goto err_exit;
+
+  addr_ctx = addr_ctx_create(hostname, port, hints);
+  if(!addr_ctx)
+    goto err_exit;
+  thrdd->addr = addr_ctx;
+
+  Curl_mutex_acquire(&addr_ctx->mutx);
+  DEBUGASSERT(addr_ctx->ref_count == 1);
+  /* passing addr_ctx to the thread adds a reference */
+  addr_ctx->start = curlx_now();
+  ++addr_ctx->ref_count;
+#ifdef HAVE_GETADDRINFO
+  addr_ctx->thread_hnd = Curl_thread_create(getaddrinfo_thread, addr_ctx);
+#else
+  addr_ctx->thread_hnd = Curl_thread_create(gethostbyname_thread, addr_ctx);
+#endif
+  if(addr_ctx->thread_hnd == curl_thread_t_null) {
+    /* The thread never started, remove its reference that never happened. */
+    --addr_ctx->ref_count;
+    err = errno;
+    Curl_mutex_release(&addr_ctx->mutx);
+    goto err_exit;
+  }
+  Curl_mutex_release(&addr_ctx->mutx);
+
+#ifdef USE_HTTPSRR_ARES
+  if(async_rr_start(data))
+    infof(data, "Failed HTTPS RR operation");
+#endif
+  CURL_TRC_DNS(data, "resolve thread started for of %s:%d", hostname, port);
+  return TRUE;
+
+err_exit:
+  CURL_TRC_DNS(data, "resolve thread failed init: %d", err);
+  async_thrdd_destroy(data);
+  CURL_SETERRNO(err);
+  return FALSE;
+}
+
+/*
+ * 'entry' may be NULL and then no data is returned
+ */
+static CURLcode asyn_thrdd_await(struct Curl_easy *data,
+                                 struct async_thrdd_addr_ctx *addr_ctx,
+                                 struct Curl_dns_entry **entry)
+{
+  CURLcode result = CURLE_OK;
+
+  DEBUGASSERT(addr_ctx->thread_hnd != curl_thread_t_null);
+
+  CURL_TRC_DNS(data, "resolve, wait for thread to finish");
+  /* wait for the thread to resolve the name */
+  if(Curl_thread_join(&addr_ctx->thread_hnd)) {
+    if(entry)
+      result = Curl_async_is_resolved(data, entry);
+  }
+  else
+    DEBUGASSERT(0);
+
+  data->state.async.done = TRUE;
+  if(entry)
+    *entry = data->state.async.dns;
+
+  async_thrdd_destroy(data);
+  return result;
+}
+
+
+/*
+ * Until we gain a way to signal the resolver threads to stop early, we must
+ * simply wait for them and ignore their results.
+ */
+void Curl_async_thrdd_shutdown(struct Curl_easy *data)
+{
+  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
+
+  /* If we are still resolving, we must wait for the threads to fully clean up,
+     unfortunately. Otherwise, we can simply cancel to clean up any resolver
+     data. */
+  if(thrdd->addr && (thrdd->addr->thread_hnd != curl_thread_t_null) &&
+     !data->set.quick_exit)
+    (void)asyn_thrdd_await(data, thrdd->addr, NULL);
+  else
+    async_thrdd_destroy(data);
+}
+
+void Curl_async_thrdd_destroy(struct Curl_easy *data)
+{
+  Curl_async_thrdd_shutdown(data);
+}
+
+/*
+ * Curl_async_await()
+ *
+ * Waits for a resolve to finish. This function should be avoided since using
+ * this risk getting the multi interface to "hang".
+ *
+ * If 'entry' is non-NULL, make it point to the resolved dns entry
+ *
+ * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
+ * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
+ *
+ * This is the version for resolves-in-a-thread.
+ */
+CURLcode Curl_async_await(struct Curl_easy *data,
+                          struct Curl_dns_entry **entry)
+{
+  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
+  if(thrdd->addr)
+    return asyn_thrdd_await(data, thrdd->addr, entry);
+  return CURLE_FAILED_INIT;
+}
+
+/*
+ * Curl_async_is_resolved() is called repeatedly to check if a previous
+ * name resolve request has completed. It should also make sure to time-out if
+ * the operation seems to take too long.
+ */
+CURLcode Curl_async_is_resolved(struct Curl_easy *data,
+                                struct Curl_dns_entry **dns)
+{
+  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
+  bool done = FALSE;
+
+  DEBUGASSERT(dns);
+  *dns = NULL;
+
+  if(data->state.async.done) {
+    *dns = data->state.async.dns;
+    CURL_TRC_DNS(data, "threaded: is_resolved(), already done, dns=%sfound",
+                 *dns ? "" : "not ");
+    return CURLE_OK;
+  }
+
+#ifdef USE_HTTPSRR_ARES
+  /* best effort, ignore errors */
+  if(thrdd->rr.channel)
+    (void)Curl_ares_perform(thrdd->rr.channel, 0);
+#endif
+
+  DEBUGASSERT(thrdd->addr);
+  if(!thrdd->addr)
+    return CURLE_FAILED_INIT;
+
+  Curl_mutex_acquire(&thrdd->addr->mutx);
+  done = (thrdd->addr->ref_count == 1);
+  Curl_mutex_release(&thrdd->addr->mutx);
+
+  if(done) {
+    CURLcode result = CURLE_OK;
+
+    data->state.async.done = TRUE;
+    Curl_resolv_unlink(data, &data->state.async.dns);
+
+    if(thrdd->addr->res) {
+      data->state.async.dns =
+        Curl_dnscache_mk_entry(data, thrdd->addr->res,
+                               data->state.async.hostname, 0,
+                               data->state.async.port, FALSE);
+      thrdd->addr->res = NULL;
+      if(!data->state.async.dns)
+        result = CURLE_OUT_OF_MEMORY;
+
+#ifdef USE_HTTPSRR_ARES
+      if(thrdd->rr.channel) {
+        result = thrdd->rr.result;
+        if(!result) {
+          struct Curl_https_rrinfo *lhrr;
+          lhrr = Curl_httpsrr_dup_move(&thrdd->rr.hinfo);
+          if(!lhrr)
+            result = CURLE_OUT_OF_MEMORY;
+          else
+            data->state.async.dns->hinfo = lhrr;
+        }
+      }
+#endif
+      if(!result && data->state.async.dns)
+        result = Curl_dnscache_add(data, data->state.async.dns);
+    }
+
+    if(!result && !data->state.async.dns)
+      result = Curl_resolver_error(data);
+    if(result)
+      Curl_resolv_unlink(data, &data->state.async.dns);
+    *dns = data->state.async.dns;
+    CURL_TRC_DNS(data, "is_resolved() result=%d, dns=%sfound",
+                 result, *dns ? "" : "not ");
+    async_thrdd_destroy(data);
+    return result;
+  }
+  else {
+    /* poll for name lookup done with exponential backoff up to 250ms */
+    /* should be fine even if this converts to 32-bit */
+    timediff_t elapsed = curlx_timediff(curlx_now(),
+                                       data->progress.t_startsingle);
+    if(elapsed < 0)
+      elapsed = 0;
+
+    if(thrdd->addr->poll_interval == 0)
+      /* Start at 1ms poll interval */
+      thrdd->addr->poll_interval = 1;
+    else if(elapsed >= thrdd->addr->interval_end)
+      /* Back-off exponentially if last interval expired  */
+      thrdd->addr->poll_interval *= 2;
+
+    if(thrdd->addr->poll_interval > 250)
+      thrdd->addr->poll_interval = 250;
+
+    thrdd->addr->interval_end = elapsed + thrdd->addr->poll_interval;
+    Curl_expire(data, thrdd->addr->poll_interval, EXPIRE_ASYNC_NAME);
+    return CURLE_OK;
+  }
+}
+
+int Curl_async_getsock(struct Curl_easy *data, curl_socket_t *socks)
+{
+  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
+  int ret_val = 0;
+#if !defined(CURL_DISABLE_SOCKETPAIR) || defined(USE_HTTPSRR_ARES)
+  int socketi = 0;
+#else
+  (void)socks;
+#endif
+
+#ifdef USE_HTTPSRR_ARES
+  if(thrdd->rr.channel) {
+    ret_val = Curl_ares_getsock(data, thrdd->rr.channel, socks);
+    for(socketi = 0; socketi < (MAX_SOCKSPEREASYHANDLE - 1); socketi++)
+      if(!ARES_GETSOCK_READABLE(ret_val, socketi) &&
+         !ARES_GETSOCK_WRITABLE(ret_val, socketi))
+        break;
+  }
+#endif
+  if(!thrdd->addr)
+    return ret_val;
+
+#ifndef CURL_DISABLE_SOCKETPAIR
+  if(thrdd->addr) {
+    /* return read fd to client for polling the DNS resolution status */
+    socks[socketi] = thrdd->addr->sock_pair[0];
+    ret_val |= GETSOCK_READSOCK(socketi);
+  }
+  else
+#endif
+  {
+    timediff_t milli;
+    timediff_t ms = curlx_timediff(curlx_now(), thrdd->addr->start);
+    if(ms < 3)
+      milli = 0;
+    else if(ms <= 50)
+      milli = ms/3;
+    else if(ms <= 250)
+      milli = 50;
+    else
+      milli = 200;
+    Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
+  }
+
+  return ret_val;
+}
+
+#ifndef HAVE_GETADDRINFO
+/*
+ * Curl_async_getaddrinfo() - for platforms without getaddrinfo
+ */
+struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data,
+                                             const char *hostname,
+                                             int port,
+                                             int ip_version,
+                                             int *waitp)
+{
+  (void)ip_version;
+  *waitp = 0; /* default to synchronous response */
+
+  /* fire up a new resolver thread! */
+  if(async_thrdd_init(data, hostname, port, ip_version, NULL)) {
+    *waitp = 1; /* expect asynchronous response */
+    return NULL;
+  }
+
+  failf(data, "getaddrinfo() thread failed");
+
+  return NULL;
+}
+
+#else /* !HAVE_GETADDRINFO */
+
+/*
+ * Curl_async_getaddrinfo() - for getaddrinfo
+ */
+struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data,
+                                             const char *hostname,
+                                             int port,
+                                             int ip_version,
+                                             int *waitp)
+{
+  struct addrinfo hints;
+  int pf = PF_INET;
+  *waitp = 0; /* default to synchronous response */
+
+  CURL_TRC_DNS(data, "init threaded resolve of %s:%d", hostname, port);
+#ifdef CURLRES_IPV6
+  if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
+    /* The stack seems to be IPv6-enabled */
+    if(ip_version == CURL_IPRESOLVE_V6)
+      pf = PF_INET6;
+    else
+      pf = PF_UNSPEC;
+  }
+#else
+  (void)ip_version;
+#endif /* CURLRES_IPV6 */
+
+  memset(&hints, 0, sizeof(hints));
+  hints.ai_family = pf;
+  hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP) ?
+    SOCK_STREAM : SOCK_DGRAM;
+
+  /* fire up a new resolver thread! */
+  if(async_thrdd_init(data, hostname, port, ip_version, &hints)) {
+    *waitp = 1; /* expect asynchronous response */
+    return NULL;
+  }
+
+  failf(data, "getaddrinfo() thread failed to start");
+  return NULL;
+
+}
+
+#endif /* !HAVE_GETADDRINFO */
+
+#endif /* CURLRES_THREADED */
diff --git a/extra/curl/curl-8.12.1/lib/asyn.h b/extra/curl/curl-8.14.1/lib/asyn.h
similarity index 50%
rename from extra/curl/curl-8.12.1/lib/asyn.h
rename to extra/curl/curl-8.14.1/lib/asyn.h
index 5a21329cf36b..1cc7175bebc1 100644
--- a/extra/curl/curl-8.12.1/lib/asyn.h
+++ b/extra/curl/curl-8.14.1/lib/asyn.h
@@ -25,77 +25,23 @@
  ***************************************************************************/
 
 #include "curl_setup.h"
-#include "curl_addrinfo.h"
-#include "httpsrr.h"
 
-struct addrinfo;
-struct hostent;
 struct Curl_easy;
-struct connectdata;
 struct Curl_dns_entry;
 
-#ifdef CURLRES_THREADED
-#include "curl_threads.h"
-
-/* Data for synchronization between resolver thread and its parent */
-struct thread_sync_data {
-  curl_mutex_t *mtx;
-  char *hostname;        /* hostname to resolve, Curl_async.hostname
-                            duplicate */
-#ifndef CURL_DISABLE_SOCKETPAIR
-  curl_socket_t sock_pair[2]; /* eventfd/pipes/socket pair */
-#endif
-  struct Curl_addrinfo *res;
-#ifdef HAVE_GETADDRINFO
-  struct addrinfo hints;
-#endif
-  struct thread_data *td; /* for thread-self cleanup */
-  int port;
-  int sock_error;
-  bool done;
-};
-
-struct thread_data {
-  curl_thread_t thread_hnd;
-  unsigned int poll_interval;
-  timediff_t interval_end;
-  struct thread_sync_data tsd;
-#if defined(USE_HTTPSRR) && defined(USE_ARES)
-  struct Curl_https_rrinfo hinfo;
-  ares_channel channel;
-#endif
-};
-
-#elif defined(CURLRES_ARES) /* CURLRES_THREADED */
-
-struct thread_data {
-  int num_pending; /* number of outstanding c-ares requests */
-  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
-#ifdef USE_HTTPSRR
-  struct Curl_https_rrinfo hinfo;
-#endif
-  char hostname[1];
-};
+#ifdef CURLRES_ASYNCH
 
-#endif /* CURLRES_ARES */
+#include "curl_addrinfo.h"
+#include "httpsrr.h"
 
-#ifdef USE_ARES
-#include <ares.h>
+struct addrinfo;
+struct hostent;
+struct connectdata;
 
-/* for HTTPS RR purposes as well */
-int Curl_ares_getsock(struct Curl_easy *data,
-                      ares_channel channel,
-                      curl_socket_t *socks);
-int Curl_ares_perform(ares_channel channel,
-                      timediff_t timeout_ms);
+#if defined(CURLRES_ARES) && defined(CURLRES_THREADED)
+#error cannot have both CURLRES_ARES and CURLRES_THREADED defined
 #endif
 
-
 /*
  * This header defines all functions in the internal asynch resolver interface.
  * All asynch resolvers need to provide these functions.
@@ -104,85 +50,38 @@ int Curl_ares_perform(ares_channel channel,
  */
 
 /*
- * Curl_resolver_global_init()
+ * Curl_async_global_init()
  *
  * Called from curl_global_init() to initialize global resolver environment.
  * Returning anything else than CURLE_OK fails curl_global_init().
  */
-int Curl_resolver_global_init(void);
+int Curl_async_global_init(void);
 
 /*
- * Curl_resolver_global_cleanup()
+ * Curl_async_global_cleanup()
  * Called from curl_global_cleanup() to destroy global resolver environment.
  */
-void Curl_resolver_global_cleanup(void);
-
-/*
- * Curl_resolver_init()
- * Called from curl_easy_init() -> Curl_open() to initialize resolver
- * URL-state specific environment ('resolver' member of the UrlState
- * structure). Should fill the passed pointer by the initialized handler.
- * Returning anything else than CURLE_OK fails curl_easy_init() with the
- * correspondent code.
- */
-CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver);
-
-/*
- * Curl_resolver_cleanup()
- * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
- * URL-state specific environment ('resolver' member of the UrlState
- * structure). Should destroy the handler and free all resources connected to
- * it.
- */
-void Curl_resolver_cleanup(void *resolver);
-
-/*
- * Curl_resolver_duphandle()
- * Called from curl_easy_duphandle() to duplicate resolver URL-state specific
- * environment ('resolver' member of the UrlState structure). Should
- * duplicate the 'from' handle and pass the resulting handle to the 'to'
- * pointer. Returning anything else than CURLE_OK causes failed
- * curl_easy_duphandle() call.
- */
-CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to,
-                                 void *from);
+void Curl_async_global_cleanup(void);
 
 /*
- * Curl_resolver_cancel().
- *
- * It is called from inside other functions to cancel currently performing
- * resolver request. Should also free any temporary resources allocated to
- * perform a request. This never waits for resolver threads to complete.
- *
- * It is safe to call this when conn is in any state.
- */
-void Curl_resolver_cancel(struct Curl_easy *data);
-
-/*
- * Curl_resolver_kill().
- *
- * This acts like Curl_resolver_cancel() except it will block until any threads
- * associated with the resolver are complete. This never blocks for resolvers
- * that do not use threads. This is intended to be the "last chance" function
- * that cleans up an in-progress resolver completely (before its owner is about
- * to die).
- *
- * It is safe to call this when conn is in any state.
+ * Curl_async_get_impl()
+ * Get the resolver implementation instance (c-ares channel) or NULL
+ * for passing to application callback.
  */
-void Curl_resolver_kill(struct Curl_easy *data);
+CURLcode Curl_async_get_impl(struct Curl_easy *easy, void **impl);
 
-/* Curl_resolver_getsock()
+/* Curl_async_getsock()
  *
- * This function is called from the multi_getsock() function.  'sock' is a
+ * This function is called from the Curl_multi_getsock() function.  'sock' is a
  * pointer to an array to hold the file descriptors, with 'numsock' being the
  * size of that array (in number of entries). This function is supposed to
  * return bitmask indicating what file descriptors (referring to array indexes
  * in the 'sock' array) to wait for, read/write.
  */
-int Curl_resolver_getsock(struct Curl_easy *data, curl_socket_t *sock);
+int Curl_async_getsock(struct Curl_easy *data, curl_socket_t *sock);
 
 /*
- * Curl_resolver_is_resolved()
+ * Curl_async_is_resolved()
  *
  * Called repeatedly to check if a previous name resolve request has
  * completed. It should also make sure to time-out if the operation seems to
@@ -190,25 +89,25 @@ int Curl_resolver_getsock(struct Curl_easy *data, curl_socket_t *sock);
  *
  * Returns normal CURLcode errors.
  */
-CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
-                                   struct Curl_dns_entry **dns);
+CURLcode Curl_async_is_resolved(struct Curl_easy *data,
+                                struct Curl_dns_entry **dns);
 
 /*
- * Curl_resolver_wait_resolv()
+ * Curl_async_await()
  *
  * Waits for a resolve to finish. This function should be avoided since using
  * this risk getting the multi interface to "hang".
  *
- * If 'entry' is non-NULL, make it point to the resolved dns entry
+ * On return 'entry' is assigned the resolved dns (CURLE_OK or NULL otherwise.
  *
  * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
  * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
  */
-CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
-                                   struct Curl_dns_entry **dnsentry);
+CURLcode Curl_async_await(struct Curl_easy *data,
+                          struct Curl_dns_entry **dnsentry);
 
 /*
- * Curl_resolver_getaddrinfo() - when using this resolver
+ * Curl_async_getaddrinfo() - when using this resolver
  *
  * Returns name information about the given hostname and port number. If
  * successful, the 'hostent' is returned and the fourth argument will point to
@@ -218,29 +117,157 @@ CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
  * Each resolver backend must of course make sure to return data in the
  * correct format to comply with this.
  */
-struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
-                                                const char *hostname,
-                                                int port,
-                                                int *waitp);
+struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data,
+                                             const char *hostname,
+                                             int port,
+                                             int ip_version,
+                                             int *waitp);
+
+#ifdef USE_ARES
+/* common functions for c-ares and threaded resolver with HTTPSRR */
+#include <ares.h>
+
+int Curl_ares_getsock(struct Curl_easy *data,
+                      ares_channel channel,
+                      curl_socket_t *socks);
+int Curl_ares_perform(ares_channel channel,
+                      timediff_t timeout_ms);
+#endif
+
+#ifdef CURLRES_ARES
+/* async resolving implementation using c-ares alone */
+struct async_ares_ctx {
+  ares_channel channel;
+  int num_pending; /* number of outstanding c-ares requests */
+  struct Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares
+                                    parts */
+  int last_status;
+  CURLcode result; /* CURLE_OK or error handling response */
+#ifndef HAVE_CARES_GETADDRINFO
+  struct curltime happy_eyeballs_dns_time; /* when this timer started, or 0 */
+#endif
+#ifdef USE_HTTPSRR
+  struct Curl_https_rrinfo hinfo;
+#endif
+};
+
+void Curl_async_ares_shutdown(struct Curl_easy *data);
+void Curl_async_ares_destroy(struct Curl_easy *data);
+
+/* Set the DNS server to use by ares, from `data` settings. */
+CURLcode Curl_async_ares_set_dns_servers(struct Curl_easy *data);
+
+/* Set the DNS interfacer to use by ares, from `data` settings. */
+CURLcode Curl_async_ares_set_dns_interface(struct Curl_easy *data);
+
+/* Set the local ipv4 address to use by ares, from `data` settings. */
+CURLcode Curl_async_ares_set_dns_local_ip4(struct Curl_easy *data);
+
+/* Set the local ipv6 address to use by ares, from `data` settings. */
+CURLcode Curl_async_ares_set_dns_local_ip6(struct Curl_easy *data);
+
+#endif /* CURLRES_ARES */
+
+#ifdef CURLRES_THREADED
+/* async resolving implementation using POSIX threads */
+#include "curl_threads.h"
+
+/* Context for threaded address resolver */
+struct async_thrdd_addr_ctx {
+  curl_thread_t thread_hnd;
+  char *hostname;        /* hostname to resolve, Curl_async.hostname
+                            duplicate */
+  curl_mutex_t mutx;
+#ifndef CURL_DISABLE_SOCKETPAIR
+  curl_socket_t sock_pair[2]; /* eventfd/pipes/socket pair */
+#endif
+  struct Curl_addrinfo *res;
+#ifdef HAVE_GETADDRINFO
+  struct addrinfo hints;
+#endif
+  struct curltime start;
+  timediff_t interval_end;
+  unsigned int poll_interval;
+  int port;
+  int sock_error;
+  int ref_count;
+};
+
+/* Context for threaded resolver */
+struct async_thrdd_ctx {
+  /* `addr` is a pointer since this memory is shared with a started
+   * thread. Since threads cannot be killed, we use reference counting
+   * so that we can "release" our pointer to this memory while the
+   * thread is still running. */
+  struct async_thrdd_addr_ctx *addr;
+#if defined(USE_HTTPSRR) && defined(USE_ARES)
+  struct {
+    ares_channel channel;
+    struct Curl_https_rrinfo hinfo;
+    CURLcode result;
+    BIT(done);
+  } rr;
+#endif
+};
+
+void Curl_async_thrdd_shutdown(struct Curl_easy *data);
+void Curl_async_thrdd_destroy(struct Curl_easy *data);
+
+#endif /* CURLRES_THREADED */
+
+#ifndef CURL_DISABLE_DOH
+struct doh_probes;
+#endif
+
+#else /* CURLRES_ASYNCH */
 
-#ifndef CURLRES_ASYNCH
 /* convert these functions if an asynch resolver is not used */
-#define Curl_resolver_cancel(x) Curl_nop_stmt
-#define Curl_resolver_kill(x) Curl_nop_stmt
-#define Curl_resolver_is_resolved(x,y) CURLE_COULDNT_RESOLVE_HOST
-#define Curl_resolver_wait_resolv(x,y) CURLE_COULDNT_RESOLVE_HOST
-#define Curl_resolver_duphandle(x,y,z) CURLE_OK
-#define Curl_resolver_init(x,y) CURLE_OK
-#define Curl_resolver_global_init() CURLE_OK
-#define Curl_resolver_global_cleanup() Curl_nop_stmt
-#define Curl_resolver_cleanup(x) Curl_nop_stmt
+#define Curl_async_get_impl(x,y)    (*(y) = NULL, CURLE_OK)
+#define Curl_async_is_resolved(x,y) CURLE_COULDNT_RESOLVE_HOST
+#define Curl_async_await(x,y) CURLE_COULDNT_RESOLVE_HOST
+#define Curl_async_global_init() CURLE_OK
+#define Curl_async_global_cleanup() Curl_nop_stmt
+
+#endif /* !CURLRES_ASYNCH */
+
+#if defined(CURLRES_ASYNCH) || !defined(CURL_DISABLE_DOH)
+#define USE_CURL_ASYNC
 #endif
 
-#ifdef CURLRES_ASYNCH
-#define Curl_resolver_asynch() 1
-#else
-#define Curl_resolver_asynch() 0
+#ifdef USE_CURL_ASYNC
+struct Curl_async {
+#ifdef CURLRES_ARES /*  */
+  struct async_ares_ctx ares;
+#elif defined(CURLRES_THREADED)
+  struct async_thrdd_ctx thrdd;
 #endif
+#ifndef CURL_DISABLE_DOH
+  struct doh_probes *doh; /* DoH specific data for this request */
+#endif
+  struct Curl_dns_entry *dns; /* result of resolving on success */
+  char *hostname; /* copy of the params resolv started with */
+  int port;
+  int ip_version;
+  BIT(done);
+};
+
+/*
+ * Curl_async_shutdown().
+ *
+ * This shuts down all ongoing operations.
+ */
+void Curl_async_shutdown(struct Curl_easy *data);
+
+/*
+ * Curl_async_destroy().
+ *
+ * This frees the resources of any async resolve.
+ */
+void Curl_async_destroy(struct Curl_easy *data);
+#else /* !USE_CURL_ASYNC */
+#define Curl_async_shutdown(x) Curl_nop_stmt
+#define Curl_async_destroy(x) Curl_nop_stmt
+#endif /* USE_CURL_ASYNC */
 
 
 /********** end of generic resolver interface functions *****************/
diff --git a/extra/curl/curl-8.12.1/lib/bufq.c b/extra/curl/curl-8.14.1/lib/bufq.c
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/bufq.c
rename to extra/curl/curl-8.14.1/lib/bufq.c
diff --git a/extra/curl/curl-8.12.1/lib/bufq.h b/extra/curl/curl-8.14.1/lib/bufq.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/bufq.h
rename to extra/curl/curl-8.14.1/lib/bufq.h
diff --git a/extra/curl/curl-8.12.1/lib/bufref.c b/extra/curl/curl-8.14.1/lib/bufref.c
similarity index 98%
rename from extra/curl/curl-8.12.1/lib/bufref.c
rename to extra/curl/curl-8.14.1/lib/bufref.c
index f048b5701199..ac0612071d6c 100644
--- a/extra/curl/curl-8.12.1/lib/bufref.c
+++ b/extra/curl/curl-8.14.1/lib/bufref.c
@@ -30,7 +30,9 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
+#ifdef DEBUGBUILD
 #define SIGNATURE 0x5c48e9b2    /* Random pattern. */
+#endif
 
 /*
  * Init a bufref struct.
@@ -59,7 +61,7 @@ void Curl_bufref_free(struct bufref *br)
   DEBUGASSERT(br->ptr || !br->len);
 
   if(br->ptr && br->dtor)
-    br->dtor((void *) br->ptr);
+    br->dtor(CURL_UNCONST(br->ptr));
 
   br->dtor = NULL;
   br->ptr = NULL;
diff --git a/extra/curl/curl-8.12.1/lib/bufref.h b/extra/curl/curl-8.14.1/lib/bufref.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/bufref.h
rename to extra/curl/curl-8.14.1/lib/bufref.h
diff --git a/extra/curl/curl-8.12.1/lib/cf-h1-proxy.c b/extra/curl/curl-8.14.1/lib/cf-h1-proxy.c
similarity index 94%
rename from extra/curl/curl-8.12.1/lib/cf-h1-proxy.c
rename to extra/curl/curl-8.14.1/lib/cf-h1-proxy.c
index 9b75d086e5fc..df6f575454a2 100644
--- a/extra/curl/curl-8.12.1/lib/cf-h1-proxy.c
+++ b/extra/curl/curl-8.14.1/lib/cf-h1-proxy.c
@@ -28,7 +28,7 @@
 
 #include <curl/curl.h>
 #include "urldata.h"
-#include "dynbuf.h"
+#include "curlx/dynbuf.h"
 #include "sendf.h"
 #include "http.h"
 #include "http1.h"
@@ -40,10 +40,11 @@
 #include "cf-h1-proxy.h"
 #include "connect.h"
 #include "curl_trc.h"
-#include "curlx.h"
+#include "strcase.h"
 #include "vtls/vtls.h"
 #include "transfer.h"
 #include "multiif.h"
+#include "curlx/strparse.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -96,8 +97,8 @@ static CURLcode tunnel_reinit(struct Curl_cfilter *cf,
   (void)data;
   (void)cf;
   DEBUGASSERT(ts);
-  Curl_dyn_reset(&ts->rcvbuf);
-  Curl_dyn_reset(&ts->request_data);
+  curlx_dyn_reset(&ts->rcvbuf);
+  curlx_dyn_reset(&ts->request_data);
   ts->tunnel_state = H1_TUNNEL_INIT;
   ts->keepon = KEEPON_CONNECT;
   ts->cl = 0;
@@ -122,8 +123,8 @@ static CURLcode tunnel_init(struct Curl_cfilter *cf,
 
   infof(data, "allocate connect buffer");
 
-  Curl_dyn_init(&ts->rcvbuf, DYN_PROXY_CONNECT_HEADERS);
-  Curl_dyn_init(&ts->request_data, DYN_HTTP_REQUEST);
+  curlx_dyn_init(&ts->rcvbuf, DYN_PROXY_CONNECT_HEADERS);
+  curlx_dyn_init(&ts->request_data, DYN_HTTP_REQUEST);
   Curl_httpchunk_init(data, &ts->ch, TRUE);
 
   *pts =  ts;
@@ -149,7 +150,7 @@ static void h1_tunnel_go_state(struct Curl_cfilter *cf,
     CURL_TRC_CF(data, cf, "new tunnel state 'connect'");
     ts->tunnel_state = H1_TUNNEL_CONNECT;
     ts->keepon = KEEPON_CONNECT;
-    Curl_dyn_reset(&ts->rcvbuf);
+    curlx_dyn_reset(&ts->rcvbuf);
     break;
 
   case H1_TUNNEL_RECEIVE:
@@ -172,8 +173,8 @@ static void h1_tunnel_go_state(struct Curl_cfilter *cf,
     if(new_state == H1_TUNNEL_FAILED)
       CURL_TRC_CF(data, cf, "new tunnel state 'failed'");
     ts->tunnel_state = new_state;
-    Curl_dyn_reset(&ts->rcvbuf);
-    Curl_dyn_reset(&ts->request_data);
+    curlx_dyn_reset(&ts->rcvbuf);
+    curlx_dyn_reset(&ts->request_data);
     /* restore the protocol pointer */
     data->info.httpcode = 0; /* clear it as it might've been used for the
                                 proxy */
@@ -192,8 +193,8 @@ static void tunnel_free(struct Curl_cfilter *cf,
     struct h1_tunnel_state *ts = cf->ctx;
     if(ts) {
       h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data);
-      Curl_dyn_free(&ts->rcvbuf);
-      Curl_dyn_free(&ts->request_data);
+      curlx_dyn_free(&ts->rcvbuf);
+      curlx_dyn_free(&ts->request_data);
       Curl_httpchunk_free(data, &ts->ch);
       free(ts);
       cf->ctx = NULL;
@@ -225,7 +226,7 @@ static CURLcode start_CONNECT(struct Curl_cfilter *cf,
 
   infof(data, "Establish HTTP proxy tunnel to %s", req->authority);
 
-  Curl_dyn_reset(&ts->request_data);
+  curlx_dyn_reset(&ts->request_data);
   ts->nsent = 0;
   ts->headerlines = 0;
   http_minor = (cf->conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? 0 : 1;
@@ -247,8 +248,8 @@ static CURLcode send_CONNECT(struct Curl_cfilter *cf,
                              struct h1_tunnel_state *ts,
                              bool *done)
 {
-  char *buf = Curl_dyn_ptr(&ts->request_data);
-  size_t request_len = Curl_dyn_len(&ts->request_data);
+  char *buf = curlx_dyn_ptr(&ts->request_data);
+  size_t request_len = curlx_dyn_len(&ts->request_data);
   size_t blen = request_len;
   CURLcode result = CURLE_OK;
   ssize_t nwritten;
@@ -314,8 +315,11 @@ static CURLcode on_resp_header(struct Curl_cfilter *cf,
             k->httpcode);
     }
     else {
-      (void)curlx_strtoofft(header + strlen("Content-Length:"),
-                            NULL, 10, &ts->cl);
+      const char *p = header + strlen("Content-Length:");
+      if(curlx_str_numblanks(&p, &ts->cl)) {
+        failf(data, "Unsupported Content-Length value");
+        return CURLE_WEIRD_SERVER_REPLY;
+      }
     }
   }
   else if(Curl_compareheader(header,
@@ -440,7 +444,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
       continue;
     }
 
-    if(Curl_dyn_addn(&ts->rcvbuf, &byte, 1)) {
+    if(curlx_dyn_addn(&ts->rcvbuf, &byte, 1)) {
       failf(data, "CONNECT response too large");
       return CURLE_RECV_ERROR;
     }
@@ -450,8 +454,8 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
       continue;
 
     ts->headerlines++;
-    linep = Curl_dyn_ptr(&ts->rcvbuf);
-    line_len = Curl_dyn_len(&ts->rcvbuf); /* amount of bytes in this line */
+    linep = curlx_dyn_ptr(&ts->rcvbuf);
+    line_len = curlx_dyn_len(&ts->rcvbuf); /* amount of bytes in this line */
 
     /* output debug if that is requested */
     Curl_debug(data, CURLINFO_HEADER_IN, linep, line_len);
@@ -508,7 +512,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
     if(result)
       return result;
 
-    Curl_dyn_reset(&ts->rcvbuf);
+    curlx_dyn_reset(&ts->rcvbuf);
   } /* while there is buffer left and loop is requested */
 
   if(error)
@@ -597,7 +601,7 @@ static CURLcode H1_CONNECT(struct Curl_cfilter *cf,
           infof(data, "Connect me again please");
           Curl_conn_cf_close(cf, data);
           connkeep(conn, "HTTP proxy CONNECT");
-          result = Curl_conn_cf_connect(cf->next, data, FALSE, &done);
+          result = Curl_conn_cf_connect(cf->next, data, &done);
           goto out;
         }
         else {
@@ -637,7 +641,7 @@ static CURLcode H1_CONNECT(struct Curl_cfilter *cf,
 
 static CURLcode cf_h1_proxy_connect(struct Curl_cfilter *cf,
                                     struct Curl_easy *data,
-                                    bool blocking, bool *done)
+                                    bool *done)
 {
   CURLcode result;
   struct h1_tunnel_state *ts = cf->ctx;
@@ -648,7 +652,7 @@ static CURLcode cf_h1_proxy_connect(struct Curl_cfilter *cf,
   }
 
   CURL_TRC_CF(data, cf, "connect");
-  result = cf->next->cft->do_connect(cf->next, data, blocking, done);
+  result = cf->next->cft->do_connect(cf->next, data, done);
   if(result || !*done)
     return result;
 
diff --git a/extra/curl/curl-8.12.1/lib/cf-h1-proxy.h b/extra/curl/curl-8.14.1/lib/cf-h1-proxy.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/cf-h1-proxy.h
rename to extra/curl/curl-8.14.1/lib/cf-h1-proxy.h
diff --git a/extra/curl/curl-8.12.1/lib/cf-h2-proxy.c b/extra/curl/curl-8.14.1/lib/cf-h2-proxy.c
similarity index 99%
rename from extra/curl/curl-8.12.1/lib/cf-h2-proxy.c
rename to extra/curl/curl-8.14.1/lib/cf-h2-proxy.c
index d8b91280c291..d3bc4b07680e 100644
--- a/extra/curl/curl-8.12.1/lib/cf-h2-proxy.c
+++ b/extra/curl/curl-8.14.1/lib/cf-h2-proxy.c
@@ -32,7 +32,7 @@
 #include "connect.h"
 #include "curl_trc.h"
 #include "bufq.h"
-#include "dynbuf.h"
+#include "curlx/dynbuf.h"
 #include "dynhds.h"
 #include "http1.h"
 #include "http2.h"
@@ -619,7 +619,7 @@ static int proxy_h2_fr_print(const nghttp2_frame *frame,
                        frame->hd.flags & NGHTTP2_FLAG_ACK);
     case NGHTTP2_GOAWAY: {
       char scratch[128];
-      size_t s_len = sizeof(scratch)/sizeof(scratch[0]);
+      size_t s_len = CURL_ARRAYSIZE(scratch);
       size_t len = (frame->goaway.opaque_data_len < s_len) ?
         frame->goaway.opaque_data_len : s_len-1;
       if(len)
@@ -1090,7 +1090,7 @@ static CURLcode H2_CONNECT(struct Curl_cfilter *cf,
 
 static CURLcode cf_h2_proxy_connect(struct Curl_cfilter *cf,
                                     struct Curl_easy *data,
-                                    bool blocking, bool *done)
+                                    bool *done)
 {
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
   CURLcode result = CURLE_OK;
@@ -1105,7 +1105,7 @@ static CURLcode cf_h2_proxy_connect(struct Curl_cfilter *cf,
 
   /* Connect the lower filters first */
   if(!cf->next->connected) {
-    result = Curl_conn_cf_connect(cf->next, data, blocking, done);
+    result = Curl_conn_cf_connect(cf->next, data, done);
     if(result || !*done)
       return result;
   }
diff --git a/extra/curl/curl-8.12.1/lib/cf-h2-proxy.h b/extra/curl/curl-8.14.1/lib/cf-h2-proxy.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/cf-h2-proxy.h
rename to extra/curl/curl-8.14.1/lib/cf-h2-proxy.h
diff --git a/extra/curl/curl-8.12.1/lib/cf-haproxy.c b/extra/curl/curl-8.14.1/lib/cf-haproxy.c
similarity index 87%
rename from extra/curl/curl-8.12.1/lib/cf-haproxy.c
rename to extra/curl/curl-8.14.1/lib/cf-haproxy.c
index ae2402f22411..7bc12dbbb15e 100644
--- a/extra/curl/curl-8.12.1/lib/cf-haproxy.c
+++ b/extra/curl/curl-8.14.1/lib/cf-haproxy.c
@@ -54,13 +54,13 @@ static void cf_haproxy_ctx_reset(struct cf_haproxy_ctx *ctx)
 {
   DEBUGASSERT(ctx);
   ctx->state = HAPROXY_INIT;
-  Curl_dyn_reset(&ctx->data_out);
+  curlx_dyn_reset(&ctx->data_out);
 }
 
 static void cf_haproxy_ctx_free(struct cf_haproxy_ctx *ctx)
 {
   if(ctx) {
-    Curl_dyn_free(&ctx->data_out);
+    curlx_dyn_free(&ctx->data_out);
     free(ctx);
   }
 }
@@ -79,7 +79,7 @@ static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter*cf,
 #ifdef USE_UNIX_SOCKETS
   if(cf->conn->unix_domain_socket)
     /* the buffer is large enough to hold this! */
-    result = Curl_dyn_addn(&ctx->data_out, STRCONST("PROXY UNKNOWN\r\n"));
+    result = curlx_dyn_addn(&ctx->data_out, STRCONST("PROXY UNKNOWN\r\n"));
   else {
 #endif /* USE_UNIX_SOCKETS */
   result = Curl_conn_cf_get_ip_info(cf->next, data, &is_ipv6, &ipquad);
@@ -92,10 +92,10 @@ static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter*cf,
   else
     client_ip = ipquad.local_ip;
 
-  result = Curl_dyn_addf(&ctx->data_out, "PROXY %s %s %s %i %i\r\n",
-                         is_ipv6 ? "TCP6" : "TCP4",
-                         client_ip, ipquad.remote_ip,
-                         ipquad.local_port, ipquad.remote_port);
+  result = curlx_dyn_addf(&ctx->data_out, "PROXY %s %s %s %i %i\r\n",
+                          is_ipv6 ? "TCP6" : "TCP4",
+                          client_ip, ipquad.remote_ip,
+                          ipquad.local_port, ipquad.remote_port);
 
 #ifdef USE_UNIX_SOCKETS
   }
@@ -105,7 +105,7 @@ static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter*cf,
 
 static CURLcode cf_haproxy_connect(struct Curl_cfilter *cf,
                                    struct Curl_easy *data,
-                                   bool blocking, bool *done)
+                                   bool *done)
 {
   struct cf_haproxy_ctx *ctx = cf->ctx;
   CURLcode result;
@@ -117,7 +117,7 @@ static CURLcode cf_haproxy_connect(struct Curl_cfilter *cf,
     return CURLE_OK;
   }
 
-  result = cf->next->cft->do_connect(cf->next, data, blocking, done);
+  result = cf->next->cft->do_connect(cf->next, data, done);
   if(result || !*done)
     return result;
 
@@ -129,11 +129,11 @@ static CURLcode cf_haproxy_connect(struct Curl_cfilter *cf,
     ctx->state = HAPROXY_SEND;
     FALLTHROUGH();
   case HAPROXY_SEND:
-    len = Curl_dyn_len(&ctx->data_out);
+    len = curlx_dyn_len(&ctx->data_out);
     if(len > 0) {
       ssize_t nwritten;
       nwritten = Curl_conn_cf_send(cf->next, data,
-                                   Curl_dyn_ptr(&ctx->data_out), len, FALSE,
+                                   curlx_dyn_ptr(&ctx->data_out), len, FALSE,
                                    &result);
       if(nwritten < 0) {
         if(result != CURLE_AGAIN)
@@ -141,8 +141,8 @@ static CURLcode cf_haproxy_connect(struct Curl_cfilter *cf,
         result = CURLE_OK;
         nwritten = 0;
       }
-      Curl_dyn_tail(&ctx->data_out, len - (size_t)nwritten);
-      if(Curl_dyn_len(&ctx->data_out) > 0) {
+      curlx_dyn_tail(&ctx->data_out, len - (size_t)nwritten);
+      if(curlx_dyn_len(&ctx->data_out) > 0) {
         result = CURLE_OK;
         goto out;
       }
@@ -150,7 +150,7 @@ static CURLcode cf_haproxy_connect(struct Curl_cfilter *cf,
     ctx->state = HAPROXY_DONE;
     FALLTHROUGH();
   default:
-    Curl_dyn_free(&ctx->data_out);
+    curlx_dyn_free(&ctx->data_out);
     break;
   }
 
@@ -222,7 +222,7 @@ static CURLcode cf_haproxy_create(struct Curl_cfilter **pcf,
     goto out;
   }
   ctx->state = HAPROXY_INIT;
-  Curl_dyn_init(&ctx->data_out, DYN_HAXPROXY);
+  curlx_dyn_init(&ctx->data_out, DYN_HAXPROXY);
 
   result = Curl_cf_create(&cf, &Curl_cft_haproxy, ctx);
   if(result)
diff --git a/extra/curl/curl-8.12.1/lib/cf-haproxy.h b/extra/curl/curl-8.14.1/lib/cf-haproxy.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/cf-haproxy.h
rename to extra/curl/curl-8.14.1/lib/cf-haproxy.h
diff --git a/extra/curl/curl-8.12.1/lib/cf-https-connect.c b/extra/curl/curl-8.14.1/lib/cf-https-connect.c
similarity index 80%
rename from extra/curl/curl-8.12.1/lib/cf-https-connect.c
rename to extra/curl/curl-8.14.1/lib/cf-https-connect.c
index f073647e558e..cd0d226efdad 100644
--- a/extra/curl/curl-8.12.1/lib/cf-https-connect.c
+++ b/extra/curl/curl-8.14.1/lib/cf-https-connect.c
@@ -113,7 +113,6 @@ static CURLcode cf_hc_baller_cntrl(struct cf_hc_baller *b,
 
 struct cf_hc_ctx {
   cf_hc_state state;
-  const struct Curl_dns_entry *remotehost;
   struct curltime started;  /* when connect started */
   CURLcode result;          /* overall result */
   struct cf_hc_baller ballers[2];
@@ -147,11 +146,10 @@ static void cf_hc_baller_init(struct cf_hc_baller *b,
                               struct Curl_easy *data,
                               int transport)
 {
-  struct cf_hc_ctx *ctx = cf->ctx;
   struct Curl_cfilter *save = cf->next;
 
   cf->next = NULL;
-  b->started = Curl_now();
+  b->started = curlx_now();
   switch(b->alpn_id) {
   case ALPN_h3:
     transport = TRNSPRT_QUIC;
@@ -161,8 +159,8 @@ static void cf_hc_baller_init(struct cf_hc_baller *b,
   }
 
   if(!b->result)
-    b->result = Curl_cf_setup_insert_after(cf, data, ctx->remotehost,
-                                           transport, CURL_CF_SSL_ENABLE);
+    b->result = Curl_cf_setup_insert_after(cf, data, transport,
+                                           CURL_CF_SSL_ENABLE);
   b->cf = cf->next;
   cf->next = save;
 }
@@ -175,7 +173,7 @@ static CURLcode cf_hc_baller_connect(struct cf_hc_baller *b,
   struct Curl_cfilter *save = cf->next;
 
   cf->next = b->cf;
-  b->result = Curl_conn_cf_connect(cf->next, data, FALSE, done);
+  b->result = Curl_conn_cf_connect(cf->next, data, done);
   b->cf = cf->next; /* it might mutate */
   cf->next = save;
   return b->result;
@@ -192,7 +190,7 @@ static void cf_hc_reset(struct Curl_cfilter *cf, struct Curl_easy *data)
     ctx->state = CF_HC_INIT;
     ctx->result = CURLE_OK;
     ctx->hard_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout;
-    ctx->soft_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout / 2;
+    ctx->soft_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout / 4;
   }
 }
 
@@ -213,11 +211,12 @@ static CURLcode baller_connected(struct Curl_cfilter *cf,
   reply_ms = cf_hc_baller_reply_ms(winner, data);
   if(reply_ms >= 0)
     CURL_TRC_CF(data, cf, "connect+handshake %s: %dms, 1st data: %dms",
-                winner->name, (int)Curl_timediff(Curl_now(), winner->started),
-                reply_ms);
+                winner->name, (int)curlx_timediff(curlx_now(),
+                                                  winner->started), reply_ms);
   else
     CURL_TRC_CF(data, cf, "deferred handshake %s: %dms",
-                winner->name, (int)Curl_timediff(Curl_now(), winner->started));
+                winner->name, (int)curlx_timediff(curlx_now(),
+                                                  winner->started));
 
   cf->next = winner->cf;
   winner->cf = NULL;
@@ -263,11 +262,11 @@ static bool time_to_start_next(struct Curl_cfilter *cf,
       break;
   }
   if(i == idx) {
-    CURL_TRC_CF(data, cf, "all previous ballers have failed, time to start "
-                "baller %zu [%s]", idx, ctx->ballers[idx].name);
+    CURL_TRC_CF(data, cf, "all previous attempts failed, starting %s",
+                ctx->ballers[idx].name);
     return TRUE;
   }
-  elapsed_ms = Curl_timediff(now, ctx->started);
+  elapsed_ms = curlx_timediff(now, ctx->started);
   if(elapsed_ms >= ctx->hard_eyeballs_timeout_ms) {
     CURL_TRC_CF(data, cf, "hard timeout of %dms reached, starting %s",
                 ctx->hard_eyeballs_timeout_ms, ctx->ballers[idx].name);
@@ -291,21 +290,20 @@ static bool time_to_start_next(struct Curl_cfilter *cf,
 
 static CURLcode cf_hc_connect(struct Curl_cfilter *cf,
                               struct Curl_easy *data,
-                              bool blocking, bool *done)
+                              bool *done)
 {
   struct cf_hc_ctx *ctx = cf->ctx;
   struct curltime now;
   CURLcode result = CURLE_OK;
   size_t i, failed_ballers;
 
-  (void)blocking;
   if(cf->connected) {
     *done = TRUE;
     return CURLE_OK;
   }
 
   *done = FALSE;
-  now = Curl_now();
+  now = curlx_now();
   switch(ctx->state) {
   case CF_HC_INIT:
     DEBUGASSERT(!cf->next);
@@ -316,7 +314,7 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf,
     cf_hc_baller_init(&ctx->ballers[0], cf, data, cf->conn->transport);
     if(ctx->baller_count > 1) {
       Curl_expire(data, ctx->soft_eyeballs_timeout_ms, EXPIRE_ALPN_EYEBALLS);
-      CURL_TRC_CF(data, cf, "set expire for starting next baller in %ums",
+      CURL_TRC_CF(data, cf, "set next attempt to start in %ums",
                   ctx->soft_eyeballs_timeout_ms);
     }
     ctx->state = CF_HC_CONNECT;
@@ -352,7 +350,7 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf,
 
     if(failed_ballers == ctx->baller_count) {
       /* all have failed. we give up */
-      CURL_TRC_CF(data, cf, "connect, all failed");
+      CURL_TRC_CF(data, cf, "connect, all attempts failed");
       for(i = 0; i < ctx->baller_count; i++) {
         if(ctx->ballers[i].result) {
           result = ctx->ballers[i].result;
@@ -451,7 +449,6 @@ static bool cf_hc_data_pending(struct Curl_cfilter *cf,
   if(cf->connected)
     return cf->next->cft->has_data_pending(cf->next, data);
 
-  CURL_TRC_CF((struct Curl_easy *)data, cf, "data_pending");
   for(i = 0; i < ctx->baller_count; i++)
     if(cf_hc_baller_data_pending(&ctx->ballers[i], data))
       return TRUE;
@@ -471,7 +468,7 @@ static struct curltime cf_get_max_baller_time(struct Curl_cfilter *cf,
     struct Curl_cfilter *cfb = ctx->ballers[i].cf;
     memset(&t, 0, sizeof(t));
     if(cfb && !cfb->cft->query(cfb, data, query, NULL, &t)) {
-      if((t.tv_sec || t.tv_usec) && Curl_timediff_us(t, tmax) > 0)
+      if((t.tv_sec || t.tv_usec) && curlx_timediff_us(t, tmax) > 0)
         tmax = t;
     }
   }
@@ -577,7 +574,6 @@ struct Curl_cftype Curl_cft_http_connect = {
 
 static CURLcode cf_hc_create(struct Curl_cfilter **pcf,
                              struct Curl_easy *data,
-                             const struct Curl_dns_entry *remotehost,
                              enum alpnid *alpnids, size_t alpn_count)
 {
   struct Curl_cfilter *cf = NULL;
@@ -599,7 +595,6 @@ static CURLcode cf_hc_create(struct Curl_cfilter **pcf,
     result = CURLE_OUT_OF_MEMORY;
     goto out;
   }
-  ctx->remotehost = remotehost;
   for(i = 0; i < alpn_count; ++i)
     cf_hc_baller_assign(&ctx->ballers[i], alpnids[i]);
   for(; i < CURL_ARRAYSIZE(ctx->ballers); ++i)
@@ -607,8 +602,6 @@ static CURLcode cf_hc_create(struct Curl_cfilter **pcf,
   ctx->baller_count = alpn_count;
 
   result = Curl_cf_create(&cf, &Curl_cft_http_connect, ctx);
-  CURL_TRC_CF(data, cf, "created with %zu ALPNs -> %d",
-              ctx->baller_count, result);
   if(result)
     goto out;
   ctx = NULL;
@@ -623,14 +616,13 @@ static CURLcode cf_hc_create(struct Curl_cfilter **pcf,
 static CURLcode cf_http_connect_add(struct Curl_easy *data,
                                     struct connectdata *conn,
                                     int sockindex,
-                                    const struct Curl_dns_entry *remotehost,
                                     enum alpnid *alpn_ids, size_t alpn_count)
 {
   struct Curl_cfilter *cf;
   CURLcode result = CURLE_OK;
 
   DEBUGASSERT(data);
-  result = cf_hc_create(&cf, data, remotehost, alpn_ids, alpn_count);
+  result = cf_hc_create(&cf, data, alpn_ids, alpn_count);
   if(result)
     goto out;
   Curl_conn_cf_add(data, conn, sockindex, cf);
@@ -638,88 +630,110 @@ static CURLcode cf_http_connect_add(struct Curl_easy *data,
   return result;
 }
 
+static bool cf_https_alpns_contain(enum alpnid id,
+                                   enum alpnid *list, size_t len)
+{
+  size_t i;
+  for(i = 0; i < len; ++i) {
+    if(id == list[i])
+      return TRUE;
+  }
+  return FALSE;
+}
+
 CURLcode Curl_cf_https_setup(struct Curl_easy *data,
                              struct connectdata *conn,
-                             int sockindex,
-                             const struct Curl_dns_entry *remotehost)
+                             int sockindex)
 {
   enum alpnid alpn_ids[2];
   size_t alpn_count = 0;
   CURLcode result = CURLE_OK;
+  struct Curl_cfilter cf_fake, *cf = NULL;
 
   (void)sockindex;
-  (void)remotehost;
+  /* we want to log for the filter before we create it, fake it. */
+  memset(&cf_fake, 0, sizeof(cf_fake));
+  cf_fake.cft = &Curl_cft_http_connect;
+  cf = &cf_fake;
 
   if(conn->bits.tls_enable_alpn) {
-    switch(data->state.httpwant) {
-    case CURL_HTTP_VERSION_NONE:
-      /* No preferences by transfer setup. Choose best defaults */
 #ifdef USE_HTTPSRR
-      if(conn->dns_entry && conn->dns_entry->hinfo &&
-         !conn->dns_entry->hinfo->no_def_alpn) {
-        size_t i, j;
-        for(i = 0; i < CURL_ARRAYSIZE(conn->dns_entry->hinfo->alpns) &&
-                   alpn_count < CURL_ARRAYSIZE(alpn_ids); ++i) {
-          bool present = FALSE;
-          enum alpnid alpn = conn->dns_entry->hinfo->alpns[i];
-          for(j = 0; j < alpn_count; ++j) {
-            if(alpn == alpn_ids[j]) {
-              present = TRUE;
-              break;
-            }
+    /* Is there an HTTPSRR use its ALPNs here.
+     * We are here after having selected a connection to a host+port and
+     * can no longer change that. Any HTTPSRR advice for other hosts and ports
+     * we need to ignore. */
+    struct Curl_dns_entry *dns = data->state.dns[sockindex];
+    struct Curl_https_rrinfo *rr = dns ? dns->hinfo : NULL;
+    if(rr && !rr->no_def_alpn &&  /* ALPNs are defaults */
+       (!rr->target ||      /* for same host */
+        !rr->target[0] ||
+        (rr->target[0] == '.' &&
+         !rr->target[1])) &&
+       (rr->port < 0 ||    /* for same port */
+        rr->port == conn->remote_port)) {
+      size_t i;
+      for(i = 0; i < CURL_ARRAYSIZE(rr->alpns) &&
+                 alpn_count < CURL_ARRAYSIZE(alpn_ids); ++i) {
+        enum alpnid alpn = rr->alpns[i];
+        if(cf_https_alpns_contain(alpn, alpn_ids, alpn_count))
+          continue;
+        switch(alpn) {
+        case ALPN_h3:
+          if(Curl_conn_may_http3(data, conn))
+            break;  /* not possible */
+          if(data->state.http_neg.allowed & CURL_HTTP_V3x) {
+            CURL_TRC_CF(data, cf, "adding h3 via HTTPS-RR");
+            alpn_ids[alpn_count++] = alpn;
+          }
+          break;
+        case ALPN_h2:
+          if(data->state.http_neg.allowed & CURL_HTTP_V2x) {
+            CURL_TRC_CF(data, cf, "adding h2 via HTTPS-RR");
+            alpn_ids[alpn_count++] = alpn;
           }
-          if(!present) {
-            switch(alpn) {
-            case ALPN_h3:
-              if(Curl_conn_may_http3(data, conn))
-                break;  /* not possible */
-              FALLTHROUGH();
-            case ALPN_h2:
-            case ALPN_h1:
-              alpn_ids[alpn_count++] = alpn;
-              break;
-            default: /* ignore */
-              break;
-            }
+          break;
+        case ALPN_h1:
+          if(data->state.http_neg.allowed & CURL_HTTP_V1x) {
+            CURL_TRC_CF(data, cf, "adding h1 via HTTPS-RR");
+            alpn_ids[alpn_count++] = alpn;
           }
+          break;
+        default: /* ignore */
+          break;
         }
       }
+    }
 #endif
-      if(!alpn_count)
-        alpn_ids[alpn_count++] = ALPN_h2;
-      break;
-    case CURL_HTTP_VERSION_3ONLY:
+
+    if((alpn_count < CURL_ARRAYSIZE(alpn_ids)) &&
+       (data->state.http_neg.wanted & CURL_HTTP_V3x) &&
+       !cf_https_alpns_contain(ALPN_h3, alpn_ids, alpn_count)) {
       result = Curl_conn_may_http3(data, conn);
-      if(result) /* cannot do it */
-        goto out;
-      alpn_ids[alpn_count++] = ALPN_h3;
-      break;
-    case CURL_HTTP_VERSION_3:
-      /* We assume that silently not even trying H3 is ok here */
-      if(Curl_conn_may_http3(data, conn) == CURLE_OK)
+      if(!result) {
+        CURL_TRC_CF(data, cf, "adding wanted h3");
         alpn_ids[alpn_count++] = ALPN_h3;
+      }
+      else if(data->state.http_neg.wanted == CURL_HTTP_V3x)
+        goto out; /* only h3 allowed, not possible, error out */
+    }
+    if((alpn_count < CURL_ARRAYSIZE(alpn_ids)) &&
+       (data->state.http_neg.wanted & CURL_HTTP_V2x) &&
+       !cf_https_alpns_contain(ALPN_h2, alpn_ids, alpn_count)) {
+      CURL_TRC_CF(data, cf, "adding wanted h2");
       alpn_ids[alpn_count++] = ALPN_h2;
-      break;
-    case CURL_HTTP_VERSION_2_0:
-    case CURL_HTTP_VERSION_2TLS:
-    case CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE:
-      alpn_ids[alpn_count++] = ALPN_h2;
-      break;
-    case CURL_HTTP_VERSION_1_0:
-    case CURL_HTTP_VERSION_1_1:
+    }
+    else if((alpn_count < CURL_ARRAYSIZE(alpn_ids)) &&
+            (data->state.http_neg.wanted & CURL_HTTP_V1x) &&
+            !cf_https_alpns_contain(ALPN_h1, alpn_ids, alpn_count)) {
+      CURL_TRC_CF(data, cf, "adding wanted h1");
       alpn_ids[alpn_count++] = ALPN_h1;
-      break;
-    default:
-      alpn_ids[alpn_count++] = ALPN_h2;
-      break;
     }
   }
 
   /* If we identified ALPNs to use, install our filter. Otherwise,
    * install nothing, so our call will use a default connect setup. */
   if(alpn_count) {
-    result = cf_http_connect_add(data, conn, sockindex, remotehost,
-                                 alpn_ids, alpn_count);
+    result = cf_http_connect_add(data, conn, sockindex, alpn_ids, alpn_count);
   }
 
 out:
diff --git a/extra/curl/curl-8.12.1/lib/cf-https-connect.h b/extra/curl/curl-8.14.1/lib/cf-https-connect.h
similarity index 87%
rename from extra/curl/curl-8.12.1/lib/cf-https-connect.h
rename to extra/curl/curl-8.14.1/lib/cf-https-connect.h
index 4ff9ef8d37f8..c36726f0a222 100644
--- a/extra/curl/curl-8.12.1/lib/cf-https-connect.h
+++ b/extra/curl/curl-8.14.1/lib/cf-https-connect.h
@@ -38,20 +38,17 @@ extern struct Curl_cftype Curl_cft_http_connect;
 CURLcode Curl_cf_http_connect_add(struct Curl_easy *data,
                                   struct connectdata *conn,
                                   int sockindex,
-                                  const struct Curl_dns_entry *remotehost,
                                   bool try_h3, bool try_h21);
 
 CURLcode
 Curl_cf_http_connect_insert_after(struct Curl_cfilter *cf_at,
                                   struct Curl_easy *data,
-                                  const struct Curl_dns_entry *remotehost,
                                   bool try_h3, bool try_h21);
 
 
 CURLcode Curl_cf_https_setup(struct Curl_easy *data,
                              struct connectdata *conn,
-                             int sockindex,
-                             const struct Curl_dns_entry *remotehost);
+                             int sockindex);
 
 
 #endif /* !defined(CURL_DISABLE_HTTP) */
diff --git a/extra/curl/curl-8.12.1/lib/cf-socket.c b/extra/curl/curl-8.14.1/lib/cf-socket.c
similarity index 94%
rename from extra/curl/curl-8.12.1/lib/cf-socket.c
rename to extra/curl/curl-8.14.1/lib/cf-socket.c
index a6f98886c50f..e31977201c68 100644
--- a/extra/curl/curl-8.12.1/lib/cf-socket.c
+++ b/extra/curl/curl-8.14.1/lib/cf-socket.c
@@ -74,15 +74,17 @@
 #include "multiif.h"
 #include "sockaddr.h" /* required for Curl_sockaddr_storage */
 #include "inet_ntop.h"
-#include "inet_pton.h"
+#include "curlx/inet_pton.h"
 #include "progress.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
 #include "conncache.h"
 #include "multihandle.h"
 #include "rand.h"
 #include "share.h"
 #include "strdup.h"
-#include "version_win32.h"
+#include "system_win32.h"
+#include "curlx/version_win32.h"
+#include "curlx/strparse.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -113,8 +115,8 @@ static void tcpnodelay(struct Curl_easy *data, curl_socket_t sockfd)
   int level = IPPROTO_TCP;
   char buffer[STRERROR_LEN];
 
-  if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff,
-                sizeof(onoff)) < 0)
+  if(setsockopt(sockfd, level, TCP_NODELAY,
+                (void *)&onoff, sizeof(onoff)) < 0)
     infof(data, "Could not set TCP_NODELAY: %s",
           Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
 #else
@@ -133,8 +135,8 @@ static void nosigpipe(struct Curl_easy *data,
 {
   int onoff = 1;
   (void)data;
-  if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&onoff,
-                sizeof(onoff)) < 0) {
+  if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE,
+                (void *)&onoff, sizeof(onoff)) < 0) {
 #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
     char buffer[STRERROR_LEN];
     infof(data, "Could not set SO_NOSIGPIPE: %s",
@@ -181,7 +183,7 @@ tcpkeepalive(struct Curl_easy *data,
 
   /* only set IDLE and INTVL if setting KEEPALIVE is successful */
   if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE,
-        (void *)&optval, sizeof(optval)) < 0) {
+                (void *)&optval, sizeof(optval)) < 0) {
     infof(data, "Failed to set SO_KEEPALIVE on fd "
           "%" FMT_SOCKET_T ": errno %d",
           sockfd, SOCKERRNO);
@@ -234,7 +236,7 @@ tcpkeepalive(struct Curl_easy *data,
     optval = curlx_sltosi(data->set.tcp_keepidle);
     KEEPALIVE_FACTOR(optval);
     if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE,
-          (void *)&optval, sizeof(optval)) < 0) {
+                  (void *)&optval, sizeof(optval)) < 0) {
       infof(data, "Failed to set TCP_KEEPIDLE on fd "
             "%" FMT_SOCKET_T ": errno %d",
             sockfd, SOCKERRNO);
@@ -244,7 +246,7 @@ tcpkeepalive(struct Curl_easy *data,
     optval = curlx_sltosi(data->set.tcp_keepidle);
     KEEPALIVE_FACTOR(optval);
     if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE,
-      (void *)&optval, sizeof(optval)) < 0) {
+                  (void *)&optval, sizeof(optval)) < 0) {
       infof(data, "Failed to set TCP_KEEPALIVE on fd "
             "%" FMT_SOCKET_T ": errno %d",
             sockfd, SOCKERRNO);
@@ -254,7 +256,7 @@ tcpkeepalive(struct Curl_easy *data,
     optval = curlx_sltosi(data->set.tcp_keepidle);
     KEEPALIVE_FACTOR(optval);
     if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE_THRESHOLD,
-      (void *)&optval, sizeof(optval)) < 0) {
+                  (void *)&optval, sizeof(optval)) < 0) {
       infof(data, "Failed to set TCP_KEEPALIVE_THRESHOLD on fd "
             "%" FMT_SOCKET_T ": errno %d",
             sockfd, SOCKERRNO);
@@ -264,7 +266,7 @@ tcpkeepalive(struct Curl_easy *data,
     optval = curlx_sltosi(data->set.tcp_keepintvl);
     KEEPALIVE_FACTOR(optval);
     if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL,
-          (void *)&optval, sizeof(optval)) < 0) {
+                  (void *)&optval, sizeof(optval)) < 0) {
       infof(data, "Failed to set TCP_KEEPINTVL on fd "
             "%" FMT_SOCKET_T ": errno %d",
             sockfd, SOCKERRNO);
@@ -285,7 +287,7 @@ tcpkeepalive(struct Curl_easy *data,
              curlx_sltosi(data->set.tcp_keepintvl);
     KEEPALIVE_FACTOR(optval);
     if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE_ABORT_THRESHOLD,
-          (void *)&optval, sizeof(optval)) < 0) {
+                  (void *)&optval, sizeof(optval)) < 0) {
       infof(data, "Failed to set TCP_KEEPALIVE_ABORT_THRESHOLD on fd "
             "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO);
     }
@@ -293,7 +295,7 @@ tcpkeepalive(struct Curl_easy *data,
 #ifdef TCP_KEEPCNT
     optval = curlx_sltosi(data->set.tcp_keepcnt);
     if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPCNT,
-                (void *)&optval, sizeof(optval)) < 0) {
+                  (void *)&optval, sizeof(optval)) < 0) {
       infof(data, "Failed to set TCP_KEEPCNT on fd "
             "%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO);
     }
@@ -420,7 +422,7 @@ static int socket_close(struct Curl_easy *data, struct connectdata *conn,
 
   if(use_callback && conn && conn->fclosesocket) {
     int rc;
-    Curl_multi_closed(data, sock);
+    Curl_multi_will_close(data, sock);
     Curl_set_in_callback(data, TRUE);
     rc = conn->fclosesocket(conn->closesocket_client, sock);
     Curl_set_in_callback(data, FALSE);
@@ -429,7 +431,7 @@ static int socket_close(struct Curl_easy *data, struct connectdata *conn,
 
   if(conn)
     /* tell the multi-socket code about this */
-    Curl_multi_closed(data, sock);
+    Curl_multi_will_close(data, sock);
 
   sclose(sock);
 
@@ -460,9 +462,6 @@ int Curl_socket_close(struct Curl_easy *data, struct connectdata *conn,
    Windows. Following function trying to detect OS version and skips
    SO_SNDBUF adjustment for Windows Vista and above.
 */
-#define DETECT_OS_NONE 0
-#define DETECT_OS_PREVISTA 1
-#define DETECT_OS_VISTA_OR_LATER 2
 
 void Curl_sndbuf_init(curl_socket_t sockfd)
 {
@@ -470,17 +469,7 @@ void Curl_sndbuf_init(curl_socket_t sockfd)
   int curval = 0;
   int curlen = sizeof(curval);
 
-  static int detectOsState = DETECT_OS_NONE;
-
-  if(detectOsState == DETECT_OS_NONE) {
-    if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT,
-                                    VERSION_GREATER_THAN_EQUAL))
-      detectOsState = DETECT_OS_VISTA_OR_LATER;
-    else
-      detectOsState = DETECT_OS_PREVISTA;
-  }
-
-  if(detectOsState == DETECT_OS_VISTA_OR_LATER)
+  if(Curl_isVistaOrGreater)
     return;
 
   if(getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&curval, &curlen) == 0)
@@ -685,21 +674,14 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
        * of the connection. The resolve functions should really be changed
        * to take a type parameter instead.
        */
-      unsigned char ipver = conn->ip_version;
-      int rc;
-
-      if(af == AF_INET)
-        conn->ip_version = CURL_IPRESOLVE_V4;
+      int ip_version = (af == AF_INET) ?
+                       CURL_IPRESOLVE_V4 : CURL_IPRESOLVE_WHATEVER;
 #ifdef USE_IPV6
-      else if(af == AF_INET6)
-        conn->ip_version = CURL_IPRESOLVE_V6;
+      if(af == AF_INET6)
+        ip_version = CURL_IPRESOLVE_V6;
 #endif
 
-      rc = Curl_resolv(data, host, 80, FALSE, &h);
-      if(rc == CURLRESOLV_PENDING)
-        (void)Curl_resolver_wait_resolv(data, &h);
-      conn->ip_version = ipver;
-
+      (void)Curl_resolv_blocking(data, host, 80, ip_version, &h);
       if(h) {
         int h_af = h->addr->ai_family;
         /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
@@ -732,7 +714,7 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
         if(scope_ptr)
           *(scope_ptr++) = '\0';
 #endif
-        if(Curl_inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0) {
+        if(curlx_inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0) {
           si6->sin6_family = AF_INET6;
           si6->sin6_port = htons(port);
 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
@@ -741,10 +723,10 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
                Curl_printable_address. The latter returns only numeric scope
                IDs and the former returns none at all. So the scope ID, if
                present, is known to be numeric */
-            unsigned long scope_id = strtoul(scope_ptr, NULL, 10);
-            if(scope_id > UINT_MAX)
+            curl_off_t scope_id;
+            if(curlx_str_number((const char **)CURL_UNCONST(&scope_ptr),
+                               &scope_id, UINT_MAX))
               return CURLE_UNSUPPORTED_PROTOCOL;
-
             si6->sin6_scope_id = (unsigned int)scope_id;
           }
 #endif
@@ -755,7 +737,7 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
 #endif
       /* IPv4 address */
       if((af == AF_INET) &&
-         (Curl_inet_pton(AF_INET, myhost, &si4->sin_addr) > 0)) {
+         (curlx_inet_pton(AF_INET, myhost, &si4->sin_addr) > 0)) {
         si4->sin_family = AF_INET;
         si4->sin_port = htons(port);
         sizeof_sa = sizeof(struct sockaddr_in);
@@ -855,7 +837,7 @@ static bool verifyconnect(curl_socket_t sockfd, int *error)
    *    Someone got to verify this on Win-NT 4.0, 2000."
    */
 
-#ifdef _WIN32_WCE
+#ifdef UNDER_CE
   Sleep(0);
 #else
   SleepEx(0, FALSE);
@@ -865,7 +847,7 @@ static bool verifyconnect(curl_socket_t sockfd, int *error)
 
   if(0 != getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&err, &errSize))
     err = SOCKERRNO;
-#ifdef _WIN32_WCE
+#ifdef UNDER_CE
   /* Old Windows CE versions do not support SO_ERROR */
   if(WSAENOPROTOOPT == err) {
     SET_SOCKERRNO(0);
@@ -879,7 +861,7 @@ static bool verifyconnect(curl_socket_t sockfd, int *error)
     err = 0;
   }
 #endif
-  if((0 == err) || (EISCONN == err))
+  if((0 == err) || (SOCKEISCONN == err))
     /* we are connected, awesome! */
     rc = TRUE;
   else
@@ -902,10 +884,10 @@ static CURLcode socket_connect_result(struct Curl_easy *data,
                                       const char *ipaddress, int error)
 {
   switch(error) {
-  case EINPROGRESS:
-  case EWOULDBLOCK:
+  case SOCKEINPROGRESS:
+  case SOCKEWOULDBLOCK:
 #if defined(EAGAIN)
-#if (EAGAIN) != (EWOULDBLOCK)
+#if (EAGAIN) != (SOCKEWOULDBLOCK)
     /* On some platforms EAGAIN and EWOULDBLOCK are the
      * same value, and on others they are different, hence
      * the odd #if
@@ -932,15 +914,6 @@ static CURLcode socket_connect_result(struct Curl_easy *data,
   }
 }
 
-/* We have a recv buffer to enhance reads with len < NW_SMALL_READS.
- * This happens often on TLS connections where the TLS implementation
- * tries to read the head of a TLS record, determine the length of the
- * full record and then make a subsequent read for that.
- * On large reads, we will not fill the buffer to avoid the double copy. */
-#define NW_RECV_CHUNK_SIZE    (64 * 1024)
-#define NW_RECV_CHUNKS         1
-#define NW_SMALL_READS        (1024)
-
 struct cf_socket_ctx {
   int transport;
   struct Curl_sockaddr_ex addr;      /* address to connect to */
@@ -983,28 +956,28 @@ static CURLcode cf_socket_ctx_init(struct cf_socket_ctx *ctx,
 
 #ifdef DEBUGBUILD
   {
-    char *p = getenv("CURL_DBG_SOCK_WBLOCK");
+    const char *p = getenv("CURL_DBG_SOCK_WBLOCK");
     if(p) {
-      long l = strtol(p, NULL, 10);
-      if(l >= 0 && l <= 100)
+      curl_off_t l;
+      if(!curlx_str_number(&p, &l, 100))
         ctx->wblock_percent = (int)l;
     }
     p = getenv("CURL_DBG_SOCK_WPARTIAL");
     if(p) {
-      long l = strtol(p, NULL, 10);
-      if(l >= 0 && l <= 100)
+      curl_off_t l;
+      if(!curlx_str_number(&p, &l, 100))
         ctx->wpartial_percent = (int)l;
     }
     p = getenv("CURL_DBG_SOCK_RBLOCK");
     if(p) {
-      long l = strtol(p, NULL, 10);
-      if(l >= 0 && l <= 100)
+      curl_off_t l;
+      if(!curlx_str_number(&p, &l, 100))
         ctx->rblock_percent = (int)l;
     }
     p = getenv("CURL_DBG_SOCK_RMAX");
     if(p) {
-      long l = strtol(p, NULL, 10);
-      if(l >= 0)
+      curl_off_t l;
+      if(!curlx_str_number(&p, &l, CURL_OFF_T_MAX))
         ctx->recv_max = (size_t)l;
     }
   }
@@ -1018,7 +991,7 @@ static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data)
   struct cf_socket_ctx *ctx = cf->ctx;
 
   if(ctx && CURL_SOCKET_BAD != ctx->sock) {
-    CURL_TRC_CF(data, cf, "cf_socket_close(%" FMT_SOCKET_T ")", ctx->sock);
+    CURL_TRC_CF(data, cf, "cf_socket_close, fd=%" FMT_SOCKET_T, ctx->sock);
     if(ctx->sock == cf->conn->sock[cf->sockindex])
       cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD;
     socket_close(data, cf->conn, !ctx->accepted, ctx->sock);
@@ -1040,7 +1013,7 @@ static CURLcode cf_socket_shutdown(struct Curl_cfilter *cf,
   if(cf->connected) {
     struct cf_socket_ctx *ctx = cf->ctx;
 
-    CURL_TRC_CF(data, cf, "cf_socket_shutdown(%" FMT_SOCKET_T ")", ctx->sock);
+    CURL_TRC_CF(data, cf, "cf_socket_shutdown, fd=%" FMT_SOCKET_T, ctx->sock);
     /* On TCP, and when the socket looks well and non-blocking mode
      * can be enabled, receive dangling bytes before close to avoid
      * entering RST states unnecessarily. */
@@ -1132,7 +1105,7 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf,
 
   (void)data;
   DEBUGASSERT(ctx->sock == CURL_SOCKET_BAD);
-  ctx->started_at = Curl_now();
+  ctx->started_at = curlx_now();
 #ifdef SOCK_NONBLOCK
   /* Do not tuck SOCK_NONBLOCK into socktype when opensocket callback is set
    * because we would not know how socketype is about to be used in the
@@ -1248,7 +1221,7 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf,
   }
   else if(isconnected) {
     set_local_ip(cf, data);
-    ctx->connected_at = Curl_now();
+    ctx->connected_at = curlx_now();
     cf->connected = TRUE;
   }
   CURL_TRC_CF(data, cf, "cf_socket_open() -> %d, fd=%" FMT_SOCKET_T,
@@ -1313,7 +1286,7 @@ static int do_connect(struct Curl_cfilter *cf, struct Curl_easy *data,
 
 static CURLcode cf_tcp_connect(struct Curl_cfilter *cf,
                                struct Curl_easy *data,
-                               bool blocking, bool *done)
+                               bool *done)
 {
   struct cf_socket_ctx *ctx = cf->ctx;
   CURLcode result = CURLE_COULDNT_CONNECT;
@@ -1325,9 +1298,6 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf,
     return CURLE_OK;
   }
 
-  if(blocking)
-    return CURLE_UNSUPPORTED_PROTOCOL;
-
   *done = FALSE; /* a negative world view is best */
   if(ctx->sock == CURL_SOCKET_BAD) {
     int error;
@@ -1370,7 +1340,7 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf,
   else if(rc == CURL_CSELECT_OUT || cf->conn->bits.tcp_fastopen) {
     if(verifyconnect(ctx->sock, &ctx->error)) {
       /* we are connected with TCP, awesome! */
-      ctx->connected_at = Curl_now();
+      ctx->connected_at = curlx_now();
       set_local_ip(cf, data);
       *done = TRUE;
       cf->connected = TRUE;
@@ -1471,9 +1441,9 @@ static void win_update_sndbuf_size(struct cf_socket_ctx *ctx)
 {
   ULONG ideal;
   DWORD ideallen;
-  struct curltime n = Curl_now();
+  struct curltime n = curlx_now();
 
-  if(Curl_timediff(n, ctx->last_sndbuf_query_at) > 1000) {
+  if(curlx_timediff(n, ctx->last_sndbuf_query_at) > 1000) {
     if(!WSAIoctl(ctx->sock, SIO_IDEAL_SEND_BACKLOG_QUERY, 0, 0,
                   &ideal, sizeof(ideal), &ideallen, 0, 0) &&
        ideal != ctx->sndbuf_size &&
@@ -1538,15 +1508,16 @@ static ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data,
     int sockerr = SOCKERRNO;
 
     if(
-#ifdef WSAEWOULDBLOCK
+#ifdef USE_WINSOCK
       /* This is how Windows does it */
-      (WSAEWOULDBLOCK == sockerr)
+      (SOCKEWOULDBLOCK == sockerr)
 #else
       /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned
          due to its inability to send off data without blocking. We therefore
          treat both error codes the same here */
-      (EWOULDBLOCK == sockerr) || (EAGAIN == sockerr) || (EINTR == sockerr) ||
-      (EINPROGRESS == sockerr)
+      (SOCKEWOULDBLOCK == sockerr) ||
+      (EAGAIN == sockerr) || (SOCKEINTR == sockerr) ||
+      (SOCKEINPROGRESS == sockerr)
 #endif
       ) {
       /* this is just a case of EWOULDBLOCK */
@@ -1606,14 +1577,15 @@ static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
     int sockerr = SOCKERRNO;
 
     if(
-#ifdef WSAEWOULDBLOCK
+#ifdef USE_WINSOCK
       /* This is how Windows does it */
-      (WSAEWOULDBLOCK == sockerr)
+      (SOCKEWOULDBLOCK == sockerr)
 #else
       /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned
          due to its inability to send off data without blocking. We therefore
          treat both error codes the same here */
-      (EWOULDBLOCK == sockerr) || (EAGAIN == sockerr) || (EINTR == sockerr)
+      (SOCKEWOULDBLOCK == sockerr) ||
+      (EAGAIN == sockerr) || (SOCKEINTR == sockerr)
 #endif
       ) {
       /* this is just a case of EWOULDBLOCK */
@@ -1632,7 +1604,7 @@ static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
   CURL_TRC_CF(data, cf, "recv(len=%zu) -> %d, err=%d", len, (int)nread,
               *err);
   if(nread > 0 && !ctx->got_first_byte) {
-    ctx->first_byte_at = Curl_now();
+    ctx->first_byte_at = curlx_now();
     ctx->got_first_byte = TRUE;
   }
   return nread;
@@ -1743,7 +1715,7 @@ static CURLcode cf_socket_query(struct Curl_cfilter *cf,
     return CURLE_OK;
   case CF_QUERY_CONNECT_REPLY_MS:
     if(ctx->got_first_byte) {
-      timediff_t ms = Curl_timediff(ctx->first_byte_at, ctx->started_at);
+      timediff_t ms = curlx_timediff(ctx->first_byte_at, ctx->started_at);
       *pres1 = (ms < INT_MAX) ? (int)ms : INT_MAX;
     }
     else
@@ -1849,7 +1821,9 @@ static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf,
   /* QUIC needs a connected socket, nonblocking */
   DEBUGASSERT(ctx->sock != CURL_SOCKET_BAD);
 
-  rc = connect(ctx->sock, &ctx->addr.curl_sa_addr,  /* NOLINT */
+  /* error: The 1st argument to 'connect' is -1 but should be >= 0
+     NOLINTNEXTLINE(clang-analyzer-unix.StdCLibraryFunctions) */
+  rc = connect(ctx->sock, &ctx->addr.curl_sa_addr,
                (curl_socklen_t)ctx->addr.addrlen);
   if(-1 == rc) {
     return socket_connect_result(data, ctx->ip.remote_ip, SOCKERRNO);
@@ -1867,8 +1841,9 @@ static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf,
    * non-blocking socket created by cf_socket_open() to it. Thus, we
    * do not need to call curlx_nonblock() in cf_udp_setup_quic() anymore.
    */
+#ifdef __linux__
   switch(ctx->addr.family) {
-#if defined(__linux__) && defined(IP_MTU_DISCOVER)
+#ifdef IP_MTU_DISCOVER
   case AF_INET: {
     int val = IP_PMTUDISC_DO;
     (void)setsockopt(ctx->sock, IPPROTO_IP, IP_MTU_DISCOVER, &val,
@@ -1876,7 +1851,7 @@ static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf,
     break;
   }
 #endif
-#if defined(__linux__) && defined(IPV6_MTU_DISCOVER)
+#ifdef IPV6_MTU_DISCOVER
   case AF_INET6: {
     int val = IPV6_PMTUDISC_DO;
     (void)setsockopt(ctx->sock, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val,
@@ -1886,11 +1861,12 @@ static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf,
 #endif
   }
 
-#if defined(__linux__) && defined(UDP_GRO) &&                                 \
+#if defined(UDP_GRO) &&                                                       \
   (defined(HAVE_SENDMMSG) || defined(HAVE_SENDMSG)) &&                        \
   ((defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || defined(USE_QUICHE))
   (void)setsockopt(ctx->sock, IPPROTO_UDP, UDP_GRO, &one,
                    (socklen_t)sizeof(one));
+#endif
 #endif
 
   return CURLE_OK;
@@ -1898,12 +1874,11 @@ static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf,
 
 static CURLcode cf_udp_connect(struct Curl_cfilter *cf,
                                struct Curl_easy *data,
-                               bool blocking, bool *done)
+                               bool *done)
 {
   struct cf_socket_ctx *ctx = cf->ctx;
   CURLcode result = CURLE_COULDNT_CONNECT;
 
-  (void)blocking;
   if(cf->connected) {
     *done = TRUE;
     return CURLE_OK;
@@ -2057,7 +2032,7 @@ static timediff_t cf_tcp_accept_timeleft(struct Curl_cfilter *cf,
     timeout_ms = data->set.accepttimeout;
 #endif
 
-  now = Curl_now();
+  now = curlx_now();
   /* check if the generic timeout possibly is set shorter */
   other = Curl_timeleft(data, &now, FALSE);
   if(other && (other < timeout_ms))
@@ -2066,7 +2041,7 @@ static timediff_t cf_tcp_accept_timeleft(struct Curl_cfilter *cf,
     timeout_ms = other;
   else {
     /* subtract elapsed time */
-    timeout_ms -= Curl_timediff(now, ctx->started_at);
+    timeout_ms -= curlx_timediff(now, ctx->started_at);
     if(!timeout_ms)
       /* avoid returning 0 as that means no timeout! */
       timeout_ms = -1;
@@ -2108,7 +2083,7 @@ static void cf_tcp_set_accepted_remote_ip(struct Curl_cfilter *cf,
 
 static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf,
                                       struct Curl_easy *data,
-                                      bool blocking, bool *done)
+                                      bool *done)
 {
   struct cf_socket_ctx *ctx = cf->ctx;
 #ifdef USE_IPV6
@@ -2124,7 +2099,6 @@ static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf,
 
   /* we start accepted, if we ever close, we cannot go on */
   (void)data;
-  (void)blocking;
   if(cf->connected) {
     *done = TRUE;
     return CURLE_OK;
@@ -2163,7 +2137,12 @@ static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf,
 
   if(0 == getsockname(ctx->sock, (struct sockaddr *) &add, &size)) {
     size = sizeof(add);
+#ifdef HAVE_ACCEPT4
+    s_accepted = accept4(ctx->sock, (struct sockaddr *) &add, &size,
+                         SOCK_NONBLOCK | SOCK_CLOEXEC);
+#else
     s_accepted = accept(ctx->sock, (struct sockaddr *) &add, &size);
+#endif
   }
 
   if(CURL_SOCKET_BAD == s_accepted) {
@@ -2172,7 +2151,9 @@ static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf,
   }
 
   infof(data, "Connection accepted from server");
+#ifndef HAVE_ACCEPT4
   (void)curlx_nonblock(s_accepted, TRUE); /* enable non-blocking */
+#endif
   /* Replace any filter on SECONDARY with one listening on this socket */
   ctx->listening = FALSE;
   ctx->accepted = TRUE;
@@ -2183,7 +2164,7 @@ static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf,
   cf_tcp_set_accepted_remote_ip(cf, data);
   set_local_ip(cf, data);
   ctx->active = TRUE;
-  ctx->connected_at = Curl_now();
+  ctx->connected_at = curlx_now();
   cf->connected = TRUE;
   CURL_TRC_CF(data, cf, "accepted_set(sock=%" FMT_SOCKET_T
               ", remote=%s port=%d)",
@@ -2201,6 +2182,7 @@ static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf,
     if(error)
       return CURLE_ABORTED_BY_CALLBACK;
   }
+  *done = TRUE;
   return CURLE_OK;
 }
 
@@ -2249,7 +2231,7 @@ CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data,
     goto out;
   Curl_conn_cf_add(data, conn, sockindex, cf);
 
-  ctx->started_at = Curl_now();
+  ctx->started_at = curlx_now();
   conn->sock[sockindex] = ctx->sock;
   set_local_ip(cf, data);
   CURL_TRC_CF(data, cf, "set filter for listen socket fd=%" FMT_SOCKET_T
diff --git a/extra/curl/curl-8.12.1/lib/cf-socket.h b/extra/curl/curl-8.14.1/lib/cf-socket.h
similarity index 98%
rename from extra/curl/curl-8.12.1/lib/cf-socket.h
rename to extra/curl/curl-8.14.1/lib/cf-socket.h
index 1ce8404c07d0..d3e35098421e 100644
--- a/extra/curl/curl-8.12.1/lib/cf-socket.h
+++ b/extra/curl/curl-8.14.1/lib/cf-socket.h
@@ -25,7 +25,7 @@
  ***************************************************************************/
 #include "curl_setup.h"
 
-#include "nonblock.h" /* for curlx_nonblock(), formerly Curl_nonblock() */
+#include "curlx/nonblock.h" /* for curlx_nonblock() */
 #include "sockaddr.h"
 
 struct Curl_addrinfo;
diff --git a/extra/curl/curl-8.12.1/lib/cfilters.c b/extra/curl/curl-8.14.1/lib/cfilters.c
similarity index 90%
rename from extra/curl/curl-8.12.1/lib/cfilters.c
rename to extra/curl/curl-8.14.1/lib/cfilters.c
index 6a894e8ce6e9..00090f0c7198 100644
--- a/extra/curl/curl-8.12.1/lib/cfilters.c
+++ b/extra/curl/curl-8.14.1/lib/cfilters.c
@@ -28,13 +28,14 @@
 #include "strerror.h"
 #include "cfilters.h"
 #include "connect.h"
-#include "url.h" /* for Curl_safefree() */
+#include "url.h"
 #include "sendf.h"
 #include "sockaddr.h" /* required for Curl_sockaddr_storage */
 #include "multiif.h"
 #include "progress.h"
 #include "select.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
+#include "curlx/strparse.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -197,11 +198,11 @@ CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done)
   }
 
   *done = FALSE;
-  now = Curl_now();
+  now = curlx_now();
   if(!Curl_shutdown_started(data, sockindex)) {
-    DEBUGF(infof(data, "shutdown start on%s connection",
-           sockindex ? " secondary" : ""));
-    Curl_shutdown_start(data, sockindex, &now);
+    CURL_TRC_M(data, "shutdown start on%s connection",
+               sockindex ? " secondary" : "");
+    Curl_shutdown_start(data, sockindex, 0, &now);
   }
   else {
     timeout_ms = Curl_shutdown_timeleft(data->conn, sockindex, &now);
@@ -367,10 +368,10 @@ bool Curl_conn_cf_discard_sub(struct Curl_cfilter *cf,
 
 CURLcode Curl_conn_cf_connect(struct Curl_cfilter *cf,
                               struct Curl_easy *data,
-                              bool blocking, bool *done)
+                              bool *done)
 {
   if(cf)
-    return cf->cft->do_connect(cf, data, blocking, done);
+    return cf->cft->do_connect(cf, data, done);
   return CURLE_FAILED_INIT;
 }
 
@@ -404,6 +405,9 @@ CURLcode Curl_conn_connect(struct Curl_easy *data,
                            bool blocking,
                            bool *done)
 {
+#define CF_CONN_NUM_POLLS_ON_STACK 5
+  struct pollfd a_few_on_stack[CF_CONN_NUM_POLLS_ON_STACK];
+  struct curl_pollfds cpfds;
   struct Curl_cfilter *cf;
   CURLcode result = CURLE_OK;
 
@@ -411,14 +415,17 @@ CURLcode Curl_conn_connect(struct Curl_easy *data,
   DEBUGASSERT(data->conn);
 
   cf = data->conn->cfilter[sockindex];
-  DEBUGASSERT(cf);
   if(!cf) {
     *done = FALSE;
     return CURLE_FAILED_INIT;
   }
 
   *done = cf->connected;
-  if(!*done) {
+  if(*done)
+    return CURLE_OK;
+
+  Curl_pollfds_init(&cpfds, a_few_on_stack, CF_CONN_NUM_POLLS_ON_STACK);
+  while(!*done) {
     if(Curl_conn_needs_flush(data, sockindex)) {
       DEBUGF(infof(data, "Curl_conn_connect(index=%d), flush", sockindex));
       result = Curl_conn_flush(data, sockindex);
@@ -426,24 +433,75 @@ CURLcode Curl_conn_connect(struct Curl_easy *data,
         return result;
     }
 
-    result = cf->cft->do_connect(cf, data, blocking, done);
+    result = cf->cft->do_connect(cf, data, done);
+    CURL_TRC_CF(data, cf, "Curl_conn_connect(block=%d) -> %d, done=%d",
+                blocking, result, *done);
     if(!result && *done) {
       /* Now that the complete filter chain is connected, let all filters
        * persist information at the connection. E.g. cf-socket sets the
        * socket and ip related information. */
       cf_cntrl_update_info(data, data->conn);
       conn_report_connect_stats(data, data->conn);
-      data->conn->keepalive = Curl_now();
+      data->conn->keepalive = curlx_now();
       Curl_verboseconnect(data, data->conn, sockindex);
+      goto out;
     }
     else if(result) {
+      CURL_TRC_CF(data, cf, "Curl_conn_connect(), filter returned %d",
+                  result);
       conn_report_connect_stats(data, data->conn);
+      goto out;
+    }
+
+    if(!blocking)
+      goto out;
+    else {
+      /* check allowed time left */
+      const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
+      curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
+      struct easy_pollset ps;
+      int rc;
+
+      if(timeout_ms < 0) {
+        /* no need to continue if time already is up */
+        failf(data, "connect timeout");
+        result = CURLE_OPERATION_TIMEDOUT;
+        goto out;
+      }
+
+      CURL_TRC_CF(data, cf, "Curl_conn_connect(block=1), do poll");
+      Curl_pollfds_reset(&cpfds);
+      memset(&ps, 0, sizeof(ps));
+      /* In general, we want to send after connect, wait on that. */
+      if(sockfd != CURL_SOCKET_BAD)
+        Curl_pollset_set_out_only(data, &ps, sockfd);
+      Curl_conn_adjust_pollset(data, data->conn, &ps);
+      result = Curl_pollfds_add_ps(&cpfds, &ps);
+      if(result)
+        goto out;
+
+      rc = Curl_poll(cpfds.pfds, cpfds.n,
+                     CURLMIN(timeout_ms, (cpfds.n ? 1000 : 10)));
+      CURL_TRC_CF(data, cf, "Curl_conn_connect(block=1), Curl_poll() -> %d",
+                  rc);
+      if(rc < 0) {
+        result = CURLE_COULDNT_CONNECT;
+        goto out;
+      }
+      /* continue iterating */
     }
   }
 
+out:
+  Curl_pollfds_cleanup(&cpfds);
   return result;
 }
 
+bool Curl_conn_is_setup(struct connectdata *conn, int sockindex)
+{
+  return (conn->cfilter[sockindex] != NULL);
+}
+
 bool Curl_conn_is_connected(struct connectdata *conn, int sockindex)
 {
   struct Curl_cfilter *cf;
@@ -496,13 +554,14 @@ bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex)
   return FALSE;
 }
 
-unsigned char Curl_conn_http_version(struct Curl_easy *data)
+unsigned char Curl_conn_http_version(struct Curl_easy *data,
+                                     struct connectdata *conn)
 {
   struct Curl_cfilter *cf;
   CURLcode result = CURLE_UNKNOWN_OPTION;
   unsigned char v = 0;
 
-  cf = data->conn ? data->conn->cfilter[FIRSTSOCKET] : NULL;
+  cf = conn->cfilter[FIRSTSOCKET];
   for(; cf; cf = cf->next) {
     if(cf->cft->flags & CF_TYPE_HTTP) {
       int value = 0;
@@ -571,14 +630,15 @@ void Curl_conn_cf_adjust_pollset(struct Curl_cfilter *cf,
 }
 
 void Curl_conn_adjust_pollset(struct Curl_easy *data,
-                               struct easy_pollset *ps)
+                              struct connectdata *conn,
+                              struct easy_pollset *ps)
 {
   int i;
 
   DEBUGASSERT(data);
-  DEBUGASSERT(data->conn);
+  DEBUGASSERT(conn);
   for(i = 0; i < 2; ++i) {
-    Curl_conn_cf_adjust_pollset(data->conn->cfilter[i], data, ps);
+    Curl_conn_cf_adjust_pollset(conn->cfilter[i], data, ps);
   }
 }
 
@@ -880,14 +940,14 @@ CURLcode Curl_conn_send(struct Curl_easy *data, int sockindex,
   DEBUGASSERT(data->conn);
   conn = data->conn;
 #ifdef DEBUGBUILD
-  {
+  if(write_len) {
     /* Allow debug builds to override this logic to force short sends
     */
-    char *p = getenv("CURL_SMALLSENDS");
+    const char *p = getenv("CURL_SMALLSENDS");
     if(p) {
-      size_t altsize = (size_t)strtoul(p, NULL, 10);
-      if(altsize)
-        write_len = CURLMIN(write_len, altsize);
+      curl_off_t altsize;
+      if(!curlx_str_number(&p, &altsize, write_len))
+        write_len = (size_t)altsize;
     }
   }
 #endif
diff --git a/extra/curl/curl-8.12.1/lib/cfilters.h b/extra/curl/curl-8.14.1/lib/cfilters.h
similarity index 95%
rename from extra/curl/curl-8.12.1/lib/cfilters.h
rename to extra/curl/curl-8.14.1/lib/cfilters.h
index 2d5599a90abf..4c604db38f7c 100644
--- a/extra/curl/curl-8.12.1/lib/cfilters.h
+++ b/extra/curl/curl-8.14.1/lib/cfilters.h
@@ -24,7 +24,7 @@
  *
  ***************************************************************************/
 
-#include "timediff.h"
+#include "curlx/timediff.h"
 
 struct Curl_cfilter;
 struct Curl_easy;
@@ -51,7 +51,7 @@ typedef CURLcode Curl_cft_shutdown(struct Curl_cfilter *cf,
 
 typedef CURLcode Curl_cft_connect(struct Curl_cfilter *cf,
                                   struct Curl_easy *data,
-                                  bool blocking, bool *done);
+                                  bool *done);
 
 /* Return the hostname and port the connection goes to.
  * This may change with the connection state of filters when tunneling
@@ -65,10 +65,10 @@ typedef CURLcode Curl_cft_connect(struct Curl_cfilter *cf,
  * @param pport  on return, contains the port number
  */
 typedef void     Curl_cft_get_host(struct Curl_cfilter *cf,
-                                  struct Curl_easy *data,
-                                  const char **phost,
-                                  const char **pdisplay_host,
-                                  int *pport);
+                                   struct Curl_easy *data,
+                                   const char **phost,
+                                   const char **pdisplay_host,
+                                   int *pport);
 
 struct easy_pollset;
 
@@ -96,8 +96,8 @@ struct easy_pollset;
  * @param ps     the pollset (inout) for the easy handle
  */
 typedef void     Curl_cft_adjust_pollset(struct Curl_cfilter *cf,
-                                          struct Curl_easy *data,
-                                          struct easy_pollset *ps);
+                                         struct Curl_easy *data,
+                                         struct easy_pollset *ps);
 
 typedef bool     Curl_cft_data_pending(struct Curl_cfilter *cf,
                                        const struct Curl_easy *data);
@@ -245,8 +245,8 @@ void     Curl_cf_def_get_host(struct Curl_cfilter *cf, struct Curl_easy *data,
                               const char **phost, const char **pdisplay_host,
                               int *pport);
 void     Curl_cf_def_adjust_pollset(struct Curl_cfilter *cf,
-                                     struct Curl_easy *data,
-                                     struct easy_pollset *ps);
+                                    struct Curl_easy *data,
+                                    struct easy_pollset *ps);
 bool     Curl_cf_def_data_pending(struct Curl_cfilter *cf,
                                   const struct Curl_easy *data);
 ssize_t  Curl_cf_def_send(struct Curl_cfilter *cf, struct Curl_easy *data,
@@ -255,8 +255,8 @@ ssize_t  Curl_cf_def_send(struct Curl_cfilter *cf, struct Curl_easy *data,
 ssize_t  Curl_cf_def_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
                           char *buf, size_t len, CURLcode *err);
 CURLcode Curl_cf_def_cntrl(struct Curl_cfilter *cf,
-                                struct Curl_easy *data,
-                                int event, int arg1, void *arg2);
+                           struct Curl_easy *data,
+                           int event, int arg1, void *arg2);
 bool     Curl_cf_def_conn_is_alive(struct Curl_cfilter *cf,
                                    struct Curl_easy *data,
                                    bool *input_pending);
@@ -324,7 +324,7 @@ void Curl_conn_cf_discard_all(struct Curl_easy *data,
 
 CURLcode Curl_conn_cf_connect(struct Curl_cfilter *cf,
                               struct Curl_easy *data,
-                              bool blocking, bool *done);
+                              bool *done);
 void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data);
 ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data,
                           const void *buf, size_t len, bool eos,
@@ -370,6 +370,11 @@ bool Curl_conn_cf_needs_flush(struct Curl_cfilter *cf,
 CURLcode Curl_conn_connect(struct Curl_easy *data, int sockindex,
                            bool blocking, bool *done);
 
+/**
+ * Check if a filter chain at `sockindex` for connection `conn` exists.
+ */
+bool Curl_conn_is_setup(struct connectdata *conn, int sockindex);
+
 /**
  * Check if the filter chain at `sockindex` for connection `conn` is
  * completely connected.
@@ -399,7 +404,8 @@ bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex);
  * Return the HTTP version used on the FIRSTSOCKET connection filters
  * or 0 if unknown. Value otherwise is 09, 10, 11, etc.
  */
-unsigned char Curl_conn_http_version(struct Curl_easy *data);
+unsigned char Curl_conn_http_version(struct Curl_easy *data,
+                                     struct connectdata *conn);
 
 /**
  * Close the filter chain at `sockindex` for connection `data->conn`.
@@ -454,7 +460,8 @@ void Curl_conn_cf_adjust_pollset(struct Curl_cfilter *cf,
  * Adjust pollset from filters installed at transfer's connection.
  */
 void Curl_conn_adjust_pollset(struct Curl_easy *data,
-                               struct easy_pollset *ps);
+                              struct connectdata *conn,
+                              struct easy_pollset *ps);
 
 /**
  * Curl_poll() the filter chain at `cf` with timeout `timeout_ms`.
@@ -654,7 +661,7 @@ struct cf_call_data {
     (save) = CF_CTX_CALL_DATA(cf); \
     DEBUGASSERT((save).data == NULL || (save).depth > 0); \
     CF_CTX_CALL_DATA(cf).depth++;  \
-    CF_CTX_CALL_DATA(cf).data = (struct Curl_easy *)data; \
+    CF_CTX_CALL_DATA(cf).data = (struct Curl_easy *)CURL_UNCONST(data); \
   } while(0)
 
 #define CF_DATA_RESTORE(cf, save) \
@@ -669,7 +676,7 @@ struct cf_call_data {
 #define CF_DATA_SAVE(save, cf, data) \
   do { \
     (save) = CF_CTX_CALL_DATA(cf); \
-    CF_CTX_CALL_DATA(cf).data = (struct Curl_easy *)data; \
+    CF_CTX_CALL_DATA(cf).data = (struct Curl_easy *)CURL_UNCONST(data); \
   } while(0)
 
 #define CF_DATA_RESTORE(cf, save) \
diff --git a/extra/curl/curl-8.12.1/lib/config-mac.h b/extra/curl/curl-8.14.1/lib/config-mac.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/config-mac.h
rename to extra/curl/curl-8.14.1/lib/config-mac.h
diff --git a/extra/curl/curl-8.12.1/lib/config-os400.h b/extra/curl/curl-8.14.1/lib/config-os400.h
similarity index 96%
rename from extra/curl/curl-8.12.1/lib/config-os400.h
rename to extra/curl/curl-8.14.1/lib/config-os400.h
index 0bbbc514dd47..4316c58acd9c 100644
--- a/extra/curl/curl-8.12.1/lib/config-os400.h
+++ b/extra/curl/curl-8.14.1/lib/config-os400.h
@@ -152,12 +152,6 @@
 /* Define if you have the <stropts.h> header file. */
 #undef HAVE_STROPTS_H
 
-/* Define if you have the `strtok_r' function. */
-#define HAVE_STRTOK_R
-
-/* Define if you have the `strtoll' function. */
-#undef HAVE_STRTOLL             /* Allows ASCII compile on V5R1. */
-
 /* Define if you have the <sys/param.h> header file. */
 #define HAVE_SYS_PARAM_H
 
@@ -234,9 +228,6 @@
 /* Define to empty if `const' does not conform to ANSI C. */
 #undef const
 
-/* type to use in place of in_addr_t if not defined */
-#define in_addr_t       unsigned long
-
 /* Define to `unsigned' if <sys/types.h> does not define. */
 #undef size_t
 
diff --git a/extra/curl/curl-8.12.1/lib/config-plan9.h b/extra/curl/curl-8.14.1/lib/config-plan9.h
similarity index 98%
rename from extra/curl/curl-8.12.1/lib/config-plan9.h
rename to extra/curl/curl-8.14.1/lib/config-plan9.h
index a6091ef83028..857b7866489b 100644
--- a/extra/curl/curl-8.12.1/lib/config-plan9.h
+++ b/extra/curl/curl-8.14.1/lib/config-plan9.h
@@ -109,8 +109,6 @@
 #define HAVE_STDBOOL_H 1
 #define HAVE_STRCASECMP 1
 #define HAVE_STRDUP 1
-#define HAVE_STRTOK_R 1
-#define HAVE_STRTOLL 1
 #define HAVE_STRUCT_TIMEVAL 1
 #define HAVE_SYS_IOCTL_H 1
 #define HAVE_SYS_PARAM_H 1
diff --git a/extra/curl/curl-8.12.1/lib/config-riscos.h b/extra/curl/curl-8.14.1/lib/config-riscos.h
similarity index 97%
rename from extra/curl/curl-8.12.1/lib/config-riscos.h
rename to extra/curl/curl-8.14.1/lib/config-riscos.h
index 65ca53e222cd..b8aaa5d413b3 100644
--- a/extra/curl/curl-8.12.1/lib/config-riscos.h
+++ b/extra/curl/curl-8.14.1/lib/config-riscos.h
@@ -138,12 +138,6 @@
 /* Define if you have the <strings.h> header file. */
 #undef HAVE_STRINGS_H
 
-/* Define if you have the `strtok_r' function. */
-#undef HAVE_STRTOK_R
-
-/* Define if you have the `strtoll' function. */
-#undef HAVE_STRTOLL
-
 /* Define if you have the <sys/param.h> header file. */
 #undef HAVE_SYS_PARAM_H
 
diff --git a/extra/curl/curl-8.12.1/lib/config-win32.h b/extra/curl/curl-8.14.1/lib/config-win32.h
similarity index 93%
rename from extra/curl/curl-8.12.1/lib/config-win32.h
rename to extra/curl/curl-8.14.1/lib/config-win32.h
index 7c71eb684a2e..c9ba42762d6f 100644
--- a/extra/curl/curl-8.12.1/lib/config-win32.h
+++ b/extra/curl/curl-8.14.1/lib/config-win32.h
@@ -35,15 +35,17 @@
 /* Define if you have the <arpa/inet.h> header file. */
 /* #define HAVE_ARPA_INET_H 1 */
 
+#ifndef UNDER_CE
+
 /* Define if you have the <fcntl.h> header file. */
-#define HAVE_FCNTL_H 1
+#define HAVE_FCNTL_H 1  /* exists on __MINGW32CE__ */
 
 /* Define if you have the <io.h> header file. */
-#define HAVE_IO_H 1
+#define HAVE_IO_H 1  /* exists on __MINGW32CE__ */
 
 /* Define if you have the <locale.h> header file. */
-#ifndef UNDER_CE
 #define HAVE_LOCALE_H 1
+
 #endif
 
 /* Define if you have the <netdb.h> header file. */
@@ -53,8 +55,15 @@
 /* #define HAVE_NETINET_IN_H 1 */
 
 /* Define to 1 if you have the <stdbool.h> header file. */
+#ifndef UNDER_CE
 #if (defined(_MSC_VER) && (_MSC_VER >= 1800)) || defined(__MINGW32__)
-#define HAVE_STDBOOL_H 1
+#define HAVE_STDBOOL_H 1  /* exists on __MINGW32CE__ */
+#endif
+#endif
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#if (defined(_MSC_VER) && (_MSC_VER >= 1600)) || defined(__MINGW32__)
+#define HAVE_STDINT_H 1
 #endif
 
 /* Define if you have the <sys/param.h> header file. */
@@ -109,8 +118,10 @@
 #define STDC_HEADERS 1
 
 /* Define to 1 if bool is an available type. */
+#ifndef UNDER_CE
 #if (defined(_MSC_VER) && (_MSC_VER >= 1800)) || defined(__MINGW32__)
-#define HAVE_BOOL_T 1
+#define HAVE_BOOL_T 1  /* exists on __MINGW32CE__ */
+#endif
 #endif
 
 /* ---------------------------------------------------------------- */
@@ -165,11 +176,6 @@
 /* Define if you have the strdup function. */
 #define HAVE_STRDUP 1
 
-/* Define if you have the strtoll function. */
-#if (defined(_MSC_VER) && (_MSC_VER >= 1800)) || defined(__MINGW32__)
-#define HAVE_STRTOLL 1
-#endif
-
 /* Define if you have the utime function. */
 #define HAVE_UTIME 1
 
@@ -219,9 +225,9 @@
 
 /* Vista */
 #if (defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x600) && !defined(UNDER_CE)
-/* Define to 1 if you have a IPv6 capable working inet_ntop function. */
+/* Define to 1 if you have an IPv6 capable working inet_ntop function. */
 #define HAVE_INET_NTOP 1
-/* Define to 1 if you have a IPv6 capable working inet_pton function. */
+/* Define to 1 if you have an IPv6 capable working inet_pton function. */
 #define HAVE_INET_PTON 1
 #endif
 
@@ -230,11 +236,6 @@
 #define HAVE_BASENAME 1
 #endif
 
-/* Define to 1 if you have the strtok_r function. */
-#if defined(__MINGW32__) && !defined(__MINGW32CE__)
-#define HAVE_STRTOK_R 1
-#endif
-
 /* Define to 1 if you have the signal function. */
 #ifndef UNDER_CE
 #define HAVE_SIGNAL 1
@@ -244,9 +245,6 @@
 /*                       TYPEDEF REPLACEMENTS                       */
 /* ---------------------------------------------------------------- */
 
-/* Define if in_addr_t is not an available 'typedefed' type. */
-#define in_addr_t unsigned long
-
 /* Define if ssize_t is not an available 'typedefed' type. */
 #ifndef _SSIZE_T_DEFINED
 #  ifdef __MINGW32__
@@ -296,13 +294,12 @@
 /* #undef HAVE_GMTIME_R */
 
 /* Define if the compiler supports the 'long long' data type. */
-#if (defined(_MSC_VER) && (_MSC_VER >= 1310)) || defined(__MINGW32__)
+#if defined(_MSC_VER) || defined(__MINGW32__)
 #define HAVE_LONGLONG 1
 #endif
 
-/* mingw-w64 and visual studio >= 2005 (MSVCR80)
-   all default to 64-bit time_t unless _USE_32BIT_TIME_T is defined */
-#if (defined(_MSC_VER) && (_MSC_VER >= 1400)) || defined(__MINGW32__)
+/* Default to 64-bit time_t unless _USE_32BIT_TIME_T is defined */
+#if defined(_MSC_VER) || defined(__MINGW32__)
 #  ifndef _USE_32BIT_TIME_T
 #    define SIZEOF_TIME_T 8
 #  else
@@ -344,7 +341,7 @@
 #endif
 
 /* VS2008 default target settings and minimum build target check. */
-#if defined(_MSC_VER) && (_MSC_VER >= 1500) && (_MSC_VER <= 1600)
+#if defined(_MSC_VER) && (_MSC_VER <= 1600)
 #  ifndef _WIN32_WINNT
 #  define _WIN32_WINNT VS2008_DEF_TARGET
 #  endif
@@ -402,8 +399,7 @@ Vista
 
 #ifndef UNDER_CE
 
-/* _fseeki64() requires VS2005 */
-#if (defined(_MSC_VER) && (_MSC_VER >= 1400)) || defined(__MINGW32__)
+#if defined(_MSC_VER) || defined(__MINGW32__)
 #  define USE_WIN32_LARGE_FILES
 /* Number of bits in a file offset, on hosts where this is settable. */
 #  ifdef __MINGW32__
@@ -514,11 +510,9 @@ Vista
 #define CURL_DISABLE_TELNET 1
 #define CURL_DISABLE_LDAP 1
 
-#define ENOSPC 1
-#define ENOMEM 2
-#define EAGAIN 3
-
+#ifndef _MSC_VER
 extern int stat(const char *path, struct stat *buffer);
+#endif
 
 #endif /* UNDER_CE */
 
diff --git a/extra/curl/curl-8.14.1/lib/conncache.c b/extra/curl/curl-8.14.1/lib/conncache.c
new file mode 100644
index 000000000000..f5e2ea872526
--- /dev/null
+++ b/extra/curl/curl-8.14.1/lib/conncache.c
@@ -0,0 +1,888 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Linus Nielsen Feltzing, <linus@haxx.se>
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include <curl/curl.h>
+
+#include "urldata.h"
+#include "url.h"
+#include "cfilters.h"
+#include "progress.h"
+#include "multiif.h"
+#include "multi_ev.h"
+#include "sendf.h"
+#include "cshutdn.h"
+#include "conncache.h"
+#include "http_negotiate.h"
+#include "http_ntlm.h"
+#include "share.h"
+#include "sigpipe.h"
+#include "connect.h"
+#include "select.h"
+#include "strcase.h"
+#include "curlx/strparse.h"
+#include "uint-table.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+
+#define CPOOL_IS_LOCKED(c)    ((c) && (c)->locked)
+
+#define CPOOL_LOCK(c,d)                                                 \
+  do {                                                                  \
+    if((c)) {                                                           \
+      if(CURL_SHARE_KEEP_CONNECT((c)->share))                           \
+        Curl_share_lock((d), CURL_LOCK_DATA_CONNECT,                    \
+                        CURL_LOCK_ACCESS_SINGLE);                       \
+      DEBUGASSERT(!(c)->locked);                                        \
+      (c)->locked = TRUE;                                               \
+    }                                                                   \
+  } while(0)
+
+#define CPOOL_UNLOCK(c,d)                                                 \
+  do {                                                                  \
+    if((c)) {                                                           \
+      DEBUGASSERT((c)->locked);                                         \
+      (c)->locked = FALSE;                                              \
+      if(CURL_SHARE_KEEP_CONNECT((c)->share))                           \
+        Curl_share_unlock((d), CURL_LOCK_DATA_CONNECT);                 \
+    }                                                                   \
+  } while(0)
+
+
+/* A list of connections to the same destination. */
+struct cpool_bundle {
+  struct Curl_llist conns; /* connections in the bundle */
+  size_t dest_len; /* total length of destination, including NUL */
+  char *dest[1]; /* destination of bundle, allocated to keep dest_len bytes */
+};
+
+
+static void cpool_discard_conn(struct cpool *cpool,
+                               struct Curl_easy *data,
+                               struct connectdata *conn,
+                               bool aborted);
+
+static struct cpool_bundle *cpool_bundle_create(const char *dest)
+{
+  struct cpool_bundle *bundle;
+  size_t dest_len = strlen(dest);
+
+  bundle = calloc(1, sizeof(*bundle) + dest_len);
+  if(!bundle)
+    return NULL;
+  Curl_llist_init(&bundle->conns, NULL);
+  bundle->dest_len = dest_len + 1;
+  memcpy(bundle->dest, dest, bundle->dest_len);
+  return bundle;
+}
+
+static void cpool_bundle_destroy(struct cpool_bundle *bundle)
+{
+  DEBUGASSERT(!Curl_llist_count(&bundle->conns));
+  free(bundle);
+}
+
+/* Add a connection to a bundle */
+static void cpool_bundle_add(struct cpool_bundle *bundle,
+                             struct connectdata *conn)
+{
+  DEBUGASSERT(!Curl_node_llist(&conn->cpool_node));
+  Curl_llist_append(&bundle->conns, conn, &conn->cpool_node);
+  conn->bits.in_cpool = TRUE;
+}
+
+/* Remove a connection from a bundle */
+static void cpool_bundle_remove(struct cpool_bundle *bundle,
+                                struct connectdata *conn)
+{
+  (void)bundle;
+  DEBUGASSERT(Curl_node_llist(&conn->cpool_node) == &bundle->conns);
+  Curl_node_remove(&conn->cpool_node);
+  conn->bits.in_cpool = FALSE;
+}
+
+static void cpool_bundle_free_entry(void *freethis)
+{
+  cpool_bundle_destroy((struct cpool_bundle *)freethis);
+}
+
+void Curl_cpool_init(struct cpool *cpool,
+                     struct Curl_easy *idata,
+                     struct Curl_share *share,
+                     size_t size)
+{
+  Curl_hash_init(&cpool->dest2bundle, size, Curl_hash_str,
+                 curlx_str_key_compare, cpool_bundle_free_entry);
+
+  DEBUGASSERT(idata);
+
+  cpool->idata = idata;
+  cpool->share = share;
+  cpool->initialised = TRUE;
+}
+
+/* Return the "first" connection in the pool or NULL. */
+static struct connectdata *cpool_get_first(struct cpool *cpool)
+{
+  struct Curl_hash_iterator iter;
+  struct Curl_hash_element *he;
+  struct cpool_bundle *bundle;
+  struct Curl_llist_node *conn_node;
+
+  Curl_hash_start_iterate(&cpool->dest2bundle, &iter);
+  for(he = Curl_hash_next_element(&iter); he;
+      he = Curl_hash_next_element(&iter)) {
+    bundle = he->ptr;
+    conn_node = Curl_llist_head(&bundle->conns);
+    if(conn_node)
+      return Curl_node_elem(conn_node);
+  }
+  return NULL;
+}
+
+
+static struct cpool_bundle *cpool_find_bundle(struct cpool *cpool,
+                                              struct connectdata *conn)
+{
+  return Curl_hash_pick(&cpool->dest2bundle,
+                        conn->destination, strlen(conn->destination) + 1);
+}
+
+
+static void cpool_remove_bundle(struct cpool *cpool,
+                                struct cpool_bundle *bundle)
+{
+  if(!cpool)
+    return;
+  Curl_hash_delete(&cpool->dest2bundle, bundle->dest, bundle->dest_len);
+}
+
+
+static void cpool_remove_conn(struct cpool *cpool,
+                              struct connectdata *conn)
+{
+  struct Curl_llist *list = Curl_node_llist(&conn->cpool_node);
+  DEBUGASSERT(cpool);
+  if(list) {
+    /* The connection is certainly in the pool, but where? */
+    struct cpool_bundle *bundle = cpool_find_bundle(cpool, conn);
+    if(bundle && (list == &bundle->conns)) {
+      cpool_bundle_remove(bundle, conn);
+      if(!Curl_llist_count(&bundle->conns))
+        cpool_remove_bundle(cpool, bundle);
+      conn->bits.in_cpool = FALSE;
+      cpool->num_conn--;
+    }
+    else {
+      /* Should have been in the bundle list */
+      DEBUGASSERT(NULL);
+    }
+  }
+}
+
+void Curl_cpool_destroy(struct cpool *cpool)
+{
+  if(cpool && cpool->initialised && cpool->idata) {
+    struct connectdata *conn;
+    SIGPIPE_VARIABLE(pipe_st);
+
+    CURL_TRC_M(cpool->idata, "%s[CPOOL] destroy, %zu connections",
+               cpool->share ? "[SHARE] " : "", cpool->num_conn);
+    /* Move all connections to the shutdown list */
+    sigpipe_init(&pipe_st);
+    CPOOL_LOCK(cpool, cpool->idata);
+    conn = cpool_get_first(cpool);
+    while(conn) {
+      cpool_remove_conn(cpool, conn);
+      sigpipe_apply(cpool->idata, &pipe_st);
+      connclose(conn, "kill all");
+      cpool_discard_conn(cpool, cpool->idata, conn, FALSE);
+      conn = cpool_get_first(cpool);
+    }
+    CPOOL_UNLOCK(cpool, cpool->idata);
+    sigpipe_restore(&pipe_st);
+    Curl_hash_destroy(&cpool->dest2bundle);
+  }
+}
+
+static struct cpool *cpool_get_instance(struct Curl_easy *data)
+{
+  if(data) {
+    if(CURL_SHARE_KEEP_CONNECT(data->share))
+      return &data->share->cpool;
+    else if(data->multi_easy)
+      return &data->multi_easy->cpool;
+    else if(data->multi)
+      return &data->multi->cpool;
+  }
+  return NULL;
+}
+
+void Curl_cpool_xfer_init(struct Curl_easy *data)
+{
+  struct cpool *cpool = cpool_get_instance(data);
+
+  DEBUGASSERT(cpool);
+  if(cpool) {
+    CPOOL_LOCK(cpool, data);
+    /* the identifier inside the connection cache */
+    data->id = cpool->next_easy_id++;
+    if(cpool->next_easy_id <= 0)
+      cpool->next_easy_id = 0;
+    data->state.lastconnect_id = -1;
+
+    CPOOL_UNLOCK(cpool, data);
+  }
+  else {
+    /* We should not get here, but in a non-debug build, do something */
+    data->id = 0;
+    data->state.lastconnect_id = -1;
+  }
+}
+
+static struct cpool_bundle *
+cpool_add_bundle(struct cpool *cpool, struct connectdata *conn)
+{
+  struct cpool_bundle *bundle;
+
+  bundle = cpool_bundle_create(conn->destination);
+  if(!bundle)
+    return NULL;
+
+  if(!Curl_hash_add(&cpool->dest2bundle,
+                    bundle->dest, bundle->dest_len, bundle)) {
+    cpool_bundle_destroy(bundle);
+    return NULL;
+  }
+  return bundle;
+}
+
+static struct connectdata *
+cpool_bundle_get_oldest_idle(struct cpool_bundle *bundle)
+{
+  struct Curl_llist_node *curr;
+  timediff_t highscore = -1;
+  timediff_t score;
+  struct curltime now;
+  struct connectdata *oldest_idle = NULL;
+  struct connectdata *conn;
+
+  now = curlx_now();
+  curr = Curl_llist_head(&bundle->conns);
+  while(curr) {
+    conn = Curl_node_elem(curr);
+
+    if(!CONN_INUSE(conn)) {
+      /* Set higher score for the age passed since the connection was used */
+      score = curlx_timediff(now, conn->lastused);
+
+      if(score > highscore) {
+        highscore = score;
+        oldest_idle = conn;
+      }
+    }
+    curr = Curl_node_next(curr);
+  }
+  return oldest_idle;
+}
+
+static struct connectdata *cpool_get_oldest_idle(struct cpool *cpool)
+{
+  struct Curl_hash_iterator iter;
+  struct Curl_llist_node *curr;
+  struct Curl_hash_element *he;
+  struct connectdata *oldest_idle = NULL;
+  struct cpool_bundle *bundle;
+  struct curltime now;
+  timediff_t highscore =- 1;
+  timediff_t score;
+
+  now = curlx_now();
+  Curl_hash_start_iterate(&cpool->dest2bundle, &iter);
+
+  for(he = Curl_hash_next_element(&iter); he;
+      he = Curl_hash_next_element(&iter)) {
+    struct connectdata *conn;
+    bundle = he->ptr;
+
+    for(curr = Curl_llist_head(&bundle->conns); curr;
+        curr = Curl_node_next(curr)) {
+      conn = Curl_node_elem(curr);
+      if(CONN_INUSE(conn) || conn->bits.close || conn->connect_only)
+        continue;
+      /* Set higher score for the age passed since the connection was used */
+      score = curlx_timediff(now, conn->lastused);
+      if(score > highscore) {
+        highscore = score;
+        oldest_idle = conn;
+      }
+    }
+  }
+  return oldest_idle;
+}
+
+
+int Curl_cpool_check_limits(struct Curl_easy *data,
+                            struct connectdata *conn)
+{
+  struct cpool *cpool = cpool_get_instance(data);
+  struct cpool_bundle *bundle;
+  size_t dest_limit = 0;
+  size_t total_limit = 0;
+  size_t shutdowns;
+  int result = CPOOL_LIMIT_OK;
+
+  if(!cpool)
+    return CPOOL_LIMIT_OK;
+
+  if(cpool->idata->multi) {
+    dest_limit = cpool->idata->multi->max_host_connections;
+    total_limit = cpool->idata->multi->max_total_connections;
+  }
+
+  if(!dest_limit && !total_limit)
+    return CPOOL_LIMIT_OK;
+
+  CPOOL_LOCK(cpool, cpool->idata);
+  if(dest_limit) {
+    size_t live;
+
+    bundle = cpool_find_bundle(cpool, conn);
+    live = bundle ? Curl_llist_count(&bundle->conns) : 0;
+    shutdowns = Curl_cshutdn_dest_count(data, conn->destination);
+    while((live  + shutdowns) >= dest_limit) {
+      if(shutdowns) {
+        /* close one connection in shutdown right away, if we can */
+        if(!Curl_cshutdn_close_oldest(data, conn->destination))
+          break;
+      }
+      else if(!bundle)
+        break;
+      else {
+        struct connectdata *oldest_idle = NULL;
+        /* The bundle is full. Extract the oldest connection that may
+         * be removed now, if there is one. */
+        oldest_idle = cpool_bundle_get_oldest_idle(bundle);
+        if(!oldest_idle)
+          break;
+        /* disconnect the old conn and continue */
+        CURL_TRC_M(data, "Discarding connection #%"
+                     FMT_OFF_T " from %zu to reach destination "
+                     "limit of %zu", oldest_idle->connection_id,
+                     Curl_llist_count(&bundle->conns), dest_limit);
+        Curl_conn_terminate(cpool->idata, oldest_idle, FALSE);
+
+        /* in case the bundle was destroyed in disconnect, look it up again */
+        bundle = cpool_find_bundle(cpool, conn);
+        live = bundle ? Curl_llist_count(&bundle->conns) : 0;
+      }
+      shutdowns = Curl_cshutdn_dest_count(cpool->idata, conn->destination);
+    }
+    if((live + shutdowns) >= dest_limit) {
+      result = CPOOL_LIMIT_DEST;
+      goto out;
+    }
+  }
+
+  if(total_limit) {
+    shutdowns = Curl_cshutdn_count(cpool->idata);
+    while((cpool->num_conn + shutdowns) >= total_limit) {
+      if(shutdowns) {
+        /* close one connection in shutdown right away, if we can */
+        if(!Curl_cshutdn_close_oldest(data, NULL))
+          break;
+      }
+      else {
+        struct connectdata *oldest_idle = cpool_get_oldest_idle(cpool);
+        if(!oldest_idle)
+          break;
+        /* disconnect the old conn and continue */
+        CURL_TRC_M(data, "Discarding connection #%"
+                   FMT_OFF_T " from %zu to reach total "
+                   "limit of %zu",
+                   oldest_idle->connection_id, cpool->num_conn, total_limit);
+        Curl_conn_terminate(cpool->idata, oldest_idle, FALSE);
+      }
+      shutdowns = Curl_cshutdn_count(cpool->idata);
+    }
+    if((cpool->num_conn + shutdowns) >= total_limit) {
+      result = CPOOL_LIMIT_TOTAL;
+      goto out;
+    }
+  }
+
+out:
+  CPOOL_UNLOCK(cpool, cpool->idata);
+  return result;
+}
+
+CURLcode Curl_cpool_add(struct Curl_easy *data,
+                        struct connectdata *conn)
+{
+  CURLcode result = CURLE_OK;
+  struct cpool_bundle *bundle = NULL;
+  struct cpool *cpool = cpool_get_instance(data);
+  DEBUGASSERT(conn);
+
+  DEBUGASSERT(cpool);
+  if(!cpool)
+    return CURLE_FAILED_INIT;
+
+  CPOOL_LOCK(cpool, data);
+  bundle = cpool_find_bundle(cpool, conn);
+  if(!bundle) {
+    bundle = cpool_add_bundle(cpool, conn);
+    if(!bundle) {
+      result = CURLE_OUT_OF_MEMORY;
+      goto out;
+    }
+  }
+
+  cpool_bundle_add(bundle, conn);
+  conn->connection_id = cpool->next_connection_id++;
+  cpool->num_conn++;
+  CURL_TRC_M(data, "[CPOOL] added connection %" FMT_OFF_T ". "
+             "The cache now contains %zu members",
+             conn->connection_id, cpool->num_conn);
+out:
+  CPOOL_UNLOCK(cpool, data);
+
+  return result;
+}
+
+/* This function iterates the entire connection pool and calls the function
+   func() with the connection pointer as the first argument and the supplied
+   'param' argument as the other.
+
+   The cpool lock is still held when the callback is called. It needs it,
+   so that it can safely continue traversing the lists once the callback
+   returns.
+
+   Returns TRUE if the loop was aborted due to the callback's return code.
+
+   Return 0 from func() to continue the loop, return 1 to abort it.
+ */
+static bool cpool_foreach(struct Curl_easy *data,
+                          struct cpool *cpool,
+                          void *param,
+                          int (*func)(struct Curl_easy *data,
+                                      struct connectdata *conn, void *param))
+{
+  struct Curl_hash_iterator iter;
+  struct Curl_hash_element *he;
+
+  if(!cpool)
+    return FALSE;
+
+  Curl_hash_start_iterate(&cpool->dest2bundle, &iter);
+
+  he = Curl_hash_next_element(&iter);
+  while(he) {
+    struct Curl_llist_node *curr;
+    struct cpool_bundle *bundle = he->ptr;
+    he = Curl_hash_next_element(&iter);
+
+    curr = Curl_llist_head(&bundle->conns);
+    while(curr) {
+      /* Yes, we need to update curr before calling func(), because func()
+         might decide to remove the connection */
+      struct connectdata *conn = Curl_node_elem(curr);
+      curr = Curl_node_next(curr);
+
+      if(1 == func(data, conn, param)) {
+        return TRUE;
+      }
+    }
+  }
+  return FALSE;
+}
+
+/*
+ * A connection (already in the pool) has become idle. Do any
+ * cleanups in regard to the pool's limits.
+ *
+ * Return TRUE if idle connection kept in pool, FALSE if closed.
+ */
+bool Curl_cpool_conn_now_idle(struct Curl_easy *data,
+                              struct connectdata *conn)
+{
+  unsigned int maxconnects = !data->multi->maxconnects ?
+    (Curl_multi_xfers_running(data->multi) * 4) : data->multi->maxconnects;
+  struct connectdata *oldest_idle = NULL;
+  struct cpool *cpool = cpool_get_instance(data);
+  bool kept = TRUE;
+
+  conn->lastused = curlx_now(); /* it was used up until now */
+  if(cpool && maxconnects) {
+    /* may be called form a callback already under lock */
+    bool do_lock = !CPOOL_IS_LOCKED(cpool);
+    if(do_lock)
+      CPOOL_LOCK(cpool, data);
+    if(cpool->num_conn > maxconnects) {
+      infof(data, "Connection pool is full, closing the oldest of %zu/%u",
+            cpool->num_conn, maxconnects);
+
+      oldest_idle = cpool_get_oldest_idle(cpool);
+      kept = (oldest_idle != conn);
+      if(oldest_idle) {
+        Curl_conn_terminate(data, oldest_idle, FALSE);
+      }
+    }
+    if(do_lock)
+      CPOOL_UNLOCK(cpool, data);
+  }
+
+  return kept;
+}
+
+bool Curl_cpool_find(struct Curl_easy *data,
+                     const char *destination,
+                     Curl_cpool_conn_match_cb *conn_cb,
+                     Curl_cpool_done_match_cb *done_cb,
+                     void *userdata)
+{
+  struct cpool *cpool = cpool_get_instance(data);
+  struct cpool_bundle *bundle;
+  bool result = FALSE;
+
+  DEBUGASSERT(cpool);
+  DEBUGASSERT(conn_cb);
+  if(!cpool)
+    return FALSE;
+
+  CPOOL_LOCK(cpool, data);
+  bundle = Curl_hash_pick(&cpool->dest2bundle,
+                          CURL_UNCONST(destination),
+                          strlen(destination) + 1);
+  if(bundle) {
+    struct Curl_llist_node *curr = Curl_llist_head(&bundle->conns);
+    while(curr) {
+      struct connectdata *conn = Curl_node_elem(curr);
+      /* Get next node now. callback might discard current */
+      curr = Curl_node_next(curr);
+
+      if(conn_cb(conn, userdata)) {
+        result = TRUE;
+        break;
+      }
+    }
+  }
+
+  if(done_cb) {
+    result = done_cb(result, userdata);
+  }
+  CPOOL_UNLOCK(cpool, data);
+  return result;
+}
+
+static void cpool_discard_conn(struct cpool *cpool,
+                               struct Curl_easy *data,
+                               struct connectdata *conn,
+                               bool aborted)
+{
+  bool done = FALSE;
+
+  DEBUGASSERT(data);
+  DEBUGASSERT(!data->conn);
+  DEBUGASSERT(cpool);
+  DEBUGASSERT(!conn->bits.in_cpool);
+
+  /*
+   * If this connection is not marked to force-close, leave it open if there
+   * are other users of it
+   */
+  if(CONN_INUSE(conn) && !aborted) {
+    CURL_TRC_M(data, "[CPOOL] not discarding #%" FMT_OFF_T
+               " still in use by %u transfers", conn->connection_id,
+               CONN_ATTACHED(conn));
+    return;
+  }
+
+  /* treat the connection as aborted in CONNECT_ONLY situations, we do
+   * not know what the APP did with it. */
+  if(conn->connect_only)
+    aborted = TRUE;
+  conn->bits.aborted = aborted;
+
+  /* We do not shutdown dead connections. The term 'dead' can be misleading
+   * here, as we also mark errored connections/transfers as 'dead'.
+   * If we do a shutdown for an aborted transfer, the server might think
+   * it was successful otherwise (for example an ftps: upload). This is
+   * not what we want. */
+  if(aborted)
+    done = TRUE;
+  if(!done) {
+    /* Attempt to shutdown the connection right away. */
+    Curl_cshutdn_run_once(cpool->idata, conn, &done);
+  }
+
+  if(done || !data->multi)
+    Curl_cshutdn_terminate(cpool->idata, conn, FALSE);
+  else
+    Curl_cshutdn_add(&data->multi->cshutdn, conn, cpool->num_conn);
+}
+
+void Curl_conn_terminate(struct Curl_easy *data,
+                         struct connectdata *conn,
+                         bool aborted)
+{
+  struct cpool *cpool = cpool_get_instance(data);
+  bool do_lock;
+
+  DEBUGASSERT(cpool);
+  DEBUGASSERT(data && !data->conn);
+  if(!cpool)
+    return;
+
+  /* If this connection is not marked to force-close, leave it open if there
+   * are other users of it */
+  if(CONN_INUSE(conn) && !aborted) {
+    DEBUGASSERT(0); /* does this ever happen? */
+    DEBUGF(infof(data, "Curl_disconnect when inuse: %u", CONN_ATTACHED(conn)));
+    return;
+  }
+
+  /* This method may be called while we are under lock, e.g. from a
+   * user callback in find. */
+  do_lock = !CPOOL_IS_LOCKED(cpool);
+  if(do_lock)
+    CPOOL_LOCK(cpool, data);
+
+  if(conn->bits.in_cpool) {
+    cpool_remove_conn(cpool, conn);
+    DEBUGASSERT(!conn->bits.in_cpool);
+  }
+
+  /* treat the connection as aborted in CONNECT_ONLY situations,
+   * so no graceful shutdown is attempted. */
+  if(conn->connect_only)
+    aborted = TRUE;
+
+  if(data->multi) {
+    /* Add it to the multi's cpool for shutdown handling */
+    infof(data, "%s connection #%" FMT_OFF_T,
+          aborted ? "closing" : "shutting down", conn->connection_id);
+    cpool_discard_conn(&data->multi->cpool, data, conn, aborted);
+  }
+  else {
+    /* No multi available, terminate */
+    infof(data, "closing connection #%" FMT_OFF_T, conn->connection_id);
+    Curl_cshutdn_terminate(cpool->idata, conn, !aborted);
+  }
+
+  if(do_lock)
+    CPOOL_UNLOCK(cpool, data);
+}
+
+
+struct cpool_reaper_ctx {
+  struct curltime now;
+};
+
+static int cpool_reap_dead_cb(struct Curl_easy *data,
+                              struct connectdata *conn, void *param)
+{
+  struct cpool_reaper_ctx *rctx = param;
+  if(Curl_conn_seems_dead(conn, data, &rctx->now)) {
+    /* stop the iteration here, pass back the connection that was pruned */
+    Curl_conn_terminate(data, conn, FALSE);
+    return 1;
+  }
+  return 0; /* continue iteration */
+}
+
+/*
+ * This function scans the data's connection pool for half-open/dead
+ * connections, closes and removes them.
+ * The cleanup is done at most once per second.
+ *
+ * When called, this transfer has no connection attached.
+ */
+void Curl_cpool_prune_dead(struct Curl_easy *data)
+{
+  struct cpool *cpool = cpool_get_instance(data);
+  struct cpool_reaper_ctx rctx;
+  timediff_t elapsed;
+
+  if(!cpool)
+    return;
+
+  rctx.now = curlx_now();
+  CPOOL_LOCK(cpool, data);
+  elapsed = curlx_timediff(rctx.now, cpool->last_cleanup);
+
+  if(elapsed >= 1000L) {
+    while(cpool_foreach(data, cpool, &rctx, cpool_reap_dead_cb))
+      ;
+    cpool->last_cleanup = rctx.now;
+  }
+  CPOOL_UNLOCK(cpool, data);
+}
+
+static int conn_upkeep(struct Curl_easy *data,
+                       struct connectdata *conn,
+                       void *param)
+{
+  struct curltime *now = param;
+  Curl_conn_upkeep(data, conn, now);
+  return 0; /* continue iteration */
+}
+
+CURLcode Curl_cpool_upkeep(void *data)
+{
+  struct cpool *cpool = cpool_get_instance(data);
+  struct curltime now = curlx_now();
+
+  if(!cpool)
+    return CURLE_OK;
+
+  CPOOL_LOCK(cpool, data);
+  cpool_foreach(data, cpool, &now, conn_upkeep);
+  CPOOL_UNLOCK(cpool, data);
+  return CURLE_OK;
+}
+
+struct cpool_find_ctx {
+  curl_off_t id;
+  struct connectdata *conn;
+};
+
+static int cpool_find_conn(struct Curl_easy *data,
+                           struct connectdata *conn, void *param)
+{
+  struct cpool_find_ctx *fctx = param;
+  (void)data;
+  if(conn->connection_id == fctx->id) {
+    fctx->conn = conn;
+    return 1;
+  }
+  return 0;
+}
+
+struct connectdata *Curl_cpool_get_conn(struct Curl_easy *data,
+                                        curl_off_t conn_id)
+{
+  struct cpool *cpool = cpool_get_instance(data);
+  struct cpool_find_ctx fctx;
+
+  if(!cpool)
+    return NULL;
+  fctx.id = conn_id;
+  fctx.conn = NULL;
+  CPOOL_LOCK(cpool, data);
+  cpool_foreach(data, cpool, &fctx, cpool_find_conn);
+  CPOOL_UNLOCK(cpool, data);
+  return fctx.conn;
+}
+
+struct cpool_do_conn_ctx {
+  curl_off_t id;
+  Curl_cpool_conn_do_cb *cb;
+  void *cbdata;
+};
+
+static int cpool_do_conn(struct Curl_easy *data,
+                         struct connectdata *conn, void *param)
+{
+  struct cpool_do_conn_ctx *dctx = param;
+  (void)data;
+  if(conn->connection_id == dctx->id) {
+    dctx->cb(conn, data, dctx->cbdata);
+    return 1;
+  }
+  return 0;
+}
+
+void Curl_cpool_do_by_id(struct Curl_easy *data, curl_off_t conn_id,
+                         Curl_cpool_conn_do_cb *cb, void *cbdata)
+{
+  struct cpool *cpool = cpool_get_instance(data);
+  struct cpool_do_conn_ctx dctx;
+
+  if(!cpool)
+    return;
+  dctx.id = conn_id;
+  dctx.cb = cb;
+  dctx.cbdata = cbdata;
+  CPOOL_LOCK(cpool, data);
+  cpool_foreach(data, cpool, &dctx, cpool_do_conn);
+  CPOOL_UNLOCK(cpool, data);
+}
+
+void Curl_cpool_do_locked(struct Curl_easy *data,
+                          struct connectdata *conn,
+                          Curl_cpool_conn_do_cb *cb, void *cbdata)
+{
+  struct cpool *cpool = cpool_get_instance(data);
+  if(cpool) {
+    CPOOL_LOCK(cpool, data);
+    cb(conn, data, cbdata);
+    CPOOL_UNLOCK(cpool, data);
+  }
+  else
+    cb(conn, data, cbdata);
+}
+
+#if 0
+/* Useful for debugging the connection pool */
+void Curl_cpool_print(struct cpool *cpool)
+{
+  struct Curl_hash_iterator iter;
+  struct Curl_llist_node *curr;
+  struct Curl_hash_element *he;
+
+  if(!cpool)
+    return;
+
+  fprintf(stderr, "=Bundle cache=\n");
+
+  Curl_hash_start_iterate(cpool->dest2bundle, &iter);
+
+  he = Curl_hash_next_element(&iter);
+  while(he) {
+    struct cpool_bundle *bundle;
+    struct connectdata *conn;
+
+    bundle = he->ptr;
+
+    fprintf(stderr, "%s -", he->key);
+    curr = Curl_llist_head(bundle->conns);
+    while(curr) {
+      conn = Curl_node_elem(curr);
+
+      fprintf(stderr, " [%p %d]", (void *)conn, conn->refcount);
+      curr = Curl_node_next(curr);
+    }
+    fprintf(stderr, "\n");
+
+    he = Curl_hash_next_element(&iter);
+  }
+}
+#endif
diff --git a/extra/curl/curl-8.12.1/lib/conncache.h b/extra/curl/curl-8.14.1/lib/conncache.h
similarity index 64%
rename from extra/curl/curl-8.12.1/lib/conncache.h
rename to extra/curl/curl-8.14.1/lib/conncache.h
index 5f239bc0b0a8..1314b65c6076 100644
--- a/extra/curl/curl-8.12.1/lib/conncache.h
+++ b/extra/curl/curl-8.14.1/lib/conncache.h
@@ -26,7 +26,7 @@
  ***************************************************************************/
 
 #include <curl/curl.h>
-#include "timeval.h"
+#include "curlx/timeval.h"
 
 struct connectdata;
 struct Curl_easy;
@@ -36,16 +36,17 @@ struct Curl_multi;
 struct Curl_share;
 
 /**
- * Callback invoked when disconnecting connections.
- * @param data    transfer last handling the connection, not attached
- * @param conn    the connection to discard
- * @param aborted if the connection is being aborted
- * @return if the connection is being aborted, e.g. should NOT perform
- *         a shutdown and just close.
- **/
-typedef bool Curl_cpool_disconnect_cb(struct Curl_easy *data,
-                                      struct connectdata *conn,
-                                      bool aborted);
+ * Terminate the connection, e.g. close and destroy.
+ * If the connection is in a cpool, remove it.
+ * If a `cshutdn` is available (e.g. data has a multi handle),
+ * pass the connection to that for controlled shutdown.
+ * Otherwise terminate it right away.
+ * Takes ownership of `conn`.
+ * `data` should not be attached to a connection.
+ */
+void Curl_conn_terminate(struct Curl_easy *data,
+                         struct connectdata *conn,
+                         bool aborted);
 
 struct cpool {
    /* the pooled connections, bundled per destination */
@@ -54,22 +55,19 @@ struct cpool {
   curl_off_t next_connection_id;
   curl_off_t next_easy_id;
   struct curltime last_cleanup;
-  struct Curl_llist shutdowns;  /* The connections being shut down */
-  struct Curl_easy *idata; /* internal handle used for discard */
-  struct Curl_multi *multi; /* != NULL iff pool belongs to multi */
-  struct Curl_share *share; /* != NULL iff pool belongs to share */
-  Curl_cpool_disconnect_cb *disconnect_cb;
+  struct Curl_easy *idata; /* internal handle for maintenance */
+  struct Curl_share *share; /* != NULL if pool belongs to share */
   BIT(locked);
+  BIT(initialised);
 };
 
 /* Init the pool, pass multi only if pool is owned by it.
- * returns 1 on error, 0 is fine.
+ * Cannot fail.
  */
-int Curl_cpool_init(struct cpool *cpool,
-                    Curl_cpool_disconnect_cb *disconnect_cb,
-                    struct Curl_multi *multi,
-                    struct Curl_share *share,
-                    size_t size);
+void Curl_cpool_init(struct cpool *cpool,
+                     struct Curl_easy *idata,
+                     struct Curl_share *share,
+                     size_t size);
 
 /* Destroy all connections and free all members */
 void Curl_cpool_destroy(struct cpool *connc);
@@ -78,14 +76,13 @@ void Curl_cpool_destroy(struct cpool *connc);
  * Assigns `data->id`. */
 void Curl_cpool_xfer_init(struct Curl_easy *data);
 
-/**
- * Get the connection with the given id from the transfer's pool.
- */
+/* Get the connection with the given id from `data`'s conn pool. */
 struct connectdata *Curl_cpool_get_conn(struct Curl_easy *data,
                                         curl_off_t conn_id);
 
-CURLcode Curl_cpool_add_conn(struct Curl_easy *data,
-                             struct connectdata *conn) WARN_UNUSED_RESULT;
+/* Add the connection to the pool. */
+CURLcode Curl_cpool_add(struct Curl_easy *data,
+                        struct connectdata *conn) WARN_UNUSED_RESULT;
 
 /**
  * Return if the pool has reached its configured limits for adding
@@ -110,14 +107,13 @@ typedef bool Curl_cpool_done_match_cb(bool result, void *userdata);
  * All callbacks are invoked while the pool's lock is held.
  * @param data        current transfer
  * @param destination match agaonst `conn->destination` in pool
- * @param dest_len    destination length, including terminating NUL
  * @param conn_cb     must be present, called for each connection in the
  *                    bundle until it returns TRUE
  * @return combined result of last conn_db and result_cb or FALSE if no
                       connections were present.
  */
 bool Curl_cpool_find(struct Curl_easy *data,
-                     const char *destination, size_t dest_len,
+                     const char *destination,
                      Curl_cpool_conn_match_cb *conn_cb,
                      Curl_cpool_done_match_cb *done_cb,
                      void *userdata);
@@ -131,17 +127,6 @@ bool Curl_cpool_find(struct Curl_easy *data,
 bool Curl_cpool_conn_now_idle(struct Curl_easy *data,
                               struct connectdata *conn);
 
-/**
- * Remove the connection from the pool and tear it down.
- * If `aborted` is FALSE, the connection will be shut down first
- * before closing and destroying it.
- * If the shutdown is not immediately complete, the connection
- * will be placed into the pool's shutdown queue.
- */
-void Curl_cpool_disconnect(struct Curl_easy *data,
-                           struct connectdata *conn,
-                           bool aborted);
-
 /**
  * This function scans the data's connection pool for half-open/dead
  * connections, closes and removes them.
@@ -178,26 +163,4 @@ void Curl_cpool_do_locked(struct Curl_easy *data,
                           struct connectdata *conn,
                           Curl_cpool_conn_do_cb *cb, void *cbdata);
 
-/**
- * Add sockets and POLLIN/OUT flags for connections handled by the pool.
- */
-CURLcode Curl_cpool_add_pollfds(struct cpool *connc,
-                                struct curl_pollfds *cpfds);
-unsigned int Curl_cpool_add_waitfds(struct cpool *connc,
-                                    struct Curl_waitfds *cwfds);
-
-void Curl_cpool_setfds(struct cpool *cpool,
-                       fd_set *read_fd_set, fd_set *write_fd_set,
-                       int *maxfd);
-
-/**
- * Perform maintenance on connections in the pool. Specifically,
- * progress the shutdown of connections in the queue.
- */
-void Curl_cpool_multi_perform(struct Curl_multi *multi);
-
-void Curl_cpool_multi_socket(struct Curl_multi *multi,
-                             curl_socket_t s, int ev_bitmask);
-
-
 #endif /* HEADER_CURL_CONNCACHE_H */
diff --git a/extra/curl/curl-8.12.1/lib/connect.c b/extra/curl/curl-8.14.1/lib/connect.c
similarity index 92%
rename from extra/curl/curl-8.12.1/lib/connect.c
rename to extra/curl/curl-8.14.1/lib/connect.c
index 67cdb54d313e..1dcdde3fc5a8 100644
--- a/extra/curl/curl-8.12.1/lib/connect.c
+++ b/extra/curl/curl-8.14.1/lib/connect.c
@@ -67,14 +67,14 @@
 #include "multiif.h"
 #include "sockaddr.h" /* required for Curl_sockaddr_storage */
 #include "inet_ntop.h"
-#include "inet_pton.h"
+#include "curlx/inet_pton.h"
 #include "vtls/vtls.h" /* for vtsl cfilters */
 #include "progress.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
 #include "conncache.h"
 #include "multihandle.h"
 #include "share.h"
-#include "version_win32.h"
+#include "curlx/version_win32.h"
 #include "vquic/vquic.h" /* for quic cfilters */
 #include "http_proxy.h"
 #include "socks.h"
@@ -87,7 +87,7 @@
 
 #if !defined(CURL_DISABLE_ALTSVC) || defined(USE_HTTPSRR)
 
-enum alpnid Curl_alpn2alpnid(char *name, size_t len)
+enum alpnid Curl_alpn2alpnid(const char *name, size_t len)
 {
   if(len == 2) {
     if(strncasecompare(name, "h1", 2))
@@ -112,7 +112,7 @@ enum alpnid Curl_alpn2alpnid(char *name, size_t len)
  * infinite time left). If the value is negative, the timeout time has already
  * elapsed.
  * @param data the transfer to check on
- * @param nowp timestamp to use for calculation, NULL to use Curl_now()
+ * @param nowp timestamp to use for calculation, NULL to use curlx_now()
  * @param duringconnect TRUE iff connect timeout is also taken into account.
  * @unittest: 1303
  */
@@ -133,13 +133,13 @@ timediff_t Curl_timeleft(struct Curl_easy *data,
     return 0; /* no timeout in place or checked, return "no limit" */
 
   if(!nowp) {
-    now = Curl_now();
+    now = curlx_now();
     nowp = &now;
   }
 
   if(data->set.timeout > 0) {
     timeleft_ms = data->set.timeout -
-                  Curl_timediff(*nowp, data->progress.t_startop);
+                  curlx_timediff(*nowp, data->progress.t_startop);
     if(!timeleft_ms)
       timeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */
     if(!duringconnect)
@@ -150,7 +150,7 @@ timediff_t Curl_timeleft(struct Curl_easy *data,
     timediff_t ctimeout_ms = (data->set.connecttimeout > 0) ?
       data->set.connecttimeout : DEFAULT_CONNECT_TIMEOUT;
     ctimeleft_ms = ctimeout_ms -
-                   Curl_timediff(*nowp, data->progress.t_startsingle);
+                   curlx_timediff(*nowp, data->progress.t_startsingle);
     if(!ctimeleft_ms)
       ctimeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */
     if(!timeleft_ms)
@@ -161,18 +161,24 @@ timediff_t Curl_timeleft(struct Curl_easy *data,
 }
 
 void Curl_shutdown_start(struct Curl_easy *data, int sockindex,
-                         struct curltime *nowp)
+                         int timeout_ms, struct curltime *nowp)
 {
   struct curltime now;
 
   DEBUGASSERT(data->conn);
   if(!nowp) {
-    now = Curl_now();
+    now = curlx_now();
     nowp = &now;
   }
   data->conn->shutdown.start[sockindex] = *nowp;
-  data->conn->shutdown.timeout_ms = (data->set.shutdowntimeout > 0) ?
-    data->set.shutdowntimeout : DEFAULT_SHUTDOWN_TIMEOUT_MS;
+  data->conn->shutdown.timeout_ms = (timeout_ms > 0) ?
+    (unsigned int)timeout_ms :
+    ((data->set.shutdowntimeout > 0) ?
+     data->set.shutdowntimeout : DEFAULT_SHUTDOWN_TIMEOUT_MS);
+  /* Set a timer, unless we operate on the admin handle */
+  if(data->mid && data->conn->shutdown.timeout_ms)
+    Curl_expire_ex(data, nowp, data->conn->shutdown.timeout_ms,
+                   EXPIRE_SHUTDOWN);
 }
 
 timediff_t Curl_shutdown_timeleft(struct connectdata *conn, int sockindex,
@@ -185,11 +191,11 @@ timediff_t Curl_shutdown_timeleft(struct connectdata *conn, int sockindex,
     return 0; /* not started or no limits */
 
   if(!nowp) {
-    now = Curl_now();
+    now = curlx_now();
     nowp = &now;
   }
   left_ms = conn->shutdown.timeout_ms -
-            Curl_timediff(*nowp, conn->shutdown.start[sockindex]);
+            curlx_timediff(*nowp, conn->shutdown.start[sockindex]);
   return left_ms ? left_ms : -1;
 }
 
@@ -204,7 +210,7 @@ timediff_t Curl_conn_shutdown_timeleft(struct connectdata *conn,
     if(!conn->shutdown.start[i].tv_sec)
       continue;
     if(!nowp) {
-      now = Curl_now();
+      now = curlx_now();
       nowp = &now;
     }
     ms = Curl_shutdown_timeleft(conn, i, nowp);
@@ -266,8 +272,7 @@ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
   switch(sa->sa_family) {
     case AF_INET:
       si = (struct sockaddr_in *)(void *) sa;
-      if(Curl_inet_ntop(sa->sa_family, &si->sin_addr,
-                        addr, MAX_IPADR_LEN)) {
+      if(Curl_inet_ntop(sa->sa_family, &si->sin_addr, addr, MAX_IPADR_LEN)) {
         unsigned short us_port = ntohs(si->sin_port);
         *port = us_port;
         return TRUE;
@@ -276,8 +281,7 @@ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
 #ifdef USE_IPV6
     case AF_INET6:
       si6 = (struct sockaddr_in6 *)(void *) sa;
-      if(Curl_inet_ntop(sa->sa_family, &si6->sin6_addr,
-                        addr, MAX_IPADR_LEN)) {
+      if(Curl_inet_ntop(sa->sa_family, &si6->sin6_addr, addr, MAX_IPADR_LEN)) {
         unsigned short us_port = ntohs(si6->sin6_port);
         *port = us_port;
         return TRUE;
@@ -301,7 +305,7 @@ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
 
   addr[0] = '\0';
   *port = 0;
-  errno = EAFNOSUPPORT;
+  CURL_SETERRNO(SOCKEAFNOSUPPORT);
   return FALSE;
 }
 
@@ -404,7 +408,6 @@ typedef enum {
 struct cf_he_ctx {
   int transport;
   cf_ip_connect_create *cf_create;
-  const struct Curl_dns_entry *remotehost;
   cf_connect_state state;
   struct eyeballer *baller[2];
   struct eyeballer *winner;
@@ -541,7 +544,7 @@ static CURLcode baller_start(struct Curl_cfilter *cf,
   baller->has_started = TRUE;
 
   while(baller->addr) {
-    baller->started = Curl_now();
+    baller->started = curlx_now();
     baller->timeoutms = addr_next_match(baller->addr, baller->ai_family) ?
       USETIME(timeoutms) : timeoutms;
     baller_initiate(cf, data, baller);
@@ -593,18 +596,18 @@ static CURLcode baller_connect(struct Curl_cfilter *cf,
   *connected = baller->connected;
   if(!baller->result &&  !*connected) {
     /* evaluate again */
-    baller->result = Curl_conn_cf_connect(baller->cf, data, 0, connected);
+    baller->result = Curl_conn_cf_connect(baller->cf, data, connected);
 
     if(!baller->result) {
       if(*connected) {
         baller->connected = TRUE;
         baller->is_done = TRUE;
       }
-      else if(Curl_timediff(*now, baller->started) >= baller->timeoutms) {
+      else if(curlx_timediff(*now, baller->started) >= baller->timeoutms) {
         infof(data, "%s connect timeout after %" FMT_TIMEDIFF_T
               "ms, move on!", baller->name, baller->timeoutms);
-#if defined(ETIMEDOUT)
-        baller->error = ETIMEDOUT;
+#ifdef SOCKETIMEDOUT
+        baller->error = SOCKETIMEDOUT;
 #endif
         baller->result = CURLE_OPERATION_TIMEDOUT;
       }
@@ -638,7 +641,7 @@ static CURLcode is_connected(struct Curl_cfilter *cf,
    * cot ballers in a QUIC appropriate way. */
 evaluate:
   *connected = FALSE; /* a negative world view is best */
-  now = Curl_now();
+  now = curlx_now();
   ongoing = not_started = 0;
   for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
     struct eyeballer *baller = ctx->baller[i];
@@ -693,7 +696,7 @@ static CURLcode is_connected(struct Curl_cfilter *cf,
    * start new ballers or return ok. */
   if((ongoing || not_started) && Curl_timeleft(data, &now, TRUE) < 0) {
     failf(data, "Connection timeout after %" FMT_OFF_T " ms",
-          Curl_timediff(now, data->progress.t_startsingle));
+          curlx_timediff(now, data->progress.t_startsingle));
     return CURLE_OPERATION_TIMEDOUT;
   }
 
@@ -709,7 +712,7 @@ static CURLcode is_connected(struct Curl_cfilter *cf,
       /* We start its primary baller has failed to connect or if
        * its start delay_ms have expired */
       if((baller->primary && baller->primary->is_done) ||
-          Curl_timediff(now, ctx->started) >= baller->delay_ms) {
+          curlx_timediff(now, ctx->started) >= baller->delay_ms) {
         baller_start(cf, data, baller, Curl_timeleft(data, &now, TRUE));
         if(baller->is_done) {
           CURL_TRC_CF(data, cf, "%s done", baller->name);
@@ -762,14 +765,11 @@ static CURLcode is_connected(struct Curl_cfilter *cf,
   failf(data, "Failed to connect to %s port %u after "
         "%" FMT_TIMEDIFF_T " ms: %s",
         hostname, conn->primary.remote_port,
-        Curl_timediff(now, data->progress.t_startsingle),
+        curlx_timediff(now, data->progress.t_startsingle),
         curl_easy_strerror(result));
 
-#ifdef WSAETIMEDOUT
-  if(WSAETIMEDOUT == data->state.os_errno)
-    result = CURLE_OPERATION_TIMEDOUT;
-#elif defined(ETIMEDOUT)
-  if(ETIMEDOUT == data->state.os_errno)
+#ifdef SOCKETIMEDOUT
+  if(SOCKETIMEDOUT == data->state.os_errno)
     result = CURLE_OPERATION_TIMEDOUT;
 #endif
 
@@ -781,8 +781,7 @@ static CURLcode is_connected(struct Curl_cfilter *cf,
  * There might be more than one IP address to try out.
  */
 static CURLcode start_connect(struct Curl_cfilter *cf,
-                              struct Curl_easy *data,
-                              const struct Curl_dns_entry *remotehost)
+                              struct Curl_easy *data)
 {
   struct cf_he_ctx *ctx = cf->ctx;
   struct connectdata *conn = cf->conn;
@@ -790,6 +789,10 @@ static CURLcode start_connect(struct Curl_cfilter *cf,
   int ai_family0 = 0, ai_family1 = 0;
   timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
   const struct Curl_addrinfo *addr0 = NULL, *addr1 = NULL;
+  struct Curl_dns_entry *dns = data->state.dns[cf->sockindex];
+
+  if(!dns)
+    return CURLE_FAILED_INIT;
 
   if(timeout_ms < 0) {
     /* a precaution, no need to continue if time already is up */
@@ -797,9 +800,9 @@ static CURLcode start_connect(struct Curl_cfilter *cf,
     return CURLE_OPERATION_TIMEDOUT;
   }
 
-  ctx->started = Curl_now();
+  ctx->started = curlx_now();
 
-  /* remotehost->addr is the list of addresses from the resolver, each
+  /* dns->addr is the list of addresses from the resolver, each
    * with an address family. The list has at least one entry, possibly
    * many more.
    * We try at most 2 at a time, until we either get a connection or
@@ -811,27 +814,27 @@ static CURLcode start_connect(struct Curl_cfilter *cf,
   if(conn->ip_version == CURL_IPRESOLVE_V6) {
 #ifdef USE_IPV6
     ai_family0 = AF_INET6;
-    addr0 = addr_first_match(remotehost->addr, ai_family0);
+    addr0 = addr_first_match(dns->addr, ai_family0);
 #endif
   }
   else if(conn->ip_version == CURL_IPRESOLVE_V4) {
     ai_family0 = AF_INET;
-    addr0 = addr_first_match(remotehost->addr, ai_family0);
+    addr0 = addr_first_match(dns->addr, ai_family0);
   }
   else {
     /* no user preference, we try ipv6 always first when available */
 #ifdef USE_IPV6
     ai_family0 = AF_INET6;
-    addr0 = addr_first_match(remotehost->addr, ai_family0);
+    addr0 = addr_first_match(dns->addr, ai_family0);
 #endif
     /* next candidate is ipv4 */
     ai_family1 = AF_INET;
-    addr1 = addr_first_match(remotehost->addr, ai_family1);
+    addr1 = addr_first_match(dns->addr, ai_family1);
     /* no ip address families, probably AF_UNIX or something, use the
      * address family given to us */
-    if(!addr1  && !addr0 && remotehost->addr) {
-      ai_family0 = remotehost->addr->ai_family;
-      addr0 = addr_first_match(remotehost->addr, ai_family0);
+    if(!addr1  && !addr0 && dns->addr) {
+      ai_family0 = dns->addr->ai_family;
+      addr0 = addr_first_match(dns->addr, ai_family0);
     }
   }
 
@@ -948,7 +951,7 @@ static void cf_he_adjust_pollset(struct Curl_cfilter *cf,
 
 static CURLcode cf_he_connect(struct Curl_cfilter *cf,
                               struct Curl_easy *data,
-                              bool blocking, bool *done)
+                              bool *done)
 {
   struct cf_he_ctx *ctx = cf->ctx;
   CURLcode result = CURLE_OK;
@@ -958,7 +961,6 @@ static CURLcode cf_he_connect(struct Curl_cfilter *cf,
     return CURLE_OK;
   }
 
-  (void)blocking;
   DEBUGASSERT(ctx);
   *done = FALSE;
 
@@ -966,7 +968,7 @@ static CURLcode cf_he_connect(struct Curl_cfilter *cf,
     case SCFST_INIT:
       DEBUGASSERT(CURL_SOCKET_BAD == Curl_conn_cf_get_socket(cf, data));
       DEBUGASSERT(!cf->connected);
-      result = start_connect(cf, data, ctx->remotehost);
+      result = start_connect(cf, data);
       if(result)
         return result;
       ctx->state = SCFST_WAITING;
@@ -1058,7 +1060,7 @@ static struct curltime get_max_baller_time(struct Curl_cfilter *cf,
     memset(&t, 0, sizeof(t));
     if(baller && baller->cf &&
        !baller->cf->cft->query(baller->cf, data, query, NULL, &t)) {
-      if((t.tv_sec || t.tv_usec) && Curl_timediff_us(t, tmax) > 0)
+      if((t.tv_sec || t.tv_usec) && curlx_timediff_us(t, tmax) > 0)
         tmax = t;
     }
   }
@@ -1158,7 +1160,6 @@ cf_happy_eyeballs_create(struct Curl_cfilter **pcf,
                          struct Curl_easy *data,
                          struct connectdata *conn,
                          cf_ip_connect_create *cf_create,
-                         const struct Curl_dns_entry *remotehost,
                          int transport)
 {
   struct cf_he_ctx *ctx = NULL;
@@ -1174,14 +1175,13 @@ cf_happy_eyeballs_create(struct Curl_cfilter **pcf,
   }
   ctx->transport = transport;
   ctx->cf_create = cf_create;
-  ctx->remotehost = remotehost;
 
   result = Curl_cf_create(pcf, &Curl_cft_happy_eyeballs, ctx);
 
 out:
   if(result) {
     Curl_safefree(*pcf);
-    Curl_safefree(ctx);
+    free(ctx);
   }
   return result;
 }
@@ -1220,7 +1220,6 @@ static cf_ip_connect_create *get_cf_create(int transport)
 
 static CURLcode cf_he_insert_after(struct Curl_cfilter *cf_at,
                                    struct Curl_easy *data,
-                                   const struct Curl_dns_entry *remotehost,
                                    int transport)
 {
   cf_ip_connect_create *cf_create;
@@ -1235,8 +1234,7 @@ static CURLcode cf_he_insert_after(struct Curl_cfilter *cf_at,
     return CURLE_UNSUPPORTED_PROTOCOL;
   }
   result = cf_happy_eyeballs_create(&cf, data, cf_at->conn,
-                                    cf_create, remotehost,
-                                    transport);
+                                    cf_create, transport);
   if(result)
     return result;
 
@@ -1256,17 +1254,17 @@ typedef enum {
 
 struct cf_setup_ctx {
   cf_setup_state state;
-  const struct Curl_dns_entry *remotehost;
   int ssl_mode;
   int transport;
 };
 
 static CURLcode cf_setup_connect(struct Curl_cfilter *cf,
                                  struct Curl_easy *data,
-                                 bool blocking, bool *done)
+                                 bool *done)
 {
   struct cf_setup_ctx *ctx = cf->ctx;
   CURLcode result = CURLE_OK;
+  struct Curl_dns_entry *dns = data->state.dns[cf->sockindex];
 
   if(cf->connected) {
     *done = TRUE;
@@ -1275,14 +1273,17 @@ static CURLcode cf_setup_connect(struct Curl_cfilter *cf,
 
   /* connect current sub-chain */
 connect_sub_chain:
+  if(!dns)
+    return CURLE_FAILED_INIT;
+
   if(cf->next && !cf->next->connected) {
-    result = Curl_conn_cf_connect(cf->next, data, blocking, done);
+    result = Curl_conn_cf_connect(cf->next, data, done);
     if(result || !*done)
       return result;
   }
 
   if(ctx->state < CF_SETUP_CNNCT_EYEBALLS) {
-    result = cf_he_insert_after(cf, data, ctx->remotehost, ctx->transport);
+    result = cf_he_insert_after(cf, data, ctx->transport);
     if(result)
       return result;
     ctx->state = CF_SETUP_CNNCT_EYEBALLS;
@@ -1410,7 +1411,6 @@ struct Curl_cftype Curl_cft_setup = {
 
 static CURLcode cf_setup_create(struct Curl_cfilter **pcf,
                                 struct Curl_easy *data,
-                                const struct Curl_dns_entry *remotehost,
                                 int transport,
                                 int ssl_mode)
 {
@@ -1425,7 +1425,6 @@ static CURLcode cf_setup_create(struct Curl_cfilter **pcf,
     goto out;
   }
   ctx->state = CF_SETUP_INIT;
-  ctx->remotehost = remotehost;
   ctx->ssl_mode = ssl_mode;
   ctx->transport = transport;
 
@@ -1436,14 +1435,15 @@ static CURLcode cf_setup_create(struct Curl_cfilter **pcf,
 
 out:
   *pcf = result ? NULL : cf;
-  free(ctx);
+  if(ctx) {
+    free(ctx);
+  }
   return result;
 }
 
 static CURLcode cf_setup_add(struct Curl_easy *data,
                              struct connectdata *conn,
                              int sockindex,
-                             const struct Curl_dns_entry *remotehost,
                              int transport,
                              int ssl_mode)
 {
@@ -1451,7 +1451,7 @@ static CURLcode cf_setup_add(struct Curl_easy *data,
   CURLcode result = CURLE_OK;
 
   DEBUGASSERT(data);
-  result = cf_setup_create(&cf, data, remotehost, transport, ssl_mode);
+  result = cf_setup_create(&cf, data, transport, ssl_mode);
   if(result)
     goto out;
   Curl_conn_cf_add(data, conn, sockindex, cf);
@@ -1476,7 +1476,6 @@ void Curl_debug_set_transport_provider(int transport,
 
 CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at,
                                     struct Curl_easy *data,
-                                    const struct Curl_dns_entry *remotehost,
                                     int transport,
                                     int ssl_mode)
 {
@@ -1484,7 +1483,7 @@ CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at,
   CURLcode result;
 
   DEBUGASSERT(data);
-  result = cf_setup_create(&cf, data, remotehost, transport, ssl_mode);
+  result = cf_setup_create(&cf, data, transport, ssl_mode);
   if(result)
     goto out;
   Curl_conn_cf_insert_after(cf_at, cf);
@@ -1495,19 +1494,23 @@ CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at,
 CURLcode Curl_conn_setup(struct Curl_easy *data,
                          struct connectdata *conn,
                          int sockindex,
-                         const struct Curl_dns_entry *remotehost,
+                         struct Curl_dns_entry *dns,
                          int ssl_mode)
 {
   CURLcode result = CURLE_OK;
 
   DEBUGASSERT(data);
   DEBUGASSERT(conn->handler);
+  DEBUGASSERT(dns);
+
+  Curl_resolv_unlink(data, &data->state.dns[sockindex]);
+  data->state.dns[sockindex] = dns;
 
 #if !defined(CURL_DISABLE_HTTP)
   if(!conn->cfilter[sockindex] &&
      conn->handler->protocol == CURLPROTO_HTTPS) {
     DEBUGASSERT(ssl_mode != CURL_CF_SSL_DISABLE);
-    result = Curl_cf_https_setup(data, conn, sockindex, remotehost);
+    result = Curl_cf_https_setup(data, conn, sockindex);
     if(result)
       goto out;
   }
@@ -1515,13 +1518,14 @@ CURLcode Curl_conn_setup(struct Curl_easy *data,
 
   /* Still no cfilter set, apply default. */
   if(!conn->cfilter[sockindex]) {
-    result = cf_setup_add(data, conn, sockindex, remotehost,
-                          conn->transport, ssl_mode);
+    result = cf_setup_add(data, conn, sockindex, conn->transport, ssl_mode);
     if(result)
       goto out;
   }
 
   DEBUGASSERT(conn->cfilter[sockindex]);
 out:
+  if(result)
+    Curl_resolv_unlink(data, &data->state.dns[sockindex]);
   return result;
 }
diff --git a/extra/curl/curl-8.12.1/lib/connect.h b/extra/curl/curl-8.14.1/lib/connect.h
similarity index 94%
rename from extra/curl/curl-8.12.1/lib/connect.h
rename to extra/curl/curl-8.14.1/lib/connect.h
index b59c38d7ceb9..120338eb99fb 100644
--- a/extra/curl/curl-8.12.1/lib/connect.h
+++ b/extra/curl/curl-8.14.1/lib/connect.h
@@ -25,14 +25,14 @@
  ***************************************************************************/
 #include "curl_setup.h"
 
-#include "nonblock.h" /* for curlx_nonblock(), formerly Curl_nonblock() */
+#include "curlx/nonblock.h" /* for curlx_nonblock() */
 #include "sockaddr.h"
-#include "timeval.h"
+#include "curlx/timeval.h"
 
 struct Curl_dns_entry;
 struct ip_quadruple;
 
-enum alpnid Curl_alpn2alpnid(char *name, size_t len);
+enum alpnid Curl_alpn2alpnid(const char *name, size_t len);
 
 /* generic function that returns how much time there is left to run, according
    to the timeouts set */
@@ -45,7 +45,7 @@ timediff_t Curl_timeleft(struct Curl_easy *data,
 #define DEFAULT_SHUTDOWN_TIMEOUT_MS   (2 * 1000)
 
 void Curl_shutdown_start(struct Curl_easy *data, int sockindex,
-                         struct curltime *nowp);
+                         int timeout_ms, struct curltime *nowp);
 
 /* return how much time there is left to shutdown the connection at
  * sockindex. Returns 0 if there is no limit or shutdown has not started. */
@@ -126,7 +126,6 @@ typedef CURLcode cf_ip_connect_create(struct Curl_cfilter **pcf,
 
 CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at,
                                     struct Curl_easy *data,
-                                    const struct Curl_dns_entry *remotehost,
                                     int transport,
                                     int ssl_mode);
 
@@ -138,7 +137,7 @@ CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at,
 CURLcode Curl_conn_setup(struct Curl_easy *data,
                          struct connectdata *conn,
                          int sockindex,
-                         const struct Curl_dns_entry *remotehost,
+                         struct Curl_dns_entry *dns,
                          int ssl_mode);
 
 extern struct Curl_cftype Curl_cft_happy_eyeballs;
diff --git a/extra/curl/curl-8.12.1/lib/content_encoding.c b/extra/curl/curl-8.14.1/lib/content_encoding.c
similarity index 96%
rename from extra/curl/curl-8.12.1/lib/content_encoding.c
rename to extra/curl/curl-8.14.1/lib/content_encoding.c
index e365af61682e..f2e77eede18a 100644
--- a/extra/curl/curl-8.12.1/lib/content_encoding.c
+++ b/extra/curl/curl-8.14.1/lib/content_encoding.c
@@ -65,12 +65,15 @@
 
 /* allow no more than 5 "chained" compression steps */
 #define MAX_ENCODE_STACK 5
+
+#if defined(HAVE_LIBZ) || defined(HAVE_BROTLI) || defined(HAVE_ZSTD)
 #define DECOMPRESS_BUFFER_SIZE 16384 /* buffer size for decompressed data */
+#endif
 
 #ifdef HAVE_LIBZ
 
-#if !defined(ZLIB_VERNUM) || (ZLIB_VERNUM < 0x1204)
-#error "requires zlib 1.2.0.4 or newer"
+#if !defined(ZLIB_VERNUM) || (ZLIB_VERNUM < 0x1252)
+#error "requires zlib 1.2.5.2 or newer"
 #endif
 
 typedef enum {
@@ -163,7 +166,7 @@ static CURLcode inflate_stream(struct Curl_easy *data,
   struct zlib_writer *zp = (struct zlib_writer *) writer;
   z_stream *z = &zp->z;         /* zlib state structure */
   uInt nread = z->avail_in;
-  Bytef *orig_in = z->next_in;
+  z_const Bytef *orig_in = z->next_in;
   bool done = FALSE;
   CURLcode result = CURLE_OK;   /* Curl_client_write status */
 
@@ -183,13 +186,7 @@ static CURLcode inflate_stream(struct Curl_easy *data,
     z->next_out = (Bytef *) zp->buffer;
     z->avail_out = DECOMPRESS_BUFFER_SIZE;
 
-#ifdef Z_BLOCK
-    /* Z_BLOCK is only available in zlib ver. >= 1.2.0.5 */
     status = inflate(z, Z_BLOCK);
-#else
-    /* fallback for zlib ver. < 1.2.0.5 */
-    status = inflate(z, Z_SYNC_FLUSH);
-#endif
 
     /* Flush output data if some. */
     if(z->avail_out != DECOMPRESS_BUFFER_SIZE) {
@@ -220,9 +217,7 @@ static CURLcode inflate_stream(struct Curl_easy *data,
       /* some servers seem to not generate zlib headers, so this is an attempt
          to fix and continue anyway */
       if(zp->zlib_init == ZLIB_INIT) {
-        /* Do not use inflateReset2(): only available since zlib 1.2.3.4. */
-        (void) inflateEnd(z);     /* do not care about the return code */
-        if(inflateInit2(z, -MAX_WBITS) == Z_OK) {
+        if(inflateReset2(z, -MAX_WBITS) == Z_OK) {
           z->next_in = orig_in;
           z->avail_in = nread;
           zp->zlib_init = ZLIB_INFLATING;
@@ -278,8 +273,8 @@ static CURLcode deflate_do_write(struct Curl_easy *data,
     return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
 
   /* Set the compressed input when this function is called */
-  z->next_in = (Bytef *) buf;
-  z->avail_in = (uInt) nbytes;
+  z->next_in = (z_const Bytef *)buf;
+  z->avail_in = (uInt)nbytes;
 
   if(zp->zlib_init == ZLIB_EXTERNAL_TRAILER)
     return process_trailer(data, zp);
@@ -337,8 +332,8 @@ static CURLcode gzip_do_write(struct Curl_easy *data,
 
   if(zp->zlib_init == ZLIB_INIT_GZIP) {
     /* Let zlib handle the gzip decompression entirely */
-    z->next_in = (Bytef *) buf;
-    z->avail_in = (uInt) nbytes;
+    z->next_in = (z_const Bytef *)buf;
+    z->avail_in = (uInt)nbytes;
     /* Now uncompress the data */
     return inflate_stream(data, writer, type, ZLIB_INIT_GZIP);
   }
@@ -742,6 +737,7 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
   Curl_cwriter_phase phase = is_transfer ?
     CURL_CW_TRANSFER_DECODE : CURL_CW_CONTENT_DECODE;
   CURLcode result;
+  bool has_chunked = FALSE;
 
   do {
     const char *name;
@@ -755,7 +751,7 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
     name = enclist;
 
     for(namelen = 0; *enclist && *enclist != ','; enclist++)
-      if(!ISSPACE(*enclist))
+      if(*enclist > ' ')
         namelen = enclist - name + 1;
 
     if(namelen) {
@@ -770,9 +766,21 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
        * Exception is "chunked" transfer-encoding which always must happen */
       if((is_transfer && !data->set.http_transfer_encoding && !is_chunked) ||
          (!is_transfer && data->set.http_ce_skip)) {
+        bool is_identity = strncasecompare(name, "identity", 8);
         /* not requested, ignore */
         CURL_TRC_WRITE(data, "decoder not requested, ignored: %.*s",
                        (int)namelen, name);
+        if(is_transfer && !data->set.http_te_skip) {
+          if(has_chunked)
+            failf(data, "A Transfer-Encoding (%.*s) was listed after chunked",
+                  (int)namelen, name);
+          else if(is_identity)
+            continue;
+          else
+            failf(data, "Unsolicited Transfer-Encoding (%.*s) found",
+                  (int)namelen, name);
+          return CURLE_BAD_CONTENT_ENCODING;
+        }
         return CURLE_OK;
       }
 
@@ -823,6 +831,8 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
         Curl_cwriter_free(data, writer);
         return result;
       }
+      if(is_chunked)
+        has_chunked = TRUE;
     }
   } while(*enclist);
 
diff --git a/extra/curl/curl-8.12.1/lib/content_encoding.h b/extra/curl/curl-8.14.1/lib/content_encoding.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/content_encoding.h
rename to extra/curl/curl-8.14.1/lib/content_encoding.h
diff --git a/extra/curl/curl-8.12.1/lib/cookie.c b/extra/curl/curl-8.14.1/lib/cookie.c
similarity index 88%
rename from extra/curl/curl-8.12.1/lib/cookie.c
rename to extra/curl/curl-8.14.1/lib/cookie.c
index 9819768454f3..1a8426ca27f1 100644
--- a/extra/curl/curl-8.12.1/lib/cookie.c
+++ b/extra/curl/curl-8.14.1/lib/cookie.c
@@ -76,11 +76,9 @@ Example set of cookies:
 #include "urldata.h"
 #include "cookie.h"
 #include "psl.h"
-#include "strtok.h"
 #include "sendf.h"
 #include "slist.h"
 #include "share.h"
-#include "strtoofft.h"
 #include "strcase.h"
 #include "curl_get_line.h"
 #include "curl_memrchr.h"
@@ -89,6 +87,7 @@ Example set of cookies:
 #include "fopen.h"
 #include "strdup.h"
 #include "llist.h"
+#include "curlx/strparse.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -160,12 +159,10 @@ static bool cookie_tailmatch(const char *cookie_domain,
  * matching cookie path and URL path
  * RFC6265 5.1.4 Paths and Path-Match
  */
-static bool pathmatch(const char *cookie_path, const char *request_uri)
+static bool pathmatch(const char *cookie_path, const char *uri_path)
 {
   size_t cookie_path_len;
   size_t uri_path_len;
-  char *uri_path = NULL;
-  char *pos;
   bool ret = FALSE;
 
   /* cookie_path must not have last '/' separator. ex: /sample */
@@ -175,19 +172,9 @@ static bool pathmatch(const char *cookie_path, const char *request_uri)
     return TRUE;
   }
 
-  uri_path = strdup(request_uri);
-  if(!uri_path)
-    return FALSE;
-  pos = strchr(uri_path, '?');
-  if(pos)
-    *pos = 0x0;
-
   /* #-fragments are already cut off! */
-  if(0 == strlen(uri_path) || uri_path[0] != '/') {
-    strstore(&uri_path, "/", 1);
-    if(!uri_path)
-      return FALSE;
-  }
+  if(0 == strlen(uri_path) || uri_path[0] != '/')
+    uri_path = "/";
 
   /*
    * here, RFC6265 5.1.4 says
@@ -201,16 +188,12 @@ static bool pathmatch(const char *cookie_path, const char *request_uri)
 
   uri_path_len = strlen(uri_path);
 
-  if(uri_path_len < cookie_path_len) {
-    ret = FALSE;
+  if(uri_path_len < cookie_path_len)
     goto pathmatched;
-  }
 
   /* not using checkprefix() because matching should be case-sensitive */
-  if(strncmp(cookie_path, uri_path, cookie_path_len)) {
-    ret = FALSE;
+  if(strncmp(cookie_path, uri_path, cookie_path_len))
     goto pathmatched;
-  }
 
   /* The cookie-path and the uri-path are identical. */
   if(cookie_path_len == uri_path_len) {
@@ -224,10 +207,7 @@ static bool pathmatch(const char *cookie_path, const char *request_uri)
     goto pathmatched;
   }
 
-  ret = FALSE;
-
 pathmatched:
-  free(uri_path);
   return ret;
 }
 
@@ -301,34 +281,27 @@ static size_t cookiehash(const char * const domain)
  */
 static char *sanitize_cookie_path(const char *cookie_path)
 {
-  size_t len;
-  char *new_path = strdup(cookie_path);
-  if(!new_path)
-    return NULL;
+  size_t len = strlen(cookie_path);
 
-  /* some stupid site sends path attribute with '"'. */
-  len = strlen(new_path);
-  if(new_path[0] == '\"') {
-    memmove(new_path, new_path + 1, len);
+  /* some sites send path attribute within '"'. */
+  if(cookie_path[0] == '\"') {
+    cookie_path++;
     len--;
   }
-  if(len && (new_path[len - 1] == '\"')) {
-    new_path[--len] = 0x0;
-  }
+  if(len && (cookie_path[len - 1] == '\"'))
+    len--;
 
   /* RFC6265 5.2.4 The Path Attribute */
-  if(new_path[0] != '/') {
+  if(cookie_path[0] != '/')
     /* Let cookie-path be the default-path. */
-    strstore(&new_path, "/", 1);
-    return new_path;
-  }
+    return strdup("/");
 
+  /* remove trailing slash */
   /* convert /hoge/ to /hoge */
-  if(len && new_path[len - 1] == '/') {
-    new_path[len - 1] = 0x0;
-  }
+  if(len && cookie_path[len - 1] == '/')
+    len--;
 
-  return new_path;
+  return Curl_memdup0(cookie_path, len);
 }
 
 /*
@@ -370,9 +343,12 @@ void Curl_cookie_loadfiles(struct Curl_easy *data)
  */
 static void strstore(char **str, const char *newstr, size_t len)
 {
-  DEBUGASSERT(newstr);
   DEBUGASSERT(str);
   free(*str);
+  if(!len) {
+    len++;
+    newstr = "";
+  }
   *str = Curl_memdup0(newstr, len);
 }
 
@@ -458,18 +434,16 @@ static bool bad_domain(const char *domain, size_t len)
   fine. The prime reason for filtering out control bytes is that some HTTP
   servers return 400 for requests that contain such.
 */
-static bool invalid_octets(const char *p)
+static bool invalid_octets(const char *ptr)
 {
+  const unsigned char *p = (const unsigned char *)ptr;
   /* Reject all bytes \x01 - \x1f (*except* \x09, TAB) + \x7f */
-  static const char badoctets[] = {
-    "\x01\x02\x03\x04\x05\x06\x07\x08\x0a"
-    "\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14"
-    "\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x7f"
-  };
-  size_t len;
-  /* scan for all the octets that are *not* in cookie-octet */
-  len = strcspn(p, badoctets);
-  return p[len] != '\0';
+  while(*p) {
+    if(((*p != 9) && (*p < 0x20)) || (*p == 0x7f))
+      return TRUE;
+    p++;
+  }
+  return FALSE;
 }
 
 #define CERR_OK            0
@@ -486,7 +460,9 @@ static bool invalid_octets(const char *p)
 #define CERR_COMMENT       11 /* a commented line */
 #define CERR_RANGE         12 /* expire range problem */
 #define CERR_FIELDS        13 /* incomplete netscape line */
+#ifdef USE_LIBPSL
 #define CERR_PSL           14 /* a public suffix */
+#endif
 #define CERR_LIVE_WINS     15
 
 /* The maximum length we accept a date string for the 'expire' keyword. The
@@ -517,51 +493,29 @@ parse_cookie_header(struct Curl_easy *data,
 
   now = time(NULL);
   do {
-    size_t vlen;
-    size_t nlen;
-
-    while(*ptr && ISBLANK(*ptr))
-      ptr++;
+    struct Curl_str name;
+    struct Curl_str val;
 
     /* we have a <name>=<value> pair or a stand-alone word here */
-    nlen = strcspn(ptr, ";\t\r\n=");
-    if(nlen) {
+    if(!curlx_str_cspn(&ptr, &name, ";\t\r\n=")) {
       bool done = FALSE;
       bool sep = FALSE;
-      const char *namep = ptr;
-      const char *valuep;
-
-      ptr += nlen;
-
-      /* trim trailing spaces and tabs after name */
-      while(nlen && ISBLANK(namep[nlen - 1]))
-        nlen--;
-
-      if(*ptr == '=') {
-        vlen = strcspn(++ptr, ";\r\n");
-        valuep = ptr;
-        sep = TRUE;
-        ptr = &valuep[vlen];
-
-        /* Strip off trailing whitespace from the value */
-        while(vlen && ISBLANK(valuep[vlen-1]))
-          vlen--;
+      curlx_str_trimblanks(&name);
 
-        /* Skip leading whitespace from the value */
-        while(vlen && ISBLANK(*valuep)) {
-          valuep++;
-          vlen--;
-        }
+      if(!curlx_str_single(&ptr, '=')) {
+        sep = TRUE; /* a '=' was used */
+        if(!curlx_str_cspn(&ptr, &val, ";\r\n")) {
+          curlx_str_trimblanks(&val);
 
-        /* Reject cookies with a TAB inside the value */
-        if(memchr(valuep, '\t', vlen)) {
-          infof(data, "cookie contains TAB, dropping");
-          return CERR_TAB;
+          /* Reject cookies with a TAB inside the value */
+          if(memchr(curlx_str(&val), '\t', curlx_strlen(&val))) {
+            infof(data, "cookie contains TAB, dropping");
+            return CERR_TAB;
+          }
         }
       }
       else {
-        valuep = NULL;
-        vlen = 0;
+        curlx_str_init(&val);
       }
 
       /*
@@ -569,10 +523,11 @@ parse_cookie_header(struct Curl_easy *data,
        * combination of name + contents. Chrome and Firefox support 4095 or
        * 4096 bytes combo
        */
-      if(nlen >= (MAX_NAME-1) || vlen >= (MAX_NAME-1) ||
-         ((nlen + vlen) > MAX_NAME)) {
+      if(curlx_strlen(&name) >= (MAX_NAME-1) ||
+         curlx_strlen(&val) >= (MAX_NAME-1) ||
+         ((curlx_strlen(&name) + curlx_strlen(&val)) > MAX_NAME)) {
         infof(data, "oversized cookie dropped, name/val %zu + %zu bytes",
-              nlen, vlen);
+              curlx_strlen(&name), curlx_strlen(&val));
         return CERR_TOO_BIG;
       }
 
@@ -582,12 +537,10 @@ parse_cookie_header(struct Curl_easy *data,
        * "the rest". Prefixes must start with '__' and end with a '-', so
        * only test for names where that can possibly be true.
        */
-      if(nlen >= 7 && namep[0] == '_' && namep[1] == '_') {
-        if(strncasecompare("__Secure-", namep, 9))
-          co->prefix_secure = TRUE;
-        else if(strncasecompare("__Host-", namep, 7))
-          co->prefix_host = TRUE;
-      }
+      if(!strncmp("__Secure-", curlx_str(&name), 9))
+        co->prefix_secure = TRUE;
+      else if(!strncmp("__Host-", curlx_str(&name), 7))
+        co->prefix_host = TRUE;
 
       /*
        * Use strstore() below to properly deal with received cookie
@@ -601,8 +554,8 @@ parse_cookie_header(struct Curl_easy *data,
           /* Bad name/value pair. */
           return CERR_NO_SEP;
 
-        strstore(&co->name, namep, nlen);
-        strstore(&co->value, valuep, vlen);
+        strstore(&co->name, curlx_str(&name), curlx_strlen(&name));
+        strstore(&co->value, curlx_str(&val), curlx_strlen(&val));
         done = TRUE;
         if(!co->name || !co->value)
           return CERR_NO_NAME_VALUE;
@@ -612,7 +565,7 @@ parse_cookie_header(struct Curl_easy *data,
           return CERR_INVALID_OCTET;
         }
       }
-      else if(!vlen) {
+      else if(!curlx_strlen(&val)) {
         /*
          * this was a "<name>=" with no content, and we must allow
          * 'secure' and 'httponly' specified this weirdly
@@ -623,7 +576,7 @@ parse_cookie_header(struct Curl_easy *data,
          * using a secure protocol, or when the cookie is being set by
          * reading from file
          */
-        if((nlen == 6) && strncasecompare("secure", namep, 6)) {
+        if(curlx_str_casecompare(&name, "secure")) {
           if(secure || !ci->running) {
             co->secure = TRUE;
           }
@@ -631,7 +584,7 @@ parse_cookie_header(struct Curl_easy *data,
             return CERR_BAD_SECURE;
           }
         }
-        else if((nlen == 8) && strncasecompare("httponly", namep, 8))
+        else if(curlx_str_casecompare(&name, "httponly"))
           co->httponly = TRUE;
         else if(sep)
           /* there was a '=' so we are not done parsing this field */
@@ -639,8 +592,8 @@ parse_cookie_header(struct Curl_easy *data,
       }
       if(done)
         ;
-      else if((nlen == 4) && strncasecompare("path", namep, 4)) {
-        strstore(&co->path, valuep, vlen);
+      else if(curlx_str_casecompare(&name, "path")) {
+        strstore(&co->path, curlx_str(&val), curlx_strlen(&val));
         if(!co->path)
           return CERR_OUT_OF_MEMORY;
         free(co->spath); /* if this is set again */
@@ -648,19 +601,16 @@ parse_cookie_header(struct Curl_easy *data,
         if(!co->spath)
           return CERR_OUT_OF_MEMORY;
       }
-      else if((nlen == 6) &&
-              strncasecompare("domain", namep, 6) && vlen) {
+      else if(curlx_str_casecompare(&name, "domain") && curlx_strlen(&val)) {
         bool is_ip;
-
+        const char *v = curlx_str(&val);
         /*
          * Now, we make sure that our host is within the given domain, or
          * the given domain is not valid and thus cannot be set.
          */
 
-        if('.' == valuep[0]) {
-          valuep++; /* ignore preceding dot */
-          vlen--;
-        }
+        if('.' == *v)
+          curlx_str_nudge(&val, 1);
 
 #ifndef USE_LIBPSL
         /*
@@ -668,17 +618,19 @@ parse_cookie_header(struct Curl_easy *data,
          * TLD or otherwise "protected" suffix. To reduce risk, we require a
          * dot OR the exact hostname being "localhost".
          */
-        if(bad_domain(valuep, vlen))
+        if(bad_domain(curlx_str(&val), curlx_strlen(&val)))
           domain = ":";
 #endif
 
-        is_ip = Curl_host_is_ipnum(domain ? domain : valuep);
+        is_ip = Curl_host_is_ipnum(domain ? domain : curlx_str(&val));
 
         if(!domain
-           || (is_ip && !strncmp(valuep, domain, vlen) &&
-               (vlen == strlen(domain)))
-           || (!is_ip && cookie_tailmatch(valuep, vlen, domain))) {
-          strstore(&co->domain, valuep, vlen);
+           || (is_ip && !strncmp(curlx_str(&val), domain,
+                                 curlx_strlen(&val)) &&
+               (curlx_strlen(&val) == strlen(domain)))
+           || (!is_ip && cookie_tailmatch(curlx_str(&val),
+                                          curlx_strlen(&val), domain))) {
+          strstore(&co->domain, curlx_str(&val), curlx_strlen(&val));
           if(!co->domain)
             return CERR_OUT_OF_MEMORY;
 
@@ -692,14 +644,14 @@ parse_cookie_header(struct Curl_easy *data,
            * not a domain to which the current host belongs. Mark as bad.
            */
           infof(data, "skipped cookie with bad tailmatch domain: %s",
-                valuep);
+                curlx_str(&val));
           return CERR_NO_TAILMATCH;
         }
       }
-      else if((nlen == 7) && strncasecompare("version", namep, 7)) {
+      else if(curlx_str_casecompare(&name, "version")) {
         /* just ignore */
       }
-      else if((nlen == 7) && strncasecompare("max-age", namep, 7)) {
+      else if(curlx_str_casecompare(&name, "max-age") && curlx_strlen(&val)) {
         /*
          * Defined in RFC2109:
          *
@@ -709,21 +661,22 @@ parse_cookie_header(struct Curl_easy *data,
          * client should discard the cookie. A value of zero means the
          * cookie should be discarded immediately.
          */
-        CURLofft offt;
-        const char *maxage = valuep;
-        offt = curlx_strtoofft((*maxage == '\"') ?
-                               &maxage[1] : &maxage[0], NULL, 10,
-                               &co->expires);
-        switch(offt) {
-        case CURL_OFFT_FLOW:
+        int rc;
+        const char *maxage = curlx_str(&val);
+        if(*maxage == '\"')
+          maxage++;
+        rc = curlx_str_number(&maxage, &co->expires, CURL_OFF_T_MAX);
+
+        switch(rc) {
+        case STRE_OVERFLOW:
           /* overflow, used max value */
           co->expires = CURL_OFF_T_MAX;
           break;
-        case CURL_OFFT_INVAL:
+        default:
           /* negative or otherwise bad, expire */
           co->expires = 1;
           break;
-        case CURL_OFFT_OK:
+        case STRE_OK:
           if(!co->expires)
             /* already expired */
             co->expires = 1;
@@ -736,8 +689,8 @@ parse_cookie_header(struct Curl_easy *data,
         }
         cap_expires(now, co);
       }
-      else if((nlen == 7) && strncasecompare("expires", namep, 7)) {
-        if(!co->expires && (vlen < MAX_DATE_LENGTH)) {
+      else if(curlx_str_casecompare(&name, "expires") && curlx_strlen(&val)) {
+        if(!co->expires && (curlx_strlen(&val) < MAX_DATE_LENGTH)) {
           /*
            * Let max-age have priority.
            *
@@ -745,8 +698,8 @@ parse_cookie_header(struct Curl_easy *data,
            * will be treated as a session cookie
            */
           char dbuf[MAX_DATE_LENGTH + 1];
-          memcpy(dbuf, valuep, vlen);
-          dbuf[vlen] = 0;
+          memcpy(dbuf, curlx_str(&val), curlx_strlen(&val));
+          dbuf[curlx_strlen(&val)] = 0;
           co->expires = Curl_getdate_capped(dbuf);
 
           /*
@@ -766,15 +719,8 @@ parse_cookie_header(struct Curl_easy *data,
        * Else, this is the second (or more) name we do not know about!
        */
     }
-    else {
-      /* this is an "illegal" <what>=<this> pair */
-    }
 
-    while(*ptr && ISBLANK(*ptr))
-      ptr++;
-    if(*ptr == ';')
-      ptr++;
-    else
+    if(curlx_str_single(&ptr, ';'))
       break;
   } while(1);
 
@@ -787,23 +733,11 @@ parse_cookie_header(struct Curl_easy *data,
 
   if(!co->path && path) {
     /*
-     * No path was given in the header line, set the default. Note that the
-     * passed-in path to this function MAY have a '?' and following part that
-     * MUST NOT be stored as part of the path.
+     * No path was given in the header line, set the default.
      */
-    char *queryp = strchr(path, '?');
-
-    /*
-     * queryp is where the interesting part of the path ends, so now we
-     * want to the find the last
-     */
-    char *endslash;
-    if(!queryp)
-      endslash = strrchr(path, '/');
-    else
-      endslash = memrchr(path, '/', (queryp - path));
+    const char *endslash = strrchr(path, '/');
     if(endslash) {
-      size_t pathlen = (endslash-path + 1); /* include end slash */
+      size_t pathlen = (endslash - path + 1); /* include end slash */
       co->path = Curl_memdup0(path, pathlen);
       if(co->path) {
         co->spath = sanitize_cookie_path(co->path);
@@ -916,16 +850,8 @@ parse_netscape(struct Cookie *co,
       }
       break;
     case 4:
-      {
-        char *endp;
-        const char *p;
-        /* make sure curlx_strtoofft won't read past the current field */
-        for(p = ptr; p < &ptr[len] && ISDIGIT(*p); ++p)
-          ;
-        if(p == ptr || p != &ptr[len] ||
-           curlx_strtoofft(ptr, &endp, 10, &co->expires) || endp != &ptr[len])
-          return CERR_RANGE;
-      }
+      if(curlx_str_number(&ptr, &co->expires, CURL_OFF_T_MAX))
+        return CERR_RANGE;
       break;
     case 5:
       co->name = Curl_memdup0(ptr, len);
@@ -1284,21 +1210,20 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
     ci->running = FALSE; /* this is not running, this is init */
     if(fp) {
       struct dynbuf buf;
-      Curl_dyn_init(&buf, MAX_COOKIE_LINE);
+      curlx_dyn_init(&buf, MAX_COOKIE_LINE);
       while(Curl_get_line(&buf, fp)) {
-        char *lineptr = Curl_dyn_ptr(&buf);
+        const char *lineptr = curlx_dyn_ptr(&buf);
         bool headerline = FALSE;
         if(checkprefix("Set-Cookie:", lineptr)) {
           /* This is a cookie line, get it! */
           lineptr += 11;
           headerline = TRUE;
-          while(*lineptr && ISBLANK(*lineptr))
-            lineptr++;
+          curlx_str_passblanks(&lineptr);
         }
 
         Curl_cookie_add(data, ci, headerline, TRUE, lineptr, NULL, NULL, TRUE);
       }
-      Curl_dyn_free(&buf); /* free the line buffer */
+      curlx_dyn_free(&buf); /* free the line buffer */
 
       /*
        * Remove expired cookies from the hash. We must make sure to run this
@@ -1326,8 +1251,8 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
  */
 static int cookie_sort(const void *p1, const void *p2)
 {
-  struct Cookie *c1 = *(struct Cookie **)p1;
-  struct Cookie *c2 = *(struct Cookie **)p2;
+  const struct Cookie *c1 = *(const struct Cookie * const *)p1;
+  const struct Cookie *c2 = *(const struct Cookie * const *)p2;
   size_t l1, l2;
 
   /* 1 - compare cookie path lengths */
@@ -1362,8 +1287,8 @@ static int cookie_sort(const void *p1, const void *p2)
  */
 static int cookie_sort_ct(const void *p1, const void *p2)
 {
-  struct Cookie *c1 = *(struct Cookie **)p1;
-  struct Cookie *c2 = *(struct Cookie **)p2;
+  const struct Cookie *c1 = *(const struct Cookie * const *)p1;
+  const struct Cookie *c2 = *(const struct Cookie * const *)p2;
 
   return (c2->creationtime > c1->creationtime) ? 1 : -1;
 }
diff --git a/extra/curl/curl-8.12.1/lib/cookie.h b/extra/curl/curl-8.14.1/lib/cookie.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/cookie.h
rename to extra/curl/curl-8.14.1/lib/cookie.h
diff --git a/extra/curl/curl-8.14.1/lib/cshutdn.c b/extra/curl/curl-8.14.1/lib/cshutdn.c
new file mode 100644
index 000000000000..f05b87d277af
--- /dev/null
+++ b/extra/curl/curl-8.14.1/lib/cshutdn.c
@@ -0,0 +1,581 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Linus Nielsen Feltzing, <linus@haxx.se>
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include <curl/curl.h>
+
+#include "urldata.h"
+#include "url.h"
+#include "cfilters.h"
+#include "progress.h"
+#include "multiif.h"
+#include "multi_ev.h"
+#include "sendf.h"
+#include "cshutdn.h"
+#include "http_negotiate.h"
+#include "http_ntlm.h"
+#include "sigpipe.h"
+#include "connect.h"
+#include "select.h"
+#include "strcase.h"
+#include "curlx/strparse.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+
+static void cshutdn_run_conn_handler(struct Curl_easy *data,
+                                     struct connectdata *conn)
+{
+  if(!conn->bits.shutdown_handler) {
+
+    /* Cleanup NTLM connection-related data */
+    Curl_http_auth_cleanup_ntlm(conn);
+
+    /* Cleanup NEGOTIATE connection-related data */
+    Curl_http_auth_cleanup_negotiate(conn);
+
+    if(conn->handler && conn->handler->disconnect) {
+      /* Some disconnect handlers do a blocking wait on server responses.
+       * FTP/IMAP/SMTP and SFTP are among them. When using the internal
+       * handle, set an overall short timeout so we do not hang for the
+       * default 120 seconds. */
+      if(data->state.internal) {
+        data->set.timeout = DEFAULT_SHUTDOWN_TIMEOUT_MS;
+        (void)Curl_pgrsTime(data, TIMER_STARTOP);
+      }
+
+      /* This is set if protocol-specific cleanups should be made */
+      DEBUGF(infof(data, "connection #%" FMT_OFF_T
+                   ", shutdown protocol handler (aborted=%d)",
+                   conn->connection_id, conn->bits.aborted));
+      /* There are protocol handlers that block on retrieving
+       * server responses here (FTP). Set a short timeout. */
+      conn->handler->disconnect(data, conn, conn->bits.aborted);
+    }
+
+    conn->bits.shutdown_handler = TRUE;
+  }
+}
+
+static void cshutdn_run_once(struct Curl_easy *data,
+                             struct connectdata *conn,
+                             bool *done)
+{
+  CURLcode r1, r2;
+  bool done1, done2;
+
+  /* We expect to be attached when called */
+  DEBUGASSERT(data->conn == conn);
+
+  cshutdn_run_conn_handler(data, conn);
+
+  if(conn->bits.shutdown_filters) {
+    *done = TRUE;
+    return;
+  }
+
+  if(!conn->connect_only && Curl_conn_is_connected(conn, FIRSTSOCKET))
+    r1 = Curl_conn_shutdown(data, FIRSTSOCKET, &done1);
+  else {
+    r1 = CURLE_OK;
+    done1 = TRUE;
+  }
+
+  if(!conn->connect_only && Curl_conn_is_connected(conn, SECONDARYSOCKET))
+    r2 = Curl_conn_shutdown(data, SECONDARYSOCKET, &done2);
+  else {
+    r2 = CURLE_OK;
+    done2 = TRUE;
+  }
+
+  /* we are done when any failed or both report success */
+  *done = (r1 || r2 || (done1 && done2));
+  if(*done)
+    conn->bits.shutdown_filters = TRUE;
+}
+
+void Curl_cshutdn_run_once(struct Curl_easy *data,
+                      struct connectdata *conn,
+                      bool *done)
+{
+  DEBUGASSERT(!data->conn);
+  Curl_attach_connection(data, conn);
+  cshutdn_run_once(data, conn, done);
+  CURL_TRC_M(data, "[SHUTDOWN] shutdown, done=%d", *done);
+  Curl_detach_connection(data);
+}
+
+
+void Curl_cshutdn_terminate(struct Curl_easy *data,
+                            struct connectdata *conn,
+                            bool do_shutdown)
+{
+  struct Curl_easy *admin = data;
+  bool done;
+
+  /* there must be a connection to close */
+  DEBUGASSERT(conn);
+  /* it must be removed from the connection pool */
+  DEBUGASSERT(!conn->bits.in_cpool);
+  /* the transfer must be detached from the connection */
+  DEBUGASSERT(data && !data->conn);
+
+  /* If we can obtain an internal admin handle, use that to attach
+   * and terminate the connection. Some protocol will try to mess with
+   * `data` during shutdown and we do not want that with a `data` from
+   * the application. */
+  if(data->multi && data->multi->admin)
+    admin = data->multi->admin;
+
+  Curl_attach_connection(admin, conn);
+
+  cshutdn_run_conn_handler(admin, conn);
+  if(do_shutdown) {
+    /* Make a last attempt to shutdown handlers and filters, if
+     * not done so already. */
+    cshutdn_run_once(admin, conn, &done);
+  }
+  CURL_TRC_M(admin, "[SHUTDOWN] %sclosing connection #%" FMT_OFF_T,
+             conn->bits.shutdown_filters ? "" : "force ",
+             conn->connection_id);
+  Curl_conn_close(admin, SECONDARYSOCKET);
+  Curl_conn_close(admin, FIRSTSOCKET);
+  Curl_detach_connection(admin);
+
+  if(data->multi)
+    Curl_multi_ev_conn_done(data->multi, data, conn);
+  Curl_conn_free(admin, conn);
+
+  if(data->multi) {
+    CURL_TRC_M(data, "[SHUTDOWN] trigger multi connchanged");
+    Curl_multi_connchanged(data->multi);
+  }
+}
+
+static bool cshutdn_destroy_oldest(struct cshutdn *cshutdn,
+                                   struct Curl_easy *data,
+                                   const char *destination)
+{
+  struct Curl_llist_node *e;
+  struct connectdata *conn;
+
+  e = Curl_llist_head(&cshutdn->list);
+  while(e) {
+    conn = Curl_node_elem(e);
+    if(!destination || !strcmp(destination, conn->destination))
+      break;
+    e = Curl_node_next(e);
+  }
+
+  if(e) {
+    SIGPIPE_VARIABLE(pipe_st);
+    conn = Curl_node_elem(e);
+    Curl_node_remove(e);
+    sigpipe_init(&pipe_st);
+    sigpipe_apply(data, &pipe_st);
+    Curl_cshutdn_terminate(data, conn, FALSE);
+    sigpipe_restore(&pipe_st);
+    return TRUE;
+  }
+  return FALSE;
+}
+
+bool Curl_cshutdn_close_oldest(struct Curl_easy *data,
+                               const char *destination)
+{
+  if(data && data->multi) {
+    struct cshutdn *csd = &data->multi->cshutdn;
+    return cshutdn_destroy_oldest(csd, data, destination);
+  }
+  return FALSE;
+}
+
+#define NUM_POLLS_ON_STACK 10
+
+static CURLcode cshutdn_wait(struct cshutdn *cshutdn,
+                               struct Curl_easy *data,
+                               int timeout_ms)
+{
+  struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK];
+  struct curl_pollfds cpfds;
+  CURLcode result;
+
+  Curl_pollfds_init(&cpfds, a_few_on_stack, NUM_POLLS_ON_STACK);
+
+  result = Curl_cshutdn_add_pollfds(cshutdn, data, &cpfds);
+  if(result)
+    goto out;
+
+  Curl_poll(cpfds.pfds, cpfds.n, CURLMIN(timeout_ms, 1000));
+
+out:
+  Curl_pollfds_cleanup(&cpfds);
+  return result;
+}
+
+
+static void cshutdn_perform(struct cshutdn *cshutdn,
+                              struct Curl_easy *data)
+{
+  struct Curl_llist_node *e = Curl_llist_head(&cshutdn->list);
+  struct Curl_llist_node *enext;
+  struct connectdata *conn;
+  struct curltime *nowp = NULL;
+  struct curltime now;
+  timediff_t next_expire_ms = 0, ms;
+  bool done;
+
+  if(!e)
+    return;
+
+  CURL_TRC_M(data, "[SHUTDOWN] perform on %zu connections",
+             Curl_llist_count(&cshutdn->list));
+  while(e) {
+    enext = Curl_node_next(e);
+    conn = Curl_node_elem(e);
+    Curl_cshutdn_run_once(data, conn, &done);
+    if(done) {
+      Curl_node_remove(e);
+      Curl_cshutdn_terminate(data, conn, FALSE);
+    }
+    else {
+      /* idata has one timer list, but maybe more than one connection.
+       * Set EXPIRE_SHUTDOWN to the smallest time left for all. */
+      if(!nowp) {
+        now = curlx_now();
+        nowp = &now;
+      }
+      ms = Curl_conn_shutdown_timeleft(conn, nowp);
+      if(ms && ms < next_expire_ms)
+        next_expire_ms = ms;
+    }
+    e = enext;
+  }
+
+  if(next_expire_ms)
+    Curl_expire_ex(data, nowp, next_expire_ms, EXPIRE_SHUTDOWN);
+}
+
+
+static void cshutdn_terminate_all(struct cshutdn *cshutdn,
+                                  struct Curl_easy *data,
+                                  int timeout_ms)
+{
+  struct curltime started = curlx_now();
+  struct Curl_llist_node *e;
+  SIGPIPE_VARIABLE(pipe_st);
+
+  DEBUGASSERT(cshutdn);
+  DEBUGASSERT(data);
+
+  CURL_TRC_M(data, "[SHUTDOWN] shutdown all");
+  sigpipe_init(&pipe_st);
+  sigpipe_apply(data, &pipe_st);
+
+  while(Curl_llist_head(&cshutdn->list)) {
+    timediff_t timespent;
+    int remain_ms;
+
+    cshutdn_perform(cshutdn, data);
+
+    if(!Curl_llist_head(&cshutdn->list)) {
+      CURL_TRC_M(data, "[SHUTDOWN] shutdown finished cleanly");
+      break;
+    }
+
+    /* wait for activity, timeout or "nothing" */
+    timespent = curlx_timediff(curlx_now(), started);
+    if(timespent >= (timediff_t)timeout_ms) {
+      CURL_TRC_M(data, "[SHUTDOWN] shutdown finished, %s",
+                (timeout_ms > 0) ? "timeout" : "best effort done");
+      break;
+    }
+
+    remain_ms = timeout_ms - (int)timespent;
+    if(cshutdn_wait(cshutdn, data, remain_ms)) {
+      CURL_TRC_M(data, "[SHUTDOWN] shutdown finished, aborted");
+      break;
+    }
+  }
+
+  /* Terminate any remaining. */
+  e = Curl_llist_head(&cshutdn->list);
+  while(e) {
+    struct connectdata *conn = Curl_node_elem(e);
+    Curl_node_remove(e);
+    Curl_cshutdn_terminate(data, conn, FALSE);
+    e = Curl_llist_head(&cshutdn->list);
+  }
+  DEBUGASSERT(!Curl_llist_count(&cshutdn->list));
+
+  sigpipe_restore(&pipe_st);
+}
+
+
+int Curl_cshutdn_init(struct cshutdn *cshutdn,
+                      struct Curl_multi *multi)
+{
+  DEBUGASSERT(multi);
+  cshutdn->multi = multi;
+  Curl_llist_init(&cshutdn->list, NULL);
+  cshutdn->initialised = TRUE;
+  return 0; /* good */
+}
+
+
+void Curl_cshutdn_destroy(struct cshutdn *cshutdn,
+                          struct Curl_easy *data)
+{
+  if(cshutdn->initialised && data) {
+    int timeout_ms = 0;
+    /* Just for testing, run graceful shutdown */
+#ifdef DEBUGBUILD
+    {
+      const char *p = getenv("CURL_GRACEFUL_SHUTDOWN");
+      if(p) {
+        curl_off_t l;
+        if(!curlx_str_number(&p, &l, INT_MAX))
+          timeout_ms = (int)l;
+      }
+    }
+#endif
+
+    CURL_TRC_M(data, "[SHUTDOWN] destroy, %zu connections, timeout=%dms",
+               Curl_llist_count(&cshutdn->list), timeout_ms);
+    cshutdn_terminate_all(cshutdn, data, timeout_ms);
+  }
+  cshutdn->multi = NULL;
+}
+
+size_t Curl_cshutdn_count(struct Curl_easy *data)
+{
+  if(data && data->multi) {
+    struct cshutdn *csd = &data->multi->cshutdn;
+    return Curl_llist_count(&csd->list);
+  }
+  return 0;
+}
+
+size_t Curl_cshutdn_dest_count(struct Curl_easy *data,
+                               const char *destination)
+{
+  if(data && data->multi) {
+    struct cshutdn *csd = &data->multi->cshutdn;
+    size_t n = 0;
+    struct Curl_llist_node *e = Curl_llist_head(&csd->list);
+    while(e) {
+      struct connectdata *conn = Curl_node_elem(e);
+      if(!strcmp(destination, conn->destination))
+        ++n;
+      e = Curl_node_next(e);
+    }
+    return n;
+  }
+  return 0;
+}
+
+
+static CURLMcode cshutdn_update_ev(struct cshutdn *cshutdn,
+                                     struct Curl_easy *data,
+                                     struct connectdata *conn)
+{
+  CURLMcode mresult;
+
+  DEBUGASSERT(cshutdn);
+  DEBUGASSERT(cshutdn->multi->socket_cb);
+
+  Curl_attach_connection(data, conn);
+  mresult = Curl_multi_ev_assess_conn(cshutdn->multi, data, conn);
+  Curl_detach_connection(data);
+  return mresult;
+}
+
+
+void Curl_cshutdn_add(struct cshutdn *cshutdn,
+                        struct connectdata *conn,
+                        size_t conns_in_pool)
+{
+  struct Curl_easy *data = cshutdn->multi->admin;
+  size_t max_total = (cshutdn->multi->max_total_connections > 0) ?
+                     (size_t)cshutdn->multi->max_total_connections : 0;
+
+  /* Add the connection to our shutdown list for non-blocking shutdown
+   * during multi processing. */
+  if(max_total > 0 && (max_total <=
+        (conns_in_pool + Curl_llist_count(&cshutdn->list)))) {
+    CURL_TRC_M(data, "[SHUTDOWN] discarding oldest shutdown connection "
+               "due to connection limit of %zu", max_total);
+    cshutdn_destroy_oldest(cshutdn, data, NULL);
+  }
+
+  if(cshutdn->multi->socket_cb) {
+    if(cshutdn_update_ev(cshutdn, data, conn)) {
+      CURL_TRC_M(data, "[SHUTDOWN] update events failed, discarding #%"
+                 FMT_OFF_T, conn->connection_id);
+      Curl_cshutdn_terminate(data, conn, FALSE);
+      return;
+    }
+  }
+
+  Curl_llist_append(&cshutdn->list, conn, &conn->cshutdn_node);
+  CURL_TRC_M(data, "[SHUTDOWN] added #%" FMT_OFF_T
+             " to shutdowns, now %zu conns in shutdown",
+             conn->connection_id, Curl_llist_count(&cshutdn->list));
+}
+
+
+static void cshutdn_multi_socket(struct cshutdn *cshutdn,
+                                   struct Curl_easy *data,
+                                   curl_socket_t s)
+{
+  struct Curl_llist_node *e;
+  struct connectdata *conn;
+  bool done;
+
+  DEBUGASSERT(cshutdn->multi->socket_cb);
+  e = Curl_llist_head(&cshutdn->list);
+  while(e) {
+    conn = Curl_node_elem(e);
+    if(s == conn->sock[FIRSTSOCKET] || s == conn->sock[SECONDARYSOCKET]) {
+      Curl_cshutdn_run_once(data, conn, &done);
+      if(done || cshutdn_update_ev(cshutdn, data, conn)) {
+        Curl_node_remove(e);
+        Curl_cshutdn_terminate(data, conn, FALSE);
+      }
+      break;
+    }
+    e = Curl_node_next(e);
+  }
+}
+
+
+void Curl_cshutdn_perform(struct cshutdn *cshutdn,
+                          struct Curl_easy *data,
+                          curl_socket_t s)
+{
+  if((s == CURL_SOCKET_TIMEOUT) || (!cshutdn->multi->socket_cb))
+    cshutdn_perform(cshutdn, data);
+  else
+    cshutdn_multi_socket(cshutdn, data, s);
+}
+
+/* return fd_set info about the shutdown connections */
+void Curl_cshutdn_setfds(struct cshutdn *cshutdn,
+                         struct Curl_easy *data,
+                         fd_set *read_fd_set, fd_set *write_fd_set,
+                         int *maxfd)
+{
+  if(Curl_llist_head(&cshutdn->list)) {
+    struct Curl_llist_node *e;
+
+    for(e = Curl_llist_head(&cshutdn->list); e;
+        e = Curl_node_next(e)) {
+      struct easy_pollset ps;
+      unsigned int i;
+      struct connectdata *conn = Curl_node_elem(e);
+      memset(&ps, 0, sizeof(ps));
+      Curl_attach_connection(data, conn);
+      Curl_conn_adjust_pollset(data, conn, &ps);
+      Curl_detach_connection(data);
+
+      for(i = 0; i < ps.num; i++) {
+#if defined(__DJGPP__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Warith-conversion"
+#endif
+        if(ps.actions[i] & CURL_POLL_IN)
+          FD_SET(ps.sockets[i], read_fd_set);
+        if(ps.actions[i] & CURL_POLL_OUT)
+          FD_SET(ps.sockets[i], write_fd_set);
+#if defined(__DJGPP__)
+#pragma GCC diagnostic pop
+#endif
+        if((ps.actions[i] & (CURL_POLL_OUT | CURL_POLL_IN)) &&
+           ((int)ps.sockets[i] > *maxfd))
+          *maxfd = (int)ps.sockets[i];
+      }
+    }
+  }
+}
+
+/* return information about the shutdown connections */
+unsigned int Curl_cshutdn_add_waitfds(struct cshutdn *cshutdn,
+                                      struct Curl_easy *data,
+                                      struct Curl_waitfds *cwfds)
+{
+  unsigned int need = 0;
+
+  if(Curl_llist_head(&cshutdn->list)) {
+    struct Curl_llist_node *e;
+    struct easy_pollset ps;
+    struct connectdata *conn;
+
+    for(e = Curl_llist_head(&cshutdn->list); e;
+        e = Curl_node_next(e)) {
+      conn = Curl_node_elem(e);
+      memset(&ps, 0, sizeof(ps));
+      Curl_attach_connection(data, conn);
+      Curl_conn_adjust_pollset(data, conn, &ps);
+      Curl_detach_connection(data);
+
+      need += Curl_waitfds_add_ps(cwfds, &ps);
+    }
+  }
+  return need;
+}
+
+CURLcode Curl_cshutdn_add_pollfds(struct cshutdn *cshutdn,
+                                  struct Curl_easy *data,
+                                  struct curl_pollfds *cpfds)
+{
+  CURLcode result = CURLE_OK;
+
+  if(Curl_llist_head(&cshutdn->list)) {
+    struct Curl_llist_node *e;
+    struct easy_pollset ps;
+    struct connectdata *conn;
+
+    for(e = Curl_llist_head(&cshutdn->list); e;
+        e = Curl_node_next(e)) {
+      conn = Curl_node_elem(e);
+      memset(&ps, 0, sizeof(ps));
+      Curl_attach_connection(data, conn);
+      Curl_conn_adjust_pollset(data, conn, &ps);
+      Curl_detach_connection(data);
+
+      result = Curl_pollfds_add_ps(cpfds, &ps);
+      if(result) {
+        Curl_pollfds_cleanup(cpfds);
+        goto out;
+      }
+    }
+  }
+out:
+  return result;
+}
diff --git a/extra/curl/curl-8.14.1/lib/cshutdn.h b/extra/curl/curl-8.14.1/lib/cshutdn.h
new file mode 100644
index 000000000000..510d5bf50613
--- /dev/null
+++ b/extra/curl/curl-8.14.1/lib/cshutdn.h
@@ -0,0 +1,110 @@
+#ifndef HEADER_CURL_CSHUTDN_H
+#define HEADER_CURL_CSHUTDN_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Linus Nielsen Feltzing, <linus@haxx.se>
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include <curl/curl.h>
+#include "curlx/timeval.h"
+
+struct connectdata;
+struct Curl_easy;
+struct curl_pollfds;
+struct Curl_waitfds;
+struct Curl_multi;
+struct Curl_share;
+
+/* Run the shutdown of the connection once.
+ * Will shortly attach/detach `data` to `conn` while doing so.
+ * `done` will be set TRUE if any error was encountered or if
+ * the connection was shut down completely. */
+void Curl_cshutdn_run_once(struct Curl_easy *data,
+                           struct connectdata *conn,
+                           bool *done);
+
+/* Terminates the connection, e.g. closes and destroys it.
+ * If `run_shutdown` is TRUE, the shutdown will be run once before
+ * terminating it.
+ * Takes ownership of `conn`. */
+void Curl_cshutdn_terminate(struct Curl_easy *data,
+                            struct connectdata *conn,
+                            bool run_shutdown);
+
+/* A `cshutdown` is always owned by a multi handle to maintain
+ * the connections to be shut down. It registers timers and
+ * sockets to monitor via the multi handle. */
+struct cshutdn {
+  struct Curl_llist list;    /* connections being shut down */
+  struct Curl_multi *multi;  /* the multi owning this */
+  BIT(initialised);
+};
+
+/* Init as part of the given multi handle. */
+int Curl_cshutdn_init(struct cshutdn *cshutdn,
+                      struct Curl_multi *multi);
+
+/* Terminate all remaining connections and free resources. */
+void Curl_cshutdn_destroy(struct cshutdn *cshutdn,
+                          struct Curl_easy *data);
+
+/* Number of connections being shut down. */
+size_t Curl_cshutdn_count(struct Curl_easy *data);
+
+/* Number of connections to the destination being shut down. */
+size_t Curl_cshutdn_dest_count(struct Curl_easy *data,
+                               const char *destination);
+
+/* Close the oldest connection in shutdown to destination or,
+ * when destination is NULL for any destination.
+ * Return TRUE if a connection has been closed. */
+bool Curl_cshutdn_close_oldest(struct Curl_easy *data,
+                               const char *destination);
+
+/* Add a connection to have it shut down. Will terminate the oldest
+ * connection when total connection limit of multi is being reached. */
+void Curl_cshutdn_add(struct cshutdn *cshutdn,
+                      struct connectdata *conn,
+                      size_t conns_in_pool);
+
+/* Add sockets and POLLIN/OUT flags for connections being shut down. */
+CURLcode Curl_cshutdn_add_pollfds(struct cshutdn *cshutdn,
+                                  struct Curl_easy *data,
+                                  struct curl_pollfds *cpfds);
+
+unsigned int Curl_cshutdn_add_waitfds(struct cshutdn *cshutdn,
+                                      struct Curl_easy *data,
+                                      struct Curl_waitfds *cwfds);
+
+void Curl_cshutdn_setfds(struct cshutdn *cshutdn,
+                         struct Curl_easy *data,
+                         fd_set *read_fd_set, fd_set *write_fd_set,
+                         int *maxfd);
+
+/* Run shut down connections using socket. If socket is CURL_SOCKET_TIMEOUT,
+ * run maintenance on all connections. */
+void Curl_cshutdn_perform(struct cshutdn *cshutdn,
+                          struct Curl_easy *data,
+                          curl_socket_t s);
+
+#endif /* HEADER_CURL_CSHUTDN_H */
diff --git a/extra/curl/curl-8.12.1/lib/curl_addrinfo.c b/extra/curl/curl-8.14.1/lib/curl_addrinfo.c
similarity index 93%
rename from extra/curl/curl-8.12.1/lib/curl_addrinfo.c
rename to extra/curl/curl-8.14.1/lib/curl_addrinfo.c
index 52f0f9125519..b131c747b10b 100644
--- a/extra/curl/curl-8.12.1/lib/curl_addrinfo.c
+++ b/extra/curl/curl-8.14.1/lib/curl_addrinfo.c
@@ -50,8 +50,9 @@
 #include <stddef.h>
 
 #include "curl_addrinfo.h"
-#include "inet_pton.h"
-#include "warnless.h"
+#include "fake_addrinfo.h"
+#include "curlx/inet_pton.h"
+#include "curlx/warnless.h"
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
@@ -118,7 +119,7 @@ Curl_getaddrinfo_ex(const char *nodename,
 
   *result = NULL; /* assume failure */
 
-  error = getaddrinfo(nodename, servname, hints, &aihead);
+  error = CURL_GETADDRINFO(nodename, servname, hints, &aihead);
   if(error)
     return error;
 
@@ -184,7 +185,7 @@ Curl_getaddrinfo_ex(const char *nodename,
 
   /* destroy the addrinfo list */
   if(aihead)
-    freeaddrinfo(aihead);
+    CURL_FREEADDRINFO(aihead);
 
   /* if we failed, also destroy the Curl_addrinfo list */
   if(error) {
@@ -217,7 +218,7 @@ Curl_getaddrinfo_ex(const char *nodename,
  *
  * This function returns a pointer to the first element of a newly allocated
  * Curl_addrinfo struct linked list filled with the data of a given hostent.
- * Curl_addrinfo is meant to work like the addrinfo struct does for a IPv6
+ * Curl_addrinfo is meant to work like the addrinfo struct does for an IPv6
  * stack, but usable also for IPv4, all hosts and environments.
  *
  * The memory allocated by this function *MUST* be free'd later on calling
@@ -430,13 +431,13 @@ Curl_ip2addr(int af, const void *inaddr, const char *hostname, int port)
 struct Curl_addrinfo *Curl_str2addr(char *address, int port)
 {
   struct in_addr in;
-  if(Curl_inet_pton(AF_INET, address, &in) > 0)
+  if(curlx_inet_pton(AF_INET, address, &in) > 0)
     /* This is a dotted IP address 123.123.123.123-style */
     return Curl_ip2addr(AF_INET, &in, address, port);
 #ifdef USE_IPV6
   {
     struct in6_addr in6;
-    if(Curl_inet_pton(AF_INET6, address, &in6) > 0)
+    if(curlx_inet_pton(AF_INET6, address, &in6) > 0)
       /* This is a dotted IPv6 address ::1-style */
       return Curl_ip2addr(AF_INET6, &in6, address, port);
   }
@@ -467,7 +468,7 @@ struct Curl_addrinfo *Curl_unix2addr(const char *path, bool *longpath,
   sa_un = (void *) ai->ai_addr;
   sa_un->sun_family = AF_UNIX;
 
-  /* sun_path must be able to store the NUL-terminated path */
+  /* sun_path must be able to store the null-terminated path */
   path_len = strlen(path) + 1;
   if(path_len > sizeof(sa_un->sun_path)) {
     free(ai);
@@ -508,8 +509,16 @@ curl_dbg_freeaddrinfo(struct addrinfo *freethis,
                source, line, (void *)freethis);
 #ifdef USE_LWIPSOCK
   lwip_freeaddrinfo(freethis);
+#elif defined(USE_FAKE_GETADDRINFO)
+  {
+    const char *env = getenv("CURL_DNS_SERVER");
+    if(env)
+      r_freeaddrinfo(freethis);
+    else
+      freeaddrinfo(freethis);
+  }
 #else
-  (freeaddrinfo)(freethis);
+  freeaddrinfo(freethis);
 #endif
 }
 #endif /* defined(CURLDEBUG) && defined(HAVE_FREEADDRINFO) */
@@ -526,15 +535,22 @@ curl_dbg_freeaddrinfo(struct addrinfo *freethis,
 
 int
 curl_dbg_getaddrinfo(const char *hostname,
-                    const char *service,
-                    const struct addrinfo *hints,
-                    struct addrinfo **result,
-                    int line, const char *source)
+                     const char *service,
+                     const struct addrinfo *hints,
+                     struct addrinfo **result,
+                     int line, const char *source)
 {
 #ifdef USE_LWIPSOCK
   int res = lwip_getaddrinfo(hostname, service, hints, result);
+#elif defined(USE_FAKE_GETADDRINFO)
+  int res;
+  const char *env = getenv("CURL_DNS_SERVER");
+  if(env)
+    res = r_getaddrinfo(hostname, service, hints, result);
+  else
+    res = getaddrinfo(hostname, service, hints, result);
 #else
-  int res = (getaddrinfo)(hostname, service, hints, result);
+  int res = getaddrinfo(hostname, service, hints, result);
 #endif
   if(0 == res)
     /* success */
diff --git a/extra/curl/curl-8.12.1/lib/curl_addrinfo.h b/extra/curl/curl-8.14.1/lib/curl_addrinfo.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/curl_addrinfo.h
rename to extra/curl/curl-8.14.1/lib/curl_addrinfo.h
diff --git a/extra/curl/curl-8.12.1/lib/curl_config.h.cmake b/extra/curl/curl-8.14.1/lib/curl_config.h.cmake
similarity index 96%
rename from extra/curl/curl-8.12.1/lib/curl_config.h.cmake
rename to extra/curl/curl-8.14.1/lib/curl_config.h.cmake
index f4f3a186d4a8..efaa8a4594a2 100644
--- a/extra/curl/curl-8.12.1/lib/curl_config.h.cmake
+++ b/extra/curl/curl-8.14.1/lib/curl_config.h.cmake
@@ -197,6 +197,9 @@
 /* Define to 1 if you have _Atomic support. */
 #cmakedefine HAVE_ATOMIC 1
 
+/* Define to 1 if you have the `accept4' function. */
+#cmakedefine HAVE_ACCEPT4 1
+
 /* Define to 1 if you have the `fnmatch' function. */
 #cmakedefine HAVE_FNMATCH 1
 
@@ -324,10 +327,10 @@
 /* Define to 1 if you have the <ifaddrs.h> header file. */
 #cmakedefine HAVE_IFADDRS_H 1
 
-/* Define to 1 if you have a IPv6 capable working inet_ntop function. */
+/* Define to 1 if you have an IPv6 capable working inet_ntop function. */
 #cmakedefine HAVE_INET_NTOP 1
 
-/* Define to 1 if you have a IPv6 capable working inet_pton function. */
+/* Define to 1 if you have an IPv6 capable working inet_pton function. */
 #cmakedefine HAVE_INET_PTON 1
 
 /* Define to 1 if symbol `sa_family_t' exists */
@@ -427,6 +430,9 @@
 /* Define to 1 if you have the `pipe' function. */
 #cmakedefine HAVE_PIPE 1
 
+/* Define to 1 if you have the `pipe2' function. */
+#cmakedefine HAVE_PIPE2 1
+
 /* Define to 1 if you have the `eventfd' function. */
 #cmakedefine HAVE_EVENTFD 1
 
@@ -469,6 +475,9 @@
 /* Define to 1 if you have the sendmmsg function. */
 #cmakedefine HAVE_SENDMMSG 1
 
+/* Define to 1 if you have the <stdint.h> header file. */
+#cmakedefine HAVE_STDINT_H 1
+
 /* Define to 1 if you have the 'fsetxattr' function. */
 #cmakedefine HAVE_FSETXATTR 1
 
@@ -547,12 +556,6 @@
 /* Define to 1 if you have the <stropts.h> header file. */
 #cmakedefine HAVE_STROPTS_H 1
 
-/* Define to 1 if you have the strtok_r function. */
-#cmakedefine HAVE_STRTOK_R 1
-
-/* Define to 1 if you have the strtoll function. */
-#cmakedefine HAVE_STRTOLL 1
-
 /* Define to 1 if you have the memrchr function. */
 #cmakedefine HAVE_MEMRCHR 1
 
@@ -700,14 +703,20 @@ ${SIZEOF_TIME_T_CODE}
 /* if wolfSSL is enabled */
 #cmakedefine USE_WOLFSSL 1
 
+/* if wolfSSL has the wolfSSL_get_peer_certificate function. */
+#cmakedefine HAVE_WOLFSSL_GET_PEER_CERTIFICATE 1
+
+/* if wolfSSL has the wolfSSL_UseALPN function. */
+#cmakedefine HAVE_WOLFSSL_USEALPN 1
+
 /* if wolfSSL has the wolfSSL_DES_ecb_encrypt function. */
 #cmakedefine HAVE_WOLFSSL_DES_ECB_ENCRYPT 1
 
 /* if wolfSSL has the wolfSSL_BIO_new function. */
-#cmakedefine HAVE_WOLFSSL_BIO 1
+#cmakedefine HAVE_WOLFSSL_BIO_NEW 1
 
 /* if wolfSSL has the wolfSSL_BIO_set_shutdown function. */
-#cmakedefine HAVE_WOLFSSL_FULL_BIO 1
+#cmakedefine HAVE_WOLFSSL_BIO_SET_SHUTDOWN 1
 
 /* if libssh is in use */
 #cmakedefine USE_LIBSSH 1
@@ -761,6 +770,9 @@ ${SIZEOF_TIME_T_CODE}
 /* to enable openssl + nghttp3 */
 #cmakedefine USE_OPENSSL_QUIC 1
 
+/* to enable openssl + ngtcp2 + nghttp3 */
+#cmakedefine OPENSSL_QUIC_API2 1
+
 /* Define to 1 if you have the quiche_conn_set_qlog_fd function. */
 #cmakedefine HAVE_QUICHE_CONN_SET_QLOG_FD 1
 
@@ -797,9 +809,6 @@ ${SIZEOF_TIME_T_CODE}
 /* Define to empty if `const' does not conform to ANSI C. */
 #cmakedefine const ${const}
 
-/* Type to use in place of in_addr_t when system does not provide it. */
-#cmakedefine in_addr_t ${in_addr_t}
-
 /* Define to `unsigned int' if <sys/types.h> does not define. */
 #cmakedefine size_t ${size_t}
 
diff --git a/extra/curl/curl-8.12.1/lib/curl_config.h.in b/extra/curl/curl-8.14.1/lib/curl_config.h.in
similarity index 96%
rename from extra/curl/curl-8.12.1/lib/curl_config.h.in
rename to extra/curl/curl-8.14.1/lib/curl_config.h.in
index 7685d752dff7..d9fbdc228b35 100644
--- a/extra/curl/curl-8.12.1/lib/curl_config.h.in
+++ b/extra/curl/curl-8.14.1/lib/curl_config.h.in
@@ -1,7 +1,6 @@
 /* lib/curl_config.h.in.  Generated from configure.ac by autoheader.  */
 
-/* to enable curl debug memory tracking */
-#undef CURLDEBUG
+/* !checksrc! disable COPYRIGHT all */
 
 /* Location of default ca bundle */
 #undef CURL_CA_BUNDLE
@@ -162,12 +161,12 @@
 /* built with multiple SSL backends */
 #undef CURL_WITH_MULTI_SSL
 
-/* enable debug build options */
-#undef DEBUGBUILD
-
 /* Define to the type of arg 2 for gethostname. */
 #undef GETHOSTNAME_TYPE_ARG2
 
+/* Define to 1 if you have the `accept4' function. */
+#undef HAVE_ACCEPT4
+
 /* Define to 1 if symbol `ADDRESS_FAMILY' exists */
 #undef HAVE_ADDRESS_FAMILY
 
@@ -337,10 +336,10 @@
 /* Define to 1 if you have the `if_nametoindex' function. */
 #undef HAVE_IF_NAMETOINDEX
 
-/* Define to 1 if you have a IPv6 capable working inet_ntop function. */
+/* Define to 1 if you have an IPv6 capable working inet_ntop function. */
 #undef HAVE_INET_NTOP
 
-/* Define to 1 if you have a IPv6 capable working inet_pton function. */
+/* Define to 1 if you have an IPv6 capable working inet_pton function. */
 #undef HAVE_INET_PTON
 
 /* Define to 1 if you have the <inttypes.h> header file. */
@@ -502,6 +501,9 @@
 /* Define to 1 if you have the `pipe' function. */
 #undef HAVE_PIPE
 
+/* Define to 1 if you have the `pipe2' function. */
+#undef HAVE_PIPE2
+
 /* Define to 1 if you have the `poll' function. */
 #undef HAVE_POLL
 
@@ -589,6 +591,9 @@
 /* Define to 1 if you have the `SSL_set1_ech_config_list' function. */
 #undef HAVE_SSL_SET1_ECH_CONFIG_LIST
 
+/* Define to 1 if you have the `SSL_set_quic_tls_cbs' function. */
+#undef HAVE_SSL_SET_QUIC_TLS_CBS
+
 /* Define to 1 if you have the `SSL_set_quic_use_legacy_codepoint' function.
    */
 #undef HAVE_SSL_SET_QUIC_USE_LEGACY_CODEPOINT
@@ -632,12 +637,6 @@
 /* Define to 1 if you have the <stropts.h> header file. */
 #undef HAVE_STROPTS_H
 
-/* Define to 1 if you have the strtok_r function. */
-#undef HAVE_STRTOK_R
-
-/* Define to 1 if you have the strtoll function. */
-#undef HAVE_STRTOLL
-
 /* if struct sockaddr_storage is defined */
 #undef HAVE_STRUCT_SOCKADDR_STORAGE
 
@@ -722,21 +721,25 @@
 /* Define to 1 if you have the <wolfssh/ssh.h> header file. */
 #undef HAVE_WOLFSSH_SSH_H
 
-/* if you have wolfSSL_BIO_new */
-#undef HAVE_WOLFSSL_BIO
+/* Define to 1 if you have the `wolfSSL_BIO_new' function. */
+#undef HAVE_WOLFSSL_BIO_NEW
+
+/* Define to 1 if you have the `wolfSSL_BIO_set_shutdown' function. */
+#undef HAVE_WOLFSSL_BIO_SET_SHUTDOWN
 
 /* Define to 1 if you have the `wolfSSL_CTX_GenerateEchConfig' function. */
 #undef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG
 
-/* if you have wolfSSL_DES_ecb_encrypt */
+/* Define to 1 if you have the `wolfSSL_DES_ecb_encrypt' function. */
 #undef HAVE_WOLFSSL_DES_ECB_ENCRYPT
 
-/* if you have wolfSSL_BIO_set_shutdown */
-#undef HAVE_WOLFSSL_FULL_BIO
-
 /* Define to 1 if you have the `wolfSSL_get_peer_certificate' function. */
 #undef HAVE_WOLFSSL_GET_PEER_CERTIFICATE
 
+/* Define to 1 if you have the `wolfSSL_set_quic_use_legacy_codepoint'
+   function. */
+#undef HAVE_WOLFSSL_SET_QUIC_USE_LEGACY_CODEPOINT
+
 /* Define to 1 if you have the `wolfSSL_UseALPN' function. */
 #undef HAVE_WOLFSSL_USEALPN
 
@@ -764,6 +767,9 @@
 /* Define to 1 if _THREAD_SAFE preprocessor symbol must be defined. */
 #undef NEED_THREAD_SAFE
 
+/* openssl with new QUIC API */
+#undef OPENSSL_QUIC_API2
+
 /* Name of package */
 #undef PACKAGE
 
@@ -950,9 +956,6 @@
 /* Define to empty if `const' does not conform to ANSI C. */
 #undef const
 
-/* Type to use in place of in_addr_t when system does not provide it. */
-#undef in_addr_t
-
 /* Define to `unsigned int' if <sys/types.h> does not define. */
 #undef size_t
 
diff --git a/extra/curl/curl-8.12.1/lib/curl_ctype.h b/extra/curl/curl-8.14.1/lib/curl_ctype.h
similarity index 97%
rename from extra/curl/curl-8.12.1/lib/curl_ctype.h
rename to extra/curl/curl-8.14.1/lib/curl_ctype.h
index b70acf3c5a66..48c3c37c359c 100644
--- a/extra/curl/curl-8.12.1/lib/curl_ctype.h
+++ b/extra/curl/curl-8.14.1/lib/curl_ctype.h
@@ -37,6 +37,7 @@
 #define ISCNTRL(x) (ISLOWCNTRL(x) || IS7F(x))
 #define ISALPHA(x) (ISLOWER(x) || ISUPPER(x))
 #define ISXDIGIT(x) (ISDIGIT(x) || ISLOWHEXALHA(x) || ISUPHEXALHA(x))
+#define ISODIGIT(x) (((x) >= '0') && ((x) <= '7'))
 #define ISALNUM(x)  (ISDIGIT(x) || ISLOWER(x) || ISUPPER(x))
 #define ISUPPER(x)  (((x) >= 'A') && ((x) <= 'Z'))
 #define ISLOWER(x)  (((x) >= 'a') && ((x) <= 'z'))
diff --git a/extra/curl/curl-8.12.1/lib/curl_des.c b/extra/curl/curl-8.14.1/lib/curl_des.c
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/curl_des.c
rename to extra/curl/curl-8.14.1/lib/curl_des.c
diff --git a/extra/curl/curl-8.12.1/lib/curl_des.h b/extra/curl/curl-8.14.1/lib/curl_des.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/curl_des.h
rename to extra/curl/curl-8.14.1/lib/curl_des.h
diff --git a/extra/curl/curl-8.12.1/lib/curl_endian.c b/extra/curl/curl-8.14.1/lib/curl_endian.c
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/curl_endian.c
rename to extra/curl/curl-8.14.1/lib/curl_endian.c
diff --git a/extra/curl/curl-8.12.1/lib/curl_endian.h b/extra/curl/curl-8.14.1/lib/curl_endian.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/curl_endian.h
rename to extra/curl/curl-8.14.1/lib/curl_endian.h
diff --git a/extra/curl/curl-8.12.1/lib/curl_fnmatch.c b/extra/curl/curl-8.14.1/lib/curl_fnmatch.c
similarity index 94%
rename from extra/curl/curl-8.12.1/lib/curl_fnmatch.c
rename to extra/curl/curl-8.14.1/lib/curl_fnmatch.c
index ffac8048f673..21eca4ceda54 100644
--- a/extra/curl/curl-8.12.1/lib/curl_fnmatch.c
+++ b/extra/curl/curl-8.14.1/lib/curl_fnmatch.c
@@ -71,13 +71,13 @@ typedef enum {
 #define SETCHARSET_OK     1
 #define SETCHARSET_FAIL   0
 
-static int parsekeyword(unsigned char **pattern, unsigned char *charset)
+static int parsekeyword(const unsigned char **pattern, unsigned char *charset)
 {
   parsekey_state state = CURLFNM_PKW_INIT;
 #define KEYLEN 10
   char keyword[KEYLEN] = { 0 };
   int i;
-  unsigned char *p = *pattern;
+  const unsigned char *p = *pattern;
   bool found = FALSE;
   for(i = 0; !found; i++) {
     char c = (char)*p++;
@@ -140,9 +140,9 @@ static char_class charclass(unsigned char c)
 }
 
 /* Include a character or a range in set. */
-static void setcharorrange(unsigned char **pp, unsigned char *charset)
+static void setcharorrange(const unsigned char **pp, unsigned char *charset)
 {
-  unsigned char *p = (*pp)++;
+  const unsigned char *p = (*pp)++;
   unsigned char c = *p++;
 
   charset[c] = 1;
@@ -162,7 +162,7 @@ static void setcharorrange(unsigned char **pp, unsigned char *charset)
 }
 
 /* returns 1 (TRUE) if pattern is OK, 0 if is bad ("p" is pattern pointer) */
-static int setcharset(unsigned char **p, unsigned char *charset)
+static int setcharset(const unsigned char **p, unsigned char *charset)
 {
   setcharset_state state = CURLFNM_SCHS_DEFAULT;
   bool something_found = FALSE;
@@ -185,7 +185,7 @@ static int setcharset(unsigned char **p, unsigned char *charset)
         (*p)++;
       }
       else if(c == '[') {
-        unsigned char *pp = *p + 1;
+        const unsigned char *pp = *p + 1;
 
         if(*pp++ == ':' && parsekeyword(&pp, charset))
           *p = pp;
@@ -257,12 +257,12 @@ static int setcharset(unsigned char **p, unsigned char *charset)
 static int loop(const unsigned char *pattern, const unsigned char *string,
                 int maxstars)
 {
-  unsigned char *p = (unsigned char *)pattern;
-  unsigned char *s = (unsigned char *)string;
+  const unsigned char *p = (const unsigned char *)pattern;
+  const unsigned char *s = (const unsigned char *)string;
   unsigned char charset[CURLFNM_CHSET_SIZE] = { 0 };
 
   for(;;) {
-    unsigned char *pp;
+    const unsigned char *pp;
 
     switch(*p) {
     case '*':
@@ -319,7 +319,7 @@ static int loop(const unsigned char *pattern, const unsigned char *string,
         else if(charset[CURLFNM_PRINT])
           found = ISPRINT(*s);
         else if(charset[CURLFNM_SPACE])
-          found = ISSPACE(*s);
+          found = ISBLANK(*s);
         else if(charset[CURLFNM_UPPER])
           found = ISUPPER(*s);
         else if(charset[CURLFNM_LOWER])
@@ -359,7 +359,8 @@ int Curl_fnmatch(void *ptr, const char *pattern, const char *string)
   if(!pattern || !string) {
     return CURL_FNMATCH_FAIL;
   }
-  return loop((unsigned char *)pattern, (unsigned char *)string, 2);
+  return loop((const unsigned char *)pattern,
+              (const unsigned char *)string, 2);
 }
 #else
 #include <fnmatch.h>
diff --git a/extra/curl/curl-8.12.1/lib/curl_fnmatch.h b/extra/curl/curl-8.14.1/lib/curl_fnmatch.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/curl_fnmatch.h
rename to extra/curl/curl-8.14.1/lib/curl_fnmatch.h
diff --git a/extra/curl/curl-8.12.1/lib/curl_get_line.c b/extra/curl/curl-8.14.1/lib/curl_get_line.c
similarity index 92%
rename from extra/curl/curl-8.12.1/lib/curl_get_line.c
rename to extra/curl/curl-8.14.1/lib/curl_get_line.c
index 7ee3b65af77f..2bb57492b306 100644
--- a/extra/curl/curl-8.12.1/lib/curl_get_line.c
+++ b/extra/curl/curl-8.14.1/lib/curl_get_line.c
@@ -28,9 +28,7 @@
   !defined(CURL_DISABLE_HSTS) || !defined(CURL_DISABLE_NETRC)
 
 #include "curl_get_line.h"
-#ifdef BUILDING_LIBCURL
 #include "curl_memory.h"
-#endif
 /* The last #include file should be: */
 #include "memdebug.h"
 
@@ -42,7 +40,7 @@ int Curl_get_line(struct dynbuf *buf, FILE *input)
 {
   CURLcode result;
   char buffer[128];
-  Curl_dyn_reset(buf);
+  curlx_dyn_reset(buf);
   while(1) {
     char *b = fgets(buffer, sizeof(buffer), input);
 
@@ -52,7 +50,7 @@ int Curl_get_line(struct dynbuf *buf, FILE *input)
       if(!rlen)
         break;
 
-      result = Curl_dyn_addn(buf, b, rlen);
+      result = curlx_dyn_addn(buf, b, rlen);
       if(result)
         /* too long line or out of memory */
         return 0; /* error */
@@ -63,13 +61,15 @@ int Curl_get_line(struct dynbuf *buf, FILE *input)
 
       else if(feof(input)) {
         /* append a newline */
-        result = Curl_dyn_addn(buf, "\n", 1);
+        result = curlx_dyn_addn(buf, "\n", 1);
         if(result)
           /* too long line or out of memory */
           return 0; /* error */
         return 1; /* all good */
       }
     }
+    else if(curlx_dyn_len(buf))
+      return 1; /* all good */
     else
       break;
   }
diff --git a/extra/curl/curl-8.12.1/lib/curl_get_line.h b/extra/curl/curl-8.14.1/lib/curl_get_line.h
similarity index 85%
rename from extra/curl/curl-8.12.1/lib/curl_get_line.h
rename to extra/curl/curl-8.14.1/lib/curl_get_line.h
index 1e3b0f0357c7..d4877123f261 100644
--- a/extra/curl/curl-8.12.1/lib/curl_get_line.h
+++ b/extra/curl/curl-8.14.1/lib/curl_get_line.h
@@ -24,13 +24,7 @@
  *
  ***************************************************************************/
 
-#include "dynbuf.h"
-
-#ifndef BUILDING_LIBCURL
-/* this renames functions so that the tool code can use the same code
-   without getting symbol collisions */
-#define Curl_get_line(a,b) curlx_get_line(a,b)
-#endif
+#include "curlx/dynbuf.h"
 
 /* Curl_get_line() returns complete lines that end with a newline. */
 int Curl_get_line(struct dynbuf *buf, FILE *input);
diff --git a/extra/curl/curl-8.12.1/lib/curl_gethostname.c b/extra/curl/curl-8.14.1/lib/curl_gethostname.c
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/curl_gethostname.c
rename to extra/curl/curl-8.14.1/lib/curl_gethostname.c
diff --git a/extra/curl/curl-8.12.1/lib/curl_gethostname.h b/extra/curl/curl-8.14.1/lib/curl_gethostname.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/curl_gethostname.h
rename to extra/curl/curl-8.14.1/lib/curl_gethostname.h
diff --git a/extra/curl/curl-8.12.1/lib/curl_gssapi.c b/extra/curl/curl-8.14.1/lib/curl_gssapi.c
similarity index 97%
rename from extra/curl/curl-8.12.1/lib/curl_gssapi.c
rename to extra/curl/curl-8.14.1/lib/curl_gssapi.c
index 0b383f506690..f83701ad6454 100644
--- a/extra/curl/curl-8.12.1/lib/curl_gssapi.c
+++ b/extra/curl/curl-8.14.1/lib/curl_gssapi.c
@@ -46,10 +46,10 @@
 #endif
 
 gss_OID_desc Curl_spnego_mech_oid CURL_ALIGN8 = {
-  6, (char *)"\x2b\x06\x01\x05\x05\x02"
+  6, CURL_UNCONST("\x2b\x06\x01\x05\x05\x02")
 };
 gss_OID_desc Curl_krb5_mech_oid CURL_ALIGN8 = {
-  9, (char *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"
+  9, CURL_UNCONST("\x2a\x86\x48\x86\xf7\x12\x01\x02\x02")
 };
 
 OM_uint32 Curl_gss_init_sec_context(
diff --git a/extra/curl/curl-8.12.1/lib/curl_gssapi.h b/extra/curl/curl-8.14.1/lib/curl_gssapi.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/curl_gssapi.h
rename to extra/curl/curl-8.14.1/lib/curl_gssapi.h
diff --git a/extra/curl/curl-8.12.1/lib/curl_hmac.h b/extra/curl/curl-8.14.1/lib/curl_hmac.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/curl_hmac.h
rename to extra/curl/curl-8.14.1/lib/curl_hmac.h
diff --git a/extra/curl/curl-8.12.1/lib/curl_krb5.h b/extra/curl/curl-8.14.1/lib/curl_krb5.h
similarity index 88%
rename from extra/curl/curl-8.12.1/lib/curl_krb5.h
rename to extra/curl/curl-8.14.1/lib/curl_krb5.h
index ccf6b96a875e..574340fd3c58 100644
--- a/extra/curl/curl-8.12.1/lib/curl_krb5.h
+++ b/extra/curl/curl-8.14.1/lib/curl_krb5.h
@@ -39,14 +39,16 @@ struct Curl_sec_client_mech {
 #define AUTH_CONTINUE   1
 #define AUTH_ERROR      2
 
-#ifdef HAVE_GSSAPI
+#if defined(HAVE_GSSAPI) && !defined(CURL_DISABLE_FTP)
+void Curl_sec_conn_init(struct connectdata *);
+void Curl_sec_conn_destroy(struct connectdata *);
 int Curl_sec_read_msg(struct Curl_easy *data, struct connectdata *conn, char *,
                       enum protection_level);
-void Curl_sec_end(struct connectdata *);
 CURLcode Curl_sec_login(struct Curl_easy *, struct connectdata *);
 int Curl_sec_request_prot(struct connectdata *conn, const char *level);
 #else
-#define Curl_sec_end(x)
+#define Curl_sec_conn_init(x)     Curl_nop_stmt
+#define Curl_sec_conn_destroy(x)  Curl_nop_stmt
 #endif
 
 #endif /* HEADER_CURL_KRB5_H */
diff --git a/extra/curl/curl-8.12.1/lib/curl_ldap.h b/extra/curl/curl-8.14.1/lib/curl_ldap.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/curl_ldap.h
rename to extra/curl/curl-8.14.1/lib/curl_ldap.h
diff --git a/extra/curl/curl-8.12.1/lib/curl_md4.h b/extra/curl/curl-8.14.1/lib/curl_md4.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/curl_md4.h
rename to extra/curl/curl-8.14.1/lib/curl_md4.h
diff --git a/extra/curl/curl-8.12.1/lib/curl_md5.h b/extra/curl/curl-8.14.1/lib/curl_md5.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/curl_md5.h
rename to extra/curl/curl-8.14.1/lib/curl_md5.h
diff --git a/extra/curl/curl-8.12.1/lib/curl_memory.h b/extra/curl/curl-8.14.1/lib/curl_memory.h
similarity index 94%
rename from extra/curl/curl-8.12.1/lib/curl_memory.h
rename to extra/curl/curl-8.14.1/lib/curl_memory.h
index 7f110dab7de8..bc3e944fea2c 100644
--- a/extra/curl/curl-8.12.1/lib/curl_memory.h
+++ b/extra/curl/curl-8.14.1/lib/curl_memory.h
@@ -84,20 +84,6 @@
 #undef socketpair
 #endif
 
-#ifndef CURL_NO_GETADDRINFO_OVERRIDE
-#ifdef HAVE_GETADDRINFO
-#if defined(getaddrinfo) && defined(__osf__)
-#undef ogetaddrinfo
-#else
-#undef getaddrinfo
-#endif
-#endif /* HAVE_GETADDRINFO */
-
-#ifdef HAVE_FREEADDRINFO
-#undef freeaddrinfo
-#endif /* HAVE_FREEADDRINFO */
-#endif /* !CURL_NO_GETADDRINFO_OVERRIDE */
-
 /* sclose is probably already defined, redefine it! */
 #undef sclose
 #undef fopen
diff --git a/extra/curl/curl-8.12.1/lib/curl_memrchr.c b/extra/curl/curl-8.14.1/lib/curl_memrchr.c
similarity index 91%
rename from extra/curl/curl-8.12.1/lib/curl_memrchr.c
rename to extra/curl/curl-8.14.1/lib/curl_memrchr.c
index c6d55f10423b..5b6a39c022cc 100644
--- a/extra/curl/curl-8.12.1/lib/curl_memrchr.c
+++ b/extra/curl/curl-8.14.1/lib/curl_memrchr.c
@@ -33,10 +33,6 @@
 #include "memdebug.h"
 
 #ifndef HAVE_MEMRCHR
-#if (!defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)) || \
-  defined(USE_OPENSSL) || \
-  defined(USE_SCHANNEL)
-
 /*
  * Curl_memrchr()
  *
@@ -57,12 +53,10 @@ Curl_memrchr(const void *s, int c, size_t n)
 
     while(p >= q) {
       if(*p == (unsigned char)c)
-        return (void *)p;
+        return CURL_UNCONST(p);
       p--;
     }
   }
   return NULL;
 }
-
-#endif
 #endif /* HAVE_MEMRCHR */
diff --git a/extra/curl/curl-8.12.1/lib/curl_memrchr.h b/extra/curl/curl-8.14.1/lib/curl_memrchr.h
similarity index 91%
rename from extra/curl/curl-8.12.1/lib/curl_memrchr.h
rename to extra/curl/curl-8.14.1/lib/curl_memrchr.h
index 67a21ef36175..3c7dda96ac98 100644
--- a/extra/curl/curl-8.12.1/lib/curl_memrchr.h
+++ b/extra/curl/curl-8.14.1/lib/curl_memrchr.h
@@ -34,15 +34,9 @@
 #endif
 
 #else /* HAVE_MEMRCHR */
-#if (!defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)) || \
-  defined(USE_OPENSSL) || \
-  defined(USE_SCHANNEL)
-
 void *Curl_memrchr(const void *s, int c, size_t n);
-
 #define memrchr(x,y,z) Curl_memrchr((x),(y),(z))
 
-#endif
 #endif /* HAVE_MEMRCHR */
 
 #endif /* HEADER_CURL_MEMRCHR_H */
diff --git a/extra/curl/curl-8.12.1/lib/curl_ntlm_core.c b/extra/curl/curl-8.14.1/lib/curl_ntlm_core.c
similarity index 94%
rename from extra/curl/curl-8.12.1/lib/curl_ntlm_core.c
rename to extra/curl/curl-8.14.1/lib/curl_ntlm_core.c
index 54491fc0a89a..d6cd44d963eb 100644
--- a/extra/curl/curl-8.12.1/lib/curl_ntlm_core.c
+++ b/extra/curl/curl-8.14.1/lib/curl_ntlm_core.c
@@ -71,16 +71,7 @@
 #  include <openssl/md5.h>
 #  include <openssl/ssl.h>
 #  include <openssl/rand.h>
-#  if (defined(OPENSSL_VERSION_NUMBER) && \
-       (OPENSSL_VERSION_NUMBER < 0x00907001L)) && !defined(USE_WOLFSSL)
-#    define DES_key_schedule des_key_schedule
-#    define DES_cblock des_cblock
-#    define DES_set_odd_parity des_set_odd_parity
-#    define DES_set_key des_set_key
-#    define DES_ecb_encrypt des_ecb_encrypt
-#    define DESKEY(x) x
-#    define DESKEYARG(x) x
-#  elif defined(OPENSSL_IS_AWSLC)
+#  if defined(OPENSSL_IS_AWSLC)
 #    define DES_set_key_unchecked (void)DES_set_key
 #    define DESKEYARG(x) *x
 #    define DESKEY(x) &x
@@ -135,7 +126,7 @@
 #include "curl_ntlm_core.h"
 #include "curl_md5.h"
 #include "curl_hmac.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
 #include "curl_endian.h"
 #include "curl_des.h"
 #include "curl_md4.h"
@@ -144,9 +135,6 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
-#define NTLMv2_BLOB_SIGNATURE "\x01\x01\x00\x00"
-#define NTLMv2_BLOB_LEN       (44 -16 + ntlm->target_info_len + 4)
-
 #if !defined(CURL_NTLM_NOT_SUPPORTED)
 /*
 * Turns a 56-bit key into being 64-bit wide.
@@ -333,16 +321,16 @@ void Curl_ntlm_core_lm_resp(const unsigned char *keys,
   DES_key_schedule ks;
 
   setup_des_key(keys, DESKEY(ks));
-  DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) results,
-                  DESKEY(ks), DES_ENCRYPT);
+  DES_ecb_encrypt((DES_cblock*)CURL_UNCONST(plaintext),
+                  (DES_cblock*)results, DESKEY(ks), DES_ENCRYPT);
 
   setup_des_key(keys + 7, DESKEY(ks));
-  DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) (results + 8),
-                  DESKEY(ks), DES_ENCRYPT);
+  DES_ecb_encrypt((DES_cblock*)CURL_UNCONST(plaintext),
+                  (DES_cblock*)(results + 8), DESKEY(ks), DES_ENCRYPT);
 
   setup_des_key(keys + 14, DESKEY(ks));
-  DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) (results + 16),
-                  DESKEY(ks), DES_ENCRYPT);
+  DES_ecb_encrypt((DES_cblock*)CURL_UNCONST(plaintext),
+                  (DES_cblock*)(results + 16), DESKEY(ks), DES_ENCRYPT);
 #elif defined(USE_GNUTLS)
   struct des_ctx des;
   setup_des_key(keys, &des);
@@ -387,12 +375,12 @@ CURLcode Curl_ntlm_core_mk_lm_hash(const char *password,
     DES_key_schedule ks;
 
     setup_des_key(pw, DESKEY(ks));
-    DES_ecb_encrypt((DES_cblock *)magic, (DES_cblock *)lmbuffer,
-                    DESKEY(ks), DES_ENCRYPT);
+    DES_ecb_encrypt((DES_cblock *)CURL_UNCONST(magic),
+                    (DES_cblock *)lmbuffer, DESKEY(ks), DES_ENCRYPT);
 
     setup_des_key(pw + 7, DESKEY(ks));
-    DES_ecb_encrypt((DES_cblock *)magic, (DES_cblock *)(lmbuffer + 8),
-                    DESKEY(ks), DES_ENCRYPT);
+    DES_ecb_encrypt((DES_cblock *)CURL_UNCONST(magic),
+                    (DES_cblock *)(lmbuffer + 8), DESKEY(ks), DES_ENCRYPT);
 #elif defined(USE_GNUTLS)
     struct des_ctx des;
     setup_des_key(pw, &des);
@@ -465,6 +453,9 @@ CURLcode Curl_ntlm_core_mk_nt_hash(const char *password,
 
 #if !defined(USE_WINDOWS_SSPI)
 
+#define NTLMv2_BLOB_SIGNATURE "\x01\x01\x00\x00"
+#define NTLMv2_BLOB_LEN       (44 -16 + ntlm->target_info_len + 4)
+
 /* Timestamp in tenths of a microsecond since January 1, 1601 00:00:00 UTC. */
 struct ms_filetime {
   unsigned int dwLowDateTime;
@@ -626,7 +617,7 @@ CURLcode Curl_ntlm_core_mk_ntlmv2_resp(unsigned char *ntlmv2hash,
   /* Concatenate the Type 2 challenge with the BLOB and do HMAC MD5 */
   memcpy(ptr + 8, &ntlm->nonce[0], 8);
   result = Curl_hmacit(&Curl_HMAC_MD5, ntlmv2hash, HMAC_MD5_LENGTH, ptr + 8,
-                    NTLMv2_BLOB_LEN + 8, hmac_output);
+                       NTLMv2_BLOB_LEN + 8, hmac_output);
   if(result) {
     free(ptr);
     return result;
diff --git a/extra/curl/curl-8.12.1/lib/curl_ntlm_core.h b/extra/curl/curl-8.14.1/lib/curl_ntlm_core.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/curl_ntlm_core.h
rename to extra/curl/curl-8.14.1/lib/curl_ntlm_core.h
diff --git a/extra/curl/curl-8.12.1/lib/curl_printf.h b/extra/curl/curl-8.14.1/lib/curl_printf.h
similarity index 89%
rename from extra/curl/curl-8.12.1/lib/curl_printf.h
rename to extra/curl/curl-8.14.1/lib/curl_printf.h
index e851b14a5076..6e0fa1fa8d84 100644
--- a/extra/curl/curl-8.12.1/lib/curl_printf.h
+++ b/extra/curl/curl-8.14.1/lib/curl_printf.h
@@ -24,21 +24,25 @@
  *
  ***************************************************************************/
 
-/*
- * This header should be included by ALL code in libcurl that uses any
- * *rintf() functions.
- */
-
-#ifndef CURL_TEMP_PRINTF
-#error "CURL_TEMP_PRINTF must be set before including curl/mprintf.h"
-#endif
-
 #include <curl/mprintf.h>
 
 #define MERR_OK        0
 #define MERR_MEM       1
 #define MERR_TOO_LARGE 2
 
+/* Lower-case digits.  */
+extern const unsigned char Curl_ldigits[];
+
+/* Upper-case digits.  */
+extern const unsigned char Curl_udigits[];
+
+#ifdef BUILDING_LIBCURL
+
+/*
+ * This header should be included by ALL code in libcurl that uses any
+ * *rintf() functions.
+ */
+
 # undef printf
 # undef fprintf
 # undef msnprintf
@@ -55,4 +59,6 @@
 # define mvsnprintf curl_mvsnprintf
 # define aprintf curl_maprintf
 # define vaprintf curl_mvaprintf
+
+#endif /* BUILDING_LIBCURL */
 #endif /* HEADER_CURL_PRINTF_H */
diff --git a/extra/curl/curl-8.12.1/lib/curl_range.c b/extra/curl/curl-8.14.1/lib/curl_range.c
similarity index 84%
rename from extra/curl/curl-8.12.1/lib/curl_range.c
rename to extra/curl/curl-8.14.1/lib/curl_range.c
index 49fb5f07783d..e9620a29b535 100644
--- a/extra/curl/curl-8.12.1/lib/curl_range.c
+++ b/extra/curl/curl-8.14.1/lib/curl_range.c
@@ -26,7 +26,7 @@
 #include <curl/curl.h>
 #include "curl_range.h"
 #include "sendf.h"
-#include "strtoofft.h"
+#include "curlx/strparse.h"
 
 /* Only include this function if one or more of FTP, FILE are enabled. */
 #if !defined(CURL_DISABLE_FTP) || !defined(CURL_DISABLE_FILE)
@@ -37,28 +37,29 @@
  */
 CURLcode Curl_range(struct Curl_easy *data)
 {
-  curl_off_t from, to;
-  char *ptr;
-  char *ptr2;
-
   if(data->state.use_range && data->state.range) {
-    CURLofft from_t;
-    CURLofft to_t;
-    from_t = curlx_strtoofft(data->state.range, &ptr, 10, &from);
-    if(from_t == CURL_OFFT_FLOW)
-      return CURLE_RANGE_ERROR;
-    while(*ptr && (ISBLANK(*ptr) || (*ptr == '-')))
-      ptr++;
-    to_t = curlx_strtoofft(ptr, &ptr2, 10, &to);
-    if(to_t == CURL_OFFT_FLOW)
+    curl_off_t from, to;
+    bool first_num = TRUE;
+    const char *p = data->state.range;
+    if(curlx_str_number(&p, &from, CURL_OFF_T_MAX))
+      first_num = FALSE;
+
+    if(curlx_str_single(&p, '-'))
+      /* no leading dash or after the first number is an error */
       return CURLE_RANGE_ERROR;
-    if((to_t == CURL_OFFT_INVAL) && !from_t) {
+
+    if(curlx_str_number(&p, &to, CURL_OFF_T_MAX)) {
+      /* no second number */
       /* X - */
       data->state.resume_from = from;
       DEBUGF(infof(data, "RANGE %" FMT_OFF_T " to end of file", from));
     }
-    else if((from_t == CURL_OFFT_INVAL) && !to_t) {
+    else if(!first_num) {
       /* -Y */
+      if(!to)
+        /* "-0" is just wrong */
+        return CURLE_RANGE_ERROR;
+
       data->req.maxdownload = to;
       data->state.resume_from = -to;
       DEBUGF(infof(data, "RANGE the last %" FMT_OFF_T " bytes", to));
diff --git a/extra/curl/curl-8.12.1/lib/curl_range.h b/extra/curl/curl-8.14.1/lib/curl_range.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/curl_range.h
rename to extra/curl/curl-8.14.1/lib/curl_range.h
diff --git a/extra/curl/curl-8.12.1/lib/curl_rtmp.c b/extra/curl/curl-8.14.1/lib/curl_rtmp.c
similarity index 92%
rename from extra/curl/curl-8.12.1/lib/curl_rtmp.c
rename to extra/curl/curl-8.14.1/lib/curl_rtmp.c
index caa92e7af3c9..62632c1e9cb3 100644
--- a/extra/curl/curl-8.12.1/lib/curl_rtmp.c
+++ b/extra/curl/curl-8.14.1/lib/curl_rtmp.c
@@ -29,10 +29,11 @@
 
 #include "curl_rtmp.h"
 #include "urldata.h"
-#include "nonblock.h" /* for curlx_nonblock */
+#include "url.h"
+#include "curlx/nonblock.h" /* for curlx_nonblock */
 #include "progress.h" /* for Curl_pgrsSetUploadSize */
 #include "transfer.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
 #include <curl/curl.h>
 #include <librtmp/rtmp.h>
 
@@ -52,6 +53,10 @@
 
 #define DEF_BUFTIME    (2*60*60*1000)    /* 2 hours */
 
+/* meta key for storing RTMP* at connection */
+#define CURL_META_RTMP_CONN   "meta:proto:rtmp:conn"
+
+
 static CURLcode rtmp_setup_connection(struct Curl_easy *data,
                                       struct connectdata *conn);
 static CURLcode rtmp_do(struct Curl_easy *data, bool *done);
@@ -217,11 +222,21 @@ const struct Curl_handler Curl_handler_rtmpts = {
   PROTOPT_NONE                          /* flags */
 };
 
+static void rtmp_conn_dtor(void *key, size_t klen, void *entry)
+{
+  RTMP *r = entry;
+  (void)key;
+  (void)klen;
+  RTMP_Close(r);
+  RTMP_Free(r);
+}
+
 static CURLcode rtmp_setup_connection(struct Curl_easy *data,
                                       struct connectdata *conn)
 {
   RTMP *r = RTMP_Alloc();
-  if(!r)
+  if(!r ||
+    Curl_conn_meta_set(conn, CURL_META_RTMP_CONN, r, rtmp_conn_dtor))
     return CURLE_OUT_OF_MEMORY;
 
   RTMP_Init(r);
@@ -230,16 +245,18 @@ static CURLcode rtmp_setup_connection(struct Curl_easy *data,
     RTMP_Free(r);
     return CURLE_URL_MALFORMAT;
   }
-  conn->proto.rtmp = r;
   return CURLE_OK;
 }
 
 static CURLcode rtmp_connect(struct Curl_easy *data, bool *done)
 {
   struct connectdata *conn = data->conn;
-  RTMP *r = conn->proto.rtmp;
+  RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN);
   SET_RCVTIMEO(tv, 10);
 
+  if(!r)
+    return CURLE_FAILED_INIT;
+
   r->m_sb.sb_socket = (int)conn->sock[FIRSTSOCKET];
 
   /* We have to know if it is a write before we send the
@@ -272,9 +289,9 @@ static CURLcode rtmp_connect(struct Curl_easy *data, bool *done)
 static CURLcode rtmp_do(struct Curl_easy *data, bool *done)
 {
   struct connectdata *conn = data->conn;
-  RTMP *r = conn->proto.rtmp;
+  RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN);
 
-  if(!RTMP_ConnectStream(r, 0))
+  if(!r || !RTMP_ConnectStream(r, 0))
     return CURLE_FAILED_INIT;
 
   if(data->state.upload) {
@@ -301,14 +318,11 @@ static CURLcode rtmp_disconnect(struct Curl_easy *data,
                                 struct connectdata *conn,
                                 bool dead_connection)
 {
-  RTMP *r = conn->proto.rtmp;
+  RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN);
   (void)data;
   (void)dead_connection;
-  if(r) {
-    conn->proto.rtmp = NULL;
-    RTMP_Close(r);
-    RTMP_Free(r);
-  }
+  if(r)
+    Curl_conn_meta_remove(conn, CURL_META_RTMP_CONN);
   return CURLE_OK;
 }
 
@@ -316,10 +330,14 @@ static ssize_t rtmp_recv(struct Curl_easy *data, int sockindex, char *buf,
                          size_t len, CURLcode *err)
 {
   struct connectdata *conn = data->conn;
-  RTMP *r = conn->proto.rtmp;
+  RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN);
   ssize_t nread;
 
   (void)sockindex; /* unused */
+  if(!r) {
+    *err = CURLE_FAILED_INIT;
+    return -1;
+  }
 
   nread = RTMP_Read(r, buf, curlx_uztosi(len));
   if(nread < 0) {
@@ -338,13 +356,17 @@ static ssize_t rtmp_send(struct Curl_easy *data, int sockindex,
                          const void *buf, size_t len, bool eos, CURLcode *err)
 {
   struct connectdata *conn = data->conn;
-  RTMP *r = conn->proto.rtmp;
+  RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN);
   ssize_t num;
 
   (void)sockindex; /* unused */
   (void)eos; /* unused */
+  if(!r) {
+    *err = CURLE_FAILED_INIT;
+    return -1;
+  }
 
-  num = RTMP_Write(r, (char *)buf, curlx_uztosi(len));
+  num = RTMP_Write(r, (const char *)buf, curlx_uztosi(len));
   if(num < 0)
     *err = CURLE_SEND_ERROR;
 
diff --git a/extra/curl/curl-8.12.1/lib/curl_rtmp.h b/extra/curl/curl-8.14.1/lib/curl_rtmp.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/curl_rtmp.h
rename to extra/curl/curl-8.14.1/lib/curl_rtmp.h
diff --git a/extra/curl/curl-8.12.1/lib/curl_sasl.c b/extra/curl/curl-8.14.1/lib/curl_sasl.c
similarity index 87%
rename from extra/curl/curl-8.12.1/lib/curl_sasl.c
rename to extra/curl/curl-8.14.1/lib/curl_sasl.c
index 24f8c8c53c19..4fcbaac263bd 100644
--- a/extra/curl/curl-8.12.1/lib/curl_sasl.c
+++ b/extra/curl/curl-8.14.1/lib/curl_sasl.c
@@ -42,15 +42,14 @@
 #include <curl/curl.h>
 #include "urldata.h"
 
-#include "curl_base64.h"
+#include "curlx/base64.h"
 #include "curl_md5.h"
 #include "vauth/vauth.h"
 #include "cfilters.h"
 #include "vtls/vtls.h"
 #include "curl_hmac.h"
 #include "curl_sasl.h"
-#include "warnless.h"
-#include "strtok.h"
+#include "curlx/warnless.h"
 #include "sendf.h"
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -284,7 +283,7 @@ static CURLcode get_server_message(struct SASL *sasl, struct Curl_easy *data,
     if(!*serverdata || *serverdata == '=')
       Curl_bufref_set(out, NULL, 0, NULL);
     else {
-      result = Curl_base64_decode(serverdata, &msg, &msglen);
+      result = curlx_base64_decode(serverdata, &msg, &msglen);
       if(!result)
         Curl_bufref_set(out, msg, msglen, curl_free);
     }
@@ -307,8 +306,8 @@ static CURLcode build_message(struct SASL *sasl, struct bufref *msg)
       char *base64;
       size_t base64len;
 
-      result = Curl_base64_encode((const char *) Curl_bufref_ptr(msg),
-                                  Curl_bufref_len(msg), &base64, &base64len);
+      result = curlx_base64_encode((const char *) Curl_bufref_ptr(msg),
+                                   Curl_bufref_len(msg), &base64, &base64len);
       if(!result)
         Curl_bufref_set(msg, base64, base64len, curl_free);
     }
@@ -757,4 +756,101 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
 
   return result;
 }
+
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+static void sasl_unchosen(struct Curl_easy *data, unsigned short mech,
+                          unsigned short enabledmechs,
+                          bool built_in, bool platform,
+                          const char *param_missing)
+{
+  const char *mname = NULL;
+  size_t i;
+
+  if(!(enabledmechs & mech))
+    return;
+
+  for(i = 0; mechtable[i].name; ++i) {
+    if(mechtable[i].bit == mech) {
+      mname = mechtable[i].name;
+      break;
+    }
+  }
+  if(!mname)  /* should not happen */
+    return;
+  if(!built_in)
+    infof(data, "SASL: %s not builtin", mname);
+  else if(!platform)
+    infof(data, "SASL: %s not supported by the platform/libraries", mname);
+  else {
+    if(param_missing)
+      infof(data, "SASL: %s is missing %s", mname, param_missing);
+    if(!data->state.aptr.user)
+      infof(data, "SASL: %s is missing username", mname);
+  }
+}
+#endif /* CURL_DISABLE_VERBOSE_STRINGS */
+
+CURLcode Curl_sasl_is_blocked(struct SASL *sasl, struct Curl_easy *data)
+{
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+#ifdef USE_KERBEROS5
+#define CURL_SASL_KERBEROS5   TRUE
+#else
+#define CURL_SASL_KERBEROS5   FALSE
+#endif
+#ifdef USE_GSASL
+#define CURL_SASL_GASL        TRUE
+#else
+#define CURL_SASL_GASL        FALSE
+#endif
+#ifdef CURL_DISABLE_DIGEST_AUTH
+#define CURL_SASL_DIGEST      TRUE
+#else
+#define CURL_SASL_DIGEST      FALSE
+#endif
+#ifndef USE_NTLM
+#define CURL_SASL_NTLM        TRUE
+#else
+#define CURL_SASL_NTLM        FALSE
+#endif
+  /* Failing SASL authentication is a pain. Give a helping hand if
+   * we were unable to select an AUTH mechanism.
+   * `sasl->authmechs` are mechanisms offered by the peer
+   * `sasl->prefmech`  are mechanisms preferred by us */
+  unsigned short enabledmechs = sasl->authmechs & sasl->prefmech;
+
+  if(!sasl->authmechs)
+    infof(data, "SASL: no auth mechanism was offered or recognized");
+  else if(!enabledmechs)
+    infof(data, "SASL: no overlap between offered and configured "
+          "auth mechanisms");
+  else {
+    infof(data, "SASL: no auth mechanism offered could be selected");
+    if((enabledmechs & SASL_MECH_EXTERNAL) && data->conn->passwd[0])
+      infof(data, "SASL: auth EXTERNAL not chosen with password");
+    sasl_unchosen(data, SASL_MECH_GSSAPI, enabledmechs,
+                  CURL_SASL_KERBEROS5, Curl_auth_is_gssapi_supported(), NULL);
+    sasl_unchosen(data, SASL_MECH_SCRAM_SHA_256, enabledmechs,
+                  CURL_SASL_GASL, FALSE, NULL);
+    sasl_unchosen(data, SASL_MECH_SCRAM_SHA_1, enabledmechs,
+                  CURL_SASL_GASL, FALSE, NULL);
+    sasl_unchosen(data, SASL_MECH_DIGEST_MD5, enabledmechs,
+                  CURL_SASL_DIGEST, Curl_auth_is_digest_supported(), NULL);
+    sasl_unchosen(data, SASL_MECH_CRAM_MD5, enabledmechs,
+                  CURL_SASL_DIGEST, TRUE, NULL);
+    sasl_unchosen(data, SASL_MECH_NTLM, enabledmechs,
+                  CURL_SASL_NTLM, Curl_auth_is_ntlm_supported(), NULL);
+    sasl_unchosen(data, SASL_MECH_OAUTHBEARER, enabledmechs,  TRUE, TRUE,
+                  data->set.str[STRING_BEARER] ?
+                  NULL : "CURLOPT_XOAUTH2_BEARER");
+    sasl_unchosen(data, SASL_MECH_XOAUTH2, enabledmechs,  TRUE, TRUE,
+                  data->set.str[STRING_BEARER] ?
+                  NULL : "CURLOPT_XOAUTH2_BEARER");
+  }
+#endif /* CURL_DISABLE_VERBOSE_STRINGS */
+  (void)sasl;
+  (void)data;
+  return CURLE_LOGIN_DENIED;
+}
+
 #endif /* protocols are enabled that use SASL */
diff --git a/extra/curl/curl-8.12.1/lib/curl_sasl.h b/extra/curl/curl-8.14.1/lib/curl_sasl.h
similarity index 98%
rename from extra/curl/curl-8.12.1/lib/curl_sasl.h
rename to extra/curl/curl-8.14.1/lib/curl_sasl.h
index e94e6431a242..59983f7c669d 100644
--- a/extra/curl/curl-8.12.1/lib/curl_sasl.h
+++ b/extra/curl/curl-8.14.1/lib/curl_sasl.h
@@ -162,4 +162,6 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data,
 CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
                             int code, saslprogress *progress);
 
+CURLcode Curl_sasl_is_blocked(struct SASL *sasl, struct Curl_easy *data);
+
 #endif /* HEADER_CURL_SASL_H */
diff --git a/extra/curl/curl-8.12.1/lib/curl_setup.h b/extra/curl/curl-8.14.1/lib/curl_setup.h
similarity index 93%
rename from extra/curl/curl-8.12.1/lib/curl_setup.h
rename to extra/curl/curl-8.14.1/lib/curl_setup.h
index 7a6ef10b2dcb..1d484c61ab40 100644
--- a/extra/curl/curl-8.12.1/lib/curl_setup.h
+++ b/extra/curl/curl-8.14.1/lib/curl_setup.h
@@ -28,9 +28,6 @@
 #define CURL_NO_OLDIES
 #endif
 
-/* Tell "curl/curl.h" not to include "curl/mprintf.h" */
-#define CURL_SKIP_INCLUDE_MPRINTF
-
 /* Set default _WIN32_WINNT */
 #ifdef __MINGW32__
 #include <_mingw.h>
@@ -56,7 +53,7 @@
 #  endif
 #endif
 
-#if defined(__APPLE__)
+#ifdef __APPLE__
 #include <sys/types.h>
 #include <TargetConditionals.h>
 /* Fixup faulty target macro initialization in macOS SDK since v14.4 (as of
@@ -78,6 +75,12 @@
 #endif
 #endif
 
+/* Visual Studio 2008 is the minimum Visual Studio version we support.
+   Workarounds for older versions of Visual Studio have been removed. */
+#if defined(_MSC_VER) && (_MSC_VER < 1500)
+#error "Ancient versions of Visual Studio are no longer supported due to bugs."
+#endif
+
 #ifdef _MSC_VER
 /* Disable Visual Studio warnings: 4127 "conditional expression is constant" */
 #pragma warning(disable:4127)
@@ -114,6 +117,14 @@
 #  endif
 #endif
 
+/* Avoid bogus format check warnings with mingw32ce gcc 4.4.0 in
+   C99 (-std=gnu99) mode */
+#if defined(__MINGW32CE__) && !defined(CURL_NO_FMT_CHECKS) && \
+  (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) && \
+  (defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 4))
+#define CURL_NO_FMT_CHECKS
+#endif
+
 /* Compatibility */
 #ifdef ENABLE_IPV6
 #define USE_IPV6 1
@@ -161,6 +172,12 @@
 /* system header files in our config files, avoid this at any cost. */
 /* ================================================================ */
 
+#ifdef HAVE_LIBZ
+#  ifndef ZLIB_CONST
+#  define ZLIB_CONST  /* Use z_const. Supported by v1.2.5.2 and upper. */
+#  endif
+#endif
+
 /*
  * AIX 4.3 and newer needs _THREAD_SAFE defined to build
  * proper reentrant code. Others may also need it.
@@ -262,7 +279,7 @@
  * When HTTP is disabled, disable HTTP-only features
  */
 
-#if defined(CURL_DISABLE_HTTP)
+#ifdef CURL_DISABLE_HTTP
 #  define CURL_DISABLE_ALTSVC 1
 #  define CURL_DISABLE_COOKIES 1
 #  define CURL_DISABLE_BASIC_AUTH 1
@@ -435,6 +452,11 @@
 #  define __NO_NET_API
 #endif
 
+/* Whether to use eventfd() */
+#if defined(HAVE_EVENTFD) && defined(HAVE_SYS_EVENTFD_H)
+#define USE_EVENTFD
+#endif
+
 #include <stdio.h>
 #include <assert.h>
 
@@ -448,18 +470,14 @@
 #include <curl/stdcheaders.h>
 #endif
 
-#ifdef _WIN32
-#define Curl_getpid() GetCurrentProcessId()
-#else
-#define Curl_getpid() getpid()
-#endif
-
 /*
  * Large file (>2Gb) support using Win32 functions.
  */
 
 #ifdef USE_WIN32_LARGE_FILES
+#  ifdef HAVE_IO_H
 #  include <io.h>
+#  endif
 #  include <sys/types.h>
 #  include <sys/stat.h>
 #  undef  lseek
@@ -490,10 +508,12 @@
  */
 
 #if defined(_WIN32) && !defined(USE_WIN32_LARGE_FILES)
+#  ifdef HAVE_IO_H
 #  include <io.h>
+#  endif
 #  include <sys/types.h>
 #  include <sys/stat.h>
-#  ifndef _WIN32_WCE
+#  ifndef UNDER_CE
 #    undef  lseek
 #    define lseek(fdes,offset,whence)  _lseek(fdes, (long)offset, whence)
 #    define fstat(fdes,stp)            _fstat(fdes, stp)
@@ -668,7 +688,6 @@
 
 #  ifdef __minix
      /* Minix 3 versions up to at least 3.1.3 are missing these prototypes */
-     extern char *strtok_r(char *s, const char *delim, char **last);
      extern struct tm *gmtime_r(const time_t * const timep, struct tm *tmp);
 #  endif
 
@@ -681,16 +700,6 @@
 /*         CURLRES_* defines to use in the host*.c sources          */
 /* ---------------------------------------------------------------- */
 
-/*
- * MSVC threads support requires a multi-threaded runtime library.
- * _beginthreadex() is not available in single-threaded ones.
- * Single-threaded option was last available in VS2005: _MSC_VER <= 1400
- */
-#if defined(_MSC_VER) && !defined(_MT)  /* available in _MSC_VER <= 1400 */
-#  undef USE_THREADS_POSIX
-#  undef USE_THREADS_WIN32
-#endif
-
 /*
  * Mutually exclusive CURLRES_* definitions.
  */
@@ -764,7 +773,7 @@
 #endif
 
 /* Single point where USE_NTLM definition might be defined */
-#if !defined(CURL_DISABLE_NTLM)
+#ifndef CURL_DISABLE_NTLM
 #  if defined(USE_OPENSSL) || defined(USE_MBEDTLS) ||                   \
   defined(USE_GNUTLS) || defined(USE_SECTRANSP) ||                      \
   defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) ||              \
@@ -804,7 +813,7 @@
 
 /* noreturn attribute */
 
-#if !defined(CURL_NORETURN)
+#ifndef CURL_NORETURN
 #if (defined(__GNUC__) && (__GNUC__ >= 3)) || defined(__clang__) || \
   defined(__IAR_SYSTEMS_ICC__)
 #  define CURL_NORETURN  __attribute__((__noreturn__))
@@ -817,7 +826,7 @@
 
 /* fallthrough attribute */
 
-#if !defined(FALLTHROUGH)
+#ifndef FALLTHROUGH
 #if (defined(__GNUC__) && __GNUC__ >= 7) || \
     (defined(__clang__) && __clang_major__ >= 10)
 #  define FALLTHROUGH()  __attribute__((fallthrough))
@@ -834,6 +843,27 @@
 #include "curl_setup_once.h"
 #endif
 
+#ifdef UNDER_CE
+#define getenv curl_getenv  /* Windows CE does not support getenv() */
+#define raise(s) ((void)(s))
+/* Terrible workarounds to make Windows CE compile */
+#define errno 0
+#define CURL_SETERRNO(x) ((void)(x))
+#define EINTR  4
+#define EAGAIN 11
+#define ENOMEM 12
+#define EACCES 13
+#define EEXIST 17
+#define EISDIR 21
+#define EINVAL 22
+#define ENOSPC 28
+#define strerror(x) "?"
+#undef STDIN_FILENO
+#define STDIN_FILENO 0
+#else
+#define CURL_SETERRNO(x) (errno = (x))
+#endif
+
 /*
  * Definition of our NOP statement Object-like macro
  */
@@ -878,6 +908,17 @@
 #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
 #endif
 
+/* For MSVC (all versions as of VS2022) */
+#ifndef STDIN_FILENO
+#define STDIN_FILENO  fileno(stdin)
+#endif
+#ifndef STDOUT_FILENO
+#define STDOUT_FILENO  fileno(stdout)
+#endif
+#ifndef STDERR_FILENO
+#define STDERR_FILENO  fileno(stderr)
+#endif
+
 /* Since O_BINARY is used in bitmasks, setting it to zero makes it usable in
    source code but yet it does not ruin anything */
 #ifdef O_BINARY
@@ -935,6 +976,16 @@ endings either CRLF or LF so 't' is appropriate.
 
 #define CURL_ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
 
+#ifdef CURLDEBUG
+#define CURL_GETADDRINFO(host,serv,hint,res) \
+  curl_dbg_getaddrinfo(host, serv, hint, res, __LINE__, __FILE__)
+#define CURL_FREEADDRINFO(data) \
+  curl_dbg_freeaddrinfo(data, __LINE__, __FILE__)
+#else
+#define CURL_GETADDRINFO getaddrinfo
+#define CURL_FREEADDRINFO freeaddrinfo
+#endif
+
 /* Some versions of the Android NDK is missing the declaration */
 #if defined(HAVE_GETPWUID_R) && \
   defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
@@ -997,7 +1048,7 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf,
 #  endif
 #endif
 
-#if defined(CURL_INLINE)
+#ifdef CURL_INLINE
 /* 'CURL_INLINE' defined, use as-is */
 #elif defined(inline)
 #  define CURL_INLINE inline /* 'inline' defined, assumed correct */
diff --git a/extra/curl/curl-8.12.1/lib/curl_setup_once.h b/extra/curl/curl-8.14.1/lib/curl_setup_once.h
similarity index 67%
rename from extra/curl/curl-8.12.1/lib/curl_setup_once.h
rename to extra/curl/curl-8.14.1/lib/curl_setup_once.h
index 037bffb45cb3..c1051e0faefe 100644
--- a/extra/curl/curl-8.12.1/lib/curl_setup_once.h
+++ b/extra/curl/curl-8.14.1/lib/curl_setup_once.h
@@ -33,7 +33,9 @@
 #include <string.h>
 #include <stdarg.h>
 #include <time.h>
+#ifndef UNDER_CE
 #include <errno.h>
+#endif
 
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
@@ -63,10 +65,20 @@
 #include <unistd.h>
 #endif
 
-#ifdef USE_WOLFSSL
+#if defined(HAVE_STDINT_H) || defined(USE_WOLFSSL)
 #include <stdint.h>
 #endif
 
+/* Macro to strip 'const' without triggering a compiler warning.
+   Use it for APIs that do not or cannot support the const qualifier. */
+#ifdef HAVE_STDINT_H
+#  define CURL_UNCONST(p) ((void *)(uintptr_t)(const void *)(p))
+#elif defined(_WIN32)  /* for VS2008 */
+#  define CURL_UNCONST(p) ((void *)(ULONG_PTR)(const void *)(p))
+#else
+#  define CURL_UNCONST(p) ((void *)(p))  /* Fall back to simple cast */
+#endif
+
 #ifdef USE_SCHANNEL
 /* Must set this before <schannel.h> is included directly or indirectly by
    another Windows header. */
@@ -123,7 +135,7 @@ struct timeval {
 #endif
 
 
-#if defined(__minix)
+#ifdef __minix
 /* Minix does not support recv on TCP sockets */
 #define sread(x,y,z) (ssize_t)read((RECV_TYPE_ARG1)(x), \
                                    (RECV_TYPE_ARG2)(y), \
@@ -163,15 +175,14 @@ struct timeval {
 #endif /* HAVE_RECV */
 
 
-#if defined(__minix)
+#ifdef __minix
 /* Minix does not support send on TCP sockets */
 #define swrite(x,y,z) (ssize_t)write((SEND_TYPE_ARG1)(x), \
-                                    (SEND_TYPE_ARG2)(y), \
-                                    (SEND_TYPE_ARG3)(z))
-
+                                     (SEND_TYPE_ARG2)CURL_UNCONST(y), \
+                                     (SEND_TYPE_ARG3)(z))
 #elif defined(HAVE_SEND)
 #define swrite(x,y,z) (ssize_t)send((SEND_TYPE_ARG1)(x), \
-                                    (SEND_QUAL_ARG2 SEND_TYPE_ARG2)(y), \
+                              (SEND_QUAL_ARG2 SEND_TYPE_ARG2)CURL_UNCONST(y), \
                                     (SEND_TYPE_ARG3)(z), \
                                     (SEND_TYPE_ARG4)(SEND_4TH_ARG))
 #else /* HAVE_SEND */
@@ -185,7 +196,7 @@ struct timeval {
  * Function-like macro definition used to close a socket.
  */
 
-#if defined(HAVE_CLOSESOCKET)
+#ifdef HAVE_CLOSESOCKET
 #  define sclose(x)  closesocket((x))
 #elif defined(HAVE_CLOSESOCKET_CAMEL)
 #  define sclose(x)  CloseSocket((x))
@@ -200,7 +211,7 @@ struct timeval {
 /*
  * Stack-independent version of fcntl() on sockets:
  */
-#if defined(USE_LWIPSOCK)
+#ifdef USE_LWIPSOCK
 #  define sfcntl  lwip_fcntl
 #else
 #  define sfcntl  fcntl
@@ -284,7 +295,7 @@ typedef unsigned int bit;
  */
 
 #undef DEBUGASSERT
-#if defined(DEBUGBUILD)
+#ifdef DEBUGBUILD
 #define DEBUGASSERT(x) assert(x)
 #else
 #define DEBUGASSERT(x) do { } while(0)
@@ -310,78 +321,39 @@ typedef unsigned int bit;
  */
 
 #ifdef USE_WINSOCK
-#undef  EBADF            /* override definition in errno.h */
-#define EBADF            WSAEBADF
-#undef  EINTR            /* override definition in errno.h */
-#define EINTR            WSAEINTR
-#undef  EINVAL           /* override definition in errno.h */
-#define EINVAL           WSAEINVAL
-#undef  EWOULDBLOCK      /* override definition in errno.h */
-#define EWOULDBLOCK      WSAEWOULDBLOCK
-#undef  EINPROGRESS      /* override definition in errno.h */
-#define EINPROGRESS      WSAEINPROGRESS
-#undef  EALREADY         /* override definition in errno.h */
-#define EALREADY         WSAEALREADY
-#undef  ENOTSOCK         /* override definition in errno.h */
-#define ENOTSOCK         WSAENOTSOCK
-#undef  EDESTADDRREQ     /* override definition in errno.h */
-#define EDESTADDRREQ     WSAEDESTADDRREQ
-#undef  EMSGSIZE         /* override definition in errno.h */
-#define EMSGSIZE         WSAEMSGSIZE
-#undef  EPROTOTYPE       /* override definition in errno.h */
-#define EPROTOTYPE       WSAEPROTOTYPE
-#undef  ENOPROTOOPT      /* override definition in errno.h */
-#define ENOPROTOOPT      WSAENOPROTOOPT
-#undef  EPROTONOSUPPORT  /* override definition in errno.h */
-#define EPROTONOSUPPORT  WSAEPROTONOSUPPORT
-#define ESOCKTNOSUPPORT  WSAESOCKTNOSUPPORT
-#undef  EOPNOTSUPP       /* override definition in errno.h */
-#define EOPNOTSUPP       WSAEOPNOTSUPP
-#define EPFNOSUPPORT     WSAEPFNOSUPPORT
-#undef  EAFNOSUPPORT     /* override definition in errno.h */
-#define EAFNOSUPPORT     WSAEAFNOSUPPORT
-#undef  EADDRINUSE       /* override definition in errno.h */
-#define EADDRINUSE       WSAEADDRINUSE
-#undef  EADDRNOTAVAIL    /* override definition in errno.h */
-#define EADDRNOTAVAIL    WSAEADDRNOTAVAIL
-#undef  ENETDOWN         /* override definition in errno.h */
-#define ENETDOWN         WSAENETDOWN
-#undef  ENETUNREACH      /* override definition in errno.h */
-#define ENETUNREACH      WSAENETUNREACH
-#undef  ENETRESET        /* override definition in errno.h */
-#define ENETRESET        WSAENETRESET
-#undef  ECONNABORTED     /* override definition in errno.h */
-#define ECONNABORTED     WSAECONNABORTED
-#undef  ECONNRESET       /* override definition in errno.h */
-#define ECONNRESET       WSAECONNRESET
-#undef  ENOBUFS          /* override definition in errno.h */
-#define ENOBUFS          WSAENOBUFS
-#undef  EISCONN          /* override definition in errno.h */
-#define EISCONN          WSAEISCONN
-#undef  ENOTCONN         /* override definition in errno.h */
-#define ENOTCONN         WSAENOTCONN
-#define ESHUTDOWN        WSAESHUTDOWN
-#define ETOOMANYREFS     WSAETOOMANYREFS
-#undef  ETIMEDOUT        /* override definition in errno.h */
-#define ETIMEDOUT        WSAETIMEDOUT
-#undef  ECONNREFUSED     /* override definition in errno.h */
-#define ECONNREFUSED     WSAECONNREFUSED
-#undef  ELOOP            /* override definition in errno.h */
-#define ELOOP            WSAELOOP
-#ifndef ENAMETOOLONG     /* possible previous definition in errno.h */
-#define ENAMETOOLONG     WSAENAMETOOLONG
-#endif
-#define EHOSTDOWN        WSAEHOSTDOWN
-#undef  EHOSTUNREACH     /* override definition in errno.h */
-#define EHOSTUNREACH     WSAEHOSTUNREACH
-#ifndef ENOTEMPTY        /* possible previous definition in errno.h */
-#define ENOTEMPTY        WSAENOTEMPTY
+#define SOCKEACCES        WSAEACCES
+#define SOCKEADDRINUSE    WSAEADDRINUSE
+#define SOCKEADDRNOTAVAIL WSAEADDRNOTAVAIL
+#define SOCKEAFNOSUPPORT  WSAEAFNOSUPPORT
+#define SOCKEBADF         WSAEBADF
+#define SOCKECONNREFUSED  WSAECONNREFUSED
+#define SOCKECONNRESET    WSAECONNRESET
+#define SOCKEINPROGRESS   WSAEINPROGRESS
+#define SOCKEINTR         WSAEINTR
+#define SOCKEINVAL        WSAEINVAL
+#define SOCKEISCONN       WSAEISCONN
+#define SOCKEMSGSIZE      WSAEMSGSIZE
+#define SOCKENOMEM        WSA_NOT_ENOUGH_MEMORY
+#define SOCKETIMEDOUT     WSAETIMEDOUT
+#define SOCKEWOULDBLOCK   WSAEWOULDBLOCK
+#else
+#define SOCKEACCES        EACCES
+#define SOCKEADDRINUSE    EADDRINUSE
+#define SOCKEADDRNOTAVAIL EADDRNOTAVAIL
+#define SOCKEAFNOSUPPORT  EAFNOSUPPORT
+#define SOCKEBADF         EBADF
+#define SOCKECONNREFUSED  ECONNREFUSED
+#define SOCKECONNRESET    ECONNRESET
+#define SOCKEINPROGRESS   EINPROGRESS
+#define SOCKEINTR         EINTR
+#define SOCKEINVAL        EINVAL
+#define SOCKEISCONN       EISCONN
+#define SOCKEMSGSIZE      EMSGSIZE
+#define SOCKENOMEM        ENOMEM
+#ifdef ETIMEDOUT
+#define SOCKETIMEDOUT     ETIMEDOUT
 #endif
-#define EPROCLIM         WSAEPROCLIM
-#define EUSERS           WSAEUSERS
-#define EDQUOT           WSAEDQUOT
-#define ESTALE           WSAESTALE
-#define EREMOTE          WSAEREMOTE
+#define SOCKEWOULDBLOCK   EWOULDBLOCK
 #endif
 
 /*
@@ -390,7 +362,7 @@ typedef unsigned int bit;
 
 #ifdef __VMS
 #define argv_item_t  __char_ptr32
-#elif defined(_UNICODE)
+#elif defined(_UNICODE) && !defined(UNDER_CE)
 #define argv_item_t  wchar_t *
 #else
 #define argv_item_t  char *
diff --git a/extra/curl/curl-8.12.1/lib/curl_sha256.h b/extra/curl/curl-8.14.1/lib/curl_sha256.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/curl_sha256.h
rename to extra/curl/curl-8.14.1/lib/curl_sha256.h
diff --git a/extra/curl/curl-8.12.1/lib/curl_sha512_256.c b/extra/curl/curl-8.14.1/lib/curl_sha512_256.c
similarity index 99%
rename from extra/curl/curl-8.12.1/lib/curl_sha512_256.c
rename to extra/curl/curl-8.14.1/lib/curl_sha512_256.c
index 46c0bd534118..2d8a23f19a7c 100644
--- a/extra/curl/curl-8.12.1/lib/curl_sha512_256.c
+++ b/extra/curl/curl-8.14.1/lib/curl_sha512_256.c
@@ -27,7 +27,7 @@
 #if !defined(CURL_DISABLE_DIGEST_AUTH) && !defined(CURL_DISABLE_SHA512_256)
 
 #include "curl_sha512_256.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
 
 /* The recommended order of the TLS backends:
  * * OpenSSL
@@ -82,7 +82,6 @@
 #  include <nettle/sha.h>
 #  if defined(SHA512_256_DIGEST_SIZE)
 #    define USE_GNUTLS_SHA512_256           1
-#    define HAS_SHA512_256_IMPLEMENTATION   1
 #  endif
 #endif /* ! HAS_SHA512_256_IMPLEMENTATION && USE_GNUTLS */
 
@@ -269,9 +268,6 @@ Curl_sha512_256_finish(unsigned char *digest,
 
 #else /* No system or TLS backend SHA-512/256 implementation available */
 
-/* Use local implementation */
-#define HAS_SHA512_256_IMPLEMENTATION   1
-
 /* ** This implementation of SHA-512/256 hash calculation was originally ** *
  * ** written by Evgeny Grin (Karlson2k) for GNU libmicrohttpd.          ** *
  * ** The author ported the code to libcurl. The ported code is provided ** *
diff --git a/extra/curl/curl-8.12.1/lib/curl_sha512_256.h b/extra/curl/curl-8.14.1/lib/curl_sha512_256.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/curl_sha512_256.h
rename to extra/curl/curl-8.14.1/lib/curl_sha512_256.h
diff --git a/extra/curl/curl-8.12.1/lib/curl_sspi.c b/extra/curl/curl-8.14.1/lib/curl_sspi.c
similarity index 94%
rename from extra/curl/curl-8.12.1/lib/curl_sspi.c
rename to extra/curl/curl-8.14.1/lib/curl_sspi.c
index 680bb661b285..cd577e8a5e20 100644
--- a/extra/curl/curl-8.12.1/lib/curl_sspi.c
+++ b/extra/curl/curl-8.14.1/lib/curl_sspi.c
@@ -28,10 +28,10 @@
 
 #include <curl/curl.h>
 #include "curl_sspi.h"
-#include "curl_multibyte.h"
+#include "curlx/multibyte.h"
 #include "system_win32.h"
-#include "version_win32.h"
-#include "warnless.h"
+#include "curlx/version_win32.h"
+#include "curlx/warnless.h"
 
 /* The last #include files should be: */
 #include "curl_memory.h"
@@ -42,7 +42,7 @@ typedef PSecurityFunctionTable (APIENTRY *INITSECURITYINTERFACE_FN)(VOID);
 
 /* See definition of SECURITY_ENTRYPOINT in sspi.h */
 #ifdef UNICODE
-#  ifdef _WIN32_WCE
+#  ifdef UNDER_CE
 #    define SECURITYENTRYPOINT L"InitSecurityInterfaceW"
 #  else
 #    define SECURITYENTRYPOINT "InitSecurityInterfaceW"
@@ -129,7 +129,7 @@ void Curl_sspi_global_cleanup(void)
 /*
  * Curl_create_sspi_identity()
  *
- * This is used to populate a SSPI identity structure based on the supplied
+ * This is used to populate an SSPI identity structure based on the supplied
  * username and password.
  *
  * Parameters:
@@ -154,7 +154,7 @@ CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp,
   /* Initialize the identity */
   memset(identity, 0, sizeof(*identity));
 
-  useranddomain.tchar_ptr = curlx_convert_UTF8_to_tchar((char *)userp);
+  useranddomain.tchar_ptr = curlx_convert_UTF8_to_tchar(userp);
   if(!useranddomain.tchar_ptr)
     return CURLE_OUT_OF_MEMORY;
 
@@ -198,7 +198,7 @@ CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp,
   curlx_unicodefree(useranddomain.tchar_ptr);
 
   /* Setup the identity's password and length */
-  passwd.tchar_ptr = curlx_convert_UTF8_to_tchar((char *)passwdp);
+  passwd.tchar_ptr = curlx_convert_UTF8_to_tchar(passwdp);
   if(!passwd.tchar_ptr)
     return CURLE_OUT_OF_MEMORY;
   dup_passwd.tchar_ptr = _tcsdup(passwd.tchar_ptr);
@@ -221,7 +221,7 @@ CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp,
 /*
  * Curl_sspi_free_identity()
  *
- * This is used to free the contents of a SSPI identifier structure.
+ * This is used to free the contents of an SSPI identifier structure.
  *
  * Parameters:
  *
diff --git a/extra/curl/curl-8.14.1/lib/curl_sspi.h b/extra/curl/curl-8.14.1/lib/curl_sspi.h
new file mode 100644
index 000000000000..8fdf8ef2496f
--- /dev/null
+++ b/extra/curl/curl-8.14.1/lib/curl_sspi.h
@@ -0,0 +1,368 @@
+#ifndef HEADER_CURL_SSPI_H
+#define HEADER_CURL_SSPI_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef USE_WINDOWS_SSPI
+
+#include <curl/curl.h>
+
+/*
+ * When including the following three headers, it is mandatory to define either
+ * SECURITY_WIN32 or SECURITY_KERNEL, indicating who is compiling the code.
+ */
+
+#undef SECURITY_WIN32
+#undef SECURITY_KERNEL
+#define SECURITY_WIN32 1
+#include <security.h>
+#include <sspi.h>
+#include <rpc.h>
+
+CURLcode Curl_sspi_global_init(void);
+void Curl_sspi_global_cleanup(void);
+
+/* This is used to populate the domain in an SSPI identity structure */
+CURLcode Curl_override_sspi_http_realm(const char *chlg,
+                                       SEC_WINNT_AUTH_IDENTITY *identity);
+
+/* This is used to generate an SSPI identity structure */
+CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp,
+                                   SEC_WINNT_AUTH_IDENTITY *identity);
+
+/* This is used to free an SSPI identity structure */
+void Curl_sspi_free_identity(SEC_WINNT_AUTH_IDENTITY *identity);
+
+/* Forward-declaration of global variables defined in curl_sspi.c */
+extern HMODULE Curl_hSecDll;
+extern PSecurityFunctionTable Curl_pSecFn;
+
+/* Provide some definitions missing in old headers */
+#define SP_NAME_DIGEST              "WDigest"
+#define SP_NAME_NTLM                "NTLM"
+#define SP_NAME_NEGOTIATE           "Negotiate"
+#define SP_NAME_KERBEROS            "Kerberos"
+
+#ifndef ISC_REQ_USE_HTTP_STYLE
+#define ISC_REQ_USE_HTTP_STYLE                0x01000000
+#endif
+
+#ifdef __MINGW32CE__
+#ifndef ISC_RET_REPLAY_DETECT
+#define ISC_RET_REPLAY_DETECT                 0x00000004
+#endif
+#ifndef ISC_RET_SEQUENCE_DETECT
+#define ISC_RET_SEQUENCE_DETECT               0x00000008
+#endif
+#ifndef ISC_RET_CONFIDENTIALITY
+#define ISC_RET_CONFIDENTIALITY               0x00000010
+#endif
+#ifndef ISC_RET_ALLOCATED_MEMORY
+#define ISC_RET_ALLOCATED_MEMORY              0x00000100
+#endif
+#ifndef ISC_RET_STREAM
+#define ISC_RET_STREAM                        0x00008000
+#endif
+
+#ifndef SEC_E_INSUFFICIENT_MEMORY
+#define SEC_E_INSUFFICIENT_MEMORY             ((HRESULT)0x80090300L)
+#endif
+#ifndef SEC_E_INVALID_HANDLE
+#define SEC_E_INVALID_HANDLE                  ((HRESULT)0x80090301L)
+#endif
+#ifndef SEC_E_UNSUPPORTED_FUNCTION
+#define SEC_E_UNSUPPORTED_FUNCTION            ((HRESULT)0x80090302L)
+#endif
+#ifndef SEC_E_TARGET_UNKNOWN
+#define SEC_E_TARGET_UNKNOWN                  ((HRESULT)0x80090303L)
+#endif
+#ifndef SEC_E_INTERNAL_ERROR
+#define SEC_E_INTERNAL_ERROR                  ((HRESULT)0x80090304L)
+#endif
+#ifndef SEC_E_SECPKG_NOT_FOUND
+#define SEC_E_SECPKG_NOT_FOUND                ((HRESULT)0x80090305L)
+#endif
+#ifndef SEC_E_NOT_OWNER
+#define SEC_E_NOT_OWNER                       ((HRESULT)0x80090306L)
+#endif
+#ifndef SEC_E_CANNOT_INSTALL
+#define SEC_E_CANNOT_INSTALL                  ((HRESULT)0x80090307L)
+#endif
+#ifndef SEC_E_INVALID_TOKEN
+#define SEC_E_INVALID_TOKEN                   ((HRESULT)0x80090308L)
+#endif
+#ifndef SEC_E_CANNOT_PACK
+#define SEC_E_CANNOT_PACK                     ((HRESULT)0x80090309L)
+#endif
+#ifndef SEC_E_QOP_NOT_SUPPORTED
+#define SEC_E_QOP_NOT_SUPPORTED               ((HRESULT)0x8009030AL)
+#endif
+#ifndef SEC_E_NO_IMPERSONATION
+#define SEC_E_NO_IMPERSONATION                ((HRESULT)0x8009030BL)
+#endif
+#ifndef SEC_E_LOGON_DENIED
+#define SEC_E_LOGON_DENIED                    ((HRESULT)0x8009030CL)
+#endif
+#ifndef SEC_E_UNKNOWN_CREDENTIALS
+#define SEC_E_UNKNOWN_CREDENTIALS             ((HRESULT)0x8009030DL)
+#endif
+#ifndef SEC_E_NO_CREDENTIALS
+#define SEC_E_NO_CREDENTIALS                  ((HRESULT)0x8009030EL)
+#endif
+#ifndef SEC_E_MESSAGE_ALTERED
+#define SEC_E_MESSAGE_ALTERED                 ((HRESULT)0x8009030FL)
+#endif
+#ifndef SEC_E_OUT_OF_SEQUENCE
+#define SEC_E_OUT_OF_SEQUENCE                 ((HRESULT)0x80090310L)
+#endif
+#ifndef SEC_E_NO_AUTHENTICATING_AUTHORITY
+#define SEC_E_NO_AUTHENTICATING_AUTHORITY     ((HRESULT)0x80090311L)
+#endif
+#ifndef SEC_E_BAD_PKGID
+#define SEC_E_BAD_PKGID                       ((HRESULT)0x80090316L)
+#endif
+#ifndef SEC_E_CONTEXT_EXPIRED
+#define SEC_E_CONTEXT_EXPIRED                 ((HRESULT)0x80090317L)
+#endif
+#ifndef SEC_E_INCOMPLETE_MESSAGE
+#define SEC_E_INCOMPLETE_MESSAGE              ((HRESULT)0x80090318L)
+#endif
+#ifndef SEC_E_INCOMPLETE_CREDENTIALS
+#define SEC_E_INCOMPLETE_CREDENTIALS          ((HRESULT)0x80090320L)
+#endif
+#ifndef SEC_E_BUFFER_TOO_SMALL
+#define SEC_E_BUFFER_TOO_SMALL                ((HRESULT)0x80090321L)
+#endif
+#ifndef SEC_E_WRONG_PRINCIPAL
+#define SEC_E_WRONG_PRINCIPAL                 ((HRESULT)0x80090322L)
+#endif
+#ifndef SEC_E_TIME_SKEW
+#define SEC_E_TIME_SKEW                       ((HRESULT)0x80090324L)
+#endif
+#ifndef SEC_E_UNTRUSTED_ROOT
+#define SEC_E_UNTRUSTED_ROOT                  ((HRESULT)0x80090325L)
+#endif
+#ifndef SEC_E_ILLEGAL_MESSAGE
+#define SEC_E_ILLEGAL_MESSAGE                 ((HRESULT)0x80090326L)
+#endif
+#ifndef SEC_E_CERT_UNKNOWN
+#define SEC_E_CERT_UNKNOWN                    ((HRESULT)0x80090327L)
+#endif
+#ifndef SEC_E_CERT_EXPIRED
+#define SEC_E_CERT_EXPIRED                    ((HRESULT)0x80090328L)
+#endif
+#ifndef SEC_E_ENCRYPT_FAILURE
+#define SEC_E_ENCRYPT_FAILURE                 ((HRESULT)0x80090329L)
+#endif
+#ifndef SEC_E_DECRYPT_FAILURE
+#define SEC_E_DECRYPT_FAILURE                 ((HRESULT)0x80090330L)
+#endif
+#ifndef SEC_E_ALGORITHM_MISMATCH
+#define SEC_E_ALGORITHM_MISMATCH              ((HRESULT)0x80090331L)
+#endif
+#ifndef SEC_E_SECURITY_QOS_FAILED
+#define SEC_E_SECURITY_QOS_FAILED             ((HRESULT)0x80090332L)
+#endif
+#ifndef SEC_E_UNFINISHED_CONTEXT_DELETED
+#define SEC_E_UNFINISHED_CONTEXT_DELETED      ((HRESULT)0x80090333L)
+#endif
+#ifndef SEC_E_NO_TGT_REPLY
+#define SEC_E_NO_TGT_REPLY                    ((HRESULT)0x80090334L)
+#endif
+#ifndef SEC_E_NO_IP_ADDRESSES
+#define SEC_E_NO_IP_ADDRESSES                 ((HRESULT)0x80090335L)
+#endif
+#ifndef SEC_E_WRONG_CREDENTIAL_HANDLE
+#define SEC_E_WRONG_CREDENTIAL_HANDLE         ((HRESULT)0x80090336L)
+#endif
+#ifndef SEC_E_CRYPTO_SYSTEM_INVALID
+#define SEC_E_CRYPTO_SYSTEM_INVALID           ((HRESULT)0x80090337L)
+#endif
+#ifndef SEC_E_MAX_REFERRALS_EXCEEDED
+#define SEC_E_MAX_REFERRALS_EXCEEDED          ((HRESULT)0x80090338L)
+#endif
+#ifndef SEC_E_MUST_BE_KDC
+#define SEC_E_MUST_BE_KDC                     ((HRESULT)0x80090339L)
+#endif
+#ifndef SEC_E_STRONG_CRYPTO_NOT_SUPPORTED
+#define SEC_E_STRONG_CRYPTO_NOT_SUPPORTED     ((HRESULT)0x8009033AL)
+#endif
+#ifndef SEC_E_TOO_MANY_PRINCIPALS
+#define SEC_E_TOO_MANY_PRINCIPALS             ((HRESULT)0x8009033BL)
+#endif
+#ifndef SEC_E_NO_PA_DATA
+#define SEC_E_NO_PA_DATA                      ((HRESULT)0x8009033CL)
+#endif
+#ifndef SEC_E_PKINIT_NAME_MISMATCH
+#define SEC_E_PKINIT_NAME_MISMATCH            ((HRESULT)0x8009033DL)
+#endif
+#ifndef SEC_E_SMARTCARD_LOGON_REQUIRED
+#define SEC_E_SMARTCARD_LOGON_REQUIRED        ((HRESULT)0x8009033EL)
+#endif
+#ifndef SEC_E_SHUTDOWN_IN_PROGRESS
+#define SEC_E_SHUTDOWN_IN_PROGRESS            ((HRESULT)0x8009033FL)
+#endif
+#ifndef SEC_E_KDC_INVALID_REQUEST
+#define SEC_E_KDC_INVALID_REQUEST             ((HRESULT)0x80090340L)
+#endif
+#ifndef SEC_E_KDC_UNABLE_TO_REFER
+#define SEC_E_KDC_UNABLE_TO_REFER             ((HRESULT)0x80090341L)
+#endif
+#ifndef SEC_E_KDC_UNKNOWN_ETYPE
+#define SEC_E_KDC_UNKNOWN_ETYPE               ((HRESULT)0x80090342L)
+#endif
+#ifndef SEC_E_UNSUPPORTED_PREAUTH
+#define SEC_E_UNSUPPORTED_PREAUTH             ((HRESULT)0x80090343L)
+#endif
+#ifndef SEC_E_DELEGATION_REQUIRED
+#define SEC_E_DELEGATION_REQUIRED             ((HRESULT)0x80090345L)
+#endif
+#ifndef SEC_E_BAD_BINDINGS
+#define SEC_E_BAD_BINDINGS                    ((HRESULT)0x80090346L)
+#endif
+#ifndef SEC_E_MULTIPLE_ACCOUNTS
+#define SEC_E_MULTIPLE_ACCOUNTS               ((HRESULT)0x80090347L)
+#endif
+#ifndef SEC_E_NO_KERB_KEY
+#define SEC_E_NO_KERB_KEY                     ((HRESULT)0x80090348L)
+#endif
+#ifndef SEC_E_CERT_WRONG_USAGE
+#define SEC_E_CERT_WRONG_USAGE                ((HRESULT)0x80090349L)
+#endif
+#ifndef SEC_E_DOWNGRADE_DETECTED
+#define SEC_E_DOWNGRADE_DETECTED              ((HRESULT)0x80090350L)
+#endif
+#ifndef SEC_E_SMARTCARD_CERT_REVOKED
+#define SEC_E_SMARTCARD_CERT_REVOKED          ((HRESULT)0x80090351L)
+#endif
+#ifndef SEC_E_ISSUING_CA_UNTRUSTED
+#define SEC_E_ISSUING_CA_UNTRUSTED            ((HRESULT)0x80090352L)
+#endif
+#ifndef SEC_E_REVOCATION_OFFLINE_C
+#define SEC_E_REVOCATION_OFFLINE_C            ((HRESULT)0x80090353L)
+#endif
+#ifndef SEC_E_PKINIT_CLIENT_FAILURE
+#define SEC_E_PKINIT_CLIENT_FAILURE           ((HRESULT)0x80090354L)
+#endif
+#ifndef SEC_E_SMARTCARD_CERT_EXPIRED
+#define SEC_E_SMARTCARD_CERT_EXPIRED          ((HRESULT)0x80090355L)
+#endif
+#ifndef SEC_E_NO_S4U_PROT_SUPPORT
+#define SEC_E_NO_S4U_PROT_SUPPORT             ((HRESULT)0x80090356L)
+#endif
+#ifndef SEC_E_CROSSREALM_DELEGATION_FAILURE
+#define SEC_E_CROSSREALM_DELEGATION_FAILURE   ((HRESULT)0x80090357L)
+#endif
+#ifndef SEC_E_REVOCATION_OFFLINE_KDC
+#define SEC_E_REVOCATION_OFFLINE_KDC          ((HRESULT)0x80090358L)
+#endif
+#ifndef SEC_E_ISSUING_CA_UNTRUSTED_KDC
+#define SEC_E_ISSUING_CA_UNTRUSTED_KDC        ((HRESULT)0x80090359L)
+#endif
+#ifndef SEC_E_KDC_CERT_EXPIRED
+#define SEC_E_KDC_CERT_EXPIRED                ((HRESULT)0x8009035AL)
+#endif
+#ifndef SEC_E_KDC_CERT_REVOKED
+#define SEC_E_KDC_CERT_REVOKED                ((HRESULT)0x8009035BL)
+#endif
+#endif /* __MINGW32CE__ */
+#ifndef SEC_E_INVALID_PARAMETER
+# define SEC_E_INVALID_PARAMETER              ((HRESULT)0x8009035DL)
+#endif
+#ifndef SEC_E_DELEGATION_POLICY
+# define SEC_E_DELEGATION_POLICY              ((HRESULT)0x8009035EL)
+#endif
+#ifndef SEC_E_POLICY_NLTM_ONLY
+# define SEC_E_POLICY_NLTM_ONLY               ((HRESULT)0x8009035FL)
+#endif
+
+#ifdef __MINGW32CE__
+#ifndef SEC_I_CONTINUE_NEEDED
+#define SEC_I_CONTINUE_NEEDED                 ((HRESULT)0x00090312L)
+#endif
+#ifndef SEC_I_COMPLETE_NEEDED
+#define SEC_I_COMPLETE_NEEDED                 ((HRESULT)0x00090313L)
+#endif
+#ifndef SEC_I_COMPLETE_AND_CONTINUE
+#define SEC_I_COMPLETE_AND_CONTINUE           ((HRESULT)0x00090314L)
+#endif
+#ifndef SEC_I_LOCAL_LOGON
+#define SEC_I_LOCAL_LOGON                     ((HRESULT)0x00090315L)
+#endif
+#ifndef SEC_I_CONTEXT_EXPIRED
+#define SEC_I_CONTEXT_EXPIRED                 ((HRESULT)0x00090317L)
+#endif
+#ifndef SEC_I_INCOMPLETE_CREDENTIALS
+#define SEC_I_INCOMPLETE_CREDENTIALS          ((HRESULT)0x00090320L)
+#endif
+#ifndef SEC_I_RENEGOTIATE
+#define SEC_I_RENEGOTIATE                     ((HRESULT)0x00090321L)
+#endif
+#ifndef SEC_I_NO_LSA_CONTEXT
+#define SEC_I_NO_LSA_CONTEXT                  ((HRESULT)0x00090323L)
+#endif
+#endif /* __MINGW32CE__ */
+#ifndef SEC_I_SIGNATURE_NEEDED
+#define SEC_I_SIGNATURE_NEEDED                ((HRESULT)0x0009035CL)
+#endif
+
+#ifndef CRYPT_E_REVOKED
+#define CRYPT_E_REVOKED                       ((HRESULT)0x80092010L)
+#endif
+
+#ifndef CRYPT_E_NO_REVOCATION_DLL
+#define CRYPT_E_NO_REVOCATION_DLL             ((HRESULT)0x80092011L)
+#endif
+
+#ifndef CRYPT_E_NO_REVOCATION_CHECK
+#define CRYPT_E_NO_REVOCATION_CHECK           ((HRESULT)0x80092012L)
+#endif
+
+#ifndef CRYPT_E_REVOCATION_OFFLINE
+#define CRYPT_E_REVOCATION_OFFLINE            ((HRESULT)0x80092013L)
+#endif
+
+#ifndef CRYPT_E_NOT_IN_REVOCATION_DATABASE
+#define CRYPT_E_NOT_IN_REVOCATION_DATABASE    ((HRESULT)0x80092014L)
+#endif
+
+#ifdef UNICODE
+#  define SECFLAG_WINNT_AUTH_IDENTITY \
+     (unsigned long)SEC_WINNT_AUTH_IDENTITY_UNICODE
+#else
+#  define SECFLAG_WINNT_AUTH_IDENTITY \
+     (unsigned long)SEC_WINNT_AUTH_IDENTITY_ANSI
+#endif
+
+/*
+ * Definitions required from ntsecapi.h are directly provided below this point
+ * to avoid including ntsecapi.h due to a conflict with OpenSSL's safestack.h
+ */
+#define KERB_WRAP_NO_ENCRYPT 0x80000001
+
+#endif /* USE_WINDOWS_SSPI */
+
+#endif /* HEADER_CURL_SSPI_H */
diff --git a/extra/curl/curl-8.12.1/lib/curl_threads.c b/extra/curl/curl-8.14.1/lib/curl_threads.c
similarity index 83%
rename from extra/curl/curl-8.12.1/lib/curl_threads.c
rename to extra/curl/curl-8.14.1/lib/curl_threads.c
index fbbbf9b2d321..eae754401618 100644
--- a/extra/curl/curl-8.12.1/lib/curl_threads.c
+++ b/extra/curl/curl-8.14.1/lib/curl_threads.c
@@ -35,9 +35,7 @@
 #endif
 
 #include "curl_threads.h"
-#ifdef BUILDING_LIBCURL
 #include "curl_memory.h"
-#endif
 /* The last #include file should be: */
 #include "memdebug.h"
 
@@ -82,11 +80,12 @@ curl_thread_t Curl_thread_create(unsigned int (*func) (void *), void *arg)
   return curl_thread_t_null;
 }
 
-void Curl_thread_destroy(curl_thread_t hnd)
+void Curl_thread_destroy(curl_thread_t *hnd)
 {
-  if(hnd != curl_thread_t_null) {
-    pthread_detach(*hnd);
-    free(hnd);
+  if(*hnd != curl_thread_t_null) {
+    pthread_detach(**hnd);
+    free(*hnd);
+    *hnd = curl_thread_t_null;
   }
 }
 
@@ -103,7 +102,7 @@ int Curl_thread_join(curl_thread_t *hnd)
 #elif defined(USE_THREADS_WIN32)
 
 curl_thread_t Curl_thread_create(
-#if defined(_WIN32_WCE) || defined(CURL_WINDOWS_UWP)
+#if defined(CURL_WINDOWS_UWP) || defined(UNDER_CE)
                                  DWORD
 #else
                                  unsigned int
@@ -111,35 +110,39 @@ curl_thread_t Curl_thread_create(
                                  (CURL_STDCALL *func) (void *),
                                  void *arg)
 {
-#if defined(_WIN32_WCE) || defined(CURL_WINDOWS_UWP)
+#if defined(CURL_WINDOWS_UWP) || defined(UNDER_CE)
   typedef HANDLE curl_win_thread_handle_t;
 #else
   typedef uintptr_t curl_win_thread_handle_t;
 #endif
   curl_thread_t t;
   curl_win_thread_handle_t thread_handle;
-#if defined(_WIN32_WCE) || defined(CURL_WINDOWS_UWP)
+#if defined(CURL_WINDOWS_UWP) || defined(UNDER_CE)
   thread_handle = CreateThread(NULL, 0, func, arg, 0, NULL);
 #else
   thread_handle = _beginthreadex(NULL, 0, func, arg, 0, NULL);
 #endif
   t = (curl_thread_t)thread_handle;
   if((t == 0) || (t == LongToHandle(-1L))) {
-#ifdef _WIN32_WCE
+#ifdef UNDER_CE
     DWORD gle = GetLastError();
-    errno = ((gle == ERROR_ACCESS_DENIED ||
-              gle == ERROR_NOT_ENOUGH_MEMORY) ?
-             EACCES : EINVAL);
+    /* !checksrc! disable ERRNOVAR 1 */
+    int err = (gle == ERROR_ACCESS_DENIED ||
+               gle == ERROR_NOT_ENOUGH_MEMORY) ?
+               EACCES : EINVAL;
+    CURL_SETERRNO(err);
 #endif
     return curl_thread_t_null;
   }
   return t;
 }
 
-void Curl_thread_destroy(curl_thread_t hnd)
+void Curl_thread_destroy(curl_thread_t *hnd)
 {
-  if(hnd != curl_thread_t_null)
-    CloseHandle(hnd);
+  if(*hnd != curl_thread_t_null) {
+    CloseHandle(*hnd);
+    *hnd = curl_thread_t_null;
+  }
 }
 
 int Curl_thread_join(curl_thread_t *hnd)
@@ -151,9 +154,7 @@ int Curl_thread_join(curl_thread_t *hnd)
   int ret = (WaitForSingleObjectEx(*hnd, INFINITE, FALSE) == WAIT_OBJECT_0);
 #endif
 
-  Curl_thread_destroy(*hnd);
-
-  *hnd = curl_thread_t_null;
+  Curl_thread_destroy(hnd);
 
   return ret;
 }
diff --git a/extra/curl/curl-8.12.1/lib/curl_threads.h b/extra/curl/curl-8.14.1/lib/curl_threads.h
similarity index 96%
rename from extra/curl/curl-8.12.1/lib/curl_threads.h
rename to extra/curl/curl-8.14.1/lib/curl_threads.h
index c9f18a4e09e1..b060d4acd3ef 100644
--- a/extra/curl/curl-8.12.1/lib/curl_threads.h
+++ b/extra/curl/curl-8.14.1/lib/curl_threads.h
@@ -53,7 +53,7 @@
 #if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32)
 
 curl_thread_t Curl_thread_create(
-#if defined(_WIN32_WCE) || defined(CURL_WINDOWS_UWP)
+#if defined(CURL_WINDOWS_UWP) || defined(UNDER_CE)
                                  DWORD
 #else
                                  unsigned int
@@ -61,7 +61,7 @@ curl_thread_t Curl_thread_create(
                                  (CURL_STDCALL *func) (void *),
                                  void *arg);
 
-void Curl_thread_destroy(curl_thread_t hnd);
+void Curl_thread_destroy(curl_thread_t *hnd);
 
 int Curl_thread_join(curl_thread_t *hnd);
 
diff --git a/extra/curl/curl-8.12.1/lib/curl_trc.c b/extra/curl/curl-8.14.1/lib/curl_trc.c
similarity index 57%
rename from extra/curl/curl-8.12.1/lib/curl_trc.c
rename to extra/curl/curl-8.14.1/lib/curl_trc.c
index 07137c1f24e5..566cdc533e27 100644
--- a/extra/curl/curl-8.12.1/lib/curl_trc.c
+++ b/extra/curl/curl-8.14.1/lib/curl_trc.c
@@ -30,7 +30,6 @@
 #include "urldata.h"
 #include "easyif.h"
 #include "cfilters.h"
-#include "timeval.h"
 #include "multiif.h"
 #include "strcase.h"
 
@@ -44,7 +43,7 @@
 #include "cf-haproxy.h"
 #include "cf-https-connect.h"
 #include "socks.h"
-#include "strtok.h"
+#include "curlx/strparse.h"
 #include "vtls/vtls.h"
 #include "vquic/vquic.h"
 
@@ -53,19 +52,20 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
-void Curl_debug(struct Curl_easy *data, curl_infotype type,
-                char *ptr, size_t size)
+static void trc_write(struct Curl_easy *data, curl_infotype type,
+                      const char *ptr, size_t size)
 {
   if(data->set.verbose) {
-    static const char s_infotype[CURLINFO_END][3] = {
-      "* ", "< ", "> ", "{ ", "} ", "{ ", "} " };
     if(data->set.fdebug) {
       bool inCallback = Curl_is_in_callback(data);
       Curl_set_in_callback(data, TRUE);
-      (void)(*data->set.fdebug)(data, type, ptr, size, data->set.debugdata);
+      (void)(*data->set.fdebug)(data, type, CURL_UNCONST(ptr), size,
+                                data->set.debugdata);
       Curl_set_in_callback(data, inCallback);
     }
     else {
+      static const char s_infotype[CURLINFO_END][3] = {
+        "* ", "< ", "> ", "{ ", "} ", "{ ", "} " };
       switch(type) {
       case CURLINFO_TEXT:
       case CURLINFO_HEADER_OUT:
@@ -80,6 +80,101 @@ void Curl_debug(struct Curl_easy *data, curl_infotype type,
   }
 }
 
+/* max length we trace before ending in '...' */
+#define TRC_LINE_MAX 2048
+
+#define CURL_TRC_FMT_IDSC   "[x-%" CURL_FORMAT_CURL_OFF_T "] "
+#define CURL_TRC_FMT_IDSD   "[%" CURL_FORMAT_CURL_OFF_T "-x] "
+#define CURL_TRC_FMT_IDSDC  "[%" CURL_FORMAT_CURL_OFF_T "-%" \
+                            CURL_FORMAT_CURL_OFF_T "] "
+
+static struct curl_trc_feat Curl_trc_feat_ids = {
+  "LIB-IDS",
+  CURL_LOG_LVL_NONE,
+};
+#define CURL_TRC_IDS(data) \
+             (Curl_trc_is_verbose(data) && \
+             Curl_trc_feat_ids.log_level >= CURL_LOG_LVL_INFO)
+
+static size_t trc_print_ids(struct Curl_easy *data, char *buf, size_t maxlen)
+{
+  curl_off_t cid = data->conn ?
+                   data->conn->connection_id : data->state.recent_conn_id;
+  if(data->id >= 0) {
+    if(cid >= 0)
+      return msnprintf(buf, maxlen, CURL_TRC_FMT_IDSDC, data->id, cid);
+    else
+      return msnprintf(buf, maxlen, CURL_TRC_FMT_IDSD, data->id);
+  }
+  else if(cid >= 0)
+    return msnprintf(buf, maxlen, CURL_TRC_FMT_IDSC, cid);
+  else {
+    return msnprintf(buf, maxlen, "[x-x] ");
+  }
+}
+
+static size_t trc_end_buf(char *buf, size_t len, size_t maxlen, bool addnl)
+{
+  /* make sure we end the trace line in `buf` properly. It needs
+   * to end with a terminating '\0' or '\n\0' */
+  if(len >= (maxlen - (addnl ? 2 : 1))) {
+    len = maxlen - 5;
+    buf[len++] = '.';
+    buf[len++] = '.';
+    buf[len++] = '.';
+    buf[len++] = '\n';
+  }
+  else if(addnl)
+    buf[len++] = '\n';
+  buf[len] = '\0';
+  return len;
+}
+
+void Curl_debug(struct Curl_easy *data, curl_infotype type,
+                const char *ptr, size_t size)
+{
+  if(data->set.verbose) {
+    static const char s_infotype[CURLINFO_END][3] = {
+      "* ", "< ", "> ", "{ ", "} ", "{ ", "} " };
+    char buf[TRC_LINE_MAX];
+    size_t len;
+    if(data->set.fdebug) {
+      bool inCallback = Curl_is_in_callback(data);
+
+      if(CURL_TRC_IDS(data) && (size < TRC_LINE_MAX)) {
+        len = trc_print_ids(data, buf, TRC_LINE_MAX);
+        len += msnprintf(buf + len, TRC_LINE_MAX - len, "%.*s",
+                         (int)size, ptr);
+        len = trc_end_buf(buf, len, TRC_LINE_MAX, FALSE);
+        Curl_set_in_callback(data, TRUE);
+        (void)(*data->set.fdebug)(data, type, buf, len, data->set.debugdata);
+        Curl_set_in_callback(data, inCallback);
+      }
+      else {
+        Curl_set_in_callback(data, TRUE);
+        (void)(*data->set.fdebug)(data, type, CURL_UNCONST(ptr),
+                                  size, data->set.debugdata);
+        Curl_set_in_callback(data, inCallback);
+      }
+    }
+    else {
+      switch(type) {
+      case CURLINFO_TEXT:
+      case CURLINFO_HEADER_OUT:
+      case CURLINFO_HEADER_IN:
+        if(CURL_TRC_IDS(data)) {
+          len = trc_print_ids(data, buf, TRC_LINE_MAX);
+          fwrite(buf, len, 1, data->set.err);
+        }
+        fwrite(s_infotype[type], 2, 1, data->set.err);
+        fwrite(ptr, size, 1, data->set.err);
+        break;
+      default: /* nada */
+        break;
+      }
+    }
+  }
+}
 
 /* Curl_failf() is for messages stating why we failed.
  * The message SHALL NOT include any LF or CR.
@@ -89,7 +184,7 @@ void Curl_failf(struct Curl_easy *data, const char *fmt, ...)
   DEBUGASSERT(!strchr(fmt, '\n'));
   if(data->set.verbose || data->set.errorbuffer) {
     va_list ap;
-    int len;
+    size_t len;
     char error[CURL_ERROR_SIZE + 2];
     va_start(ap, fmt);
     len = mvsnprintf(error, CURL_ERROR_SIZE, fmt, ap);
@@ -100,36 +195,41 @@ void Curl_failf(struct Curl_easy *data, const char *fmt, ...)
     }
     error[len++] = '\n';
     error[len] = '\0';
-    Curl_debug(data, CURLINFO_TEXT, error, len);
+    trc_write(data, CURLINFO_TEXT, error, len);
     va_end(ap);
   }
 }
 
 #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
 
-/* Curl_infof() is for info message along the way */
-#define MAXINFO 2048
 
-static void trc_infof(struct Curl_easy *data, struct curl_trc_feat *feat,
-                      const char * const fmt, va_list ap)  CURL_PRINTF(3, 0);
+static void trc_infof(struct Curl_easy *data,
+                      struct curl_trc_feat *feat,
+                      const char *opt_id, int opt_id_idx,
+                      const char * const fmt, va_list ap)  CURL_PRINTF(5, 0);
 
-static void trc_infof(struct Curl_easy *data, struct curl_trc_feat *feat,
+static void trc_infof(struct Curl_easy *data,
+                      struct curl_trc_feat *feat,
+                      const char *opt_id, int opt_id_idx,
                       const char * const fmt, va_list ap)
 {
-  int len = 0;
-  char buffer[MAXINFO + 5];
+  size_t len = 0;
+  char buf[TRC_LINE_MAX];
+
+  if(CURL_TRC_IDS(data))
+    len += trc_print_ids(data, buf + len, TRC_LINE_MAX - len);
   if(feat)
-    len = msnprintf(buffer, (MAXINFO + 1), "[%s] ", feat->name);
-  len += mvsnprintf(buffer + len, (MAXINFO + 1) - len, fmt, ap);
-  if(len >= MAXINFO) { /* too long, shorten with '...' */
-    --len;
-    buffer[len++] = '.';
-    buffer[len++] = '.';
-    buffer[len++] = '.';
+    len += msnprintf(buf + len, TRC_LINE_MAX - len, "[%s] ", feat->name);
+  if(opt_id) {
+    if(opt_id_idx > 0)
+      len += msnprintf(buf + len, TRC_LINE_MAX - len, "[%s-%d] ",
+                       opt_id, opt_id_idx);
+    else
+      len += msnprintf(buf + len, TRC_LINE_MAX - len, "[%s] ", opt_id);
   }
-  buffer[len++] = '\n';
-  buffer[len] = '\0';
-  Curl_debug(data, CURLINFO_TEXT, buffer, len);
+  len += mvsnprintf(buf + len, TRC_LINE_MAX - len, fmt, ap);
+  len = trc_end_buf(buf, len, TRC_LINE_MAX, TRUE);
+  trc_write(data, CURLINFO_TEXT, buf, len);
 }
 
 void Curl_infof(struct Curl_easy *data, const char *fmt, ...)
@@ -138,36 +238,27 @@ void Curl_infof(struct Curl_easy *data, const char *fmt, ...)
   if(Curl_trc_is_verbose(data)) {
     va_list ap;
     va_start(ap, fmt);
-    trc_infof(data, data->state.feat, fmt, ap);
+    trc_infof(data, data->state.feat, NULL, 0, fmt, ap);
     va_end(ap);
   }
 }
 
-void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf,
+void Curl_trc_cf_infof(struct Curl_easy *data, const struct Curl_cfilter *cf,
                        const char *fmt, ...)
 {
   DEBUGASSERT(cf);
   if(Curl_trc_cf_is_verbose(cf, data)) {
     va_list ap;
-    int len = 0;
-    char buffer[MAXINFO + 2];
-    if(data->state.feat)
-      len += msnprintf(buffer + len, MAXINFO - len, "[%s] ",
-                       data->state.feat->name);
-    if(cf->sockindex)
-      len += msnprintf(buffer + len, MAXINFO - len, "[%s-%d] ",
-                      cf->cft->name, cf->sockindex);
-    else
-      len += msnprintf(buffer + len, MAXINFO - len, "[%s] ", cf->cft->name);
     va_start(ap, fmt);
-    len += mvsnprintf(buffer + len, MAXINFO - len, fmt, ap);
+    trc_infof(data, data->state.feat, cf->cft->name, cf->sockindex, fmt, ap);
     va_end(ap);
-    buffer[len++] = '\n';
-    buffer[len] = '\0';
-    Curl_debug(data, CURLINFO_TEXT, buffer, len);
   }
 }
 
+struct curl_trc_feat Curl_trc_feat_multi = {
+  "MULTI",
+  CURL_LOG_LVL_NONE,
+};
 struct curl_trc_feat Curl_trc_feat_read = {
   "READ",
   CURL_LOG_LVL_NONE,
@@ -176,6 +267,52 @@ struct curl_trc_feat Curl_trc_feat_write = {
   "WRITE",
   CURL_LOG_LVL_NONE,
 };
+struct curl_trc_feat Curl_trc_feat_dns = {
+  "DNS",
+  CURL_LOG_LVL_NONE,
+};
+
+
+static const char * const Curl_trc_mstate_names[]={
+  "INIT",
+  "PENDING",
+  "SETUP",
+  "CONNECT",
+  "RESOLVING",
+  "CONNECTING",
+  "TUNNELING",
+  "PROTOCONNECT",
+  "PROTOCONNECTING",
+  "DO",
+  "DOING",
+  "DOING_MORE",
+  "DID",
+  "PERFORMING",
+  "RATELIMITING",
+  "DONE",
+  "COMPLETED",
+  "MSGSENT",
+};
+
+const char *Curl_trc_mstate_name(int state)
+{
+  if((state >= 0) && ((size_t)state < CURL_ARRAYSIZE(Curl_trc_mstate_names)))
+    return Curl_trc_mstate_names[(size_t)state];
+  return "?";
+}
+
+void Curl_trc_multi(struct Curl_easy *data, const char *fmt, ...)
+{
+  DEBUGASSERT(!strchr(fmt, '\n'));
+  if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_multi)) {
+    const char *sname = (data->id >= 0) ?
+                        Curl_trc_mstate_name(data->mstate) : NULL;
+    va_list ap;
+    va_start(ap, fmt);
+    trc_infof(data, &Curl_trc_feat_multi, sname, 0, fmt, ap);
+    va_end(ap);
+  }
+}
 
 void Curl_trc_read(struct Curl_easy *data, const char *fmt, ...)
 {
@@ -183,7 +320,7 @@ void Curl_trc_read(struct Curl_easy *data, const char *fmt, ...)
   if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_read)) {
     va_list ap;
     va_start(ap, fmt);
-    trc_infof(data, &Curl_trc_feat_read, fmt, ap);
+    trc_infof(data, &Curl_trc_feat_read, NULL, 0, fmt, ap);
     va_end(ap);
   }
 }
@@ -194,7 +331,18 @@ void Curl_trc_write(struct Curl_easy *data, const char *fmt, ...)
   if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_write)) {
     va_list ap;
     va_start(ap, fmt);
-    trc_infof(data, &Curl_trc_feat_write, fmt, ap);
+    trc_infof(data, &Curl_trc_feat_write, NULL, 0, fmt, ap);
+    va_end(ap);
+  }
+}
+
+void Curl_trc_dns(struct Curl_easy *data, const char *fmt, ...)
+{
+  DEBUGASSERT(!strchr(fmt, '\n'));
+  if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_dns)) {
+    va_list ap;
+    va_start(ap, fmt);
+    trc_infof(data, &Curl_trc_feat_dns, NULL, 0, fmt, ap);
     va_end(ap);
   }
 }
@@ -211,7 +359,7 @@ void Curl_trc_ftp(struct Curl_easy *data, const char *fmt, ...)
   if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ftp)) {
     va_list ap;
     va_start(ap, fmt);
-    trc_infof(data, &Curl_trc_feat_ftp, fmt, ap);
+    trc_infof(data, &Curl_trc_feat_ftp, NULL, 0, fmt, ap);
     va_end(ap);
   }
 }
@@ -229,7 +377,7 @@ void Curl_trc_smtp(struct Curl_easy *data, const char *fmt, ...)
   if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_smtp)) {
     va_list ap;
     va_start(ap, fmt);
-    trc_infof(data, &Curl_trc_feat_smtp, fmt, ap);
+    trc_infof(data, &Curl_trc_feat_smtp, NULL, 0, fmt, ap);
     va_end(ap);
   }
 }
@@ -247,7 +395,7 @@ void Curl_trc_ssls(struct Curl_easy *data, const char *fmt, ...)
   if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ssls)) {
     va_list ap;
     va_start(ap, fmt);
-    trc_infof(data, &Curl_trc_feat_ssls, fmt, ap);
+    trc_infof(data, &Curl_trc_feat_ssls, NULL, 0, fmt, ap);
     va_end(ap);
   }
 }
@@ -265,7 +413,7 @@ void Curl_trc_ws(struct Curl_easy *data, const char *fmt, ...)
   if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ws)) {
     va_list ap;
     va_start(ap, fmt);
-    trc_infof(data, &Curl_trc_feat_ws, fmt, ap);
+    trc_infof(data, &Curl_trc_feat_ws, NULL, 0, fmt, ap);
     va_end(ap);
   }
 }
@@ -275,6 +423,7 @@ void Curl_trc_ws(struct Curl_easy *data, const char *fmt, ...)
 #define TRC_CT_PROTOCOL    (1<<(0))
 #define TRC_CT_NETWORK     (1<<(1))
 #define TRC_CT_PROXY       (1<<(2))
+#define TRC_CT_INTERNALS   (1<<(3))
 
 struct trc_feat_def {
   struct curl_trc_feat *feat;
@@ -282,13 +431,15 @@ struct trc_feat_def {
 };
 
 static struct trc_feat_def trc_feats[] = {
+  { &Curl_trc_feat_ids,       TRC_CT_INTERNALS },
+  { &Curl_trc_feat_multi,     TRC_CT_NETWORK },
   { &Curl_trc_feat_read,      TRC_CT_NONE },
   { &Curl_trc_feat_write,     TRC_CT_NONE },
+  { &Curl_trc_feat_dns,       TRC_CT_NETWORK },
 #ifndef CURL_DISABLE_FTP
   { &Curl_trc_feat_ftp,       TRC_CT_PROTOCOL },
 #endif
 #ifndef CURL_DISABLE_DOH
-  { &Curl_doh_trc,            TRC_CT_NETWORK },
 #endif
 #ifndef CURL_DISABLE_SMTP
   { &Curl_trc_feat_smtp,      TRC_CT_PROTOCOL },
@@ -341,18 +492,18 @@ static struct trc_cft_def trc_cfts[] = {
 #endif
 };
 
-static void trc_apply_level_by_name(const char * const token, int lvl)
+static void trc_apply_level_by_name(struct Curl_str *token, int lvl)
 {
   size_t i;
 
   for(i = 0; i < CURL_ARRAYSIZE(trc_cfts); ++i) {
-    if(strcasecompare(token, trc_cfts[i].cft->name)) {
+    if(curlx_str_casecompare(token, trc_cfts[i].cft->name)) {
       trc_cfts[i].cft->log_level = lvl;
       break;
     }
   }
   for(i = 0; i < CURL_ARRAYSIZE(trc_feats); ++i) {
-    if(strcasecompare(token, trc_feats[i].feat->name)) {
+    if(curlx_str_casecompare(token, trc_feats[i].feat->name)) {
       trc_feats[i].feat->log_level = lvl;
       break;
     }
@@ -375,42 +526,36 @@ static void trc_apply_level_by_category(int category, int lvl)
 
 static CURLcode trc_opt(const char *config)
 {
-  char *token, *tok_buf, *tmp;
-  int lvl;
-
-  tmp = strdup(config);
-  if(!tmp)
-    return CURLE_OUT_OF_MEMORY;
-
-  token = Curl_strtok_r(tmp, ", ", &tok_buf);
-  while(token) {
-    switch(*token) {
-      case '-':
-        lvl = CURL_LOG_LVL_NONE;
-        ++token;
-        break;
-      case '+':
-        lvl = CURL_LOG_LVL_INFO;
-        ++token;
-        break;
-      default:
-        lvl = CURL_LOG_LVL_INFO;
-        break;
+  struct Curl_str out;
+  while(!curlx_str_until(&config, &out, 32, ',')) {
+    int lvl = CURL_LOG_LVL_INFO;
+    const char *token = curlx_str(&out);
+
+    if(*token == '-') {
+      lvl = CURL_LOG_LVL_NONE;
+      curlx_str_nudge(&out, 1);
     }
-    if(strcasecompare(token, "all"))
+    else if(*token == '+')
+      curlx_str_nudge(&out, 1);
+
+    if(curlx_str_casecompare(&out, "all"))
       trc_apply_level_by_category(TRC_CT_NONE, lvl);
-    else if(strcasecompare(token, "protocol"))
+    else if(curlx_str_casecompare(&out, "protocol"))
       trc_apply_level_by_category(TRC_CT_PROTOCOL, lvl);
-    else if(strcasecompare(token, "network"))
+    else if(curlx_str_casecompare(&out, "network"))
       trc_apply_level_by_category(TRC_CT_NETWORK, lvl);
-    else if(strcasecompare(token, "proxy"))
+    else if(curlx_str_casecompare(&out, "proxy"))
       trc_apply_level_by_category(TRC_CT_PROXY, lvl);
+    else if(curlx_str_casecompare(&out, "doh")) {
+      struct Curl_str dns = { "dns", 3 };
+      trc_apply_level_by_name(&dns, lvl);
+    }
     else
-      trc_apply_level_by_name(token, lvl);
+      trc_apply_level_by_name(&out, lvl);
 
-    token = Curl_strtok_r(NULL, ", ", &tok_buf);
+    if(curlx_str_single(&config, ','))
+      break;
   }
-  free(tmp);
   return CURLE_OK;
 }
 
@@ -449,8 +594,7 @@ void Curl_infof(struct Curl_easy *data, const char *fmt, ...)
   (void)data; (void)fmt;
 }
 
-void Curl_trc_cf_infof(struct Curl_easy *data,
-                       struct Curl_cfilter *cf,
+void Curl_trc_cf_infof(struct Curl_easy *data, const struct Curl_cfilter *cf,
                        const char *fmt, ...)
 {
   (void)data; (void)cf; (void)fmt;
@@ -458,11 +602,21 @@ void Curl_trc_cf_infof(struct Curl_easy *data,
 
 struct curl_trc_feat;
 
+void Curl_trc_multi(struct Curl_easy *data, const char *fmt, ...)
+{
+  (void)data; (void)fmt;
+}
+
 void Curl_trc_write(struct Curl_easy *data, const char *fmt, ...)
 {
   (void)data; (void)fmt;
 }
 
+void Curl_trc_dns(struct Curl_easy *data, const char *fmt, ...)
+{
+  (void)data; (void)fmt;
+}
+
 void Curl_trc_read(struct Curl_easy *data, const char *fmt, ...)
 {
   (void)data; (void)fmt;
diff --git a/extra/curl/curl-8.12.1/lib/curl_trc.h b/extra/curl/curl-8.14.1/lib/curl_trc.h
similarity index 87%
rename from extra/curl/curl-8.12.1/lib/curl_trc.h
rename to extra/curl/curl-8.14.1/lib/curl_trc.h
index 9b4e36eef993..ed7e5d89f008 100644
--- a/extra/curl/curl-8.12.1/lib/curl_trc.h
+++ b/extra/curl/curl-8.14.1/lib/curl_trc.h
@@ -52,7 +52,7 @@ CURLcode Curl_trc_opt(const char *config);
 
 /* the function used to output verbose information */
 void Curl_debug(struct Curl_easy *data, curl_infotype type,
-                char *ptr, size_t size);
+                const char *ptr, size_t size);
 
 /**
  * Output a failure message on registered callbacks for transfer.
@@ -80,12 +80,17 @@ void Curl_infof(struct Curl_easy *data,
  * Output an informational message when both transfer's verbose logging
  * and connection filters verbose logging are enabled.
  */
-void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf,
+void Curl_trc_cf_infof(struct Curl_easy *data, const struct Curl_cfilter *cf,
                        const char *fmt, ...) CURL_PRINTF(3, 4);
+void Curl_trc_multi(struct Curl_easy *data,
+                    const char *fmt, ...) CURL_PRINTF(2, 3);
+const char *Curl_trc_mstate_name(int state);
 void Curl_trc_write(struct Curl_easy *data,
                     const char *fmt, ...) CURL_PRINTF(2, 3);
 void Curl_trc_read(struct Curl_easy *data,
                    const char *fmt, ...) CURL_PRINTF(2, 3);
+void Curl_trc_dns(struct Curl_easy *data,
+                  const char *fmt, ...) CURL_PRINTF(2, 3);
 
 #ifndef CURL_DISABLE_FTP
 extern struct curl_trc_feat Curl_trc_feat_ftp;
@@ -112,6 +117,9 @@ void Curl_trc_ws(struct Curl_easy *data,
 #define infof(data, ...) \
   do { if(Curl_trc_is_verbose(data)) \
          Curl_infof(data, __VA_ARGS__); } while(0)
+#define CURL_TRC_M(data, ...) \
+  do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_multi)) \
+         Curl_trc_multi(data, __VA_ARGS__); } while(0)
 #define CURL_TRC_CF(data, cf, ...) \
   do { if(Curl_trc_cf_is_verbose(cf, data)) \
          Curl_trc_cf_infof(data, cf, __VA_ARGS__); } while(0)
@@ -121,6 +129,9 @@ void Curl_trc_ws(struct Curl_easy *data,
 #define CURL_TRC_READ(data, ...) \
   do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_read)) \
          Curl_trc_read(data, __VA_ARGS__); } while(0)
+#define CURL_TRC_DNS(data, ...) \
+  do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_dns)) \
+         Curl_trc_dns(data, __VA_ARGS__); } while(0)
 
 #ifndef CURL_DISABLE_FTP
 #define CURL_TRC_FTP(data, ...) \
@@ -146,9 +157,11 @@ void Curl_trc_ws(struct Curl_easy *data,
 #else /* CURL_HAVE_C99 */
 
 #define infof Curl_infof
+#define CURL_TRC_M  Curl_trc_multi
 #define CURL_TRC_CF Curl_trc_cf_infof
 #define CURL_TRC_WRITE Curl_trc_write
 #define CURL_TRC_READ  Curl_trc_read
+#define CURL_TRC_DNS   Curl_trc_dns
 
 #ifndef CURL_DISABLE_FTP
 #define CURL_TRC_FTP   Curl_trc_ftp
@@ -165,15 +178,18 @@ void Curl_trc_ws(struct Curl_easy *data,
 
 #endif /* !CURL_HAVE_C99 */
 
-#ifndef CURL_DISABLE_VERBOSE_STRINGS
-/* informational messages enabled */
-
 struct curl_trc_feat {
   const char *name;
   int log_level;
 };
+
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+/* informational messages enabled */
+
+extern struct curl_trc_feat Curl_trc_feat_multi;
 extern struct curl_trc_feat Curl_trc_feat_read;
 extern struct curl_trc_feat Curl_trc_feat_write;
+extern struct curl_trc_feat Curl_trc_feat_dns;
 
 #define Curl_trc_is_verbose(data) \
             ((data) && (data)->set.verbose && \
@@ -185,6 +201,7 @@ extern struct curl_trc_feat Curl_trc_feat_write;
 #define Curl_trc_ft_is_verbose(data, ft) \
             (Curl_trc_is_verbose(data) && \
              (ft)->log_level >= CURL_LOG_LVL_INFO)
+#define CURL_MSTATE_NAME(s)  Curl_trc_mstate_name((int)(s))
 
 #else /* defined(CURL_DISABLE_VERBOSE_STRINGS) */
 /* All informational messages are not compiled in for size savings */
@@ -192,6 +209,7 @@ extern struct curl_trc_feat Curl_trc_feat_write;
 #define Curl_trc_is_verbose(d)        (FALSE)
 #define Curl_trc_cf_is_verbose(x,y)   (FALSE)
 #define Curl_trc_ft_is_verbose(x,y)   (FALSE)
+#define CURL_MSTATE_NAME(x)           ((void)(x), "-")
 
 #endif /* !defined(CURL_DISABLE_VERBOSE_STRINGS) */
 
diff --git a/extra/curl/curl-8.12.1/lib/base64.c b/extra/curl/curl-8.14.1/lib/curlx/base64.c
similarity index 81%
rename from extra/curl/curl-8.12.1/lib/base64.c
rename to extra/curl/curl-8.14.1/lib/curlx/base64.c
index 8373115d2075..92ebc57e1e6c 100644
--- a/extra/curl/curl-8.12.1/lib/base64.c
+++ b/extra/curl/curl-8.14.1/lib/curlx/base64.c
@@ -24,7 +24,7 @@
 
 /* Base64 encoding/decoding */
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #if !defined(CURL_DISABLE_HTTP_AUTH) || defined(USE_SSH) || \
   !defined(CURL_DISABLE_LDAP) || \
@@ -32,21 +32,20 @@
   !defined(CURL_DISABLE_POP3) || \
   !defined(CURL_DISABLE_IMAP) || \
   !defined(CURL_DISABLE_DIGEST_AUTH) || \
-  !defined(CURL_DISABLE_DOH) || defined(USE_SSL) || defined(BUILDING_CURL)
-#include "curl/curl.h"
+  !defined(CURL_DISABLE_DOH) || defined(USE_SSL) || !defined(BUILDING_LIBCURL)
+#include <curl/curl.h>
 #include "warnless.h"
-#include "curl_base64.h"
+#include "base64.h"
 
 /* The last 2 #include files should be in this order */
 #ifdef BUILDING_LIBCURL
-#include "curl_memory.h"
+#include "../curl_memory.h"
 #endif
-#include "memdebug.h"
+#include "../memdebug.h"
 
 /* ---- Base64 Encoding/Decoding Table --- */
-/* Padding character string starts at offset 64. */
-static const char base64encdec[]=
-  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+const char Curl_base64encdec[]=
+  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
 /* The Base 64 encoding with a URL and filename safe alphabet, RFC 4648
    section 5 */
@@ -60,11 +59,11 @@ static const unsigned char decodetable[] =
   29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
   48, 49, 50, 51 };
 /*
- * Curl_base64_decode()
+ * curlx_base64_decode()
  *
- * Given a base64 NUL-terminated string at src, decode it and return a
- * pointer in *outptr to a newly allocated memory area holding decoded
- * data. Size of decoded data is returned in variable pointed by outlen.
+ * Given a base64 null-terminated string at src, decode it and return a
+ * pointer in *outptr to a newly allocated memory area holding decoded data.
+ * Size of decoded data is returned in variable pointed by outlen.
  *
  * Returns CURLE_OK on success, otherwise specific error code. Function
  * output shall not be considered valid unless CURLE_OK is returned.
@@ -73,8 +72,8 @@ static const unsigned char decodetable[] =
  *
  * @unittest: 1302
  */
-CURLcode Curl_base64_decode(const char *src,
-                            unsigned char **outptr, size_t *outlen)
+CURLcode curlx_base64_decode(const char *src,
+                             unsigned char **outptr, size_t *outlen)
 {
   size_t srclen = 0;
   size_t padding = 0;
@@ -119,14 +118,6 @@ CURLcode Curl_base64_decode(const char *src,
 
   memset(lookup, 0xff, sizeof(lookup));
   memcpy(&lookup['+'], decodetable, sizeof(decodetable));
-  /* replaces
-  {
-    unsigned char c;
-    const unsigned char *p = (const unsigned char *)base64encdec;
-    for(c = 0; *p; c++, p++)
-      lookup[*p] = c;
-  }
-  */
 
   /* Decode the complete quantums first */
   for(i = 0; i < fullQuantums; i++) {
@@ -186,13 +177,13 @@ CURLcode Curl_base64_decode(const char *src,
 }
 
 static CURLcode base64_encode(const char *table64,
+                              unsigned char padbyte,
                               const char *inputbuff, size_t insize,
                               char **outptr, size_t *outlen)
 {
   char *output;
   char *base64data;
-  const unsigned char *in = (unsigned char *)inputbuff;
-  const char *padstr = &table64[64];    /* Point to padding string. */
+  const unsigned char *in = (const unsigned char *)inputbuff;
 
   *outptr = NULL;
   *outlen = 0;
@@ -222,17 +213,17 @@ static CURLcode base64_encode(const char *table64,
     *output++ = table64[ in[0] >> 2 ];
     if(insize == 1) {
       *output++ = table64[ ((in[0] & 0x03) << 4) ];
-      if(*padstr) {
-        *output++ = *padstr;
-        *output++ = *padstr;
+      if(padbyte) {
+        *output++ = padbyte;
+        *output++ = padbyte;
       }
     }
     else {
       /* insize == 2 */
       *output++ = table64[ ((in[0] & 0x03) << 4) | ((in[1] & 0xF0) >> 4) ];
       *output++ = table64[ ((in[1] & 0x0F) << 2) ];
-      if(*padstr)
-        *output++ = *padstr;
+      if(padbyte)
+        *output++ = padbyte;
     }
   }
 
@@ -249,45 +240,46 @@ static CURLcode base64_encode(const char *table64,
 }
 
 /*
- * Curl_base64_encode()
+ * curlx_base64_encode()
  *
  * Given a pointer to an input buffer and an input size, encode it and
  * return a pointer in *outptr to a newly allocated memory area holding
  * encoded data. Size of encoded data is returned in variable pointed by
  * outlen.
  *
- * Input length of 0 indicates input buffer holds a NUL-terminated string.
+ * Input length of 0 indicates input buffer holds a null-terminated string.
  *
  * Returns CURLE_OK on success, otherwise specific error code. Function
  * output shall not be considered valid unless CURLE_OK is returned.
  *
  * @unittest: 1302
  */
-CURLcode Curl_base64_encode(const char *inputbuff, size_t insize,
-                            char **outptr, size_t *outlen)
+CURLcode curlx_base64_encode(const char *inputbuff, size_t insize,
+                             char **outptr, size_t *outlen)
 {
-  return base64_encode(base64encdec, inputbuff, insize, outptr, outlen);
+  return base64_encode(Curl_base64encdec, '=',
+                       inputbuff, insize, outptr, outlen);
 }
 
 /*
- * Curl_base64url_encode()
+ * curlx_base64url_encode()
  *
  * Given a pointer to an input buffer and an input size, encode it and
  * return a pointer in *outptr to a newly allocated memory area holding
  * encoded data. Size of encoded data is returned in variable pointed by
  * outlen.
  *
- * Input length of 0 indicates input buffer holds a NUL-terminated string.
+ * Input length of 0 indicates input buffer holds a null-terminated string.
  *
  * Returns CURLE_OK on success, otherwise specific error code. Function
  * output shall not be considered valid unless CURLE_OK is returned.
  *
  * @unittest: 1302
  */
-CURLcode Curl_base64url_encode(const char *inputbuff, size_t insize,
-                               char **outptr, size_t *outlen)
+CURLcode curlx_base64url_encode(const char *inputbuff, size_t insize,
+                                char **outptr, size_t *outlen)
 {
-  return base64_encode(base64url, inputbuff, insize, outptr, outlen);
+  return base64_encode(base64url, 0, inputbuff, insize, outptr, outlen);
 }
 
 #endif /* no users so disabled */
diff --git a/extra/curl/curl-8.12.1/lib/curl_base64.h b/extra/curl/curl-8.14.1/lib/curlx/base64.h
similarity index 61%
rename from extra/curl/curl-8.12.1/lib/curl_base64.h
rename to extra/curl/curl-8.14.1/lib/curlx/base64.h
index 7f7cd1d98a84..026f80e4d37f 100644
--- a/extra/curl/curl-8.12.1/lib/curl_base64.h
+++ b/extra/curl/curl-8.14.1/lib/curlx/base64.h
@@ -24,18 +24,13 @@
  *
  ***************************************************************************/
 
-#ifndef BUILDING_LIBCURL
-/* this renames functions so that the tool code can use the same code
-   without getting symbol collisions */
-#define Curl_base64_encode(a,b,c,d) curlx_base64_encode(a,b,c,d)
-#define Curl_base64url_encode(a,b,c,d) curlx_base64url_encode(a,b,c,d)
-#define Curl_base64_decode(a,b,c) curlx_base64_decode(a,b,c)
-#endif
+CURLcode curlx_base64_encode(const char *inputbuff, size_t insize,
+                             char **outptr, size_t *outlen);
+CURLcode curlx_base64url_encode(const char *inputbuff, size_t insize,
+                                char **outptr, size_t *outlen);
+CURLcode curlx_base64_decode(const char *src,
+                             unsigned char **outptr, size_t *outlen);
+
+extern const char Curl_base64encdec[];
 
-CURLcode Curl_base64_encode(const char *inputbuff, size_t insize,
-                            char **outptr, size_t *outlen);
-CURLcode Curl_base64url_encode(const char *inputbuff, size_t insize,
-                               char **outptr, size_t *outlen);
-CURLcode Curl_base64_decode(const char *src,
-                            unsigned char **outptr, size_t *outlen);
 #endif /* HEADER_CURL_BASE64_H */
diff --git a/extra/curl/curl-8.12.1/lib/curlx.h b/extra/curl/curl-8.14.1/lib/curlx/curlx.h
similarity index 79%
rename from extra/curl/curl-8.12.1/lib/curlx.h
rename to extra/curl/curl-8.14.1/lib/curlx/curlx.h
index f0e4e6470b18..983c7b5c75b6 100644
--- a/extra/curl/curl-8.12.1/lib/curlx.h
+++ b/extra/curl/curl-8.14.1/lib/curlx/curlx.h
@@ -31,17 +31,6 @@
  * be.
  */
 
-/* map standard printf functions to curl implementations */
-#include "curl_printf.h"
-
-#include "strcase.h"
-/* "strcase.h" provides the strcasecompare protos */
-
-#include "strtoofft.h"
-/* "strtoofft.h" provides this function: curlx_strtoofft(), returns a
-   curl_off_t number from a given string.
-*/
-
 #include "nonblock.h"
 /* "nonblock.h" provides curlx_nonblock() */
 
@@ -53,8 +42,8 @@
   curlx_uztosi()
 */
 
-#include "curl_multibyte.h"
-/* "curl_multibyte.h" provides these functions and macros:
+#include "multibyte.h"
+/* "multibyte.h" provides these functions and macros:
 
   curlx_convert_UTF8_to_wchar()
   curlx_convert_wchar_to_UTF8()
@@ -64,6 +53,22 @@
 */
 
 #include "version_win32.h"
-/* "version_win32.h" provides curlx_verify_windows_version() */
+/* provides curlx_verify_windows_version() */
+
+#include "strparse.h"
+/* The curlx_str_* parsing functions */
+
+#include "dynbuf.h"
+/* The curlx_dyn_* functions */
+
+#include "base64.h"
+#include "timeval.h"
+#include "timediff.h"
+
+#include "winapi.h"
+/* for curlx_winapi_strerror */
+
+#include "inet_pton.h"
+/* for curlx_inet_pton */
 
 #endif /* HEADER_CURL_CURLX_H */
diff --git a/extra/curl/curl-8.12.1/lib/dynbuf.c b/extra/curl/curl-8.14.1/lib/curlx/dynbuf.c
similarity index 81%
rename from extra/curl/curl-8.12.1/lib/dynbuf.c
rename to extra/curl/curl-8.14.1/lib/curlx/dynbuf.c
index 15164265f480..0b8dfe8e34f5 100644
--- a/extra/curl/curl-8.12.1/lib/dynbuf.c
+++ b/extra/curl/curl-8.14.1/lib/curlx/dynbuf.c
@@ -22,25 +22,28 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 #include "dynbuf.h"
-#include "curl_printf.h"
+#include "../curl_printf.h"
 #ifdef BUILDING_LIBCURL
-#include "curl_memory.h"
+#include "../curl_memory.h"
 #endif
-#include "memdebug.h"
+#include "../memdebug.h"
 
 #define MIN_FIRST_ALLOC 32
 
+#ifdef DEBUGBUILD
 #define DYNINIT 0xbee51da /* random pattern */
+#endif
 
 /*
  * Init a dynbuf struct.
  */
-void Curl_dyn_init(struct dynbuf *s, size_t toobig)
+void curlx_dyn_init(struct dynbuf *s, size_t toobig)
 {
   DEBUGASSERT(s);
   DEBUGASSERT(toobig);
+  DEBUGASSERT(toobig <= MAX_DYNBUF_SIZE); /* catch crazy mistakes */
   s->bufr = NULL;
   s->leng = 0;
   s->allc = 0;
@@ -54,9 +57,10 @@ void Curl_dyn_init(struct dynbuf *s, size_t toobig)
  * free the buffer and re-init the necessary fields. It does not touch the
  * 'init' field and thus this buffer can be reused to add data to again.
  */
-void Curl_dyn_free(struct dynbuf *s)
+void curlx_dyn_free(struct dynbuf *s)
 {
   DEBUGASSERT(s);
+  DEBUGASSERT(s->init == DYNINIT);
   Curl_safefree(s->bufr);
   s->leng = s->allc = 0;
 }
@@ -80,7 +84,7 @@ static CURLcode dyn_nappend(struct dynbuf *s,
   DEBUGASSERT(!len || mem);
 
   if(fit > s->toobig) {
-    Curl_dyn_free(s);
+    curlx_dyn_free(s);
     return CURLE_TOO_LARGE;
   }
   else if(!a) {
@@ -106,7 +110,7 @@ static CURLcode dyn_nappend(struct dynbuf *s,
        include that as well when it uses this code */
     void *p = realloc(s->bufr, a);
     if(!p) {
-      Curl_dyn_free(s);
+      curlx_dyn_free(s);
       return CURLE_OUT_OF_MEMORY;
     }
     s->bufr = p;
@@ -124,7 +128,7 @@ static CURLcode dyn_nappend(struct dynbuf *s,
  * Clears the string, keeps the allocation. This can also be called on a
  * buffer that already was freed.
  */
-void Curl_dyn_reset(struct dynbuf *s)
+void curlx_dyn_reset(struct dynbuf *s)
 {
   DEBUGASSERT(s);
   DEBUGASSERT(s->init == DYNINIT);
@@ -138,7 +142,7 @@ void Curl_dyn_reset(struct dynbuf *s)
  * Specify the size of the tail to keep (number of bytes from the end of the
  * buffer). The rest will be dropped.
  */
-CURLcode Curl_dyn_tail(struct dynbuf *s, size_t trail)
+CURLcode curlx_dyn_tail(struct dynbuf *s, size_t trail)
 {
   DEBUGASSERT(s);
   DEBUGASSERT(s->init == DYNINIT);
@@ -148,7 +152,7 @@ CURLcode Curl_dyn_tail(struct dynbuf *s, size_t trail)
   else if(trail == s->leng)
     return CURLE_OK;
   else if(!trail) {
-    Curl_dyn_reset(s);
+    curlx_dyn_reset(s);
   }
   else {
     memmove(&s->bufr[0], &s->bufr[s->leng - trail], trail);
@@ -162,7 +166,7 @@ CURLcode Curl_dyn_tail(struct dynbuf *s, size_t trail)
 /*
  * Appends a buffer with length.
  */
-CURLcode Curl_dyn_addn(struct dynbuf *s, const void *mem, size_t len)
+CURLcode curlx_dyn_addn(struct dynbuf *s, const void *mem, size_t len)
 {
   DEBUGASSERT(s);
   DEBUGASSERT(s->init == DYNINIT);
@@ -173,7 +177,7 @@ CURLcode Curl_dyn_addn(struct dynbuf *s, const void *mem, size_t len)
 /*
  * Append a null-terminated string at the end.
  */
-CURLcode Curl_dyn_add(struct dynbuf *s, const char *str)
+CURLcode curlx_dyn_add(struct dynbuf *s, const char *str)
 {
   size_t n;
   DEBUGASSERT(str);
@@ -181,13 +185,13 @@ CURLcode Curl_dyn_add(struct dynbuf *s, const char *str)
   DEBUGASSERT(s->init == DYNINIT);
   DEBUGASSERT(!s->leng || s->bufr);
   n = strlen(str);
-  return dyn_nappend(s, (unsigned char *)str, n);
+  return dyn_nappend(s, (const unsigned char *)str, n);
 }
 
 /*
  * Append a string vprintf()-style
  */
-CURLcode Curl_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap)
+CURLcode curlx_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap)
 {
 #ifdef BUILDING_LIBCURL
   int rc;
@@ -195,7 +199,7 @@ CURLcode Curl_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap)
   DEBUGASSERT(s->init == DYNINIT);
   DEBUGASSERT(!s->leng || s->bufr);
   DEBUGASSERT(fmt);
-  rc = Curl_dyn_vprintf(s, fmt, ap);
+  rc = curlx_dyn_vprintf(s, fmt, ap);
 
   if(!rc)
     return CURLE_OK;
@@ -204,15 +208,15 @@ CURLcode Curl_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap)
   return CURLE_OUT_OF_MEMORY;
 #else
   char *str;
-  str = vaprintf(fmt, ap); /* this allocs a new string to append */
+  str = curl_mvaprintf(fmt, ap); /* this allocs a new string to append */
 
   if(str) {
-    CURLcode result = dyn_nappend(s, (unsigned char *)str, strlen(str));
+    CURLcode result = dyn_nappend(s, (const unsigned char *)str, strlen(str));
     free(str);
     return result;
   }
   /* If we failed, we cleanup the whole buffer and return error */
-  Curl_dyn_free(s);
+  curlx_dyn_free(s);
   return CURLE_OUT_OF_MEMORY;
 #endif
 }
@@ -220,7 +224,7 @@ CURLcode Curl_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap)
 /*
  * Append a string printf()-style
  */
-CURLcode Curl_dyn_addf(struct dynbuf *s, const char *fmt, ...)
+CURLcode curlx_dyn_addf(struct dynbuf *s, const char *fmt, ...)
 {
   CURLcode result;
   va_list ap;
@@ -228,7 +232,7 @@ CURLcode Curl_dyn_addf(struct dynbuf *s, const char *fmt, ...)
   DEBUGASSERT(s->init == DYNINIT);
   DEBUGASSERT(!s->leng || s->bufr);
   va_start(ap, fmt);
-  result = Curl_dyn_vaddf(s, fmt, ap);
+  result = curlx_dyn_vaddf(s, fmt, ap);
   va_end(ap);
   return result;
 }
@@ -236,7 +240,7 @@ CURLcode Curl_dyn_addf(struct dynbuf *s, const char *fmt, ...)
 /*
  * Returns a pointer to the buffer.
  */
-char *Curl_dyn_ptr(const struct dynbuf *s)
+char *curlx_dyn_ptr(const struct dynbuf *s)
 {
   DEBUGASSERT(s);
   DEBUGASSERT(s->init == DYNINIT);
@@ -244,7 +248,7 @@ char *Curl_dyn_ptr(const struct dynbuf *s)
   return s->bufr;
 }
 
-char *Curl_dyn_take(struct dynbuf *s, size_t *plen)
+char *curlx_dyn_take(struct dynbuf *s, size_t *plen)
 {
   char *ptr = s->bufr;
   DEBUGASSERT(s);
@@ -259,7 +263,7 @@ char *Curl_dyn_take(struct dynbuf *s, size_t *plen)
 /*
  * Returns an unsigned pointer to the buffer.
  */
-unsigned char *Curl_dyn_uptr(const struct dynbuf *s)
+unsigned char *curlx_dyn_uptr(const struct dynbuf *s)
 {
   DEBUGASSERT(s);
   DEBUGASSERT(s->init == DYNINIT);
@@ -270,7 +274,7 @@ unsigned char *Curl_dyn_uptr(const struct dynbuf *s)
 /*
  * Returns the length of the buffer.
  */
-size_t Curl_dyn_len(const struct dynbuf *s)
+size_t curlx_dyn_len(const struct dynbuf *s)
 {
   DEBUGASSERT(s);
   DEBUGASSERT(s->init == DYNINIT);
@@ -281,7 +285,7 @@ size_t Curl_dyn_len(const struct dynbuf *s)
 /*
  * Set a new (smaller) length.
  */
-CURLcode Curl_dyn_setlen(struct dynbuf *s, size_t set)
+CURLcode curlx_dyn_setlen(struct dynbuf *s, size_t set)
 {
   DEBUGASSERT(s);
   DEBUGASSERT(s->init == DYNINIT);
diff --git a/extra/curl/curl-8.12.1/lib/dynbuf.h b/extra/curl/curl-8.14.1/lib/curlx/dynbuf.h
similarity index 60%
rename from extra/curl/curl-8.12.1/lib/dynbuf.h
rename to extra/curl/curl-8.14.1/lib/curlx/dynbuf.h
index 154b54cd9c29..27335a6fbf4a 100644
--- a/extra/curl/curl-8.12.1/lib/dynbuf.h
+++ b/extra/curl/curl-8.14.1/lib/curlx/dynbuf.h
@@ -26,25 +26,6 @@
 
 #include <curl/curl.h>
 
-#ifndef BUILDING_LIBCURL
-/* this renames the functions so that the tool code can use the same code
-   without getting symbol collisions */
-#define Curl_dyn_init(a,b) curlx_dyn_init(a,b)
-#define Curl_dyn_add(a,b) curlx_dyn_add(a,b)
-#define Curl_dyn_addn(a,b,c) curlx_dyn_addn(a,b,c)
-#define Curl_dyn_addf curlx_dyn_addf
-#define Curl_dyn_vaddf curlx_dyn_vaddf
-#define Curl_dyn_free(a) curlx_dyn_free(a)
-#define Curl_dyn_ptr(a) curlx_dyn_ptr(a)
-#define Curl_dyn_uptr(a) curlx_dyn_uptr(a)
-#define Curl_dyn_len(a) curlx_dyn_len(a)
-#define Curl_dyn_reset(a) curlx_dyn_reset(a)
-#define Curl_dyn_take(a,b) curlx_dyn_take(a,b)
-#define Curl_dyn_tail(a,b) curlx_dyn_tail(a,b)
-#define Curl_dyn_setlen(a,b) curlx_dyn_setlen(a,b)
-#define curlx_dynbuf dynbuf /* for the struct name */
-#endif
-
 struct dynbuf {
   char *bufr;    /* point to a null-terminated allocated buffer */
   size_t leng;   /* number of bytes *EXCLUDING* the null-terminator */
@@ -55,32 +36,34 @@ struct dynbuf {
 #endif
 };
 
-void Curl_dyn_init(struct dynbuf *s, size_t toobig);
-void Curl_dyn_free(struct dynbuf *s);
-CURLcode Curl_dyn_addn(struct dynbuf *s, const void *mem, size_t len)
+void curlx_dyn_init(struct dynbuf *s, size_t toobig);
+void curlx_dyn_free(struct dynbuf *s);
+CURLcode curlx_dyn_addn(struct dynbuf *s, const void *mem, size_t len)
   WARN_UNUSED_RESULT;
-CURLcode Curl_dyn_add(struct dynbuf *s, const char *str)
+CURLcode curlx_dyn_add(struct dynbuf *s, const char *str)
   WARN_UNUSED_RESULT;
-CURLcode Curl_dyn_addf(struct dynbuf *s, const char *fmt, ...)
+CURLcode curlx_dyn_addf(struct dynbuf *s, const char *fmt, ...)
   WARN_UNUSED_RESULT CURL_PRINTF(2, 3);
-CURLcode Curl_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap)
+CURLcode curlx_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap)
   WARN_UNUSED_RESULT CURL_PRINTF(2, 0);
-void Curl_dyn_reset(struct dynbuf *s);
-CURLcode Curl_dyn_tail(struct dynbuf *s, size_t trail);
-CURLcode Curl_dyn_setlen(struct dynbuf *s, size_t set);
-char *Curl_dyn_ptr(const struct dynbuf *s);
-unsigned char *Curl_dyn_uptr(const struct dynbuf *s);
-size_t Curl_dyn_len(const struct dynbuf *s);
+void curlx_dyn_reset(struct dynbuf *s);
+CURLcode curlx_dyn_tail(struct dynbuf *s, size_t trail);
+CURLcode curlx_dyn_setlen(struct dynbuf *s, size_t set);
+char *curlx_dyn_ptr(const struct dynbuf *s);
+unsigned char *curlx_dyn_uptr(const struct dynbuf *s);
+size_t curlx_dyn_len(const struct dynbuf *s);
 
 /* returns 0 on success, -1 on error */
 /* The implementation of this function exists in mprintf.c */
-int Curl_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save);
+int curlx_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save);
 
 /* Take the buffer out of the dynbuf. Caller has ownership and
  * dynbuf resets to initial state. */
-char *Curl_dyn_take(struct dynbuf *s, size_t *plen);
+char *curlx_dyn_take(struct dynbuf *s, size_t *plen);
 
 /* Dynamic buffer max sizes */
+#define MAX_DYNBUF_SIZE (SIZE_T_MAX/2)
+
 #define DYN_DOH_RESPONSE    3000
 #define DYN_DOH_CNAME       256
 #define DYN_PAUSE_BUFFER    (64 * 1024 * 1024)
@@ -95,4 +78,8 @@ char *Curl_dyn_take(struct dynbuf *s, size_t *plen);
 #define DYN_PINGPPONG_CMD   (64*1024)
 #define DYN_IMAP_CMD        (64*1024)
 #define DYN_MQTT_RECV       (64*1024)
+#define DYN_MQTT_SEND       0xFFFFFFF
+#define DYN_CRLFILE_SIZE    (400*1024*1024) /* 400mb */
+#define DYN_CERTFILE_SIZE   (100*1024) /* 100KiB */
+#define DYN_KEYFILE_SIZE    (100*1024) /* 100KiB */
 #endif
diff --git a/extra/curl/curl-8.12.1/lib/inet_pton.c b/extra/curl/curl-8.14.1/lib/curlx/inet_pton.c
similarity index 90%
rename from extra/curl/curl-8.12.1/lib/inet_pton.c
rename to extra/curl/curl-8.14.1/lib/curlx/inet_pton.c
index d0c04db1dae6..d2b39ae9f1a7 100644
--- a/extra/curl/curl-8.12.1/lib/inet_pton.c
+++ b/extra/curl/curl-8.14.1/lib/curlx/inet_pton.c
@@ -18,7 +18,9 @@
  * SPDX-License-Identifier: ISC
  */
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
+#include "../curl_ctype.h"
+#include "strparse.h"
 
 #ifndef HAVE_INET_PTON
 
@@ -72,7 +74,7 @@ static int      inet_pton6(const char *src, unsigned char *dst);
  *      Paul Vixie, 1996.
  */
 int
-Curl_inet_pton(int af, const char *src, void *dst)
+curlx_inet_pton(int af, const char *src, void *dst)
 {
   switch(af) {
   case AF_INET:
@@ -80,7 +82,7 @@ Curl_inet_pton(int af, const char *src, void *dst)
   case AF_INET6:
     return inet_pton6(src, (unsigned char *)dst);
   default:
-    errno = EAFNOSUPPORT;
+    CURL_SETERRNO(SOCKEAFNOSUPPORT);
     return -1;
   }
   /* NOTREACHED */
@@ -99,7 +101,6 @@ Curl_inet_pton(int af, const char *src, void *dst)
 static int
 inet_pton4(const char *src, unsigned char *dst)
 {
-  static const char digits[] = "0123456789";
   int saw_digit, octets, ch;
   unsigned char tmp[INADDRSZ], *tp;
 
@@ -108,12 +109,8 @@ inet_pton4(const char *src, unsigned char *dst)
   tp = tmp;
   *tp = 0;
   while((ch = *src++) != '\0') {
-    const char *pch;
-
-    pch = strchr(digits, ch);
-    if(pch) {
-      unsigned int val = (unsigned int)(*tp * 10) +
-                         (unsigned int)(pch - digits);
+    if(ISDIGIT(ch)) {
+      unsigned int val = (*tp * 10) + (ch - '0');
 
       if(saw_digit && *tp == 0)
         return 0;
@@ -157,8 +154,6 @@ inet_pton4(const char *src, unsigned char *dst)
 static int
 inet_pton6(const char *src, unsigned char *dst)
 {
-  static const char xdigits_l[] = "0123456789abcdef",
-    xdigits_u[] = "0123456789ABCDEF";
   unsigned char tmp[IN6ADDRSZ], *tp, *endp, *colonp;
   const char *curtok;
   int ch, saw_xdigit;
@@ -175,15 +170,9 @@ inet_pton6(const char *src, unsigned char *dst)
   saw_xdigit = 0;
   val = 0;
   while((ch = *src++) != '\0') {
-    const char *xdigits;
-    const char *pch;
-
-    pch = strchr((xdigits = xdigits_l), ch);
-    if(!pch)
-      pch = strchr((xdigits = xdigits_u), ch);
-    if(pch) {
+    if(ISXDIGIT(ch)) {
       val <<= 4;
-      val |= (pch - xdigits);
+      val |= Curl_hexval(ch);
       if(++saw_xdigit > 4)
         return 0;
       continue;
diff --git a/extra/curl/curl-8.12.1/lib/inet_pton.h b/extra/curl/curl-8.14.1/lib/curlx/inet_pton.h
similarity index 86%
rename from extra/curl/curl-8.12.1/lib/inet_pton.h
rename to extra/curl/curl-8.14.1/lib/curlx/inet_pton.h
index 915385fc2573..a9dc43085f45 100644
--- a/extra/curl/curl-8.12.1/lib/inet_pton.h
+++ b/extra/curl/curl-8.14.1/lib/curlx/inet_pton.h
@@ -24,9 +24,9 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
-int Curl_inet_pton(int, const char *, void *);
+int curlx_inet_pton(int, const char *, void *);
 
 #ifdef HAVE_INET_PTON
 #ifdef HAVE_NETINET_IN_H
@@ -38,10 +38,10 @@ int Curl_inet_pton(int, const char *, void *);
 #ifdef HAVE_ARPA_INET_H
 #include <arpa/inet.h>
 #endif
-#if defined(__AMIGA__)
-#define Curl_inet_pton(x,y,z) inet_pton(x,(unsigned char *)y,z)
+#ifdef __AMIGA__
+#define curlx_inet_pton(x,y,z) inet_pton(x,(unsigned char *)CURL_UNCONST(y),z)
 #else
-#define Curl_inet_pton(x,y,z) inet_pton(x,y,z)
+#define curlx_inet_pton(x,y,z) inet_pton(x,y,z)
 #endif
 #endif
 
diff --git a/extra/curl/curl-8.12.1/lib/curl_multibyte.c b/extra/curl/curl-8.14.1/lib/curlx/multibyte.c
similarity index 88%
rename from extra/curl/curl-8.12.1/lib/curl_multibyte.c
rename to extra/curl/curl-8.14.1/lib/curlx/multibyte.c
index 220b2fa3f620..30380275cc7e 100644
--- a/extra/curl/curl-8.12.1/lib/curl_multibyte.c
+++ b/extra/curl/curl-8.14.1/lib/curlx/multibyte.c
@@ -23,18 +23,18 @@
  ***************************************************************************/
 
 /*
- * This file is 'mem-include-scan' clean, which means memdebug.h and
- * curl_memory.h are purposely not included in this file. See test 1132.
- *
- * The functions in this file are curlx functions which are not tracked by the
- * curl memory tracker memdebug.
+ * This file is 'mem-include-scan' clean, which means its memory allocations
+ * are not tracked by the curl memory tracker memdebug, so they must not use
+ * `CURLDEBUG` macro replacements in memdebug.h for free, malloc, etc. To avoid
+ * these macro replacements, wrap the names in parentheses to call the original
+ * versions: `ptr = (malloc)(123)`, `(free)(ptr)`, etc.
  */
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #ifdef _WIN32
 
-#include "curl_multibyte.h"
+#include "multibyte.h"
 
 /*
  * MultiByte conversions using Windows kernel32 library.
@@ -48,11 +48,11 @@ wchar_t *curlx_convert_UTF8_to_wchar(const char *str_utf8)
     int str_w_len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
                                         str_utf8, -1, NULL, 0);
     if(str_w_len > 0) {
-      str_w = malloc(str_w_len * sizeof(wchar_t));
+      str_w = (malloc)(str_w_len * sizeof(wchar_t));
       if(str_w) {
         if(MultiByteToWideChar(CP_UTF8, 0, str_utf8, -1, str_w,
                                str_w_len) == 0) {
-          free(str_w);
+          (free)(str_w);
           return NULL;
         }
       }
@@ -70,11 +70,11 @@ char *curlx_convert_wchar_to_UTF8(const wchar_t *str_w)
     int bytes = WideCharToMultiByte(CP_UTF8, 0, str_w, -1,
                                     NULL, 0, NULL, NULL);
     if(bytes > 0) {
-      str_utf8 = malloc(bytes);
+      str_utf8 = (malloc)(bytes);
       if(str_utf8) {
         if(WideCharToMultiByte(CP_UTF8, 0, str_w, -1, str_utf8, bytes,
                                NULL, NULL) == 0) {
-          free(str_utf8);
+          (free)(str_utf8);
           return NULL;
         }
       }
@@ -84,6 +84,8 @@ char *curlx_convert_wchar_to_UTF8(const wchar_t *str_w)
   return str_utf8;
 }
 
+#ifndef UNDER_CE
+
 /* declare GetFullPathNameW for mingw-w64 UWP builds targeting old windows */
 #if defined(CURL_WINDOWS_UWP) && defined(__MINGW32__) && \
   (_WIN32_WINNT < _WIN32_WINNT_WIN10)
@@ -134,7 +136,7 @@ static bool fix_excessive_path(const TCHAR *in, TCHAR **out)
   if(needed == (size_t)-1 || needed >= max_path_len)
     goto cleanup;
   ++needed; /* for NUL */
-  ibuf = malloc(needed * sizeof(wchar_t));
+  ibuf = (malloc)(needed * sizeof(wchar_t));
   if(!ibuf)
     goto cleanup;
   count = mbstowcs(ibuf, in, needed);
@@ -154,7 +156,7 @@ static bool fix_excessive_path(const TCHAR *in, TCHAR **out)
   /* skip paths that are not excessive and do not need modification */
   if(needed <= MAX_PATH)
     goto cleanup;
-  fbuf = malloc(needed * sizeof(wchar_t));
+  fbuf = (malloc)(needed * sizeof(wchar_t));
   if(!fbuf)
     goto cleanup;
   count = (size_t)GetFullPathNameW(in_w, (DWORD)needed, fbuf, NULL);
@@ -187,7 +189,7 @@ static bool fix_excessive_path(const TCHAR *in, TCHAR **out)
       if(needed > max_path_len)
         goto cleanup;
 
-      temp = malloc(needed * sizeof(wchar_t));
+      temp = (malloc)(needed * sizeof(wchar_t));
       if(!temp)
         goto cleanup;
 
@@ -200,7 +202,7 @@ static bool fix_excessive_path(const TCHAR *in, TCHAR **out)
       if(needed > max_path_len)
         goto cleanup;
 
-      temp = malloc(needed * sizeof(wchar_t));
+      temp = (malloc)(needed * sizeof(wchar_t));
       if(!temp)
         goto cleanup;
 
@@ -208,7 +210,7 @@ static bool fix_excessive_path(const TCHAR *in, TCHAR **out)
       wcscpy(temp + 4, fbuf);
     }
 
-    free(fbuf);
+    (free)(fbuf);
     fbuf = temp;
   }
 
@@ -218,7 +220,7 @@ static bool fix_excessive_path(const TCHAR *in, TCHAR **out)
   if(needed == (size_t)-1 || needed >= max_path_len)
     goto cleanup;
   ++needed; /* for NUL */
-  obuf = malloc(needed);
+  obuf = (malloc)(needed);
   if(!obuf)
     goto cleanup;
   count = wcstombs(obuf, fbuf, needed);
@@ -232,10 +234,10 @@ static bool fix_excessive_path(const TCHAR *in, TCHAR **out)
 #endif
 
 cleanup:
-  free(fbuf);
+  (free)(fbuf);
 #ifndef _UNICODE
-  free(ibuf);
-  free(obuf);
+  (free)(ibuf);
+  (free)(obuf);
 #endif
   return *out ? true : false;
 }
@@ -267,16 +269,17 @@ int curlx_win32_open(const char *filename, int oflag, ...)
     curlx_unicodefree(filename_w);
   }
   else
-    errno = EINVAL;
+    /* !checksrc! disable ERRNOVAR 1 */
+    CURL_SETERRNO(EINVAL);
 #else
   if(fix_excessive_path(filename, &fixed))
     target = fixed;
   else
     target = filename;
-  result = (_open)(target, oflag, pmode);
+  result = _open(target, oflag, pmode);
 #endif
 
-  free(fixed);
+  (free)(fixed);
   return result;
 }
 
@@ -297,7 +300,8 @@ FILE *curlx_win32_fopen(const char *filename, const char *mode)
     result = _wfopen(target, mode_w);
   }
   else
-    errno = EINVAL;
+    /* !checksrc! disable ERRNOVAR 1 */
+    CURL_SETERRNO(EINVAL);
   curlx_unicodefree(filename_w);
   curlx_unicodefree(mode_w);
 #else
@@ -308,7 +312,7 @@ FILE *curlx_win32_fopen(const char *filename, const char *mode)
   result = (fopen)(target, mode);
 #endif
 
-  free(fixed);
+  (free)(fixed);
   return result;
 }
 
@@ -333,7 +337,8 @@ int curlx_win32_stat(const char *path, struct_stat *buffer)
     curlx_unicodefree(path_w);
   }
   else
-    errno = EINVAL;
+    /* !checksrc! disable ERRNOVAR 1 */
+    CURL_SETERRNO(EINVAL);
 #else
   if(fix_excessive_path(path, &fixed))
     target = fixed;
@@ -346,8 +351,10 @@ int curlx_win32_stat(const char *path, struct_stat *buffer)
 #endif
 #endif
 
-  free(fixed);
+  (free)(fixed);
   return result;
 }
 
+#endif /* UNDER_CE */
+
 #endif /* _WIN32 */
diff --git a/extra/curl/curl-8.12.1/lib/curl_multibyte.h b/extra/curl/curl-8.14.1/lib/curlx/multibyte.h
similarity index 88%
rename from extra/curl/curl-8.12.1/lib/curl_multibyte.h
rename to extra/curl/curl-8.14.1/lib/curlx/multibyte.h
index dec384e2fe23..7835fdc3b5e4 100644
--- a/extra/curl/curl-8.12.1/lib/curl_multibyte.h
+++ b/extra/curl/curl-8.14.1/lib/curlx/multibyte.h
@@ -23,9 +23,9 @@
  * SPDX-License-Identifier: curl
  *
  ***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
-#if defined(_WIN32)
+#ifdef _WIN32
 
  /*
   * MultiByte conversions using Windows kernel32 library.
@@ -81,12 +81,7 @@ typedef union {
 
 #endif /* UNICODE && _WIN32 */
 
-#define curlx_unicodefree(ptr)                          \
-  do {                                                  \
-    if(ptr) {                                           \
-      (free)(ptr);                                      \
-      (ptr) = NULL;                                     \
-    }                                                   \
-  } while(0)
+/* the purpose of this macro is to free() without being traced by memdebug */
+#define curlx_unicodefree(ptr) (free)(CURL_UNCONST(ptr))
 
 #endif /* HEADER_CURL_MULTIBYTE_H */
diff --git a/extra/curl/curl-8.12.1/lib/nonblock.c b/extra/curl/curl-8.14.1/lib/curlx/nonblock.c
similarity index 98%
rename from extra/curl/curl-8.12.1/lib/nonblock.c
rename to extra/curl/curl-8.14.1/lib/curlx/nonblock.c
index a59cab83253e..409b187f1fda 100644
--- a/extra/curl/curl-8.12.1/lib/nonblock.c
+++ b/extra/curl/curl-8.14.1/lib/curlx/nonblock.c
@@ -22,7 +22,7 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #ifdef HAVE_SYS_IOCTL_H
 #include <sys/ioctl.h>
diff --git a/extra/curl/curl-8.12.1/lib/nonblock.h b/extra/curl/curl-8.14.1/lib/curlx/nonblock.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/nonblock.h
rename to extra/curl/curl-8.14.1/lib/curlx/nonblock.h
diff --git a/extra/curl/curl-8.14.1/lib/curlx/strparse.c b/extra/curl/curl-8.14.1/lib/curlx/strparse.c
new file mode 100644
index 000000000000..b8b2a14d3cb1
--- /dev/null
+++ b/extra/curl/curl-8.14.1/lib/curlx/strparse.c
@@ -0,0 +1,303 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "strparse.h"
+#include "../strcase.h"
+
+void curlx_str_init(struct Curl_str *out)
+{
+  out->str = NULL;
+  out->len = 0;
+}
+
+void curlx_str_assign(struct Curl_str *out, const char *str, size_t len)
+{
+  out->str = str;
+  out->len = len;
+}
+
+/* Get a word until the first DELIM or end of string. At least one byte long.
+   return non-zero on error */
+int curlx_str_until(const char **linep, struct Curl_str *out,
+                   const size_t max, char delim)
+{
+  const char *s = *linep;
+  size_t len = 0;
+  DEBUGASSERT(linep && *linep && out && max && delim);
+
+  curlx_str_init(out);
+  while(*s && (*s != delim)) {
+    s++;
+    if(++len > max) {
+      return STRE_BIG;
+    }
+  }
+  if(!len)
+    return STRE_SHORT;
+  out->str = *linep;
+  out->len = len;
+  *linep = s; /* point to the first byte after the word */
+  return STRE_OK;
+}
+
+/* Get a word until the first space or end of string. At least one byte long.
+   return non-zero on error */
+int curlx_str_word(const char **linep, struct Curl_str *out,
+                  const size_t max)
+{
+  return curlx_str_until(linep, out, max, ' ');
+}
+
+/* Get a word until a newline byte or end of string. At least one byte long.
+   return non-zero on error */
+int curlx_str_untilnl(const char **linep, struct Curl_str *out,
+                     const size_t max)
+{
+  const char *s = *linep;
+  size_t len = 0;
+  DEBUGASSERT(linep && *linep && out && max);
+
+  curlx_str_init(out);
+  while(*s && !ISNEWLINE(*s)) {
+    s++;
+    if(++len > max)
+      return STRE_BIG;
+  }
+  if(!len)
+    return STRE_SHORT;
+  out->str = *linep;
+  out->len = len;
+  *linep = s; /* point to the first byte after the word */
+  return STRE_OK;
+}
+
+
+/* Get a "quoted" word. No escaping possible.
+   return non-zero on error */
+int curlx_str_quotedword(const char **linep, struct Curl_str *out,
+                        const size_t max)
+{
+  const char *s = *linep;
+  size_t len = 0;
+  DEBUGASSERT(linep && *linep && out && max);
+
+  curlx_str_init(out);
+  if(*s != '\"')
+    return STRE_BEGQUOTE;
+  s++;
+  while(*s && (*s != '\"')) {
+    s++;
+    if(++len > max)
+      return STRE_BIG;
+  }
+  if(*s != '\"')
+    return STRE_ENDQUOTE;
+  out->str = (*linep) + 1;
+  out->len = len;
+  *linep = s + 1;
+  return STRE_OK;
+}
+
+/* Advance over a single character.
+   return non-zero on error */
+int curlx_str_single(const char **linep, char byte)
+{
+  DEBUGASSERT(linep && *linep);
+  if(**linep != byte)
+    return STRE_BYTE;
+  (*linep)++; /* move over it */
+  return STRE_OK;
+}
+
+/* Advance over a single space.
+   return non-zero on error */
+int curlx_str_singlespace(const char **linep)
+{
+  return curlx_str_single(linep, ' ');
+}
+
+/* given an ASCII character and max ascii, return TRUE if valid */
+#define valid_digit(x,m) \
+  (((x) >= '0') && ((x) <= m) && Curl_hexasciitable[(x)-'0'])
+
+/* We use 16 for the zero index (and the necessary bitwise AND in the loop)
+   to be able to have a non-zero value there to make valid_digit() able to
+   use the info */
+const unsigned char Curl_hexasciitable[] = {
+  16, 1, 2, 3, 4, 5, 6, 7, 8, 9, /* 0x30: 0 - 9 */
+  0, 0, 0, 0, 0, 0, 0,
+  10, 11, 12, 13, 14, 15,        /* 0x41: A - F */
+  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+  10, 11, 12, 13, 14, 15         /* 0x61: a - f */
+};
+
+/* no support for 0x prefix nor leading spaces */
+static int str_num_base(const char **linep, curl_off_t *nump, curl_off_t max,
+                        int base) /* 8, 10 or 16, nothing else */
+{
+  curl_off_t num = 0;
+  const char *p;
+  int m = (base == 10) ? '9' :   /* the largest digit possible */
+    (base == 16) ? 'f' : '7';
+  DEBUGASSERT(linep && *linep && nump);
+  DEBUGASSERT((base == 8) || (base == 10) || (base == 16));
+  DEBUGASSERT(max >= 0); /* mostly to catch SIZE_T_MAX, which is too large */
+  *nump = 0;
+  p = *linep;
+  if(!valid_digit(*p, m))
+    return STRE_NO_NUM;
+  if(max < base) {
+    /* special-case low max scenario because check needs to be different */
+    do {
+      int n = Curl_hexval(*p++);
+      num = num * base + n;
+      if(num > max)
+        return STRE_OVERFLOW;
+    } while(valid_digit(*p, m));
+  }
+  else {
+    do {
+      int n = Curl_hexval(*p++);
+      if(num > ((max - n) / base))
+        return STRE_OVERFLOW;
+      num = num * base + n;
+    } while(valid_digit(*p, m));
+  }
+  *nump = num;
+  *linep = p;
+  return STRE_OK;
+}
+
+/* Get an unsigned decimal number with no leading space or minus. Leading
+   zeroes are accepted. return non-zero on error */
+int curlx_str_number(const char **linep, curl_off_t *nump, curl_off_t max)
+{
+  return str_num_base(linep, nump, max, 10);
+}
+
+/* Get an unsigned hexadecimal number with no leading space or minus and no
+   "0x" support. Leading zeroes are accepted. return non-zero on error */
+int curlx_str_hex(const char **linep, curl_off_t *nump, curl_off_t max)
+{
+  return str_num_base(linep, nump, max, 16);
+}
+
+/* Get an unsigned octal number with no leading space or minus and no "0"
+   prefix support. Leading zeroes are accepted. return non-zero on error */
+int curlx_str_octal(const char **linep, curl_off_t *nump, curl_off_t max)
+{
+  return str_num_base(linep, nump, max, 8);
+}
+
+/*
+ * Parse a positive number up to 63-bit number written in ASCII. Skip leading
+ * blanks. No support for prefixes.
+ */
+int curlx_str_numblanks(const char **str, curl_off_t *num)
+{
+  curlx_str_passblanks(str);
+  return curlx_str_number(str, num, CURL_OFF_T_MAX);
+}
+
+/* CR or LF
+   return non-zero on error */
+int curlx_str_newline(const char **linep)
+{
+  DEBUGASSERT(linep && *linep);
+  if(ISNEWLINE(**linep)) {
+    (*linep)++;
+    return STRE_OK; /* yessir */
+  }
+  return STRE_NEWLINE;
+}
+
+#ifndef WITHOUT_LIBCURL
+/* case insensitive compare that the parsed string matches the given string.
+   Returns non-zero on match. */
+int curlx_str_casecompare(struct Curl_str *str, const char *check)
+{
+  size_t clen = check ? strlen(check) : 0;
+  return ((str->len == clen) && strncasecompare(str->str, check, clen));
+}
+#endif
+
+/* case sensitive string compare. Returns non-zero on match. */
+int curlx_str_cmp(struct Curl_str *str, const char *check)
+{
+  if(check) {
+    size_t clen = strlen(check);
+    return ((str->len == clen) && !strncmp(str->str, check, clen));
+  }
+  return !!(str->len);
+}
+
+/* Trim off 'num' number of bytes from the beginning (left side) of the
+   string. If 'num' is larger than the string, return error. */
+int curlx_str_nudge(struct Curl_str *str, size_t num)
+{
+  if(num <= str->len) {
+    str->str += num;
+    str->len -= num;
+    return STRE_OK;
+  }
+  return STRE_OVERFLOW;
+}
+
+/* Get the following character sequence that consists only of bytes not
+   present in the 'reject' string. Like strcspn(). */
+int curlx_str_cspn(const char **linep, struct Curl_str *out,
+                   const char *reject)
+{
+  const char *s = *linep;
+  size_t len;
+  DEBUGASSERT(linep && *linep);
+
+  len = strcspn(s, reject);
+  if(len) {
+    out->str = s;
+    out->len = len;
+    *linep = &s[len];
+    return STRE_OK;
+  }
+  curlx_str_init(out);
+  return STRE_SHORT;
+}
+
+/* remove ISBLANK()s from both ends of the string */
+void curlx_str_trimblanks(struct Curl_str *out)
+{
+  while(out->len && ISBLANK(*out->str))
+    curlx_str_nudge(out, 1);
+
+  /* trim trailing spaces and tabs */
+  while(out->len && ISBLANK(out->str[out->len - 1]))
+    out->len--;
+}
+
+/* increase the pointer until it has moved over all blanks */
+void curlx_str_passblanks(const char **linep)
+{
+  while(ISBLANK(**linep))
+    (*linep)++; /* move over it */
+}
diff --git a/extra/curl/curl-8.14.1/lib/curlx/strparse.h b/extra/curl/curl-8.14.1/lib/curlx/strparse.h
new file mode 100644
index 000000000000..17bfdb80710c
--- /dev/null
+++ b/extra/curl/curl-8.14.1/lib/curlx/strparse.h
@@ -0,0 +1,112 @@
+#ifndef HEADER_CURL_STRPARSE_H
+#define HEADER_CURL_STRPARSE_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "../curl_setup.h"
+
+#define STRE_OK       0
+#define STRE_BIG      1
+#define STRE_SHORT    2
+#define STRE_BEGQUOTE 3
+#define STRE_ENDQUOTE 4
+#define STRE_BYTE     5
+#define STRE_NEWLINE  6
+#define STRE_OVERFLOW 7
+#define STRE_NO_NUM   8
+
+/* public struct, but all accesses should be done using the provided
+   functions */
+struct Curl_str {
+  const char *str;
+  size_t len;
+};
+
+void curlx_str_init(struct Curl_str *out);
+void curlx_str_assign(struct Curl_str *out, const char *str, size_t len);
+
+#define curlx_str(x) ((x)->str)
+#define curlx_strlen(x) ((x)->len)
+
+/* Get a word until the first space
+   return non-zero on error */
+int curlx_str_word(const char **linep, struct Curl_str *out, const size_t max);
+
+/* Get a word until the first DELIM or end of string
+   return non-zero on error */
+int curlx_str_until(const char **linep, struct Curl_str *out, const size_t max,
+                   char delim);
+
+/* Get a word until a newline byte or end of string. At least one byte long.
+   return non-zero on error */
+int curlx_str_untilnl(const char **linep, struct Curl_str *out,
+                     const size_t max);
+
+/* Get a "quoted" word. No escaping possible.
+   return non-zero on error */
+int curlx_str_quotedword(const char **linep, struct Curl_str *out,
+                        const size_t max);
+
+/* Advance over a single character.
+   return non-zero on error */
+int curlx_str_single(const char **linep, char byte);
+
+/* Advance over a single space.
+   return non-zero on error */
+int curlx_str_singlespace(const char **linep);
+
+/* Get an unsigned decimal number. Return non-zero on error */
+int curlx_str_number(const char **linep, curl_off_t *nump, curl_off_t max);
+
+/* As above with CURL_OFF_T_MAX but also pass leading blanks */
+int curlx_str_numblanks(const char **str, curl_off_t *num);
+
+/* Get an unsigned hexadecimal number. Return non-zero on error */
+int curlx_str_hex(const char **linep, curl_off_t *nump, curl_off_t max);
+
+/* Get an unsigned octal number. Return non-zero on error */
+int curlx_str_octal(const char **linep, curl_off_t *nump, curl_off_t max);
+
+/* Check for CR or LF
+   return non-zero on error */
+int curlx_str_newline(const char **linep);
+
+/* case insensitive compare that the parsed string matches the
+   given string. */
+int curlx_str_casecompare(struct Curl_str *str, const char *check);
+int curlx_str_cmp(struct Curl_str *str, const char *check);
+
+int curlx_str_nudge(struct Curl_str *str, size_t num);
+
+int curlx_str_cspn(const char **linep, struct Curl_str *out, const char *cspn);
+void curlx_str_trimblanks(struct Curl_str *out);
+void curlx_str_passblanks(const char **linep);
+
+/* given a hexadecimal letter, return the binary value. '0' returns 0, 'a'
+   returns 10. THIS ONLY WORKS ON VALID HEXADECIMAL LETTER INPUT. Verify
+   before calling this!
+*/
+extern const unsigned char Curl_hexasciitable[];
+#define Curl_hexval(x) (unsigned char)(Curl_hexasciitable[(x) - '0'] & 0x0f)
+
+#endif /* HEADER_CURL_STRPARSE_H */
diff --git a/extra/curl/curl-8.12.1/lib/timediff.c b/extra/curl/curl-8.14.1/lib/curlx/timediff.c
similarity index 94%
rename from extra/curl/curl-8.12.1/lib/timediff.c
rename to extra/curl/curl-8.14.1/lib/curlx/timediff.c
index d0824d1448b7..a90da961ab83 100644
--- a/extra/curl/curl-8.12.1/lib/timediff.c
+++ b/extra/curl/curl-8.14.1/lib/curlx/timediff.c
@@ -44,7 +44,7 @@ struct timeval *curlx_mstotv(struct timeval *tv, timediff_t ms)
 
   if(ms > 0) {
     timediff_t tv_sec = ms / 1000;
-    timediff_t tv_usec = (ms % 1000) * 1000; /* max=999999 */
+    timediff_t tv_usec = (ms % 1000) * 1000; /* max=999000 */
 #ifdef HAVE_SUSECONDS_T
 #if TIMEDIFF_T_MAX > TIME_T_MAX
     /* tv_sec overflow check in case time_t is signed */
@@ -84,5 +84,5 @@ struct timeval *curlx_mstotv(struct timeval *tv, timediff_t ms)
  */
 timediff_t curlx_tvtoms(struct timeval *tv)
 {
-  return (tv->tv_sec*1000) + (timediff_t)(((double)tv->tv_usec)/1000.0);
+  return (tv->tv_sec*1000) + (timediff_t)(tv->tv_usec/1000);
 }
diff --git a/extra/curl/curl-8.12.1/lib/timediff.h b/extra/curl/curl-8.14.1/lib/curlx/timediff.h
similarity index 98%
rename from extra/curl/curl-8.12.1/lib/timediff.h
rename to extra/curl/curl-8.14.1/lib/curlx/timediff.h
index 75f996c55cf9..aa224381dd93 100644
--- a/extra/curl/curl-8.12.1/lib/timediff.h
+++ b/extra/curl/curl-8.14.1/lib/curlx/timediff.h
@@ -24,7 +24,7 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 /* Use a larger type even for 32-bit time_t systems so that we can keep
    microsecond accuracy in it */
diff --git a/extra/curl/curl-8.12.1/lib/timeval.c b/extra/curl/curl-8.14.1/lib/curlx/timeval.c
similarity index 82%
rename from extra/curl/curl-8.12.1/lib/timeval.c
rename to extra/curl/curl-8.14.1/lib/curlx/timeval.c
index bb29bfdfee68..501bf9c3fd98 100644
--- a/extra/curl/curl-8.12.1/lib/timeval.c
+++ b/extra/curl/curl-8.14.1/lib/curlx/timeval.c
@@ -24,30 +24,52 @@
 
 #include "timeval.h"
 
-#if defined(_WIN32)
+#ifdef _WIN32
 
 #include <curl/curl.h>
-#include "system_win32.h"
+#include "version_win32.h"
+#include "../system_win32.h"
+
+LARGE_INTEGER Curl_freq;
+bool Curl_isVistaOrGreater;
+
+/* For tool or tests, we must initialize before calling curlx_now().
+   Providing this function here is wrong. */
+void curlx_now_init(void)
+{
+  if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT,
+                                  VERSION_GREATER_THAN_EQUAL))
+    Curl_isVistaOrGreater = true;
+  else
+    Curl_isVistaOrGreater = false;
+
+  QueryPerformanceFrequency(&Curl_freq);
+}
 
 /* In case of bug fix this function has a counterpart in tool_util.c */
-struct curltime Curl_now(void)
+struct curltime curlx_now(void)
 {
   struct curltime now;
-  if(Curl_isVistaOrGreater) { /* QPC timer might have issues pre-Vista */
+  bool isVistaOrGreater;
+  isVistaOrGreater = Curl_isVistaOrGreater;
+  if(isVistaOrGreater) { /* QPC timer might have issues pre-Vista */
     LARGE_INTEGER count;
+    LARGE_INTEGER freq;
+    freq = Curl_freq;
+    DEBUGASSERT(freq.QuadPart);
     QueryPerformanceCounter(&count);
-    now.tv_sec = (time_t)(count.QuadPart / Curl_freq.QuadPart);
-    now.tv_usec = (int)((count.QuadPart % Curl_freq.QuadPart) * 1000000 /
-                        Curl_freq.QuadPart);
+    now.tv_sec = (time_t)(count.QuadPart / freq.QuadPart);
+    now.tv_usec = (int)((count.QuadPart % freq.QuadPart) * 1000000 /
+                        freq.QuadPart);
   }
   else {
     /* Disable /analyze warning that GetTickCount64 is preferred  */
-#if defined(_MSC_VER)
+#ifdef _MSC_VER
 #pragma warning(push)
 #pragma warning(disable:28159)
 #endif
     DWORD milliseconds = GetTickCount();
-#if defined(_MSC_VER)
+#ifdef _MSC_VER
 #pragma warning(pop)
 #endif
 
@@ -60,7 +82,7 @@ struct curltime Curl_now(void)
 #elif defined(HAVE_CLOCK_GETTIME_MONOTONIC) ||  \
   defined(HAVE_CLOCK_GETTIME_MONOTONIC_RAW)
 
-struct curltime Curl_now(void)
+struct curltime curlx_now(void)
 {
   /*
   ** clock_gettime() is granted to be increased monotonically when the
@@ -134,7 +156,7 @@ struct curltime Curl_now(void)
 #include <stdint.h>
 #include <mach/mach_time.h>
 
-struct curltime Curl_now(void)
+struct curltime curlx_now(void)
 {
   /*
   ** Monotonic timer on macOS is provided by mach_absolute_time(), which
@@ -162,7 +184,7 @@ struct curltime Curl_now(void)
 
 #elif defined(HAVE_GETTIMEOFDAY)
 
-struct curltime Curl_now(void)
+struct curltime curlx_now(void)
 {
   /*
   ** gettimeofday() is not granted to be increased monotonically, due to
@@ -179,7 +201,7 @@ struct curltime Curl_now(void)
 
 #else
 
-struct curltime Curl_now(void)
+struct curltime curlx_now(void)
 {
   /*
   ** time() returns the value of time in seconds since the Epoch.
@@ -198,7 +220,7 @@ struct curltime Curl_now(void)
  *
  * @unittest: 1323
  */
-timediff_t Curl_timediff(struct curltime newer, struct curltime older)
+timediff_t curlx_timediff(struct curltime newer, struct curltime older)
 {
   timediff_t diff = (timediff_t)newer.tv_sec-older.tv_sec;
   if(diff >= (TIMEDIFF_T_MAX/1000))
@@ -212,7 +234,7 @@ timediff_t Curl_timediff(struct curltime newer, struct curltime older)
  * Returns: time difference in number of milliseconds, rounded up.
  * For too large diffs it returns max value.
  */
-timediff_t Curl_timediff_ceil(struct curltime newer, struct curltime older)
+timediff_t curlx_timediff_ceil(struct curltime newer, struct curltime older)
 {
   timediff_t diff = (timediff_t)newer.tv_sec-older.tv_sec;
   if(diff >= (TIMEDIFF_T_MAX/1000))
@@ -226,7 +248,7 @@ timediff_t Curl_timediff_ceil(struct curltime newer, struct curltime older)
  * Returns: time difference in number of microseconds. For too large diffs it
  * returns max value.
  */
-timediff_t Curl_timediff_us(struct curltime newer, struct curltime older)
+timediff_t curlx_timediff_us(struct curltime newer, struct curltime older)
 {
   timediff_t diff = (timediff_t)newer.tv_sec-older.tv_sec;
   if(diff >= (TIMEDIFF_T_MAX/1000000))
diff --git a/extra/curl/curl-8.12.1/lib/timeval.h b/extra/curl/curl-8.14.1/lib/curlx/timeval.h
similarity index 82%
rename from extra/curl/curl-8.12.1/lib/timeval.h
rename to extra/curl/curl-8.14.1/lib/curlx/timeval.h
index 33dfb5b10dd2..1f8fe5e8ad1e 100644
--- a/extra/curl/curl-8.12.1/lib/timeval.h
+++ b/extra/curl/curl-8.14.1/lib/curlx/timeval.h
@@ -24,7 +24,7 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #include "timediff.h"
 
@@ -33,7 +33,12 @@ struct curltime {
   int tv_usec;   /* microseconds */
 };
 
-struct curltime Curl_now(void);
+#ifdef _WIN32
+/* For tool or tests, we must initialize before calling curlx_now() */
+void curlx_now_init(void);
+#endif
+
+struct curltime curlx_now(void);
 
 /*
  * Make sure that the first argument (newer) is the more recent time and older
@@ -41,7 +46,7 @@ struct curltime Curl_now(void);
  *
  * Returns: the time difference in number of milliseconds.
  */
-timediff_t Curl_timediff(struct curltime newer, struct curltime older);
+timediff_t curlx_timediff(struct curltime newer, struct curltime older);
 
 /*
  * Make sure that the first argument (newer) is the more recent time and older
@@ -49,7 +54,7 @@ timediff_t Curl_timediff(struct curltime newer, struct curltime older);
  *
  * Returns: the time difference in number of milliseconds, rounded up.
  */
-timediff_t Curl_timediff_ceil(struct curltime newer, struct curltime older);
+timediff_t curlx_timediff_ceil(struct curltime newer, struct curltime older);
 
 /*
  * Make sure that the first argument (newer) is the more recent time and older
@@ -57,6 +62,6 @@ timediff_t Curl_timediff_ceil(struct curltime newer, struct curltime older);
  *
  * Returns: the time difference in number of microseconds.
  */
-timediff_t Curl_timediff_us(struct curltime newer, struct curltime older);
+timediff_t curlx_timediff_us(struct curltime newer, struct curltime older);
 
 #endif /* HEADER_CURL_TIMEVAL_H */
diff --git a/extra/curl/curl-8.12.1/lib/version_win32.c b/extra/curl/curl-8.14.1/lib/curlx/version_win32.c
similarity index 73%
rename from extra/curl/curl-8.12.1/lib/version_win32.c
rename to extra/curl/curl-8.14.1/lib/curlx/version_win32.c
index 21a122f2a628..8d0af68fcfe0 100644
--- a/extra/curl/curl-8.12.1/lib/version_win32.c
+++ b/extra/curl/curl-8.14.1/lib/curlx/version_win32.c
@@ -22,19 +22,17 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
-#if defined(_WIN32)
+#ifdef _WIN32
 
 #include <curl/curl.h>
 #include "version_win32.h"
 #include "warnless.h"
 
 /* The last 2 #include files should be in this order */
-#ifdef BUILDING_LIBCURL
-#include "curl_memory.h"
-#endif
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
 
 /* This Unicode version struct works for VerifyVersionInfoW (OSVERSIONINFOEXW)
    and RtlVerifyVersionInfo (RTLOSVERSIONINFOEXW) */
@@ -79,7 +77,7 @@ bool curlx_verify_windows_version(const unsigned int majorVersion,
 {
   bool matched = FALSE;
 
-#if defined(CURL_WINDOWS_UWP)
+#ifdef CURL_WINDOWS_UWP
   /* We have no way to determine the Windows version from Windows apps,
      so let's assume we are running on the target Windows version. */
   const WORD fullVersion = MAKEWORD(minorVersion, majorVersion);
@@ -113,88 +111,12 @@ bool curlx_verify_windows_version(const unsigned int majorVersion,
     /* we are always running on PLATFORM_WINNT */
     matched = FALSE;
   }
-#elif !defined(_WIN32_WINNT) || !defined(_WIN32_WINNT_WIN2K) || \
-    (_WIN32_WINNT < _WIN32_WINNT_WIN2K)
-  OSVERSIONINFO osver;
-
-  memset(&osver, 0, sizeof(osver));
-  osver.dwOSVersionInfoSize = sizeof(osver);
-
-  /* Find out Windows version */
-  if(GetVersionEx(&osver)) {
-    /* Verify the Operating System version number */
-    switch(condition) {
-    case VERSION_LESS_THAN:
-      if(osver.dwMajorVersion < majorVersion ||
-        (osver.dwMajorVersion == majorVersion &&
-         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.dwMajorVersion == majorVersion &&
-         osver.dwMinorVersion == minorVersion &&
-         (buildVersion == 0 ||
-          osver.dwBuildNumber <= buildVersion)))
-        matched = TRUE;
-      break;
-
-    case VERSION_EQUAL:
-      if(osver.dwMajorVersion == majorVersion &&
-         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.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) ||
-        (buildVersion != 0 &&
-         (osver.dwMajorVersion == majorVersion &&
-          osver.dwMinorVersion == minorVersion &&
-          osver.dwBuildNumber > buildVersion)))
-        matched = TRUE;
-      break;
-    }
-
-    /* Verify the platform identifier (if necessary) */
-    if(matched) {
-      switch(platform) {
-      case PLATFORM_WINDOWS:
-        if(osver.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS)
-          matched = FALSE;
-        break;
-
-      case PLATFORM_WINNT:
-        if(osver.dwPlatformId != VER_PLATFORM_WIN32_NT)
-          matched = FALSE;
-        break;
-
-      default: /* like platform == PLATFORM_DONT_CARE */
-        break;
-      }
-    }
-  }
+#elif defined(UNDER_CE)
+  (void)majorVersion;
+  (void)minorVersion;
+  (void)buildVersion;
+  (void)platform;
+  (void)condition;
 #else
   ULONGLONG cm = 0;
   struct OUR_OSVERSIONINFOEXW osver;
diff --git a/extra/curl/curl-8.12.1/lib/version_win32.h b/extra/curl/curl-8.14.1/lib/curlx/version_win32.h
similarity index 97%
rename from extra/curl/curl-8.12.1/lib/version_win32.h
rename to extra/curl/curl-8.14.1/lib/curlx/version_win32.h
index 95a9e7f21521..471100a66f51 100644
--- a/extra/curl/curl-8.12.1/lib/version_win32.h
+++ b/extra/curl/curl-8.14.1/lib/curlx/version_win32.h
@@ -24,9 +24,9 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
-#if defined(_WIN32)
+#ifdef _WIN32
 
 /* Version condition */
 typedef enum {
diff --git a/extra/curl/curl-8.12.1/lib/warnless.c b/extra/curl/curl-8.14.1/lib/curlx/warnless.c
similarity index 90%
rename from extra/curl/curl-8.12.1/lib/warnless.c
rename to extra/curl/curl-8.14.1/lib/curlx/warnless.c
index 41699600beec..5ca92450d893 100644
--- a/extra/curl/curl-8.12.1/lib/warnless.c
+++ b/extra/curl/curl-8.14.1/lib/curlx/warnless.c
@@ -22,7 +22,7 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "warnless.h"
 
 #if defined(__INTEL_COMPILER) && defined(__unix__)
 
@@ -35,8 +35,6 @@
 
 #endif /* __INTEL_COMPILER && __unix__ */
 
-#include "warnless.h"
-
 #ifdef _WIN32
 #undef read
 #undef write
@@ -45,42 +43,17 @@
 #include <limits.h>
 
 #define CURL_MASK_UCHAR   ((unsigned char)~0)
-#define CURL_MASK_SCHAR   (CURL_MASK_UCHAR >> 1)
 
 #define CURL_MASK_USHORT  ((unsigned short)~0)
-#define CURL_MASK_SSHORT  (CURL_MASK_USHORT >> 1)
 
 #define CURL_MASK_UINT    ((unsigned int)~0)
 #define CURL_MASK_SINT    (CURL_MASK_UINT >> 1)
 
 #define CURL_MASK_ULONG   ((unsigned long)~0)
-#define CURL_MASK_SLONG   (CURL_MASK_ULONG >> 1)
-
-#define CURL_MASK_UCOFFT  ((unsigned CURL_TYPEOF_CURL_OFF_T)~0)
-#define CURL_MASK_SCOFFT  (CURL_MASK_UCOFFT >> 1)
 
 #define CURL_MASK_USIZE_T ((size_t)~0)
 #define CURL_MASK_SSIZE_T (CURL_MASK_USIZE_T >> 1)
 
-/*
-** unsigned long to unsigned short
-*/
-
-unsigned short curlx_ultous(unsigned long ulnum)
-{
-#ifdef __INTEL_COMPILER
-#  pragma warning(push)
-#  pragma warning(disable:810) /* conversion may lose significant bits */
-#endif
-
-  DEBUGASSERT(ulnum <= (unsigned long) CURL_MASK_USHORT);
-  return (unsigned short)(ulnum & (unsigned long) CURL_MASK_USHORT);
-
-#ifdef __INTEL_COMPILER
-#  pragma warning(pop)
-#endif
-}
-
 /*
 ** unsigned long to unsigned char
 */
@@ -323,7 +296,7 @@ size_t curlx_sitouz(int sinum)
 #endif
 }
 
-#if defined(_WIN32)
+#ifdef _WIN32
 
 ssize_t curlx_read(int fd, void *buf, size_t count)
 {
diff --git a/extra/curl/curl-8.12.1/lib/warnless.h b/extra/curl/curl-8.14.1/lib/curlx/warnless.h
similarity index 95%
rename from extra/curl/curl-8.12.1/lib/warnless.h
rename to extra/curl/curl-8.14.1/lib/curlx/warnless.h
index fe3453250bad..c78b61169bf8 100644
--- a/extra/curl/curl-8.12.1/lib/warnless.h
+++ b/extra/curl/curl-8.14.1/lib/curlx/warnless.h
@@ -24,7 +24,7 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #ifdef USE_WINSOCK
 #include <curl/curl.h> /* for curl_socket_t */
@@ -33,8 +33,6 @@
 #define CURLX_FUNCTION_CAST(target_type, func) \
   (target_type)(void (*) (void))(func)
 
-unsigned short curlx_ultous(unsigned long ulnum);
-
 unsigned char curlx_ultouc(unsigned long ulnum);
 
 int curlx_uztosi(size_t uznum);
@@ -59,7 +57,7 @@ unsigned short curlx_uitous(unsigned int uinum);
 
 size_t curlx_sitouz(int sinum);
 
-#if defined(_WIN32)
+#ifdef _WIN32
 
 ssize_t curlx_read(int fd, void *buf, size_t count);
 
@@ -72,7 +70,7 @@ ssize_t curlx_write(int fd, const void *buf, size_t count);
 #ifndef HEADER_CURL_WARNLESS_H_REDEFS
 #define HEADER_CURL_WARNLESS_H_REDEFS
 
-#if defined(_WIN32)
+#ifdef _WIN32
 #undef  read
 #define read(fd, buf, count)  curlx_read(fd, buf, count)
 #undef  write
diff --git a/extra/curl/curl-8.14.1/lib/curlx/winapi.c b/extra/curl/curl-8.14.1/lib/curlx/winapi.c
new file mode 100644
index 000000000000..6069424becc5
--- /dev/null
+++ b/extra/curl/curl-8.14.1/lib/curlx/winapi.c
@@ -0,0 +1,135 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "../curl_setup.h"
+
+/*
+ * curlx_winapi_strerror:
+ * Variant of Curl_strerror if the error code is definitely Windows API.
+ */
+#ifdef _WIN32
+#include "winapi.h"
+
+#ifdef BUILDING_LIBCURL
+#include <curl/mprintf.h>
+#define SNPRINTF curl_msnprintf
+#else
+/* when built for the test servers */
+
+/* adjust for old MSVC */
+#if defined(_MSC_VER) && (_MSC_VER < 1900)
+# define SNPRINTF _snprintf
+#else
+#define SNPRINTF snprintf
+#endif
+
+#endif /* !BUILDING_LIBCURL */
+
+#ifdef _WIN32
+/* This is a helper function for Curl_strerror that converts Windows API error
+ * codes (GetLastError) to error messages.
+ * Returns NULL if no error message was found for error code.
+ */
+const char *curlx_get_winapi_error(int err, char *buf, size_t buflen)
+{
+  char *p;
+  wchar_t wbuf[256];
+
+  if(!buflen)
+    return NULL;
+
+  *buf = '\0';
+  *wbuf = L'\0';
+
+  /* We return the local codepage version of the error string because if it is
+     output to the user's terminal it will likely be with functions which
+     expect the local codepage (eg fprintf, failf, infof).
+     FormatMessageW -> wcstombs is used for Windows CE compatibility. */
+  if(FormatMessageW((FORMAT_MESSAGE_FROM_SYSTEM |
+                     FORMAT_MESSAGE_IGNORE_INSERTS), NULL, (DWORD)err,
+                    LANG_NEUTRAL, wbuf, CURL_ARRAYSIZE(wbuf), NULL)) {
+    size_t written = wcstombs(buf, wbuf, buflen - 1);
+    if(written != (size_t)-1)
+      buf[written] = '\0';
+    else
+      *buf = '\0';
+  }
+
+  /* Truncate multiple lines */
+  p = strchr(buf, '\n');
+  if(p) {
+    if(p > buf && *(p-1) == '\r')
+      *(p-1) = '\0';
+    else
+      *p = '\0';
+  }
+
+  return *buf ? buf : NULL;
+}
+#endif /* _WIN32 */
+
+const char *curlx_winapi_strerror(DWORD err, char *buf, size_t buflen)
+{
+#ifdef _WIN32
+  DWORD old_win_err = GetLastError();
+#endif
+  int old_errno = errno;
+
+  if(!buflen)
+    return NULL;
+
+  *buf = '\0';
+
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+  if(!curlx_get_winapi_error((int)err, buf, buflen)) {
+#if defined(__GNUC__) && __GNUC__ >= 7
+#pragma GCC diagnostic push
+#pragma GCC diagnostic warning "-Wformat-truncation=1"
+#endif
+    /* some GCC compilers cause false positive warnings if we allow this
+       warning */
+    SNPRINTF(buf, buflen, "Unknown error %lu (0x%08lX)", err, err);
+#if defined(__GNUC__) && __GNUC__ >= 7
+#pragma GCC diagnostic pop
+#endif
+
+  }
+#else
+  {
+    const char *txt = (err == ERROR_SUCCESS) ? "No error" : "Error";
+    if(strlen(txt) < buflen)
+      strcpy(buf, txt);
+  }
+#endif
+
+  if(errno != old_errno)
+    CURL_SETERRNO(old_errno);
+
+#ifdef _WIN32
+  if(old_win_err != GetLastError())
+    SetLastError(old_win_err);
+#endif
+
+  return buf;
+}
+#endif /* _WIN32 */
diff --git a/extra/curl/curl-8.14.1/lib/curlx/winapi.h b/extra/curl/curl-8.14.1/lib/curlx/winapi.h
new file mode 100644
index 000000000000..76ddcc53b8bc
--- /dev/null
+++ b/extra/curl/curl-8.14.1/lib/curlx/winapi.h
@@ -0,0 +1,33 @@
+#ifndef HEADER_CURLX_WINAPI_H
+#define HEADER_CURLX_WINAPI_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#ifdef _WIN32
+#define WINAPI_ERROR_LEN 100
+const char *curlx_get_winapi_error(int err, char *buf, size_t buflen);
+const char *curlx_winapi_strerror(DWORD err, char *buf, size_t buflen);
+#endif
+
+#endif /* HEADER_CURLX_WINAPI_H */
diff --git a/extra/curl/curl-8.12.1/lib/cw-out.c b/extra/curl/curl-8.14.1/lib/cw-out.c
similarity index 84%
rename from extra/curl/curl-8.12.1/lib/cw-out.c
rename to extra/curl/curl-8.14.1/lib/cw-out.c
index 4d3df0a650a0..097ef85e8b4c 100644
--- a/extra/curl/curl-8.12.1/lib/cw-out.c
+++ b/extra/curl/curl-8.14.1/lib/cw-out.c
@@ -32,6 +32,7 @@
 #include "multiif.h"
 #include "sendf.h"
 #include "cw-out.h"
+#include "cw-pause.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -86,7 +87,7 @@ static struct cw_out_buf *cw_out_buf_create(cw_out_type otype)
   struct cw_out_buf *cwbuf = calloc(1, sizeof(*cwbuf));
   if(cwbuf) {
     cwbuf->type = otype;
-    Curl_dyn_init(&cwbuf->b, DYN_PAUSE_BUFFER);
+    curlx_dyn_init(&cwbuf->b, DYN_PAUSE_BUFFER);
   }
   return cwbuf;
 }
@@ -94,7 +95,7 @@ static struct cw_out_buf *cw_out_buf_create(cw_out_type otype)
 static void cw_out_buf_free(struct cw_out_buf *cwbuf)
 {
   if(cwbuf) {
-    Curl_dyn_free(&cwbuf->b);
+    curlx_dyn_free(&cwbuf->b);
     free(cwbuf);
   }
 }
@@ -113,7 +114,7 @@ static void cw_out_close(struct Curl_easy *data, struct Curl_cwriter *writer);
 static CURLcode cw_out_init(struct Curl_easy *data,
                             struct Curl_cwriter *writer);
 
-struct Curl_cwtype Curl_cwt_out = {
+const struct Curl_cwtype Curl_cwt_out = {
   "cw-out",
   NULL,
   cw_out_init,
@@ -145,7 +146,7 @@ static size_t cw_out_bufs_len(struct cw_out_ctx *ctx)
   struct cw_out_buf *cwbuf = ctx->buf;
   size_t len = 0;
   while(cwbuf) {
-    len += Curl_dyn_len(&cwbuf->b);
+    len += curlx_dyn_len(&cwbuf->b);
     cwbuf = cwbuf->next;
   }
   return len;
@@ -198,7 +199,7 @@ static CURLcode cw_out_ptr_flush(struct cw_out_ctx *ctx,
                                  const char *buf, size_t blen,
                                  size_t *pconsumed)
 {
-  curl_write_callback wcb;
+  curl_write_callback wcb = NULL;
   void *wcb_data;
   size_t max_write, min_write;
   size_t wlen, nwritten;
@@ -220,9 +221,9 @@ static CURLcode cw_out_ptr_flush(struct cw_out_ctx *ctx,
       break;
     wlen = max_write ? CURLMIN(blen, max_write) : blen;
     Curl_set_in_callback(data, TRUE);
-    nwritten = wcb((char *)buf, 1, wlen, wcb_data);
+    nwritten = wcb((char *)CURL_UNCONST(buf), 1, wlen, wcb_data);
     Curl_set_in_callback(data, FALSE);
-    CURL_TRC_WRITE(data, "cw_out, wrote %zu %s bytes -> %zu",
+    CURL_TRC_WRITE(data, "[OUT] wrote %zu %s bytes -> %zu",
                    wlen, (otype == CW_OUT_BODY) ? "body" : "header",
                    nwritten);
     if(CURL_WRITEFUNC_PAUSE == nwritten) {
@@ -236,7 +237,7 @@ static CURLcode cw_out_ptr_flush(struct cw_out_ctx *ctx,
       /* mark the connection as RECV paused */
       data->req.keepon |= KEEP_RECV_PAUSE;
       ctx->paused = TRUE;
-      CURL_TRC_WRITE(data, "cw_out, PAUSE requested by client");
+      CURL_TRC_WRITE(data, "[OUT] PAUSE requested by client");
       break;
     }
     else if(CURL_WRITEFUNC_ERROR == nwritten) {
@@ -262,23 +263,24 @@ static CURLcode cw_out_buf_flush(struct cw_out_ctx *ctx,
 {
   CURLcode result = CURLE_OK;
 
-  if(Curl_dyn_len(&cwbuf->b)) {
+  if(curlx_dyn_len(&cwbuf->b)) {
     size_t consumed;
 
     result = cw_out_ptr_flush(ctx, data, cwbuf->type, flush_all,
-                              Curl_dyn_ptr(&cwbuf->b),
-                              Curl_dyn_len(&cwbuf->b),
+                              curlx_dyn_ptr(&cwbuf->b),
+                              curlx_dyn_len(&cwbuf->b),
                               &consumed);
     if(result)
       return result;
 
     if(consumed) {
-      if(consumed == Curl_dyn_len(&cwbuf->b)) {
-        Curl_dyn_free(&cwbuf->b);
+      if(consumed == curlx_dyn_len(&cwbuf->b)) {
+        curlx_dyn_free(&cwbuf->b);
       }
       else {
-        DEBUGASSERT(consumed < Curl_dyn_len(&cwbuf->b));
-        result = Curl_dyn_tail(&cwbuf->b, Curl_dyn_len(&cwbuf->b) - consumed);
+        DEBUGASSERT(consumed < curlx_dyn_len(&cwbuf->b));
+        result = curlx_dyn_tail(&cwbuf->b,
+                                curlx_dyn_len(&cwbuf->b) - consumed);
         if(result)
           return result;
       }
@@ -318,7 +320,7 @@ static CURLcode cw_out_flush_chain(struct cw_out_ctx *ctx,
   result = cw_out_buf_flush(ctx, data, cwbuf, flush_all);
   if(result)
     return result;
-  if(!Curl_dyn_len(&cwbuf->b)) {
+  if(!curlx_dyn_len(&cwbuf->b)) {
     cw_out_buf_free(cwbuf);
     *pcwbuf = NULL;
   }
@@ -326,11 +328,16 @@ static CURLcode cw_out_flush_chain(struct cw_out_ctx *ctx,
 }
 
 static CURLcode cw_out_append(struct cw_out_ctx *ctx,
+                              struct Curl_easy *data,
                               cw_out_type otype,
                               const char *buf, size_t blen)
 {
-  if(cw_out_bufs_len(ctx) + blen > DYN_PAUSE_BUFFER)
+  CURL_TRC_WRITE(data, "[OUT] paused, buffering %zu more bytes (%zu/%d)",
+                 blen, cw_out_bufs_len(ctx), DYN_PAUSE_BUFFER);
+  if(cw_out_bufs_len(ctx) + blen > DYN_PAUSE_BUFFER) {
+    failf(data, "pause buffer not large enough -> CURLE_TOO_LARGE");
     return CURLE_TOO_LARGE;
+  }
 
   /* if we do not have a buffer, or it is of another type, make a new one.
    * And for CW_OUT_HDS always make a new one, so we "replay" headers
@@ -343,7 +350,7 @@ static CURLcode cw_out_append(struct cw_out_ctx *ctx,
     ctx->buf = cwbuf;
   }
   DEBUGASSERT(ctx->buf && (ctx->buf->type == otype));
-  return Curl_dyn_addn(&ctx->buf->b, buf, blen);
+  return curlx_dyn_addn(&ctx->buf->b, buf, blen);
 }
 
 static CURLcode cw_out_do_write(struct cw_out_ctx *ctx,
@@ -364,7 +371,7 @@ static CURLcode cw_out_do_write(struct cw_out_ctx *ctx,
 
   if(ctx->buf) {
     /* still have buffered data, append and flush */
-    result = cw_out_append(ctx, otype, buf, blen);
+    result = cw_out_append(ctx, data, otype, buf, blen);
     if(result)
       return result;
     result = cw_out_flush_chain(ctx, data, &ctx->buf, flush_all);
@@ -380,7 +387,8 @@ static CURLcode cw_out_do_write(struct cw_out_ctx *ctx,
       return result;
     if(consumed < blen) {
       /* did not write all, append the rest */
-      result = cw_out_append(ctx, otype, buf + consumed, blen - consumed);
+      result = cw_out_append(ctx, data, otype,
+                             buf + consumed, blen - consumed);
       if(result)
         goto out;
     }
@@ -430,44 +438,58 @@ bool Curl_cw_out_is_paused(struct Curl_easy *data)
     return FALSE;
 
   ctx = (struct cw_out_ctx *)cw_out;
-  CURL_TRC_WRITE(data, "cw-out is%spaused", ctx->paused ? "" : " not");
   return ctx->paused;
 }
 
 static CURLcode cw_out_flush(struct Curl_easy *data,
-                             bool unpause, bool flush_all)
+                             struct Curl_cwriter *cw_out,
+                             bool flush_all)
 {
-  struct Curl_cwriter *cw_out;
+  struct cw_out_ctx *ctx = (struct cw_out_ctx *)cw_out;
   CURLcode result = CURLE_OK;
 
-  cw_out = Curl_cwriter_get_by_type(data, &Curl_cwt_out);
-  if(cw_out) {
-    struct cw_out_ctx *ctx = (struct cw_out_ctx *)cw_out;
-    if(ctx->errored)
-      return CURLE_WRITE_ERROR;
-    if(unpause && ctx->paused)
-      ctx->paused = FALSE;
-    if(ctx->paused)
-      return CURLE_OK;  /* not doing it */
+  if(ctx->errored)
+    return CURLE_WRITE_ERROR;
+  if(ctx->paused)
+    return CURLE_OK;  /* not doing it */
 
-    result = cw_out_flush_chain(ctx, data, &ctx->buf, flush_all);
-    if(result) {
-      ctx->errored = TRUE;
-      cw_out_bufs_free(ctx);
-      return result;
-    }
+  result = cw_out_flush_chain(ctx, data, &ctx->buf, flush_all);
+  if(result) {
+    ctx->errored = TRUE;
+    cw_out_bufs_free(ctx);
+    return result;
   }
   return result;
 }
 
 CURLcode Curl_cw_out_unpause(struct Curl_easy *data)
 {
-  CURL_TRC_WRITE(data, "cw-out unpause");
-  return cw_out_flush(data, TRUE, FALSE);
+  struct Curl_cwriter *cw_out;
+  CURLcode result = CURLE_OK;
+
+  cw_out = Curl_cwriter_get_by_type(data, &Curl_cwt_out);
+  if(cw_out) {
+    struct cw_out_ctx *ctx = (struct cw_out_ctx *)cw_out;
+    CURL_TRC_WRITE(data, "[OUT] unpause");
+    ctx->paused = FALSE;
+    result = Curl_cw_pause_flush(data);
+    if(!result)
+      result = cw_out_flush(data, cw_out, FALSE);
+  }
+  return result;
 }
 
 CURLcode Curl_cw_out_done(struct Curl_easy *data)
 {
-  CURL_TRC_WRITE(data, "cw-out done");
-  return cw_out_flush(data, FALSE, TRUE);
+  struct Curl_cwriter *cw_out;
+  CURLcode result = CURLE_OK;
+
+  cw_out = Curl_cwriter_get_by_type(data, &Curl_cwt_out);
+  if(cw_out) {
+    CURL_TRC_WRITE(data, "[OUT] done");
+    result = Curl_cw_pause_flush(data);
+    if(!result)
+      result = cw_out_flush(data, cw_out, TRUE);
+  }
+  return result;
 }
diff --git a/extra/curl/curl-8.12.1/lib/cw-out.h b/extra/curl/curl-8.14.1/lib/cw-out.h
similarity index 97%
rename from extra/curl/curl-8.12.1/lib/cw-out.h
rename to extra/curl/curl-8.14.1/lib/cw-out.h
index ca4c2e435d2a..89b9985bb546 100644
--- a/extra/curl/curl-8.12.1/lib/cw-out.h
+++ b/extra/curl/curl-8.14.1/lib/cw-out.h
@@ -33,7 +33,7 @@
  * the client callbacks. Intended to be the last installed in the
  * client writer stack of a transfer.
  */
-extern struct Curl_cwtype Curl_cwt_out;
+extern const struct Curl_cwtype Curl_cwt_out;
 
 /**
  * Return TRUE iff 'cw-out' client write has paused data.
diff --git a/extra/curl/curl-8.14.1/lib/cw-pause.c b/extra/curl/curl-8.14.1/lib/cw-pause.c
new file mode 100644
index 000000000000..9b9554c55157
--- /dev/null
+++ b/extra/curl/curl-8.14.1/lib/cw-pause.c
@@ -0,0 +1,242 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include <curl/curl.h>
+
+#include "urldata.h"
+#include "bufq.h"
+#include "cfilters.h"
+#include "headers.h"
+#include "multiif.h"
+#include "sendf.h"
+#include "cw-pause.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+
+/* body dynbuf sizes */
+#define CW_PAUSE_BUF_CHUNK         (16 * 1024)
+/* when content decoding, write data in chunks */
+#define CW_PAUSE_DEC_WRITE_CHUNK   (4096)
+
+struct cw_pause_buf {
+  struct cw_pause_buf *next;
+  struct bufq b;
+  int type;
+};
+
+static struct cw_pause_buf *cw_pause_buf_create(int type, size_t buflen)
+{
+  struct cw_pause_buf *cwbuf = calloc(1, sizeof(*cwbuf));
+  if(cwbuf) {
+    cwbuf->type = type;
+    if(type & CLIENTWRITE_BODY)
+      Curl_bufq_init2(&cwbuf->b, CW_PAUSE_BUF_CHUNK, 1,
+                      (BUFQ_OPT_SOFT_LIMIT|BUFQ_OPT_NO_SPARES));
+    else
+      Curl_bufq_init(&cwbuf->b, buflen, 1);
+  }
+  return cwbuf;
+}
+
+static void cw_pause_buf_free(struct cw_pause_buf *cwbuf)
+{
+  if(cwbuf) {
+    Curl_bufq_free(&cwbuf->b);
+    free(cwbuf);
+  }
+}
+
+struct cw_pause_ctx {
+  struct Curl_cwriter super;
+  struct cw_pause_buf *buf;
+  size_t buf_total;
+};
+
+static CURLcode cw_pause_write(struct Curl_easy *data,
+                               struct Curl_cwriter *writer, int type,
+                               const char *buf, size_t nbytes);
+static void cw_pause_close(struct Curl_easy *data,
+                           struct Curl_cwriter *writer);
+static CURLcode cw_pause_init(struct Curl_easy *data,
+                              struct Curl_cwriter *writer);
+
+const struct Curl_cwtype Curl_cwt_pause = {
+  "cw-pause",
+  NULL,
+  cw_pause_init,
+  cw_pause_write,
+  cw_pause_close,
+  sizeof(struct cw_pause_ctx)
+};
+
+static CURLcode cw_pause_init(struct Curl_easy *data,
+                              struct Curl_cwriter *writer)
+{
+  struct cw_pause_ctx *ctx = writer->ctx;
+  (void)data;
+  ctx->buf = NULL;
+  return CURLE_OK;
+}
+
+static void cw_pause_bufs_free(struct cw_pause_ctx *ctx)
+{
+  while(ctx->buf) {
+    struct cw_pause_buf *next = ctx->buf->next;
+    cw_pause_buf_free(ctx->buf);
+    ctx->buf = next;
+  }
+}
+
+static void cw_pause_close(struct Curl_easy *data, struct Curl_cwriter *writer)
+{
+  struct cw_pause_ctx *ctx = writer->ctx;
+
+  (void)data;
+  cw_pause_bufs_free(ctx);
+}
+
+static CURLcode cw_pause_flush(struct Curl_easy *data,
+                               struct Curl_cwriter *cw_pause)
+{
+  struct cw_pause_ctx *ctx = (struct cw_pause_ctx *)cw_pause;
+  bool decoding = Curl_cwriter_is_content_decoding(data);
+  CURLcode result = CURLE_OK;
+
+  /* write the end of the chain until it blocks or gets empty */
+  while(ctx->buf && !Curl_cwriter_is_paused(data)) {
+    struct cw_pause_buf **plast = &ctx->buf;
+    size_t blen, wlen = 0;
+    const unsigned char *buf = NULL;
+
+    while((*plast)->next) /* got to last in list */
+      plast = &(*plast)->next;
+    if(Curl_bufq_peek(&(*plast)->b, &buf, &blen)) {
+      wlen = (decoding && ((*plast)->type & CLIENTWRITE_BODY)) ?
+             CURLMIN(blen, CW_PAUSE_DEC_WRITE_CHUNK) : blen;
+      result = Curl_cwriter_write(data, cw_pause->next, (*plast)->type,
+                                  (const char *)buf, wlen);
+      CURL_TRC_WRITE(data, "[PAUSE] flushed %zu/%zu bytes, type=%x -> %d",
+                     wlen, ctx->buf_total, (*plast)->type, result);
+      Curl_bufq_skip(&(*plast)->b, wlen);
+      DEBUGASSERT(ctx->buf_total >= wlen);
+      ctx->buf_total -= wlen;
+      if(result)
+        return result;
+    }
+    else if((*plast)->type & CLIENTWRITE_EOS) {
+      result = Curl_cwriter_write(data, cw_pause->next, (*plast)->type,
+                                  (const char *)buf, 0);
+      CURL_TRC_WRITE(data, "[PAUSE] flushed 0/%zu bytes, type=%x -> %d",
+                     ctx->buf_total, (*plast)->type, result);
+    }
+
+    if(Curl_bufq_is_empty(&(*plast)->b)) {
+      cw_pause_buf_free(*plast);
+      *plast = NULL;
+    }
+  }
+  return result;
+}
+
+static CURLcode cw_pause_write(struct Curl_easy *data,
+                               struct Curl_cwriter *writer, int type,
+                               const char *buf, size_t blen)
+{
+  struct cw_pause_ctx *ctx = writer->ctx;
+  CURLcode result = CURLE_OK;
+  size_t wlen = 0;
+  bool decoding = Curl_cwriter_is_content_decoding(data);
+
+  if(ctx->buf && !Curl_cwriter_is_paused(data)) {
+    result = cw_pause_flush(data, writer);
+    if(result)
+      return result;
+  }
+
+  while(!ctx->buf && !Curl_cwriter_is_paused(data)) {
+    int wtype = type;
+    DEBUGASSERT(!ctx->buf);
+    /* content decoding might blow up size considerably, write smaller
+     * chunks to make pausing need buffer less. */
+    wlen = (decoding && (type & CLIENTWRITE_BODY)) ?
+           CURLMIN(blen, CW_PAUSE_DEC_WRITE_CHUNK) : blen;
+    if(wlen < blen)
+      wtype &= ~CLIENTWRITE_EOS;
+    result = Curl_cwriter_write(data, writer->next, wtype, buf, wlen);
+    CURL_TRC_WRITE(data, "[PAUSE] writing %zu/%zu bytes of type %x -> %d",
+                   wlen, blen, wtype, result);
+    if(result)
+      return result;
+    buf += wlen;
+    blen -= wlen;
+    if(!blen)
+      return result;
+  }
+
+  do {
+    size_t nwritten = 0;
+    if(ctx->buf && (ctx->buf->type == type) && (type & CLIENTWRITE_BODY)) {
+      /* same type and body, append to current buffer which has a soft
+       * limit and should take everything up to OOM. */
+      result = Curl_bufq_cwrite(&ctx->buf->b, buf, blen, &nwritten);
+    }
+    else {
+      /* Need a new buf, type changed */
+      struct cw_pause_buf *cwbuf = cw_pause_buf_create(type, blen);
+      if(!cwbuf)
+        return CURLE_OUT_OF_MEMORY;
+      cwbuf->next = ctx->buf;
+      ctx->buf = cwbuf;
+      result = Curl_bufq_cwrite(&ctx->buf->b, buf, blen, &nwritten);
+    }
+    CURL_TRC_WRITE(data, "[PAUSE] buffer %zu more bytes of type %x, "
+                   "total=%zu -> %d", nwritten, type, ctx->buf_total + wlen,
+                   result);
+    if(result)
+      return result;
+    buf += nwritten;
+    blen -= nwritten;
+    ctx->buf_total += nwritten;
+  } while(blen);
+
+  return result;
+}
+
+CURLcode Curl_cw_pause_flush(struct Curl_easy *data)
+{
+  struct Curl_cwriter *cw_pause;
+  CURLcode result = CURLE_OK;
+
+  cw_pause = Curl_cwriter_get_by_type(data, &Curl_cwt_pause);
+  if(cw_pause)
+    result = cw_pause_flush(data, cw_pause);
+
+  return result;
+}
diff --git a/extra/curl/curl-8.14.1/lib/cw-pause.h b/extra/curl/curl-8.14.1/lib/cw-pause.h
new file mode 100644
index 000000000000..2aa1a499cddd
--- /dev/null
+++ b/extra/curl/curl-8.14.1/lib/cw-pause.h
@@ -0,0 +1,40 @@
+#ifndef HEADER_CURL_CW_PAUSE_H
+#define HEADER_CURL_CW_PAUSE_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include "sendf.h"
+
+/**
+ * The client writer type "cw-pause" that buffers writes for
+ * paused transfer writes.
+ */
+extern const struct Curl_cwtype Curl_cwt_pause;
+
+CURLcode Curl_cw_pause_flush(struct Curl_easy *data);
+
+
+#endif /* HEADER_CURL_CW_PAUSE_H */
diff --git a/extra/curl/curl-8.12.1/lib/dict.c b/extra/curl/curl-8.14.1/lib/dict.c
similarity index 91%
rename from extra/curl/curl-8.12.1/lib/dict.c
rename to extra/curl/curl-8.14.1/lib/dict.c
index 143b76f51974..637f349e7007 100644
--- a/extra/curl/curl-8.12.1/lib/dict.c
+++ b/extra/curl/curl-8.14.1/lib/dict.c
@@ -65,6 +65,15 @@
 /* The last #include file should be: */
 #include "memdebug.h"
 
+
+#define DICT_MATCH "/MATCH:"
+#define DICT_MATCH2 "/M:"
+#define DICT_MATCH3 "/FIND:"
+#define DICT_DEFINE "/DEFINE:"
+#define DICT_DEFINE2 "/D:"
+#define DICT_DEFINE3 "/LOOKUP:"
+
+
 /*
  * Forward declarations.
  */
@@ -106,7 +115,7 @@ static char *unescape_word(const char *input)
   struct dynbuf out;
   const char *ptr;
   CURLcode result = CURLE_OK;
-  Curl_dyn_init(&out, DYN_DICT_WORD);
+  curlx_dyn_init(&out, DYN_DICT_WORD);
 
   /* According to RFC2229 section 2.2, these letters need to be escaped with
      \[letter] */
@@ -114,13 +123,13 @@ static char *unescape_word(const char *input)
     char ch = *ptr;
     if((ch <= 32) || (ch == 127) ||
        (ch == '\'') || (ch == '\"') || (ch == '\\'))
-      result = Curl_dyn_addn(&out, "\\", 1);
+      result = curlx_dyn_addn(&out, "\\", 1);
     if(!result)
-      result = Curl_dyn_addn(&out, ptr, 1);
+      result = curlx_dyn_addn(&out, ptr, 1);
     if(result)
       return NULL;
   }
-  return Curl_dyn_ptr(&out);
+  return curlx_dyn_ptr(&out);
 }
 
 /* sendf() sends formatted data to the server */
@@ -212,16 +221,8 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done)
 
     if(!word || (*word == (char)0)) {
       infof(data, "lookup word is missing");
-      word = (char *)"default";
-    }
-    if(!database || (*database == (char)0)) {
-      database = (char *)"!";
     }
-    if(!strategy || (*strategy == (char)0)) {
-      strategy = (char *)".";
-    }
-
-    eword = unescape_word(word);
+    eword = unescape_word((!word || (*word == (char)0)) ? "default" : word);
     if(!eword) {
       result = CURLE_OUT_OF_MEMORY;
       goto error;
@@ -234,8 +235,8 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done)
                    "%s "    /* strategy */
                    "%s\r\n" /* word */
                    "QUIT\r\n",
-                   database,
-                   strategy,
+                   (!database || (*database == (char)0)) ? "!" : database,
+                   (!strategy || (*strategy == (char)0)) ? "." : strategy,
                    eword);
 
     if(result) {
@@ -263,13 +264,8 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done)
 
     if(!word || (*word == (char)0)) {
       infof(data, "lookup word is missing");
-      word = (char *)"default";
     }
-    if(!database || (*database == (char)0)) {
-      database = (char *)"!";
-    }
-
-    eword = unescape_word(word);
+    eword = unescape_word((!word || (*word == (char)0)) ? "default" : word);
     if(!eword) {
       result = CURLE_OUT_OF_MEMORY;
       goto error;
@@ -281,7 +277,7 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done)
                    "%s "     /* database */
                    "%s\r\n"  /* word */
                    "QUIT\r\n",
-                   database,
+                   (!database || (*database == (char)0)) ? "!" : database,
                    eword);
 
     if(result) {
diff --git a/extra/curl/curl-8.12.1/lib/dict.h b/extra/curl/curl-8.14.1/lib/dict.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/dict.h
rename to extra/curl/curl-8.14.1/lib/dict.h
diff --git a/extra/curl/curl-8.12.1/lib/dllmain.c b/extra/curl/curl-8.14.1/lib/dllmain.c
similarity index 88%
rename from extra/curl/curl-8.12.1/lib/dllmain.c
rename to extra/curl/curl-8.14.1/lib/dllmain.c
index 41e97b37eb5e..33076e057145 100644
--- a/extra/curl/curl-8.12.1/lib/dllmain.c
+++ b/extra/curl/curl-8.14.1/lib/dllmain.c
@@ -28,22 +28,13 @@
 #include <openssl/crypto.h>
 #endif
 
-/* The fourth-to-last include */
-#ifdef __CYGWIN__
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#ifdef _WIN32
-#undef _WIN32
-#endif
-#endif
-
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
 #include "memdebug.h"
 
-/* DllMain() must only be defined for Windows and Cygwin DLL builds. */
-#if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(CURL_STATICLIB)
+/* DllMain() must only be defined for Windows DLL builds. */
+#if defined(_WIN32) && !defined(CURL_STATICLIB)
 
 #if defined(USE_OPENSSL) && \
     !defined(OPENSSL_IS_AWSLC) && \
diff --git a/extra/curl/curl-8.12.1/lib/doh.c b/extra/curl/curl-8.14.1/lib/doh.c
similarity index 76%
rename from extra/curl/curl-8.12.1/lib/doh.c
rename to extra/curl/curl-8.14.1/lib/doh.c
index 617a761f9754..9f408402a038 100644
--- a/extra/curl/curl-8.12.1/lib/doh.c
+++ b/extra/curl/curl-8.14.1/lib/doh.c
@@ -34,21 +34,20 @@
 #include "multiif.h"
 #include "url.h"
 #include "share.h"
-#include "curl_base64.h"
+#include "curlx/base64.h"
 #include "connect.h"
 #include "strdup.h"
-#include "dynbuf.h"
+#include "curlx/dynbuf.h"
+#include "escape.h"
+#include "urlapi-int.h"
+
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
 #include "memdebug.h"
-#include "escape.h"
 
 #define DNS_CLASS_IN 0x01
 
-/* doh_print_buf truncates if the hex string will be more than this */
-#define LOCAL_PB_HEXMAX 400
-
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
 static const char * const errors[]={
   "",
@@ -74,10 +73,6 @@ static const char *doh_strerror(DOHcode code)
   return "bad error code";
 }
 
-struct curl_trc_feat Curl_doh_trc = {
-  "DoH",
-  CURL_LOG_LVL_NONE,
-};
 #endif /* !CURL_DISABLE_VERBOSE_STRINGS */
 
 /* @unittest 1655
@@ -120,7 +115,7 @@ UNITTEST DOHcode doh_req_encode(const char *host,
   if(host[hostlen-1]!='.')
     expected_len++;
 
-  if(expected_len > (256 + 16)) /* RFCs 1034, 1035 */
+  if(expected_len > DOH_MAX_DNSREQ_SIZE)
     return DOH_DNS_NAME_TOO_LONG;
 
   if(len < expected_len)
@@ -180,18 +175,25 @@ UNITTEST DOHcode doh_req_encode(const char *host,
 }
 
 static size_t
-doh_write_cb(char *contents, size_t size, size_t nmemb, void *userp)
+doh_probe_write_cb(char *contents, size_t size, size_t nmemb, void *userp)
 {
   size_t realsize = size * nmemb;
-  struct dynbuf *mem = (struct dynbuf *)userp;
+  struct Curl_easy *data = userp;
+  struct doh_request *doh_req = Curl_meta_get(data, CURL_EZM_DOH_PROBE);
+  if(!doh_req)
+    return CURL_WRITEFUNC_ERROR;
 
-  if(Curl_dyn_addn(mem, contents, realsize))
+  if(curlx_dyn_addn(&doh_req->resp_body, contents, realsize))
     return 0;
 
   return realsize;
 }
 
 #if defined(USE_HTTPSRR) && defined(DEBUGBUILD)
+
+/* doh_print_buf truncates if the hex string will be more than this */
+#define LOCAL_PB_HEXMAX 400
+
 static void doh_print_buf(struct Curl_easy *data,
                           const char *prefix,
                           unsigned char *buf, size_t len)
@@ -211,22 +213,45 @@ static void doh_print_buf(struct Curl_easy *data,
 }
 #endif
 
-/* called from multi.c when this DoH transfer is complete */
-static int doh_done(struct Curl_easy *doh, CURLcode result)
+/* called from multi when a sub transfer, e.g. doh probe, is done.
+ * This looks up the the probe response at its meta CURL_EZM_DOH_PROBE
+ * and copies the response body over to the struct at the master's
+ * meta at CURL_EZM_DOH_MASTER. */
+static void doh_probe_done(struct Curl_easy *data,
+                           struct Curl_easy *doh, CURLcode result)
 {
-  struct Curl_easy *data; /* the transfer that asked for the DoH probe */
+  struct doh_probes *dohp = data->state.async.doh;
+  DEBUGASSERT(dohp);
+  if(dohp) {
+    struct doh_request *doh_req = Curl_meta_get(doh, CURL_EZM_DOH_PROBE);
+    int i;
+
+    for(i = 0; i < DOH_SLOT_COUNT; ++i) {
+      if(dohp->probe_resp[i].probe_mid == doh->mid)
+        break;
+    }
+    if(i >= DOH_SLOT_COUNT) {
+      failf(data, "unknown sub request done");
+      return;
+    }
 
-  data = Curl_multi_get_handle(doh->multi, doh->set.dohfor_mid);
-  if(!data) {
-    DEBUGF(infof(doh, "doh_done: xfer for mid=%" FMT_OFF_T
-                 " not found", doh->set.dohfor_mid));
-    DEBUGASSERT(0);
-  }
-  else {
-    struct doh_probes *dohp = data->req.doh;
-    /* one of the DoH request done for the 'data' transfer is now complete! */
     dohp->pending--;
     infof(doh, "a DoH request is completed, %u to go", dohp->pending);
+    dohp->probe_resp[i].result = result;
+    /* We expect either the meta data still to exist or the sub request
+     * to have already failed. */
+    DEBUGASSERT(doh_req || result);
+    if(doh_req) {
+      if(!result) {
+        dohp->probe_resp[i].dnstype = doh_req->dnstype;
+        result = curlx_dyn_addn(&dohp->probe_resp[i].body,
+                                curlx_dyn_ptr(&doh_req->resp_body),
+                                curlx_dyn_len(&doh_req->resp_body));
+        curlx_dyn_free(&doh_req->resp_body);
+      }
+      Curl_meta_remove(doh, CURL_EZM_DOH_PROBE);
+    }
+
     if(result)
       infof(doh, "DoH request %s", curl_easy_strerror(result));
 
@@ -235,7 +260,18 @@ static int doh_done(struct Curl_easy *doh, CURLcode result)
       Curl_expire(data, 0, EXPIRE_RUN_NOW);
     }
   }
-  return 0;
+}
+
+static void doh_probe_dtor(void *key, size_t klen, void *e)
+{
+  (void)key;
+  (void)klen;
+  if(e) {
+    struct doh_request *doh_req = e;
+    curl_slist_free_all(doh_req->req_hds);
+    curlx_dyn_free(&doh_req->resp_body);
+    free(e);
+  }
 }
 
 #define ERROR_CHECK_SETOPT(x,y)                         \
@@ -247,30 +283,48 @@ static int doh_done(struct Curl_easy *doh, CURLcode result)
       goto error;                                       \
   } while(0)
 
-static CURLcode doh_run_probe(struct Curl_easy *data,
-                              struct doh_probe *p, DNStype dnstype,
+static CURLcode doh_probe_run(struct Curl_easy *data,
+                              DNStype dnstype,
                               const char *host,
                               const char *url, CURLM *multi,
-                              struct curl_slist *headers)
+                              unsigned int *pmid)
 {
   struct Curl_easy *doh = NULL;
   CURLcode result = CURLE_OK;
   timediff_t timeout_ms;
-  DOHcode d = doh_req_encode(host, dnstype, p->req_body, sizeof(p->req_body),
-                             &p->req_body_len);
+  struct doh_request *doh_req;
+  DOHcode d;
+
+  *pmid = UINT_MAX;
+
+  doh_req = calloc(1, sizeof(*doh_req));
+  if(!doh_req)
+    return CURLE_OUT_OF_MEMORY;
+  doh_req->dnstype = dnstype;
+  curlx_dyn_init(&doh_req->resp_body, DYN_DOH_RESPONSE);
+
+  d = doh_req_encode(host, dnstype, doh_req->req_body,
+                     sizeof(doh_req->req_body),
+                     &doh_req->req_body_len);
   if(d) {
     failf(data, "Failed to encode DoH packet [%d]", d);
-    return CURLE_OUT_OF_MEMORY;
+    result = CURLE_OUT_OF_MEMORY;
+    goto error;
   }
 
-  p->dnstype = dnstype;
-  Curl_dyn_init(&p->resp_body, DYN_DOH_RESPONSE);
-
   timeout_ms = Curl_timeleft(data, NULL, TRUE);
   if(timeout_ms <= 0) {
     result = CURLE_OPERATION_TIMEDOUT;
     goto error;
   }
+
+  doh_req->req_hds =
+    curl_slist_append(NULL, "Content-Type: application/dns-message");
+  if(!doh_req->req_hds) {
+    result = CURLE_OUT_OF_MEMORY;
+    goto error;
+  }
+
   /* Curl_open() is the internal version of curl_easy_init() */
   result = Curl_open(&doh);
   if(result)
@@ -278,33 +332,32 @@ static CURLcode doh_run_probe(struct Curl_easy *data,
 
   /* pass in the struct pointer via a local variable to please coverity and
      the gcc typecheck helpers */
-  doh->state.internal = TRUE;
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
-  doh->state.feat = &Curl_doh_trc;
+  doh->state.feat = &Curl_trc_feat_dns;
 #endif
   ERROR_CHECK_SETOPT(CURLOPT_URL, url);
   ERROR_CHECK_SETOPT(CURLOPT_DEFAULT_PROTOCOL, "https");
-  ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_write_cb);
-  ERROR_CHECK_SETOPT(CURLOPT_WRITEDATA, &p->resp_body);
-  ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDS, p->req_body);
-  ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDSIZE, (long)p->req_body_len);
-  ERROR_CHECK_SETOPT(CURLOPT_HTTPHEADER, headers);
+  ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_probe_write_cb);
+  ERROR_CHECK_SETOPT(CURLOPT_WRITEDATA, doh);
+  ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDS, doh_req->req_body);
+  ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDSIZE, (long)doh_req->req_body_len);
+  ERROR_CHECK_SETOPT(CURLOPT_HTTPHEADER, doh_req->req_hds);
 #ifdef USE_HTTP2
   ERROR_CHECK_SETOPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
   ERROR_CHECK_SETOPT(CURLOPT_PIPEWAIT, 1L);
 #endif
 #ifndef DEBUGBUILD
   /* enforce HTTPS if not debug */
-  ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
+  ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, (long)CURLPROTO_HTTPS);
 #else
   /* in debug mode, also allow http */
-  ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS);
+  ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, (long)CURLPROTO_HTTP|CURLPROTO_HTTPS);
 #endif
   ERROR_CHECK_SETOPT(CURLOPT_TIMEOUT_MS, (long)timeout_ms);
   ERROR_CHECK_SETOPT(CURLOPT_SHARE, (CURLSH *)data->share);
   if(data->set.err && data->set.err != stderr)
     ERROR_CHECK_SETOPT(CURLOPT_STDERR, data->set.err);
-  if(Curl_trc_ft_is_verbose(data, &Curl_doh_trc))
+  if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_dns))
     ERROR_CHECK_SETOPT(CURLOPT_VERBOSE, 1L);
   if(data->set.no_signal)
     ERROR_CHECK_SETOPT(CURLOPT_NOSIGNAL, 1L);
@@ -357,26 +410,16 @@ static CURLcode doh_run_probe(struct Curl_easy *data,
                        data->set.str[STRING_SSL_EC_CURVES]);
   }
 
-  {
-    long mask =
-      (data->set.ssl.enable_beast ?
-       CURLSSLOPT_ALLOW_BEAST : 0) |
-      (data->set.ssl.no_revoke ?
-       CURLSSLOPT_NO_REVOKE : 0) |
-      (data->set.ssl.no_partialchain ?
-       CURLSSLOPT_NO_PARTIALCHAIN : 0) |
-      (data->set.ssl.revoke_best_effort ?
-       CURLSSLOPT_REVOKE_BEST_EFFORT : 0) |
-      (data->set.ssl.native_ca_store ?
-       CURLSSLOPT_NATIVE_CA : 0) |
-      (data->set.ssl.auto_client_cert ?
-       CURLSSLOPT_AUTO_CLIENT_CERT : 0);
-
-    (void)curl_easy_setopt(doh, CURLOPT_SSL_OPTIONS, mask);
-  }
+  (void)curl_easy_setopt(doh, CURLOPT_SSL_OPTIONS,
+                         (long)data->set.ssl.primary.ssl_options);
+
+  doh->state.internal = TRUE;
+  doh->master_mid = data->mid; /* master transfer of this one */
 
-  doh->set.fmultidone = doh_done;
-  doh->set.dohfor_mid = data->mid; /* for which transfer this is done */
+  result = Curl_meta_set(doh, CURL_EZM_DOH_PROBE, doh_req, doh_probe_dtor);
+  doh_req = NULL; /* call took ownership */
+  if(result)
+    goto error;
 
   /* DoH handles must not inherit private_data. The handles may be passed to
      the user via callbacks and the user will be able to identify them as
@@ -387,12 +430,13 @@ static CURLcode doh_run_probe(struct Curl_easy *data,
   if(curl_multi_add_handle(multi, doh))
     goto error;
 
-  p->easy_mid = doh->mid;
+  *pmid = doh->mid;
   return CURLE_OK;
 
 error:
   Curl_close(&doh);
-  p->easy_mid = -1;
+  if(doh_req)
+    doh_probe_dtor(NULL, 0, doh_req);
   return result;
 }
 
@@ -404,51 +448,59 @@ static CURLcode doh_run_probe(struct Curl_easy *data,
 struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
                                const char *hostname,
                                int port,
+                               int ip_version,
                                int *waitp)
 {
   CURLcode result = CURLE_OK;
-  struct doh_probes *dohp;
+  struct doh_probes *dohp = NULL;
   struct connectdata *conn = data->conn;
   size_t i;
-  *waitp = FALSE;
-  (void)hostname;
-  (void)port;
 
-  DEBUGASSERT(!data->req.doh);
   DEBUGASSERT(conn);
+  DEBUGASSERT(!data->state.async.doh);
+  if(data->state.async.doh)
+    Curl_doh_cleanup(data);
+
+  data->state.async.done = FALSE;
+  data->state.async.port = port;
+  data->state.async.ip_version = ip_version;
+  data->state.async.hostname = strdup(hostname);
+  if(!data->state.async.hostname)
+    return NULL;
 
   /* start clean, consider allocating this struct on demand */
-  dohp = data->req.doh = calloc(1, sizeof(struct doh_probes));
+  data->state.async.doh = dohp = calloc(1, sizeof(struct doh_probes));
   if(!dohp)
     return NULL;
 
   for(i = 0; i < DOH_SLOT_COUNT; ++i) {
-    dohp->probe[i].easy_mid = -1;
+    dohp->probe_resp[i].probe_mid = UINT_MAX;
+    curlx_dyn_init(&dohp->probe_resp[i].body, DYN_DOH_RESPONSE);
   }
 
   conn->bits.doh = TRUE;
-  dohp->host = hostname;
-  dohp->port = port;
-  dohp->req_hds =
-    curl_slist_append(NULL,
-                      "Content-Type: application/dns-message");
-  if(!dohp->req_hds)
-    goto error;
+  dohp->host = data->state.async.hostname;
+  dohp->port = data->state.async.port;
+  /* We are making sub easy handles and want to be called back when
+   * one is done. */
+  data->sub_xfer_done = doh_probe_done;
 
   /* create IPv4 DoH request */
-  result = doh_run_probe(data, &dohp->probe[DOH_SLOT_IPV4],
-                         DNS_TYPE_A, hostname, data->set.str[STRING_DOH],
-                         data->multi, dohp->req_hds);
+  result = doh_probe_run(data, DNS_TYPE_A,
+                         hostname, data->set.str[STRING_DOH],
+                         data->multi,
+                         &dohp->probe_resp[DOH_SLOT_IPV4].probe_mid);
   if(result)
     goto error;
   dohp->pending++;
 
 #ifdef USE_IPV6
-  if((conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
+  if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
     /* create IPv6 DoH request */
-    result = doh_run_probe(data, &dohp->probe[DOH_SLOT_IPV6],
-                           DNS_TYPE_AAAA, hostname, data->set.str[STRING_DOH],
-                           data->multi, dohp->req_hds);
+    result = doh_probe_run(data, DNS_TYPE_AAAA,
+                           hostname, data->set.str[STRING_DOH],
+                           data->multi,
+                           &dohp->probe_resp[DOH_SLOT_IPV6].probe_mid);
     if(result)
       goto error;
     dohp->pending++;
@@ -464,10 +516,10 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
       if(!qname)
         goto error;
     }
-    result = doh_run_probe(data, &dohp->probe[DOH_SLOT_HTTPS_RR],
-                           DNS_TYPE_HTTPS,
+    result = doh_probe_run(data, DNS_TYPE_HTTPS,
                            qname ? qname : hostname, data->set.str[STRING_DOH],
-                           data->multi, dohp->req_hds);
+                           data->multi,
+                           &dohp->probe_resp[DOH_SLOT_HTTPS_RR].probe_mid);
     free(qname);
     if(result)
       goto error;
@@ -598,14 +650,14 @@ static DOHcode doh_store_cname(const unsigned char *doh, size_t dohlen,
       index++;
 
     if(length) {
-      if(Curl_dyn_len(c)) {
-        if(Curl_dyn_addn(c, STRCONST(".")))
+      if(curlx_dyn_len(c)) {
+        if(curlx_dyn_addn(c, STRCONST(".")))
           return DOH_OUT_OF_MEM;
       }
       if((index + length) > dohlen)
         return DOH_DNS_BAD_LABEL;
 
-      if(Curl_dyn_addn(c, &doh[index], length))
+      if(curlx_dyn_addn(c, &doh[index], length))
         return DOH_OUT_OF_MEM;
       index += length;
     }
@@ -669,7 +721,7 @@ UNITTEST void de_init(struct dohentry *de)
   memset(de, 0, sizeof(*de));
   de->ttl = INT_MAX;
   for(i = 0; i < DOH_MAX_CNAME; i++)
-    Curl_dyn_init(&de->cname[i], DYN_DOH_CNAME);
+    curlx_dyn_init(&de->cname[i], DYN_DOH_CNAME);
 }
 
 
@@ -856,7 +908,7 @@ static void doh_show(struct Curl_easy *data,
   }
 #endif
   for(i = 0; i < d->numcname; i++) {
-    infof(data, "CNAME: %s", Curl_dyn_ptr(&d->cname[i]));
+    infof(data, "CNAME: %s", curlx_dyn_ptr(&d->cname[i]));
   }
 }
 #else
@@ -869,7 +921,7 @@ static void doh_show(struct Curl_easy *data,
  * This function returns a pointer to the first element of a newly allocated
  * Curl_addrinfo struct linked list filled with the data from a set of DoH
  * lookups. Curl_addrinfo is meant to work like the addrinfo struct does for
- * a IPv6 stack, but usable also for IPv4, all hosts and environments.
+ * an IPv6 stack, but usable also for IPv4, all hosts and environments.
  *
  * The memory allocated by this function *MUST* be free'd later on calling
  * Curl_freeaddrinfo(). For each successful call to this function there
@@ -993,7 +1045,7 @@ UNITTEST void de_cleanup(struct dohentry *d)
 {
   int i = 0;
   for(i = 0; i < d->numcname; i++) {
-    Curl_dyn_free(&d->cname[i]);
+    curlx_dyn_free(&d->cname[i]);
   }
 #ifdef USE_HTTPSRR
   for(i = 0; i < d->numhttps_rrs; i++)
@@ -1017,87 +1069,66 @@ UNITTEST void de_cleanup(struct dohentry *d)
  * just after the end of the DNS name encoding on output. (And
  * that is why it is an "unsigned char **" :-)
  */
-static CURLcode doh_decode_rdata_name(unsigned char **buf, size_t *remaining,
-                                      char **dnsname)
+static CURLcode doh_decode_rdata_name(const unsigned char **buf,
+                                      size_t *remaining, char **dnsname)
 {
-  unsigned char *cp = NULL;
-  int rem = 0;
+  const unsigned char *cp = NULL;
+  size_t rem = 0;
   unsigned char clen = 0; /* chunk len */
   struct dynbuf thename;
 
   DEBUGASSERT(buf && remaining && dnsname);
-  if(!buf || !remaining || !dnsname)
-    return CURLE_OUT_OF_MEMORY;
-  rem = (int)*remaining;
-  if(rem <= 0) {
-    Curl_dyn_free(&thename);
+  if(!buf || !remaining || !dnsname || !*remaining)
     return CURLE_OUT_OF_MEMORY;
-  }
-  Curl_dyn_init(&thename, CURL_MAXLEN_host_name);
+  curlx_dyn_init(&thename, CURL_MAXLEN_host_name);
+  rem = *remaining;
   cp = *buf;
   clen = *cp++;
   if(clen == 0) {
     /* special case - return "." as name */
-    if(Curl_dyn_addn(&thename, ".", 1))
+    if(curlx_dyn_addn(&thename, ".", 1))
       return CURLE_OUT_OF_MEMORY;
   }
   while(clen) {
     if(clen >= rem) {
-      Curl_dyn_free(&thename);
+      curlx_dyn_free(&thename);
       return CURLE_OUT_OF_MEMORY;
     }
-    if(Curl_dyn_addn(&thename, cp, clen) ||
-       Curl_dyn_addn(&thename, ".", 1))
+    if(curlx_dyn_addn(&thename, cp, clen) ||
+       curlx_dyn_addn(&thename, ".", 1))
       return CURLE_TOO_LARGE;
 
     cp += clen;
     rem -= (clen + 1);
     if(rem <= 0) {
-      Curl_dyn_free(&thename);
+      curlx_dyn_free(&thename);
       return CURLE_OUT_OF_MEMORY;
     }
     clen = *cp++;
   }
   *buf = cp;
   *remaining = rem - 1;
-  *dnsname = Curl_dyn_ptr(&thename);
+  *dnsname = curlx_dyn_ptr(&thename);
   return CURLE_OK;
 }
 
-#ifdef DEBUGBUILD
-static CURLcode doh_test_alpn_escapes(void)
-{
-  /* we will use an example from draft-ietf-dnsop-svcb, figure 10 */
-  static unsigned char example[] = {
-    0x08,                                           /* length 8 */
-    0x66, 0x5c, 0x6f, 0x6f, 0x2c, 0x62, 0x61, 0x72, /* value "f\\oo,bar" */
-    0x02,                                           /* length 2 */
-    0x68, 0x32                                      /* value "h2" */
-  };
-  size_t example_len = sizeof(example);
-  unsigned char aval[MAX_HTTPSRR_ALPNS] = { 0 };
-  static const char expected[2] = { ALPN_h2, ALPN_none };
-
-  if(Curl_httpsrr_decode_alpn(example, example_len, aval) != CURLE_OK)
-    return CURLE_BAD_CONTENT_ENCODING;
-  if(memcmp(aval, expected, sizeof(expected)))
-    return CURLE_BAD_CONTENT_ENCODING;
-  return CURLE_OK;
-}
-#endif
+UNITTEST CURLcode doh_resp_decode_httpsrr(struct Curl_easy *data,
+                                          const unsigned char *cp, size_t len,
+                                          struct Curl_https_rrinfo **hrr);
 
-static CURLcode doh_resp_decode_httpsrr(unsigned char *cp, size_t len,
-                                        struct Curl_https_rrinfo **hrr)
+/* @unittest 1658 */
+UNITTEST CURLcode doh_resp_decode_httpsrr(struct Curl_easy *data,
+                                          const unsigned char *cp, size_t len,
+                                          struct Curl_https_rrinfo **hrr)
 {
   uint16_t pcode = 0, plen = 0;
+  uint32_t expected_min_pcode = 0;
   struct Curl_https_rrinfo *lhrr = NULL;
   char *dnsname = NULL;
+  CURLcode result = CURLE_OUT_OF_MEMORY;
+  size_t olen;
 
-#ifdef DEBUGBUILD
-  /* a few tests of escaping, should not be here but ok for now */
-  if(doh_test_alpn_escapes() != CURLE_OK)
-    return CURLE_OUT_OF_MEMORY;
-#endif
+  *hrr = NULL;
   if(len <= 2)
     return CURLE_BAD_FUNCTION_ARGUMENT;
   lhrr = calloc(1, sizeof(struct Curl_https_rrinfo));
@@ -1109,68 +1140,43 @@ static CURLcode doh_resp_decode_httpsrr(unsigned char *cp, size_t len,
   if(doh_decode_rdata_name(&cp, &len, &dnsname) != CURLE_OK)
     goto err;
   lhrr->target = dnsname;
+  if(Curl_junkscan(dnsname, &olen, FALSE)) {
+    /* unacceptable hostname content */
+    result = CURLE_WEIRD_SERVER_REPLY;
+    goto err;
+  }
   lhrr->port = -1; /* until set */
   while(len >= 4) {
     pcode = doh_get16bit(cp, 0);
     plen = doh_get16bit(cp, 2);
     cp += 4;
     len -= 4;
-    switch(pcode) {
-    case HTTPS_RR_CODE_ALPN:
-      if(Curl_httpsrr_decode_alpn(cp, plen, lhrr->alpns) != CURLE_OK)
-        goto err;
-      break;
-    case HTTPS_RR_CODE_NO_DEF_ALPN:
-      lhrr->no_def_alpn = TRUE;
-      break;
-    case HTTPS_RR_CODE_IPV4:
-      if(!plen)
-        goto err;
-      lhrr->ipv4hints = Curl_memdup(cp, plen);
-      if(!lhrr->ipv4hints)
-        goto err;
-      lhrr->ipv4hints_len = (size_t)plen;
-      break;
-    case HTTPS_RR_CODE_ECH:
-      if(!plen)
-        goto err;
-      lhrr->echconfiglist = Curl_memdup(cp, plen);
-      if(!lhrr->echconfiglist)
-        goto err;
-      lhrr->echconfiglist_len = (size_t)plen;
-      break;
-    case HTTPS_RR_CODE_IPV6:
-      if(!plen)
-        goto err;
-      lhrr->ipv6hints = Curl_memdup(cp, plen);
-      if(!lhrr->ipv6hints)
-        goto err;
-      lhrr->ipv6hints_len = (size_t)plen;
-      break;
-    case HTTPS_RR_CODE_PORT:
-      lhrr->port = doh_get16bit(cp, 0);
-      break;
-    default:
-      break;
-    }
-    if(plen > 0 && plen <= len) {
-      cp += plen;
-      len -= plen;
+    if(pcode < expected_min_pcode || plen > len) {
+      result = CURLE_WEIRD_SERVER_REPLY;
+      goto err;
     }
+    result = Curl_httpsrr_set(data, lhrr, pcode, cp, plen);
+    if(result)
+      goto err;
+    cp += plen;
+    len -= plen;
+    expected_min_pcode = pcode + 1;
   }
   DEBUGASSERT(!len);
   *hrr = lhrr;
   return CURLE_OK;
 err:
-  Curl_safefree(lhrr->target);
-  Curl_safefree(lhrr->echconfiglist);
+  Curl_httpsrr_cleanup(lhrr);
   Curl_safefree(lhrr);
-  return CURLE_OUT_OF_MEMORY;
+  return result;
 }
 
-# ifdef DEBUGBUILD
-static void doh_print_httpsrr(struct Curl_easy *data,
-                              struct Curl_https_rrinfo *hrr)
+#ifdef DEBUGBUILD
+UNITTEST void doh_print_httpsrr(struct Curl_easy *data,
+                                struct Curl_https_rrinfo *hrr);
+
+UNITTEST void doh_print_httpsrr(struct Curl_easy *data,
+                                struct Curl_https_rrinfo *hrr)
 {
   DEBUGASSERT(hrr);
   infof(data, "HTTPS RR: priority %d, target: %s",
@@ -1211,14 +1217,14 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
                               struct Curl_dns_entry **dnsp)
 {
   CURLcode result;
-  struct doh_probes *dohp = data->req.doh;
+  struct doh_probes *dohp = data->state.async.doh;
   *dnsp = NULL; /* defaults to no response */
   if(!dohp)
     return CURLE_OUT_OF_MEMORY;
 
-  if(dohp->probe[DOH_SLOT_IPV4].easy_mid < 0 &&
-     dohp->probe[DOH_SLOT_IPV6].easy_mid < 0) {
-    failf(data, "Could not DoH-resolve: %s", data->state.async.hostname);
+  if(dohp->probe_resp[DOH_SLOT_IPV4].probe_mid == UINT_MAX &&
+     dohp->probe_resp[DOH_SLOT_IPV6].probe_mid == UINT_MAX) {
+    failf(data, "Could not DoH-resolve: %s", dohp->host);
     return CONN_IS_PROXIED(data->conn) ? CURLE_COULDNT_RESOLVE_PROXY :
       CURLE_COULDNT_RESOLVE_HOST;
   }
@@ -1227,19 +1233,21 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
     struct dohentry de;
     int slot;
 
+    /* Clear any result the might still be there */
+    Curl_resolv_unlink(data, &data->state.async.dns);
+
     memset(rc, 0, sizeof(rc));
     /* remove DoH handles from multi handle and close them */
     Curl_doh_close(data);
     /* parse the responses, create the struct and return it! */
     de_init(&de);
     for(slot = 0; slot < DOH_SLOT_COUNT; slot++) {
-      struct doh_probe *p = &dohp->probe[slot];
+      struct doh_response *p = &dohp->probe_resp[slot];
       if(!p->dnstype)
         continue;
-      rc[slot] = doh_resp_decode(Curl_dyn_uptr(&p->resp_body),
-                                 Curl_dyn_len(&p->resp_body),
+      rc[slot] = doh_resp_decode(curlx_dyn_uptr(&p->body),
+                                 curlx_dyn_len(&p->body),
                                  p->dnstype, &de);
-      Curl_dyn_free(&p->resp_body);
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
       if(rc[slot]) {
         infof(data, "DoH: %s type %s for %s", doh_strerror(rc[slot]),
@@ -1255,8 +1263,8 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
       struct Curl_addrinfo *ai;
 
 
-      if(Curl_trc_ft_is_verbose(data, &Curl_doh_trc)) {
-        infof(data, "[DoH] hostname: %s", dohp->host);
+      if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_dns)) {
+        CURL_TRC_DNS(data, "hostname: %s", dohp->host);
         doh_show(data, &de);
       }
 
@@ -1266,45 +1274,35 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
         return result;
       }
 
-      if(data->share)
-        Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
-
-      /* we got a response, store it in the cache */
-      dns = Curl_cache_addr(data, ai, dohp->host, 0, dohp->port, FALSE);
-
-      if(data->share)
-        Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
-
-      if(!dns) {
-        /* returned failure, bail out nicely */
-        Curl_freeaddrinfo(ai);
-      }
-      else {
-        data->state.async.dns = dns;
-        *dnsp = dns;
-        result = CURLE_OK;      /* address resolution OK */
-      }
-    } /* address processing done */
-
-    /* Now process any build-specific attributes retrieved from DNS */
+      /* we got a response, create a dns entry. */
+      dns = Curl_dnscache_mk_entry(data, ai, dohp->host, 0, dohp->port, FALSE);
+      if(dns) {
+        /* Now add and HTTPSRR information if we have */
 #ifdef USE_HTTPSRR
-    if(de.numhttps_rrs > 0 && result == CURLE_OK && *dnsp) {
-      struct Curl_https_rrinfo *hrr = NULL;
-      result = doh_resp_decode_httpsrr(de.https_rrs->val, de.https_rrs->len,
-                                       &hrr);
-      if(result) {
-        infof(data, "Failed to decode HTTPS RR");
-        return result;
-      }
-      infof(data, "Some HTTPS RR to process");
+        if(de.numhttps_rrs > 0 && result == CURLE_OK) {
+          struct Curl_https_rrinfo *hrr = NULL;
+          result = doh_resp_decode_httpsrr(data, de.https_rrs->val,
+                                           de.https_rrs->len, &hrr);
+          if(result) {
+            infof(data, "Failed to decode HTTPS RR");
+            return result;
+          }
+          infof(data, "Some HTTPS RR to process");
 # ifdef DEBUGBUILD
-      doh_print_httpsrr(data, hrr);
+          doh_print_httpsrr(data, hrr);
 # endif
-      (*dnsp)->hinfo = hrr;
-    }
+          dns->hinfo = hrr;
+       }
 #endif
+        /* and add the entry to the cache */
+        data->state.async.dns = dns;
+        result = Curl_dnscache_add(data, dns);
+        *dnsp = data->state.async.dns;
+      }
+    } /* address processing done */
 
     /* All done */
+    data->state.async.done = TRUE;
     de_cleanup(&de);
     Curl_doh_cleanup(data);
     return result;
@@ -1317,41 +1315,43 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
 
 void Curl_doh_close(struct Curl_easy *data)
 {
-  struct doh_probes *doh = data->req.doh;
+  struct doh_probes *doh = data->state.async.doh;
   if(doh && data->multi) {
     struct Curl_easy *probe_data;
-    curl_off_t mid;
+    unsigned int mid;
     size_t slot;
     for(slot = 0; slot < DOH_SLOT_COUNT; slot++) {
-      mid = doh->probe[slot].easy_mid;
-      if(mid < 0)
+      mid = doh->probe_resp[slot].probe_mid;
+      if(mid == UINT_MAX)
         continue;
-      doh->probe[slot].easy_mid = -1;
+      doh->probe_resp[slot].probe_mid = UINT_MAX;
       /* should have been called before data is removed from multi handle */
       DEBUGASSERT(data->multi);
-      probe_data = data->multi ? Curl_multi_get_handle(data->multi, mid) :
+      probe_data = data->multi ? Curl_multi_get_easy(data->multi, mid) :
         NULL;
       if(!probe_data) {
-        DEBUGF(infof(data, "Curl_doh_close: xfer for mid=%"
-                     FMT_OFF_T " not found!",
-                     doh->probe[slot].easy_mid));
+        DEBUGF(infof(data, "Curl_doh_close: xfer for mid=%u not found!",
+                     doh->probe_resp[slot].probe_mid));
         continue;
       }
       /* data->multi might already be reset at this time */
       curl_multi_remove_handle(data->multi, probe_data);
       Curl_close(&probe_data);
     }
+    data->sub_xfer_done = NULL;
   }
 }
 
 void Curl_doh_cleanup(struct Curl_easy *data)
 {
-  struct doh_probes *doh = data->req.doh;
-  if(doh) {
+  struct doh_probes *dohp = data->state.async.doh;
+  if(dohp) {
+    int i;
     Curl_doh_close(data);
-    curl_slist_free_all(doh->req_hds);
-    data->req.doh->req_hds = NULL;
-    Curl_safefree(data->req.doh);
+    for(i = 0; i < DOH_SLOT_COUNT; ++i) {
+      curlx_dyn_free(&dohp->probe_resp[i].body);
+    }
+    Curl_safefree(data->state.async.doh);
   }
 }
 
diff --git a/extra/curl/curl-8.12.1/lib/doh.h b/extra/curl/curl-8.14.1/lib/doh.h
similarity index 88%
rename from extra/curl/curl-8.12.1/lib/doh.h
rename to extra/curl/curl-8.14.1/lib/doh.h
index 53644863e6f5..9146f53580f7 100644
--- a/extra/curl/curl-8.12.1/lib/doh.h
+++ b/extra/curl/curl-8.14.1/lib/doh.h
@@ -59,15 +59,6 @@ typedef enum {
   DNS_TYPE_HTTPS = 65
 } DNStype;
 
-/* one of these for each DoH request */
-struct doh_probe {
-  curl_off_t easy_mid; /* multi id of easy handle doing the lookup */
-  DNStype dnstype;
-  unsigned char req_body[512];
-  size_t req_body_len;
-  struct dynbuf resp_body;
-};
-
 enum doh_slot_num {
   /* Explicit values for first two symbols so as to match hard-coded
    * constants in existing code
@@ -89,9 +80,32 @@ enum doh_slot_num {
   DOH_SLOT_COUNT
 };
 
-struct doh_probes {
+#define CURL_EZM_DOH_PROBE   "ezm:doh-p"
+
+/* the largest one we can make, based on RFCs 1034, 1035 */
+#define DOH_MAX_DNSREQ_SIZE (256 + 16)
+
+/* each DoH probe request has this
+ * as easy meta for CURL_EZM_DOH_PROBE */
+struct doh_request {
+  unsigned char req_body[DOH_MAX_DNSREQ_SIZE];
   struct curl_slist *req_hds;
-  struct doh_probe probe[DOH_SLOT_COUNT];
+  struct dynbuf resp_body;
+  size_t req_body_len;
+  DNStype dnstype;
+};
+
+struct doh_response {
+  unsigned int probe_mid;
+  struct dynbuf body;
+  DNStype dnstype;
+  CURLcode result;
+};
+
+/* each transfer firing off DoH requests has this
+ * as easy meta for CURL_EZM_DOH_MASTER */
+struct doh_probes {
+  struct doh_response probe_resp[DOH_SLOT_COUNT];
   unsigned int pending; /* still outstanding probes */
   int port;
   const char *host;
@@ -105,6 +119,7 @@ struct doh_probes {
 struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
                                const char *hostname,
                                int port,
+                               int ip_version,
                                int *waitp);
 
 CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
@@ -167,10 +182,8 @@ UNITTEST void de_init(struct dohentry *d);
 UNITTEST void de_cleanup(struct dohentry *d);
 #endif
 
-extern struct curl_trc_feat Curl_doh_trc;
-
 #else /* if DoH is disabled */
-#define Curl_doh(a,b,c,d) NULL
+#define Curl_doh(a,b,c,d,e) NULL
 #define Curl_doh_is_resolved(x,y) CURLE_COULDNT_RESOLVE_HOST
 #endif
 
diff --git a/extra/curl/curl-8.12.1/lib/dynhds.c b/extra/curl/curl-8.14.1/lib/dynhds.c
similarity index 97%
rename from extra/curl/curl-8.12.1/lib/dynhds.c
rename to extra/curl/curl-8.14.1/lib/dynhds.c
index 2c92ca63fe6a..5c52d7411ace 100644
--- a/extra/curl/curl-8.12.1/lib/dynhds.c
+++ b/extra/curl/curl-8.14.1/lib/dynhds.c
@@ -359,9 +359,10 @@ CURLcode Curl_dynhds_h1_dprint(struct dynhds *dynhds, struct dynbuf *dbuf)
     return result;
 
   for(i = 0; i < dynhds->hds_len; ++i) {
-    result = Curl_dyn_addf(dbuf, "%.*s: %.*s\r\n",
-               (int)dynhds->hds[i]->namelen, dynhds->hds[i]->name,
-               (int)dynhds->hds[i]->valuelen, dynhds->hds[i]->value);
+    result = curlx_dyn_addf(dbuf, "%.*s: %.*s\r\n",
+                            (int)dynhds->hds[i]->namelen, dynhds->hds[i]->name,
+                            (int)dynhds->hds[i]->valuelen,
+                            dynhds->hds[i]->value);
     if(result)
       break;
   }
diff --git a/extra/curl/curl-8.12.1/lib/dynhds.h b/extra/curl/curl-8.14.1/lib/dynhds.h
similarity index 95%
rename from extra/curl/curl-8.12.1/lib/dynhds.h
rename to extra/curl/curl-8.14.1/lib/dynhds.h
index fb162a30debb..e533dcc3699e 100644
--- a/extra/curl/curl-8.12.1/lib/dynhds.h
+++ b/extra/curl/curl-8.14.1/lib/dynhds.h
@@ -26,13 +26,13 @@
 #include "curl_setup.h"
 
 #include <curl/curl.h>
-#include "dynbuf.h"
+#include "curlx/dynbuf.h"
 
 struct dynbuf;
 
 /**
  * A single header entry.
- * `name` and `value` are non-NULL and always NUL terminated.
+ * `name` and `value` are non-NULL and always null-terminated.
  */
 struct dynhds_entry {
   char *name;
@@ -113,7 +113,7 @@ size_t Curl_dynhds_count_name(struct dynhds *dynhds,
                               const char *name, size_t namelen);
 
 /**
- * Return how often the given 0-terminated name appears in `dynhds`.
+ * Return how often the given null-terminated name appears in `dynhds`.
  * Names are case-insensitive.
  */
 size_t Curl_dynhds_ccount_name(struct dynhds *dynhds, const char *name);
@@ -156,14 +156,14 @@ CURLcode Curl_dynhds_cadd(struct dynhds *dynhds,
 
 /**
  * Add a single header from an HTTP/1.1 formatted line at the end. Line
- * may contain a delimiting \r\n or just \n. Any characters after
+ * may contain a delimiting CRLF or just LF. Any characters after
  * that will be ignored.
  */
 CURLcode Curl_dynhds_h1_cadd_line(struct dynhds *dynhds, const char *line);
 
 /**
  * Add a single header from an HTTP/1.1 formatted line at the end. Line
- * may contain a delimiting \r\n or just \n. Any characters after
+ * may contain a delimiting CRLF or just LF. Any characters after
  * that will be ignored.
  */
 CURLcode Curl_dynhds_h1_add_line(struct dynhds *dynhds,
diff --git a/extra/curl/curl-8.12.1/lib/easy.c b/extra/curl/curl-8.14.1/lib/easy.c
similarity index 91%
rename from extra/curl/curl-8.12.1/lib/easy.c
rename to extra/curl/curl-8.14.1/lib/easy.c
index 1573a9d9297b..3f867862523f 100644
--- a/extra/curl/curl-8.12.1/lib/easy.c
+++ b/extra/curl/curl-8.14.1/lib/easy.c
@@ -49,6 +49,7 @@
 #include "transfer.h"
 #include "vtls/vtls.h"
 #include "vtls/vtls_scache.h"
+#include "vquic/vquic.h"
 #include "url.h"
 #include "getinfo.h"
 #include "hostip.h"
@@ -65,14 +66,14 @@
 #include "mime.h"
 #include "amigaos.h"
 #include "macos.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
 #include "sigpipe.h"
 #include "vssh/ssh.h"
 #include "setopt.h"
 #include "http_digest.h"
 #include "system_win32.h"
 #include "http2.h"
-#include "dynbuf.h"
+#include "curlx/dynbuf.h"
 #include "altsvc.h"
 #include "hsts.h"
 
@@ -105,7 +106,7 @@ static curl_simple_lock s_lock = CURL_SIMPLE_LOCK_INIT;
  * ways, but at this point it must be defined as the system-supplied strdup
  * so the callback pointer is initialized correctly.
  */
-#if defined(_WIN32_WCE)
+#if defined(UNDER_CE)
 #define system_strdup _strdup
 #elif !defined(HAVE_STRDUP)
 #define system_strdup Curl_strdup
@@ -170,6 +171,11 @@ static CURLcode global_init(long flags, bool memoryfuncs)
     goto fail;
   }
 
+  if(!Curl_vquic_init()) {
+    DEBUGF(fprintf(stderr, "Error: Curl_vquic_init failed\n"));
+    goto fail;
+  }
+
   if(Curl_win32_init(flags)) {
     DEBUGF(fprintf(stderr, "Error: win32_init failed\n"));
     goto fail;
@@ -185,7 +191,7 @@ static CURLcode global_init(long flags, bool memoryfuncs)
     goto fail;
   }
 
-  if(Curl_resolver_global_init()) {
+  if(Curl_async_global_init()) {
     DEBUGF(fprintf(stderr, "Error: resolver_global_init failed\n"));
     goto fail;
   }
@@ -288,7 +294,7 @@ void curl_global_cleanup(void)
   }
 
   Curl_ssl_cleanup();
-  Curl_resolver_global_cleanup();
+  Curl_async_global_cleanup();
 
 #ifdef _WIN32
   Curl_win32_cleanup(easy_init_flags);
@@ -542,12 +548,34 @@ static void events_setup(struct Curl_multi *multi, struct events *ev)
   curl_multi_setopt(multi, CURLMOPT_SOCKETDATA, ev);
 }
 
+/* populate_fds()
+ *
+ * populate the fds[] array
+ */
+static unsigned int populate_fds(struct pollfd *fds, struct events *ev)
+{
+  unsigned int numfds = 0;
+  struct pollfd *f;
+  struct socketmonitor *m;
+
+  f = &fds[0];
+  for(m = ev->list; m; m = m->next) {
+    f->fd = m->socket.fd;
+    f->events = m->socket.events;
+    f->revents = 0;
+#if DEBUG_EV_POLL
+    fprintf(stderr, "poll() %d check socket %d\n", numfds, f->fd);
+#endif
+    f++;
+    numfds++;
+  }
+  return numfds;
+}
 
 /* wait_or_timeout()
  *
  * waits for activity on any of the given sockets, or the timeout to trigger.
  */
-
 static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev)
 {
   bool done = FALSE;
@@ -556,37 +584,22 @@ static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev)
 
   while(!done) {
     CURLMsg *msg;
-    struct socketmonitor *m;
-    struct pollfd *f;
     struct pollfd fds[4];
-    int numfds = 0;
     int pollrc;
-    int i;
     struct curltime before;
-
-    /* populate the fds[] array */
-    for(m = ev->list, f = &fds[0]; m; m = m->next) {
-      f->fd = m->socket.fd;
-      f->events = m->socket.events;
-      f->revents = 0;
-#if DEBUG_EV_POLL
-      fprintf(stderr, "poll() %d check socket %d\n", numfds, f->fd);
-#endif
-      f++;
-      numfds++;
-    }
+    const unsigned int numfds = populate_fds(fds, ev);
 
     /* get the time stamp to use to figure out how long poll takes */
-    before = Curl_now();
+    before = curlx_now();
 
     if(numfds) {
       /* wait for activity or timeout */
 #if DEBUG_EV_POLL
-      fprintf(stderr, "poll(numfds=%d, timeout=%ldms)\n", numfds, ev->ms);
+      fprintf(stderr, "poll(numfds=%u, timeout=%ldms)\n", numfds, ev->ms);
 #endif
-      pollrc = Curl_poll(fds, (unsigned int)numfds, ev->ms);
+      pollrc = Curl_poll(fds, numfds, ev->ms);
 #if DEBUG_EV_POLL
-      fprintf(stderr, "poll(numfds=%d, timeout=%ldms) -> %d\n",
+      fprintf(stderr, "poll(numfds=%u, timeout=%ldms) -> %d\n",
               numfds, ev->ms, pollrc);
 #endif
       if(pollrc < 0)
@@ -612,20 +625,15 @@ static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev)
     }
     else {
       /* here pollrc is > 0 */
-      struct Curl_llist_node *e = Curl_llist_head(&multi->process);
-      struct Curl_easy *data;
-      DEBUGASSERT(e);
-      data = Curl_node_elem(e);
-      DEBUGASSERT(data);
-
       /* loop over the monitored sockets to see which ones had activity */
+      unsigned int i;
       for(i = 0; i < numfds; i++) {
         if(fds[i].revents) {
           /* socket activity, tell libcurl */
           int act = poll2cselect(fds[i].revents); /* convert */
 
           /* sending infof "randomly" to the first easy handle */
-          infof(data, "call curl_multi_socket_action(socket "
+          infof(multi->admin, "call curl_multi_socket_action(socket "
                 "%" FMT_SOCKET_T ")", (curl_socket_t)fds[i].fd);
           mcode = curl_multi_socket_action(multi, fds[i].fd, act,
                                            &ev->running_handles);
@@ -637,7 +645,7 @@ static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev)
         /* If nothing updated the timeout, we decrease it by the spent time.
          * If it was updated, it has the new timeout time stored already.
          */
-        timediff_t timediff = Curl_timediff(Curl_now(), before);
+        timediff_t timediff = curlx_timediff(curlx_now(), before);
         if(timediff > 0) {
 #if DEBUG_EV_POLL
         fprintf(stderr, "poll timeout %ldms not updated, decrease by "
@@ -770,7 +778,7 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events)
     Curl_detach_connection(data);
     s = Curl_getconnectinfo(data, &c);
     if((s != CURL_SOCKET_BAD) && c) {
-      Curl_cpool_disconnect(data, c, TRUE);
+      Curl_conn_terminate(data, c, TRUE);
     }
     DEBUGASSERT(!data->conn);
   }
@@ -780,7 +788,7 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events)
   else {
     /* this multi handle will only ever have a single easy handle attached to
        it, so make it use minimal hash sizes */
-    multi = Curl_multi_handle(1, 3, 7, 3);
+    multi = Curl_multi_handle(16, 1, 3, 7, 3);
     if(!multi)
       return CURLE_OUT_OF_MEMORY;
   }
@@ -930,6 +938,15 @@ static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src)
   return result;
 }
 
+static void dupeasy_meta_freeentry(void *p)
+{
+  (void)p;
+  /* Will always be FALSE. Cannot use a 0 assert here since compilers
+   * are not in agreement if they then want a NORETURN attribute or
+   * not. *sigh* */
+  DEBUGASSERT(p == NULL);
+}
+
 /*
  * curl_easy_duphandle() is an external interface to allow duplication of a
  * given input easy handle. The returned handle will be a new working handle
@@ -949,19 +966,28 @@ CURL *curl_easy_duphandle(CURL *d)
    */
   outcurl->set.buffer_size = data->set.buffer_size;
 
-  /* copy all userdefined values */
-  if(dupset(outcurl, data))
-    goto fail;
-
-  Curl_dyn_init(&outcurl->state.headerb, CURL_MAX_HTTP_HEADER);
+  Curl_hash_init(&outcurl->meta_hash, 23,
+                 Curl_hash_str, curlx_str_key_compare, dupeasy_meta_freeentry);
+  curlx_dyn_init(&outcurl->state.headerb, CURL_MAX_HTTP_HEADER);
   Curl_netrc_init(&outcurl->state.netrc);
 
   /* the connection pool is setup on demand */
   outcurl->state.lastconnect_id = -1;
   outcurl->state.recent_conn_id = -1;
   outcurl->id = -1;
+  outcurl->mid = UINT_MAX;
+  outcurl->master_mid = UINT_MAX;
+
+#ifndef CURL_DISABLE_HTTP
+  Curl_llist_init(&outcurl->state.httphdrs, NULL);
+#endif
+  Curl_initinfo(outcurl);
+
+  /* copy all userdefined values */
+  if(dupset(outcurl, data))
+    goto fail;
 
-  outcurl->progress.flags    = data->progress.flags;
+  outcurl->progress.hide     = data->progress.hide;
   outcurl->progress.callback = data->progress.callback;
 
 #ifndef CURL_DISABLE_COOKIES
@@ -1024,40 +1050,6 @@ CURL *curl_easy_duphandle(CURL *d)
   }
 #endif
 
-#ifdef CURLRES_ASYNCH
-  /* Clone the resolver handle, if present, for the new handle */
-  if(Curl_resolver_duphandle(outcurl,
-                             &outcurl->state.async.resolver,
-                             data->state.async.resolver))
-    goto fail;
-#endif
-
-#ifdef USE_ARES
-  {
-    CURLcode rc;
-
-    rc = Curl_set_dns_servers(outcurl, data->set.str[STRING_DNS_SERVERS]);
-    if(rc && rc != CURLE_NOT_BUILT_IN)
-      goto fail;
-
-    rc = Curl_set_dns_interface(outcurl, data->set.str[STRING_DNS_INTERFACE]);
-    if(rc && rc != CURLE_NOT_BUILT_IN)
-      goto fail;
-
-    rc = Curl_set_dns_local_ip4(outcurl, data->set.str[STRING_DNS_LOCAL_IP4]);
-    if(rc && rc != CURLE_NOT_BUILT_IN)
-      goto fail;
-
-    rc = Curl_set_dns_local_ip6(outcurl, data->set.str[STRING_DNS_LOCAL_IP6]);
-    if(rc && rc != CURLE_NOT_BUILT_IN)
-      goto fail;
-  }
-#endif /* USE_ARES */
-#ifndef CURL_DISABLE_HTTP
-  Curl_llist_init(&outcurl->state.httphdrs, NULL);
-#endif
-  Curl_initinfo(outcurl);
-
   outcurl->magic = CURLEASY_MAGIC_NUMBER;
 
   /* we reach this point and thus we are OK */
@@ -1070,7 +1062,7 @@ CURL *curl_easy_duphandle(CURL *d)
 #ifndef CURL_DISABLE_COOKIES
     free(outcurl->cookies);
 #endif
-    Curl_dyn_free(&outcurl->state.headerb);
+    curlx_dyn_free(&outcurl->state.headerb);
     Curl_altsvc_cleanup(&outcurl->asi);
     Curl_hsts_cleanup(&outcurl->hsts);
     Curl_freeset(outcurl);
@@ -1088,7 +1080,14 @@ void curl_easy_reset(CURL *d)
 {
   struct Curl_easy *data = d;
   Curl_req_hard_reset(&data->req, data);
-
+  Curl_hash_clean(&data->meta_hash);
+
+  /* clear all meta data */
+  Curl_meta_reset(data);
+  /* clear any resolve data */
+  Curl_async_shutdown(data);
+  Curl_resolv_unlink(data, &data->state.dns[0]);
+  Curl_resolv_unlink(data, &data->state.dns[1]);
   /* zero out UserDefined data: */
   Curl_freeset(data);
   memset(&data->set, 0, sizeof(struct UserDefined));
@@ -1100,7 +1099,7 @@ void curl_easy_reset(CURL *d)
   /* zero out PureInfo data: */
   Curl_initinfo(data);
 
-  data->progress.flags |= PGRS_HIDE;
+  data->progress.hide = TRUE;
   data->state.current_speed = -1; /* init to negative == impossible */
   data->state.retrycount = 0;     /* reset the retry counter */
 
@@ -1111,6 +1110,7 @@ void curl_easy_reset(CURL *d)
 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH)
   Curl_http_auth_cleanup_digest(data);
 #endif
+  data->master_mid = UINT_MAX;
 }
 
 /*
@@ -1195,10 +1195,10 @@ CURLcode curl_easy_pause(CURL *d, int action)
   }
 
 out:
-  if(!result && !data->state.done && keep_changed)
-    /* This transfer may have been moved in or out of the bundle, update the
-       corresponding socket callback, if used */
-    result = Curl_updatesocket(data);
+  if(!result && !data->state.done && keep_changed && data->multi)
+    /* pause/unpausing may result in multi event changes */
+    if(Curl_multi_ev_assess_xfer(data->multi, data))
+      result = CURLE_ABORTED_BY_CALLBACK;
 
   if(recursive)
     /* this might have called a callback recursively which might have set this
@@ -1388,3 +1388,30 @@ CURLcode curl_easy_ssls_export(CURL *d,
   return CURLE_NOT_BUILT_IN;
 #endif
 }
+
+CURLcode Curl_meta_set(struct Curl_easy *data, const char *key,
+                       void *meta_data, Curl_meta_dtor *meta_dtor)
+{
+  DEBUGASSERT(meta_data); /* never set to NULL */
+  if(!Curl_hash_add2(&data->meta_hash, CURL_UNCONST(key), strlen(key) + 1,
+                     meta_data, meta_dtor)) {
+    meta_dtor(CURL_UNCONST(key), strlen(key) + 1, meta_data);
+    return CURLE_OUT_OF_MEMORY;
+  }
+  return CURLE_OK;
+}
+
+void Curl_meta_remove(struct Curl_easy *data, const char *key)
+{
+  Curl_hash_delete(&data->meta_hash, CURL_UNCONST(key), strlen(key) + 1);
+}
+
+void *Curl_meta_get(struct Curl_easy *data, const char *key)
+{
+  return Curl_hash_pick(&data->meta_hash, CURL_UNCONST(key), strlen(key) + 1);
+}
+
+void Curl_meta_reset(struct Curl_easy *data)
+{
+  Curl_hash_clean(&data->meta_hash);
+}
diff --git a/extra/curl/curl-8.12.1/lib/easy_lock.h b/extra/curl/curl-8.14.1/lib/easy_lock.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/easy_lock.h
rename to extra/curl/curl-8.14.1/lib/easy_lock.h
diff --git a/extra/curl/curl-8.12.1/lib/easygetopt.c b/extra/curl/curl-8.14.1/lib/easygetopt.c
similarity index 94%
rename from extra/curl/curl-8.12.1/lib/easygetopt.c
rename to extra/curl/curl-8.14.1/lib/easygetopt.c
index 86833bf6b9e3..5d30d39a49f2 100644
--- a/extra/curl/curl-8.12.1/lib/easygetopt.c
+++ b/extra/curl/curl-8.14.1/lib/easygetopt.c
@@ -29,12 +29,12 @@
 #ifndef CURL_DISABLE_GETOPTIONS
 
 /* Lookups easy options at runtime */
-static struct curl_easyoption *lookup(const char *name, CURLoption id)
+static const struct curl_easyoption *lookup(const char *name, CURLoption id)
 {
   DEBUGASSERT(name || id);
   DEBUGASSERT(!Curl_easyopts_check());
   if(name || id) {
-    struct curl_easyoption *o = &Curl_easyopts[0];
+    const struct curl_easyoption *o = &Curl_easyopts[0];
     do {
       if(name) {
         if(strcasecompare(o->name, name))
diff --git a/extra/curl/curl-8.12.1/lib/easyif.h b/extra/curl/curl-8.14.1/lib/easyif.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/easyif.h
rename to extra/curl/curl-8.14.1/lib/easyif.h
diff --git a/extra/curl/curl-8.12.1/lib/easyoptions.c b/extra/curl/curl-8.14.1/lib/easyoptions.c
similarity index 98%
rename from extra/curl/curl-8.12.1/lib/easyoptions.c
rename to extra/curl/curl-8.14.1/lib/easyoptions.c
index f2ced2cb5083..03d676df0e73 100644
--- a/extra/curl/curl-8.12.1/lib/easyoptions.c
+++ b/extra/curl/curl-8.14.1/lib/easyoptions.c
@@ -28,7 +28,7 @@
 #include "easyoptions.h"
 
 /* all easy setopt options listed in alphabetical order */
-struct curl_easyoption Curl_easyopts[] = {
+const struct curl_easyoption Curl_easyopts[] = {
   {"ABSTRACT_UNIX_SOCKET", CURLOPT_ABSTRACT_UNIX_SOCKET, CURLOT_STRING, 0},
   {"ACCEPTTIMEOUT_MS", CURLOPT_ACCEPTTIMEOUT_MS, CURLOT_LONG, 0},
   {"ACCEPT_ENCODING", CURLOPT_ACCEPT_ENCODING, CURLOT_STRING, 0},
@@ -317,6 +317,8 @@ struct curl_easyoption Curl_easyopts[] = {
   {"SSL_FALSESTART", CURLOPT_SSL_FALSESTART, CURLOT_LONG, 0},
   {"SSL_OPTIONS", CURLOPT_SSL_OPTIONS, CURLOT_VALUES, 0},
   {"SSL_SESSIONID_CACHE", CURLOPT_SSL_SESSIONID_CACHE, CURLOT_LONG, 0},
+  {"SSL_SIGNATURE_ALGORITHMS", CURLOPT_SSL_SIGNATURE_ALGORITHMS,
+   CURLOT_STRING, 0},
   {"SSL_VERIFYHOST", CURLOPT_SSL_VERIFYHOST, CURLOT_LONG, 0},
   {"SSL_VERIFYPEER", CURLOPT_SSL_VERIFYPEER, CURLOT_LONG, 0},
   {"SSL_VERIFYSTATUS", CURLOPT_SSL_VERIFYSTATUS, CURLOT_LONG, 0},
@@ -353,6 +355,7 @@ struct curl_easyoption Curl_easyopts[] = {
   {"UPKEEP_INTERVAL_MS", CURLOPT_UPKEEP_INTERVAL_MS, CURLOT_LONG, 0},
   {"UPLOAD", CURLOPT_UPLOAD, CURLOT_LONG, 0},
   {"UPLOAD_BUFFERSIZE", CURLOPT_UPLOAD_BUFFERSIZE, CURLOT_LONG, 0},
+  {"UPLOAD_FLAGS", CURLOPT_UPLOAD_FLAGS, CURLOT_LONG, 0},
   {"URL", CURLOPT_URL, CURLOT_STRING, 0},
   {"USERAGENT", CURLOPT_USERAGENT, CURLOT_STRING, 0},
   {"USERNAME", CURLOPT_USERNAME, CURLOT_STRING, 0},
@@ -377,6 +380,6 @@ struct curl_easyoption Curl_easyopts[] = {
  */
 int Curl_easyopts_check(void)
 {
-  return (CURLOPT_LASTENTRY % 10000) != (326 + 1);
+  return (CURLOPT_LASTENTRY % 10000) != (328 + 1);
 }
 #endif
diff --git a/extra/curl/curl-8.12.1/lib/easyoptions.h b/extra/curl/curl-8.14.1/lib/easyoptions.h
similarity index 96%
rename from extra/curl/curl-8.12.1/lib/easyoptions.h
rename to extra/curl/curl-8.14.1/lib/easyoptions.h
index 24b4cd93ed9b..44b6a8280a8f 100644
--- a/extra/curl/curl-8.12.1/lib/easyoptions.h
+++ b/extra/curl/curl-8.14.1/lib/easyoptions.h
@@ -29,7 +29,7 @@
 #include <curl/curl.h>
 
 /* generated table with all easy options */
-extern struct curl_easyoption Curl_easyopts[];
+extern const struct curl_easyoption Curl_easyopts[];
 
 #ifdef DEBUGBUILD
 int Curl_easyopts_check(void);
diff --git a/extra/curl/curl-8.12.1/lib/escape.c b/extra/curl/curl-8.14.1/lib/escape.c
similarity index 83%
rename from extra/curl/curl-8.12.1/lib/escape.c
rename to extra/curl/curl-8.14.1/lib/escape.c
index eaad6d33ad43..3cd906dc6c2c 100644
--- a/extra/curl/curl-8.12.1/lib/escape.c
+++ b/extra/curl/curl-8.14.1/lib/escape.c
@@ -32,9 +32,11 @@
 struct Curl_easy;
 
 #include "urldata.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
 #include "escape.h"
 #include "strdup.h"
+#include "curlx/strparse.h"
+
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
@@ -69,7 +71,7 @@ char *curl_easy_escape(CURL *data, const char *string,
   if(!length)
     return strdup("");
 
-  Curl_dyn_init(&d, length * 3 + 1);
+  curlx_dyn_init(&d, length * 3 + 1);
 
   while(length--) {
     /* treat the characters unsigned */
@@ -77,33 +79,21 @@ char *curl_easy_escape(CURL *data, const char *string,
 
     if(ISUNRESERVED(in)) {
       /* append this */
-      if(Curl_dyn_addn(&d, &in, 1))
+      if(curlx_dyn_addn(&d, &in, 1))
         return NULL;
     }
     else {
       /* encode it */
-      const char hex[] = "0123456789ABCDEF";
-      char out[3]={'%'};
-      out[1] = hex[in >> 4];
-      out[2] = hex[in & 0xf];
-      if(Curl_dyn_addn(&d, out, 3))
+      unsigned char out[3]={'%'};
+      Curl_hexbyte(&out[1], in, FALSE);
+      if(curlx_dyn_addn(&d, out, 3))
         return NULL;
     }
   }
 
-  return Curl_dyn_ptr(&d);
+  return curlx_dyn_ptr(&d);
 }
 
-static const unsigned char hextable[] = {
-  0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0,       /* 0x30 - 0x3f */
-  0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 - 0x4f */
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,       /* 0x50 - 0x5f */
-  0, 10, 11, 12, 13, 14, 15                             /* 0x60 - 0x66 */
-};
-
-/* the input is a single hex digit */
-#define onehex2dec(x) hextable[x - '0']
-
 /*
  * Curl_urldecode() URL decodes the given string.
  *
@@ -144,8 +134,8 @@ CURLcode Curl_urldecode(const char *string, size_t length,
     if(('%' == in) && (alloc > 2) &&
        ISXDIGIT(string[1]) && ISXDIGIT(string[2])) {
       /* this is two hexadecimal digits following a '%' */
-      in = (unsigned char)(onehex2dec(string[1]) << 4) | onehex2dec(string[2]);
-
+      in = (unsigned char)((Curl_hexval(string[1]) << 4) |
+                           Curl_hexval(string[2]));
       string += 3;
       alloc -= 3;
     }
@@ -219,15 +209,12 @@ void curl_free(void *p)
 void Curl_hexencode(const unsigned char *src, size_t len, /* input length */
                     unsigned char *out, size_t olen) /* output buffer size */
 {
-  const char *hex = "0123456789abcdef";
   DEBUGASSERT(src && len && (olen >= 3));
   if(src && len && (olen >= 3)) {
     while(len-- && (olen >= 3)) {
-      /* clang-tidy warns on this line without this comment: */
-      /* NOLINTNEXTLINE(clang-analyzer-core.UndefinedBinaryOperatorResult) */
-      *out++ = (unsigned char)hex[(*src & 0xF0) >> 4];
-      *out++ = (unsigned char)hex[*src & 0x0F];
+      Curl_hexbyte(out, *src, TRUE);
       ++src;
+      out += 2;
       olen -= 2;
     }
     *out = 0;
@@ -235,3 +222,17 @@ void Curl_hexencode(const unsigned char *src, size_t len, /* input length */
   else if(olen)
     *out = 0;
 }
+
+/* Curl_hexbyte
+ *
+ * Output a single unsigned char as a two-digit hex number, lowercase or
+ * uppercase
+ */
+void Curl_hexbyte(unsigned char *dest, /* must fit two bytes */
+                  unsigned char val,
+                  bool lowercase)
+{
+  const unsigned char *t = lowercase ? Curl_ldigits : Curl_udigits;
+  dest[0] = t[val >> 4];
+  dest[1] = t[val & 0x0F];
+}
diff --git a/extra/curl/curl-8.12.1/lib/escape.h b/extra/curl/curl-8.14.1/lib/escape.h
similarity index 92%
rename from extra/curl/curl-8.12.1/lib/escape.h
rename to extra/curl/curl-8.14.1/lib/escape.h
index 690e4178795a..1f2bac8fac2a 100644
--- a/extra/curl/curl-8.12.1/lib/escape.h
+++ b/extra/curl/curl-8.14.1/lib/escape.h
@@ -41,4 +41,8 @@ CURLcode Curl_urldecode(const char *string, size_t length,
 void Curl_hexencode(const unsigned char *src, size_t len, /* input length */
                     unsigned char *out, size_t olen); /* output buffer size */
 
+void Curl_hexbyte(unsigned char *dest, /* must fit two bytes */
+                  unsigned char val,
+                  bool lowercase);
+
 #endif /* HEADER_CURL_ESCAPE_H */
diff --git a/extra/curl/curl-8.14.1/lib/fake_addrinfo.c b/extra/curl/curl-8.14.1/lib/fake_addrinfo.c
new file mode 100644
index 000000000000..20d55ba2d1bd
--- /dev/null
+++ b/extra/curl/curl-8.14.1/lib/fake_addrinfo.c
@@ -0,0 +1,210 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+#include "fake_addrinfo.h"
+
+#ifdef USE_FAKE_GETADDRINFO
+
+#include <string.h>
+#include <stdlib.h>
+#include <ares.h>
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+void r_freeaddrinfo(struct addrinfo *cahead)
+{
+  struct addrinfo *canext;
+  struct addrinfo *ca;
+
+  for(ca = cahead; ca; ca = canext) {
+    canext = ca->ai_next;
+    free(ca);
+  }
+}
+
+struct context {
+  struct ares_addrinfo *result;
+};
+
+static void async_addrinfo_cb(void *userp, int status, int timeouts,
+                              struct ares_addrinfo *result)
+{
+  struct context *ctx = (struct context *)userp;
+  (void)timeouts;
+  if(ARES_SUCCESS == status) {
+    ctx->result = result;
+  }
+}
+
+/* convert the c-ares version into the "native" version */
+static struct addrinfo *mk_getaddrinfo(const struct ares_addrinfo *aihead)
+{
+  const struct ares_addrinfo_node *ai;
+  struct addrinfo *ca;
+  struct addrinfo *cafirst = NULL;
+  struct addrinfo *calast = NULL;
+  const char *name = aihead->name;
+
+  /* traverse the addrinfo list */
+  for(ai = aihead->nodes; ai != NULL; ai = ai->ai_next) {
+    size_t ss_size;
+    size_t namelen = name ? strlen(name) + 1 : 0;
+    /* ignore elements with unsupported address family, */
+    /* settle family-specific sockaddr structure size.  */
+    if(ai->ai_family == AF_INET)
+      ss_size = sizeof(struct sockaddr_in);
+    else if(ai->ai_family == AF_INET6)
+      ss_size = sizeof(struct sockaddr_in6);
+    else
+      continue;
+
+    /* ignore elements without required address info */
+    if(!ai->ai_addr || !(ai->ai_addrlen > 0))
+      continue;
+
+    /* ignore elements with bogus address size */
+    if((size_t)ai->ai_addrlen < ss_size)
+      continue;
+
+    ca = malloc(sizeof(struct addrinfo) + ss_size + namelen);
+    if(!ca) {
+      r_freeaddrinfo(cafirst);
+      return NULL;
+    }
+
+    /* copy each structure member individually, member ordering, */
+    /* size, or padding might be different for each platform.    */
+
+    ca->ai_flags     = ai->ai_flags;
+    ca->ai_family    = ai->ai_family;
+    ca->ai_socktype  = ai->ai_socktype;
+    ca->ai_protocol  = ai->ai_protocol;
+    ca->ai_addrlen   = (curl_socklen_t)ss_size;
+    ca->ai_addr      = NULL;
+    ca->ai_canonname = NULL;
+    ca->ai_next      = NULL;
+
+    ca->ai_addr = (void *)((char *)ca + sizeof(struct addrinfo));
+    memcpy(ca->ai_addr, ai->ai_addr, ss_size);
+
+    if(namelen) {
+      ca->ai_canonname = (void *)((char *)ca->ai_addr + ss_size);
+      memcpy(ca->ai_canonname, name, namelen);
+
+      /* the name is only pointed to by the first entry in the "real"
+         addrinfo chain, so stop now */
+      name = NULL;
+    }
+
+    /* if the return list is empty, this becomes the first element */
+    if(!cafirst)
+      cafirst = ca;
+
+    /* add this element last in the return list */
+    if(calast)
+      calast->ai_next = ca;
+    calast = ca;
+  }
+
+  return cafirst;
+}
+
+/*
+  RETURN VALUE
+
+  getaddrinfo() returns 0 if it succeeds, or one of the following nonzero
+  error codes:
+
+  ...
+*/
+int r_getaddrinfo(const char *node,
+                  const char *service,
+                  const struct addrinfo *hints,
+                  struct addrinfo **res)
+{
+  int status;
+  struct context ctx;
+  struct ares_options options;
+  int optmask = 0;
+  struct ares_addrinfo_hints ahints;
+  ares_channel channel;
+  int rc = 0;
+
+  memset(&options, 0, sizeof(options));
+  optmask      |= ARES_OPT_EVENT_THREAD;
+  options.evsys = ARES_EVSYS_DEFAULT;
+
+  memset(&ahints, 0, sizeof(ahints));
+  memset(&ctx, 0, sizeof(ctx));
+
+  if(hints) {
+    ahints.ai_flags = hints->ai_flags;
+    ahints.ai_family = hints->ai_family;
+    ahints.ai_socktype = hints->ai_socktype;
+    ahints.ai_protocol = hints->ai_protocol;
+  }
+
+  status = ares_init_options(&channel, &options, optmask);
+  if(status)
+    return EAI_MEMORY; /* major problem */
+
+  else {
+    const char *env = getenv("CURL_DNS_SERVER");
+    if(env) {
+      rc = ares_set_servers_ports_csv(channel, env);
+      if(rc) {
+        fprintf(stderr, "ares_set_servers_ports_csv failed: %d", rc);
+        /* Cleanup */
+        ares_destroy(channel);
+        return EAI_MEMORY; /* we can't run */
+      }
+    }
+  }
+
+  ares_getaddrinfo(channel, node, service, &ahints,
+                   async_addrinfo_cb, &ctx);
+
+  /* Wait until no more requests are left to be processed */
+  ares_queue_wait_empty(channel, -1);
+
+  if(ctx.result) {
+    /* convert the c-ares version */
+    *res = mk_getaddrinfo(ctx.result);
+    /* free the old */
+    ares_freeaddrinfo(ctx.result);
+  }
+  else
+    rc = EAI_NONAME; /* got nothing */
+
+  /* Cleanup */
+  ares_destroy(channel);
+
+  return rc;
+}
+
+#endif /* USE_FAKE_GETADDRINFO */
diff --git a/extra/curl/curl-8.14.1/lib/fake_addrinfo.h b/extra/curl/curl-8.14.1/lib/fake_addrinfo.h
new file mode 100644
index 000000000000..13b0d71dba6d
--- /dev/null
+++ b/extra/curl/curl-8.14.1/lib/fake_addrinfo.h
@@ -0,0 +1,54 @@
+#ifndef HEADER_FAKE_ADDRINFO_H
+#define HEADER_FAKE_ADDRINFO_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef USE_ARES
+#include <ares.h>
+#endif
+
+#if defined(CURLDEBUG) && defined(USE_ARES) && defined(HAVE_GETADDRINFO) && \
+  (ARES_VERSION >= 0x011a00) /* >= 1.26. 0 */
+#define USE_FAKE_GETADDRINFO 1
+#endif
+
+#ifdef USE_FAKE_GETADDRINFO
+
+#ifdef HAVE_NETDB_H
+#  include <netdb.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#  include <arpa/inet.h>
+#endif
+
+void r_freeaddrinfo(struct addrinfo *res);
+int r_getaddrinfo(const char *node,
+                  const char *service,
+                  const struct addrinfo *hints,
+                  struct addrinfo **res);
+#endif /* USE_FAKE_GETADDRINFO */
+
+#endif /* HEADER_FAKE_ADDRINFO_H */
diff --git a/extra/curl/curl-8.12.1/lib/file.c b/extra/curl/curl-8.14.1/lib/file.c
similarity index 92%
rename from extra/curl/curl-8.12.1/lib/file.c
rename to extra/curl/curl-8.14.1/lib/file.c
index 7751acf40fa9..b88f61230566 100644
--- a/extra/curl/curl-8.12.1/lib/file.c
+++ b/extra/curl/curl-8.14.1/lib/file.c
@@ -58,7 +58,6 @@
 #include <dirent.h>
 #endif
 
-#include "strtoofft.h"
 #include "urldata.h"
 #include <curl/curl.h>
 #include "progress.h"
@@ -71,7 +70,7 @@
 #include "transfer.h"
 #include "url.h"
 #include "parsedate.h" /* for the week day and month names */
-#include "warnless.h"
+#include "curlx/warnless.h"
 #include "curl_range.h"
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -84,6 +83,16 @@
 #define AMIGA_FILESYSTEM 1
 #endif
 
+/* meta key for storing protocol meta at easy handle */
+#define CURL_META_FILE_EASY   "meta:proto:file:easy"
+
+struct FILEPROTO {
+  char *path; /* the path we operate on */
+  char *freepath; /* pointer to the allocated block we must free, this might
+                     differ from the 'path' pointer */
+  int fd;     /* open file descriptor to read from! */
+};
+
 /*
  * Forward declarations.
  */
@@ -128,13 +137,34 @@ const struct Curl_handler Curl_handler_file = {
 };
 
 
+static void file_cleanup(struct FILEPROTO *file)
+{
+  Curl_safefree(file->freepath);
+  file->path = NULL;
+  if(file->fd != -1) {
+    close(file->fd);
+    file->fd = -1;
+  }
+}
+
+static void file_easy_dtor(void *key, size_t klen, void *entry)
+{
+  struct FILEPROTO *file = entry;
+  (void)key;
+  (void)klen;
+  file_cleanup(file);
+  free(file);
+}
+
 static CURLcode file_setup_connection(struct Curl_easy *data,
                                       struct connectdata *conn)
 {
+  struct FILEPROTO *filep;
   (void)conn;
   /* allocate the FILE specific struct */
-  data->req.p.file = calloc(1, sizeof(struct FILEPROTO));
-  if(!data->req.p.file)
+  filep = calloc(1, sizeof(*filep));
+  if(!filep ||
+     Curl_meta_set(data, CURL_META_FILE_EASY, filep, file_easy_dtor))
     return CURLE_OUT_OF_MEMORY;
 
   return CURLE_OK;
@@ -148,7 +178,7 @@ static CURLcode file_setup_connection(struct Curl_easy *data,
 static CURLcode file_connect(struct Curl_easy *data, bool *done)
 {
   char *real_path;
-  struct FILEPROTO *file = data->req.p.file;
+  struct FILEPROTO *file = Curl_meta_get(data, CURL_META_FILE_EASY);
   int fd;
 #ifdef DOS_FILESYSTEM
   size_t i;
@@ -157,6 +187,9 @@ static CURLcode file_connect(struct Curl_easy *data, bool *done)
   size_t real_path_len;
   CURLcode result;
 
+  if(!file)
+    return CURLE_FAILED_INIT;
+
   if(file->path) {
     /* already connected.
      * the handler->connect_it() is normally only called once, but
@@ -241,7 +274,7 @@ static CURLcode file_connect(struct Curl_easy *data, bool *done)
   file->path = real_path;
   #endif
 #endif
-  Curl_safefree(file->freepath);
+  free(file->freepath);
   file->freepath = real_path; /* free this when done */
 
   file->fd = fd;
@@ -258,17 +291,12 @@ static CURLcode file_connect(struct Curl_easy *data, bool *done)
 static CURLcode file_done(struct Curl_easy *data,
                           CURLcode status, bool premature)
 {
-  struct FILEPROTO *file = data->req.p.file;
+  struct FILEPROTO *file = Curl_meta_get(data, CURL_META_FILE_EASY);
   (void)status; /* not used */
   (void)premature; /* not used */
 
-  if(file) {
-    Curl_safefree(file->freepath);
-    file->path = NULL;
-    if(file->fd != -1)
-      close(file->fd);
-    file->fd = -1;
-  }
+  if(file)
+    file_cleanup(file);
 
   return CURLE_OK;
 }
@@ -288,9 +316,9 @@ static CURLcode file_disconnect(struct Curl_easy *data,
 #define DIRSEP '/'
 #endif
 
-static CURLcode file_upload(struct Curl_easy *data)
+static CURLcode file_upload(struct Curl_easy *data,
+                            struct FILEPROTO *file)
 {
-  struct FILEPROTO *file = data->req.p.file;
   const char *dir = strchr(file->path, DIRSEP);
   int fd;
   int mode;
@@ -392,7 +420,7 @@ static CURLcode file_upload(struct Curl_easy *data)
     if(Curl_pgrsUpdate(data))
       result = CURLE_ABORTED_BY_CALLBACK;
     else
-      result = Curl_speedcheck(data, Curl_now());
+      result = Curl_speedcheck(data, curlx_now());
   }
   if(!result && Curl_pgrsUpdate(data))
     result = CURLE_ABORTED_BY_CALLBACK;
@@ -419,6 +447,7 @@ static CURLcode file_do(struct Curl_easy *data, bool *done)
      are supported. This means that files on remotely mounted directories
      (via NFS, Samba, NT sharing) can be accessed through a file:// URL
   */
+  struct FILEPROTO *file = Curl_meta_get(data, CURL_META_FILE_EASY);
   CURLcode result = CURLE_OK;
   struct_stat statbuf; /* struct_stat instead of struct stat just to allow the
                           Windows version to have a different struct without
@@ -427,16 +456,15 @@ static CURLcode file_do(struct Curl_easy *data, bool *done)
   bool size_known;
   bool fstated = FALSE;
   int fd;
-  struct FILEPROTO *file;
   char *xfer_buf;
   size_t xfer_blen;
 
   *done = TRUE; /* unconditionally */
+  if(!file)
+    return CURLE_FAILED_INIT;
 
   if(data->state.upload)
-    return file_upload(data);
-
-  file = data->req.p.file;
+    return file_upload(data, file);
 
   /* get the fd from the connection phase */
   fd = file->fd;
@@ -548,7 +576,7 @@ static CURLcode file_do(struct Curl_easy *data, bool *done)
 
   if(data->state.resume_from) {
     if(!S_ISDIR(statbuf.st_mode)) {
-#ifdef __AMIGA__
+#if defined(__AMIGA__) || defined(__MINGW32CE__)
       if(data->state.resume_from !=
           lseek(fd, (off_t)data->state.resume_from, SEEK_SET))
 #else
@@ -597,7 +625,7 @@ static CURLcode file_do(struct Curl_easy *data, bool *done)
       if(Curl_pgrsUpdate(data))
         result = CURLE_ABORTED_BY_CALLBACK;
       else
-        result = Curl_speedcheck(data, Curl_now());
+        result = Curl_speedcheck(data, curlx_now());
       if(result)
         goto out;
     }
diff --git a/extra/curl/curl-8.12.1/lib/strtok.h b/extra/curl/curl-8.14.1/lib/file.h
similarity index 79%
rename from extra/curl/curl-8.12.1/lib/strtok.h
rename to extra/curl/curl-8.14.1/lib/file.h
index 9b4d06275fb7..fea1eea57d6c 100644
--- a/extra/curl/curl-8.12.1/lib/strtok.h
+++ b/extra/curl/curl-8.14.1/lib/file.h
@@ -1,5 +1,5 @@
-#ifndef HEADER_CURL_STRTOK_H
-#define HEADER_CURL_STRTOK_H
+#ifndef HEADER_CURL_FILE_H
+#define HEADER_CURL_FILE_H
 /***************************************************************************
  *                                  _   _ ____  _
  *  Project                     ___| | | |  _ \| |
@@ -23,14 +23,9 @@
  * SPDX-License-Identifier: curl
  *
  ***************************************************************************/
-#include "curl_setup.h"
-#include <stddef.h>
 
-#ifdef HAVE_STRTOK_R
-#include <string.h>
-#define Curl_strtok_r strtok_r
-#else
-char *Curl_strtok_r(char *s, const char *delim, char **last);
+#ifndef CURL_DISABLE_FILE
+extern const struct Curl_handler Curl_handler_file;
 #endif
 
-#endif /* HEADER_CURL_STRTOK_H */
+#endif /* HEADER_CURL_FILE_H */
diff --git a/extra/curl/curl-8.12.1/lib/fileinfo.c b/extra/curl/curl-8.14.1/lib/fileinfo.c
similarity index 97%
rename from extra/curl/curl-8.12.1/lib/fileinfo.c
rename to extra/curl/curl-8.14.1/lib/fileinfo.c
index 2be3b3239b80..c3439af341e1 100644
--- a/extra/curl/curl-8.12.1/lib/fileinfo.c
+++ b/extra/curl/curl-8.14.1/lib/fileinfo.c
@@ -40,7 +40,7 @@ void Curl_fileinfo_cleanup(struct fileinfo *finfo)
   if(!finfo)
     return;
 
-  Curl_dyn_free(&finfo->buf);
+  curlx_dyn_free(&finfo->buf);
   free(finfo);
 }
 #endif
diff --git a/extra/curl/curl-8.12.1/lib/fileinfo.h b/extra/curl/curl-8.14.1/lib/fileinfo.h
similarity index 98%
rename from extra/curl/curl-8.12.1/lib/fileinfo.h
rename to extra/curl/curl-8.14.1/lib/fileinfo.h
index 0b3f56d9d493..6746ee257552 100644
--- a/extra/curl/curl-8.12.1/lib/fileinfo.h
+++ b/extra/curl/curl-8.14.1/lib/fileinfo.h
@@ -26,7 +26,7 @@
 
 #include <curl/curl.h>
 #include "llist.h"
-#include "dynbuf.h"
+#include "curlx/dynbuf.h"
 
 struct fileinfo {
   struct curl_fileinfo info;
diff --git a/extra/curl/curl-8.12.1/lib/fopen.c b/extra/curl/curl-8.14.1/lib/fopen.c
similarity index 93%
rename from extra/curl/curl-8.12.1/lib/fopen.c
rename to extra/curl/curl-8.14.1/lib/fopen.c
index 90dc0ae4d733..38b87f326e39 100644
--- a/extra/curl/curl-8.12.1/lib/fopen.c
+++ b/extra/curl/curl-8.14.1/lib/fopen.c
@@ -66,7 +66,7 @@ static char *dirslash(const char *path)
   size_t n;
   struct dynbuf out;
   DEBUGASSERT(path);
-  Curl_dyn_init(&out, CURL_MAX_INPUT_LENGTH);
+  curlx_dyn_init(&out, CURL_MAX_INPUT_LENGTH);
   n = strlen(path);
   if(n) {
     /* find the rightmost path separator, if any */
@@ -76,12 +76,12 @@ static char *dirslash(const char *path)
     while(n && IS_SEP(path[n-1]))
       --n;
   }
-  if(Curl_dyn_addn(&out, path, n))
+  if(curlx_dyn_addn(&out, path, n))
     return NULL;
   /* if there was a directory, append a single trailing slash */
-  if(n && Curl_dyn_addn(&out, PATHSEP, 1))
+  if(n && curlx_dyn_addn(&out, PATHSEP, 1))
     return NULL;
-  return Curl_dyn_ptr(&out);
+  return curlx_dyn_ptr(&out);
 }
 
 /*
@@ -105,7 +105,13 @@ CURLcode Curl_fopen(struct Curl_easy *data, const char *filename,
   *fh = fopen(filename, FOPEN_WRITETEXT);
   if(!*fh)
     goto fail;
-  if(fstat(fileno(*fh), &sb) == -1 || !S_ISREG(sb.st_mode)) {
+  if(
+#ifdef UNDER_CE
+     stat(filename, &sb) == -1
+#else
+     fstat(fileno(*fh), &sb) == -1
+#endif
+     || !S_ISREG(sb.st_mode)) {
     return CURLE_OK;
   }
   fclose(*fh);
diff --git a/extra/curl/curl-8.12.1/lib/fopen.h b/extra/curl/curl-8.14.1/lib/fopen.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/fopen.h
rename to extra/curl/curl-8.14.1/lib/fopen.h
diff --git a/extra/curl/curl-8.12.1/lib/formdata.c b/extra/curl/curl-8.14.1/lib/formdata.c
similarity index 59%
rename from extra/curl/curl-8.12.1/lib/formdata.c
rename to extra/curl/curl-8.14.1/lib/formdata.c
index 7ea7a8f396b4..2aa8eee94aa8 100644
--- a/extra/curl/curl-8.12.1/lib/formdata.c
+++ b/extra/curl/curl-8.14.1/lib/formdata.c
@@ -31,10 +31,6 @@ struct Curl_easy;
 #include "formdata.h"
 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_FORM_API)
 
-#if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME)
-#include <libgen.h>
-#endif
-
 #include "urldata.h" /* for struct Curl_easy */
 #include "mime.h"
 #include "vtls/vtls.h"
@@ -42,7 +38,7 @@ struct Curl_easy;
 #include "sendf.h"
 #include "strdup.h"
 #include "rand.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
@@ -68,36 +64,31 @@ struct Curl_easy;
  *
  ***************************************************************************/
 static struct curl_httppost *
-AddHttpPost(char *name, size_t namelength,
-            char *value, curl_off_t contentslength,
-            char *buffer, size_t bufferlength,
-            char *contenttype,
-            long flags,
-            struct curl_slist *contentHeader,
-            char *showfilename, char *userp,
+AddHttpPost(struct FormInfo *src,
             struct curl_httppost *parent_post,
             struct curl_httppost **httppost,
             struct curl_httppost **last_post)
 {
   struct curl_httppost *post;
-  if(!namelength && name)
-    namelength = strlen(name);
-  if((bufferlength > LONG_MAX) || (namelength > LONG_MAX))
+  size_t namelength = src->namelength;
+  if(!namelength && src->name)
+    namelength = strlen(src->name);
+  if((src->bufferlength > LONG_MAX) || (namelength > LONG_MAX))
     /* avoid overflow in typecasts below */
     return NULL;
   post = calloc(1, sizeof(struct curl_httppost));
   if(post) {
-    post->name = name;
+    post->name = src->name;
     post->namelength = (long)namelength;
-    post->contents = value;
-    post->contentlen = contentslength;
-    post->buffer = buffer;
-    post->bufferlength = (long)bufferlength;
-    post->contenttype = contenttype;
-    post->contentheader = contentHeader;
-    post->showfilename = showfilename;
-    post->userp = userp;
-    post->flags = flags | CURL_HTTPPOST_LARGE;
+    post->contents = src->value;
+    post->contentlen = src->contentslength;
+    post->buffer = src->buffer;
+    post->bufferlength = (long)src->bufferlength;
+    post->contenttype = src->contenttype;
+    post->flags = src->flags | CURL_HTTPPOST_LARGE;
+    post->contentheader = src->contentheader;
+    post->showfilename = src->showfilename;
+    post->userp = src->userp;
   }
   else
     return NULL;
@@ -156,6 +147,28 @@ static struct FormInfo *AddFormInfo(char *value,
   return form_info;
 }
 
+static void free_formlist(struct FormInfo *ptr)
+{
+  for(; ptr != NULL; ptr = ptr->more) {
+    if(ptr->name_alloc) {
+      Curl_safefree(ptr->name);
+      ptr->name_alloc = FALSE;
+    }
+    if(ptr->value_alloc) {
+      Curl_safefree(ptr->value);
+      ptr->value_alloc = FALSE;
+    }
+    if(ptr->contenttype_alloc) {
+      Curl_safefree(ptr->contenttype);
+      ptr->contenttype_alloc = FALSE;
+    }
+    if(ptr->showfilename_alloc) {
+      Curl_safefree(ptr->showfilename);
+      ptr->showfilename_alloc = FALSE;
+    }
+  }
+}
+
 /***************************************************************************
  *
  * FormAdd()
@@ -205,18 +218,123 @@ static struct FormInfo *AddFormInfo(char *value,
  *
  ***************************************************************************/
 
+static CURLFORMcode FormAddCheck(struct FormInfo *first_form,
+                                 struct curl_httppost **httppost,
+                                 struct curl_httppost **last_post)
+{
+  const char *prevtype = NULL;
+  struct FormInfo *form = NULL;
+  struct curl_httppost *post = NULL;
+
+  /* go through the list, check for completeness and if everything is
+   * alright add the HttpPost item otherwise set retval accordingly */
+
+  for(form = first_form;
+      form != NULL;
+      form = form->more) {
+    if(((!form->name || !form->value) && !post) ||
+       ( (form->contentslength) &&
+         (form->flags & HTTPPOST_FILENAME) ) ||
+       ( (form->flags & HTTPPOST_FILENAME) &&
+         (form->flags & HTTPPOST_PTRCONTENTS) ) ||
+
+       ( (!form->buffer) &&
+         (form->flags & HTTPPOST_BUFFER) &&
+         (form->flags & HTTPPOST_PTRBUFFER) ) ||
+
+       ( (form->flags & HTTPPOST_READFILE) &&
+         (form->flags & HTTPPOST_PTRCONTENTS) )
+      ) {
+      return CURL_FORMADD_INCOMPLETE;
+    }
+    if(((form->flags & HTTPPOST_FILENAME) ||
+        (form->flags & HTTPPOST_BUFFER)) &&
+       !form->contenttype) {
+      char *f = (form->flags & HTTPPOST_BUFFER) ?
+        form->showfilename : form->value;
+      char const *type;
+      type = Curl_mime_contenttype(f);
+      if(!type)
+        type = prevtype;
+      if(!type)
+        type = FILE_CONTENTTYPE_DEFAULT;
+
+      /* our contenttype is missing */
+      form->contenttype = strdup(type);
+      if(!form->contenttype)
+        return CURL_FORMADD_MEMORY;
+
+      form->contenttype_alloc = TRUE;
+    }
+    if(form->name && form->namelength) {
+      if(memchr(form->name, 0, form->namelength))
+        return CURL_FORMADD_NULL;
+    }
+    if(!(form->flags & HTTPPOST_PTRNAME) && form->name) {
+      /* Note that there is small risk that form->name is NULL here if the app
+         passed in a bad combo, so we check for that. */
+
+      /* copy name (without strdup; possibly not null-terminated) */
+      char *dupname = Curl_memdup0(form->name, form->namelength ?
+                                   form->namelength : strlen(form->name));
+      if(!dupname)
+        return CURL_FORMADD_MEMORY;
+
+      form->name = dupname;
+      form->name_alloc = TRUE;
+    }
+    if(!(form->flags & (HTTPPOST_FILENAME | HTTPPOST_READFILE |
+                        HTTPPOST_PTRCONTENTS | HTTPPOST_PTRBUFFER |
+                        HTTPPOST_CALLBACK)) && form->value) {
+      /* copy value (without strdup; possibly contains null characters) */
+      size_t clen  = (size_t) form->contentslength;
+      if(!clen)
+        clen = strlen(form->value) + 1;
+
+      form->value = Curl_memdup(form->value, clen);
+
+      if(!form->value)
+        return CURL_FORMADD_MEMORY;
+
+      form->value_alloc = TRUE;
+    }
+    post = AddHttpPost(form, post, httppost, last_post);
+
+    if(!post)
+      return CURL_FORMADD_MEMORY;
+
+    if(form->contenttype)
+      prevtype = form->contenttype;
+  }
+
+  return CURL_FORMADD_OK;
+}
+
+/* Shallow cleanup. Remove the newly created chain, the structs only and not
+   the content they point to */
+static void free_chain(struct curl_httppost *c)
+{
+  while(c) {
+    struct curl_httppost *next = c->next;
+    if(c->more)
+      free_chain(c->more);
+    free(c);
+    c = next;
+  }
+}
+
 static
 CURLFORMcode FormAdd(struct curl_httppost **httppost,
                      struct curl_httppost **last_post,
                      va_list params)
 {
-  struct FormInfo *first_form, *current_form, *form = NULL;
-  CURLFORMcode return_value = CURL_FORMADD_OK;
-  const char *prevtype = NULL;
-  struct curl_httppost *post = NULL;
+  struct FormInfo *first_form, *curr, *form = NULL;
+  CURLFORMcode retval = CURL_FORMADD_OK;
   CURLformoption option;
   struct curl_forms *forms = NULL;
-  char *array_value = NULL; /* value read from an array */
+  char *avalue = NULL;
+  struct curl_httppost *newchain = NULL;
+  struct curl_httppost *lastnode = NULL;
 
   /* This is a state variable, that if TRUE means that we are parsing an
      array that we got passed to us. If FALSE we are parsing the input
@@ -230,18 +348,18 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
   if(!first_form)
     return CURL_FORMADD_MEMORY;
 
-  current_form = first_form;
+  curr = first_form;
 
   /*
    * Loop through all the options set. Break if we have an error to report.
    */
-  while(return_value == CURL_FORMADD_OK) {
+  while(retval == CURL_FORMADD_OK) {
 
     /* first see if we have more parts of the array param */
     if(array_state && forms) {
       /* get the upcoming option from the given array */
       option = forms->option;
-      array_value = (char *)forms->value;
+      avalue = (char *)CURL_UNCONST(forms->value);
 
       forms++; /* advance this to next entry */
       if(CURLFORM_END == option) {
@@ -263,13 +381,13 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
     case CURLFORM_ARRAY:
       if(array_state)
         /* we do not support an array from within an array */
-        return_value = CURL_FORMADD_ILLEGAL_ARRAY;
+        retval = CURL_FORMADD_ILLEGAL_ARRAY;
       else {
         forms = va_arg(params, struct curl_forms *);
         if(forms)
           array_state = TRUE;
         else
-          return_value = CURL_FORMADD_NULL;
+          retval = CURL_FORMADD_NULL;
       }
       break;
 
@@ -277,403 +395,253 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
        * Set the Name property.
        */
     case CURLFORM_PTRNAME:
-      current_form->flags |= HTTPPOST_PTRNAME; /* fall through */
+      curr->flags |= HTTPPOST_PTRNAME; /* fall through */
 
       FALLTHROUGH();
     case CURLFORM_COPYNAME:
-      if(current_form->name)
-        return_value = CURL_FORMADD_OPTION_TWICE;
+      if(curr->name)
+        retval = CURL_FORMADD_OPTION_TWICE;
       else {
-        char *name = array_state ?
-          array_value : va_arg(params, char *);
-        if(name)
-          current_form->name = name; /* store for the moment */
+        if(!array_state)
+          avalue = va_arg(params, char *);
+        if(avalue)
+          curr->name = avalue; /* store for the moment */
         else
-          return_value = CURL_FORMADD_NULL;
+          retval = CURL_FORMADD_NULL;
       }
       break;
     case CURLFORM_NAMELENGTH:
-      if(current_form->namelength)
-        return_value = CURL_FORMADD_OPTION_TWICE;
+      if(curr->namelength)
+        retval = CURL_FORMADD_OPTION_TWICE;
       else
-        current_form->namelength =
-          array_state ? (size_t)array_value : (size_t)va_arg(params, long);
+        curr->namelength =
+          array_state ? (size_t)avalue : (size_t)va_arg(params, long);
       break;
 
       /*
        * Set the contents property.
        */
     case CURLFORM_PTRCONTENTS:
-      current_form->flags |= HTTPPOST_PTRCONTENTS;
+      curr->flags |= HTTPPOST_PTRCONTENTS;
       FALLTHROUGH();
     case CURLFORM_COPYCONTENTS:
-      if(current_form->value)
-        return_value = CURL_FORMADD_OPTION_TWICE;
+      if(curr->value)
+        retval = CURL_FORMADD_OPTION_TWICE;
       else {
-        char *value =
-          array_state ? array_value : va_arg(params, char *);
-        if(value)
-          current_form->value = value; /* store for the moment */
+        if(!array_state)
+          avalue = va_arg(params, char *);
+        if(avalue)
+          curr->value = avalue; /* store for the moment */
         else
-          return_value = CURL_FORMADD_NULL;
+          retval = CURL_FORMADD_NULL;
       }
       break;
     case CURLFORM_CONTENTSLENGTH:
-      current_form->contentslength =
-        array_state ? (size_t)array_value : (size_t)va_arg(params, long);
+      curr->contentslength =
+        array_state ? (size_t)avalue : (size_t)va_arg(params, long);
       break;
 
     case CURLFORM_CONTENTLEN:
-      current_form->flags |= CURL_HTTPPOST_LARGE;
-      current_form->contentslength =
-        array_state ? (curl_off_t)(size_t)array_value :
+      curr->flags |= CURL_HTTPPOST_LARGE;
+      curr->contentslength =
+        array_state ? (curl_off_t)(size_t)avalue :
         va_arg(params, curl_off_t);
       break;
 
       /* Get contents from a given filename */
     case CURLFORM_FILECONTENT:
-      if(current_form->flags & (HTTPPOST_PTRCONTENTS|HTTPPOST_READFILE))
-        return_value = CURL_FORMADD_OPTION_TWICE;
+      if(curr->flags & (HTTPPOST_PTRCONTENTS|HTTPPOST_READFILE))
+        retval = CURL_FORMADD_OPTION_TWICE;
       else {
-        const char *filename = array_state ?
-          array_value : va_arg(params, char *);
-        if(filename) {
-          current_form->value = strdup(filename);
-          if(!current_form->value)
-            return_value = CURL_FORMADD_MEMORY;
+        if(!array_state)
+          avalue = va_arg(params, char *);
+        if(avalue) {
+          curr->value = strdup(avalue);
+          if(!curr->value)
+            retval = CURL_FORMADD_MEMORY;
           else {
-            current_form->flags |= HTTPPOST_READFILE;
-            current_form->value_alloc = TRUE;
+            curr->flags |= HTTPPOST_READFILE;
+            curr->value_alloc = TRUE;
           }
         }
         else
-          return_value = CURL_FORMADD_NULL;
+          retval = CURL_FORMADD_NULL;
       }
       break;
 
       /* We upload a file */
     case CURLFORM_FILE:
-      {
-        const char *filename = array_state ? array_value :
-          va_arg(params, char *);
-
-        if(current_form->value) {
-          if(current_form->flags & HTTPPOST_FILENAME) {
-            if(filename) {
-              char *fname = strdup(filename);
-              if(!fname)
-                return_value = CURL_FORMADD_MEMORY;
+      if(!array_state)
+        avalue = va_arg(params, char *);
+
+      if(curr->value) {
+        if(curr->flags & HTTPPOST_FILENAME) {
+          if(avalue) {
+            char *fname = strdup(avalue);
+            if(!fname)
+              retval = CURL_FORMADD_MEMORY;
+            else {
+              form = AddFormInfo(fname, NULL, curr);
+              if(!form) {
+                free(fname);
+                retval = CURL_FORMADD_MEMORY;
+              }
               else {
-                form = AddFormInfo(fname, NULL, current_form);
-                if(!form) {
-                  free(fname);
-                  return_value = CURL_FORMADD_MEMORY;
-                }
-                else {
-                  form->value_alloc = TRUE;
-                  current_form = form;
-                  form = NULL;
-                }
+                form->value_alloc = TRUE;
+                curr = form;
+                form = NULL;
               }
             }
-            else
-              return_value = CURL_FORMADD_NULL;
           }
           else
-            return_value = CURL_FORMADD_OPTION_TWICE;
+            retval = CURL_FORMADD_NULL;
         }
-        else {
-          if(filename) {
-            current_form->value = strdup(filename);
-            if(!current_form->value)
-              return_value = CURL_FORMADD_MEMORY;
-            else {
-              current_form->flags |= HTTPPOST_FILENAME;
-              current_form->value_alloc = TRUE;
-            }
+        else
+          retval = CURL_FORMADD_OPTION_TWICE;
+      }
+      else {
+        if(avalue) {
+          curr->value = strdup(avalue);
+          if(!curr->value)
+            retval = CURL_FORMADD_MEMORY;
+          else {
+            curr->flags |= HTTPPOST_FILENAME;
+            curr->value_alloc = TRUE;
           }
-          else
-            return_value = CURL_FORMADD_NULL;
         }
-        break;
+        else
+          retval = CURL_FORMADD_NULL;
       }
+      break;
 
     case CURLFORM_BUFFERPTR:
-      current_form->flags |= HTTPPOST_PTRBUFFER|HTTPPOST_BUFFER;
-      if(current_form->buffer)
-        return_value = CURL_FORMADD_OPTION_TWICE;
+      curr->flags |= HTTPPOST_PTRBUFFER|HTTPPOST_BUFFER;
+      if(curr->buffer)
+        retval = CURL_FORMADD_OPTION_TWICE;
       else {
-        char *buffer =
-          array_state ? array_value : va_arg(params, char *);
-        if(buffer) {
-          current_form->buffer = buffer; /* store for the moment */
-          current_form->value = buffer; /* make it non-NULL to be accepted
+        if(!array_state)
+          avalue = va_arg(params, char *);
+        if(avalue) {
+          curr->buffer = avalue; /* store for the moment */
+          curr->value = avalue; /* make it non-NULL to be accepted
                                            as fine */
         }
         else
-          return_value = CURL_FORMADD_NULL;
+          retval = CURL_FORMADD_NULL;
       }
       break;
 
     case CURLFORM_BUFFERLENGTH:
-      if(current_form->bufferlength)
-        return_value = CURL_FORMADD_OPTION_TWICE;
+      if(curr->bufferlength)
+        retval = CURL_FORMADD_OPTION_TWICE;
       else
-        current_form->bufferlength =
-          array_state ? (size_t)array_value : (size_t)va_arg(params, long);
+        curr->bufferlength =
+          array_state ? (size_t)avalue : (size_t)va_arg(params, long);
       break;
 
     case CURLFORM_STREAM:
-      current_form->flags |= HTTPPOST_CALLBACK;
-      if(current_form->userp)
-        return_value = CURL_FORMADD_OPTION_TWICE;
+      curr->flags |= HTTPPOST_CALLBACK;
+      if(curr->userp)
+        retval = CURL_FORMADD_OPTION_TWICE;
       else {
-        char *userp =
-          array_state ? array_value : va_arg(params, char *);
-        if(userp) {
-          current_form->userp = userp;
-          current_form->value = userp; /* this is not strictly true but we
-                                          derive a value from this later on
-                                          and we need this non-NULL to be
-                                          accepted as a fine form part */
+        if(!array_state)
+          avalue  = va_arg(params, char *);
+        if(avalue) {
+          curr->userp = avalue;
+          curr->value = avalue; /* this is not strictly true but we derive a
+                                   value from this later on and we need this
+                                   non-NULL to be accepted as a fine form
+                                   part */
         }
         else
-          return_value = CURL_FORMADD_NULL;
+          retval = CURL_FORMADD_NULL;
       }
       break;
 
     case CURLFORM_CONTENTTYPE:
-      {
-        const char *contenttype =
-          array_state ? array_value : va_arg(params, char *);
-        if(current_form->contenttype) {
-          if(current_form->flags & HTTPPOST_FILENAME) {
-            if(contenttype) {
-              char *type = strdup(contenttype);
-              if(!type)
-                return_value = CURL_FORMADD_MEMORY;
+      if(!array_state)
+        avalue = va_arg(params, char *);
+      if(curr->contenttype) {
+        if(curr->flags & HTTPPOST_FILENAME) {
+          if(avalue) {
+            char *type = strdup(avalue);
+            if(!type)
+              retval = CURL_FORMADD_MEMORY;
+            else {
+              form = AddFormInfo(NULL, type, curr);
+              if(!form) {
+                free(type);
+                retval = CURL_FORMADD_MEMORY;
+              }
               else {
-                form = AddFormInfo(NULL, type, current_form);
-                if(!form) {
-                  free(type);
-                  return_value = CURL_FORMADD_MEMORY;
-                }
-                else {
-                  form->contenttype_alloc = TRUE;
-                  current_form = form;
-                  form = NULL;
-                }
+                form->contenttype_alloc = TRUE;
+                curr = form;
+                form = NULL;
               }
             }
-            else
-              return_value = CURL_FORMADD_NULL;
           }
           else
-            return_value = CURL_FORMADD_OPTION_TWICE;
+            retval = CURL_FORMADD_NULL;
         }
-        else {
-          if(contenttype) {
-            current_form->contenttype = strdup(contenttype);
-            if(!current_form->contenttype)
-              return_value = CURL_FORMADD_MEMORY;
-            else
-              current_form->contenttype_alloc = TRUE;
-          }
+        else
+          retval = CURL_FORMADD_OPTION_TWICE;
+      }
+      else {
+        if(avalue) {
+          curr->contenttype = strdup(avalue);
+          if(!curr->contenttype)
+            retval = CURL_FORMADD_MEMORY;
           else
-            return_value = CURL_FORMADD_NULL;
+            curr->contenttype_alloc = TRUE;
         }
-        break;
+        else
+          retval = CURL_FORMADD_NULL;
       }
+      break;
+
     case CURLFORM_CONTENTHEADER:
       {
         /* this "cast increases required alignment of target type" but
            we consider it OK anyway */
         struct curl_slist *list = array_state ?
-          (struct curl_slist *)(void *)array_value :
+          (struct curl_slist *)(void *)avalue :
           va_arg(params, struct curl_slist *);
 
-        if(current_form->contentheader)
-          return_value = CURL_FORMADD_OPTION_TWICE;
+        if(curr->contentheader)
+          retval = CURL_FORMADD_OPTION_TWICE;
         else
-          current_form->contentheader = list;
+          curr->contentheader = list;
 
         break;
       }
     case CURLFORM_FILENAME:
     case CURLFORM_BUFFER:
-      {
-        const char *filename = array_state ? array_value :
-          va_arg(params, char *);
-        if(current_form->showfilename)
-          return_value = CURL_FORMADD_OPTION_TWICE;
-        else {
-          current_form->showfilename = strdup(filename);
-          if(!current_form->showfilename)
-            return_value = CURL_FORMADD_MEMORY;
-          else
-            current_form->showfilename_alloc = TRUE;
-        }
-        break;
+      if(!array_state)
+        avalue = va_arg(params, char *);
+      if(curr->showfilename)
+        retval = CURL_FORMADD_OPTION_TWICE;
+      else {
+        curr->showfilename = strdup(avalue);
+        if(!curr->showfilename)
+          retval = CURL_FORMADD_MEMORY;
+        else
+          curr->showfilename_alloc = TRUE;
       }
+      break;
+
     default:
-      return_value = CURL_FORMADD_UNKNOWN_OPTION;
+      retval = CURL_FORMADD_UNKNOWN_OPTION;
       break;
     }
   }
 
-  if(CURL_FORMADD_OK != return_value) {
+  if(!retval)
+    retval = FormAddCheck(first_form, &newchain, &lastnode);
+
+  if(retval)
     /* On error, free allocated fields for all nodes of the FormInfo linked
        list without deallocating nodes. List nodes are deallocated later on */
-    struct FormInfo *ptr;
-    for(ptr = first_form; ptr != NULL; ptr = ptr->more) {
-      if(ptr->name_alloc) {
-        Curl_safefree(ptr->name);
-        ptr->name_alloc = FALSE;
-      }
-      if(ptr->value_alloc) {
-        Curl_safefree(ptr->value);
-        ptr->value_alloc = FALSE;
-      }
-      if(ptr->contenttype_alloc) {
-        Curl_safefree(ptr->contenttype);
-        ptr->contenttype_alloc = FALSE;
-      }
-      if(ptr->showfilename_alloc) {
-        Curl_safefree(ptr->showfilename);
-        ptr->showfilename_alloc = FALSE;
-      }
-    }
-  }
-
-  if(CURL_FORMADD_OK == return_value) {
-    /* go through the list, check for completeness and if everything is
-     * alright add the HttpPost item otherwise set return_value accordingly */
-
-    post = NULL;
-    for(form = first_form;
-        form != NULL;
-        form = form->more) {
-      if(((!form->name || !form->value) && !post) ||
-         ( (form->contentslength) &&
-           (form->flags & HTTPPOST_FILENAME) ) ||
-         ( (form->flags & HTTPPOST_FILENAME) &&
-           (form->flags & HTTPPOST_PTRCONTENTS) ) ||
-
-         ( (!form->buffer) &&
-           (form->flags & HTTPPOST_BUFFER) &&
-           (form->flags & HTTPPOST_PTRBUFFER) ) ||
-
-         ( (form->flags & HTTPPOST_READFILE) &&
-           (form->flags & HTTPPOST_PTRCONTENTS) )
-        ) {
-        return_value = CURL_FORMADD_INCOMPLETE;
-        break;
-      }
-      if(((form->flags & HTTPPOST_FILENAME) ||
-          (form->flags & HTTPPOST_BUFFER)) &&
-         !form->contenttype) {
-        char *f = (form->flags & HTTPPOST_BUFFER) ?
-          form->showfilename : form->value;
-        char const *type;
-        type = Curl_mime_contenttype(f);
-        if(!type)
-          type = prevtype;
-        if(!type)
-          type = FILE_CONTENTTYPE_DEFAULT;
-
-        /* our contenttype is missing */
-        form->contenttype = strdup(type);
-        if(!form->contenttype) {
-          return_value = CURL_FORMADD_MEMORY;
-          break;
-        }
-        form->contenttype_alloc = TRUE;
-      }
-      if(form->name && form->namelength) {
-        /* Name should not contain nul bytes. */
-        size_t i;
-        for(i = 0; i < form->namelength; i++)
-          if(!form->name[i]) {
-            return_value = CURL_FORMADD_NULL;
-            break;
-          }
-        if(return_value != CURL_FORMADD_OK)
-          break;
-      }
-      if(!(form->flags & HTTPPOST_PTRNAME) &&
-         (form == first_form) ) {
-        /* Note that there is small risk that form->name is NULL here if the
-           app passed in a bad combo, so we better check for that first. */
-        if(form->name) {
-          /* copy name (without strdup; possibly not null-terminated) */
-          form->name = Curl_memdup0(form->name, form->namelength ?
-                                    form->namelength :
-                                    strlen(form->name));
-        }
-        if(!form->name) {
-          return_value = CURL_FORMADD_MEMORY;
-          break;
-        }
-        form->name_alloc = TRUE;
-      }
-      if(!(form->flags & (HTTPPOST_FILENAME | HTTPPOST_READFILE |
-                          HTTPPOST_PTRCONTENTS | HTTPPOST_PTRBUFFER |
-                          HTTPPOST_CALLBACK)) && form->value) {
-        /* copy value (without strdup; possibly contains null characters) */
-        size_t clen  = (size_t) form->contentslength;
-        if(!clen)
-          clen = strlen(form->value) + 1;
-
-        form->value = Curl_memdup(form->value, clen);
-
-        if(!form->value) {
-          return_value = CURL_FORMADD_MEMORY;
-          break;
-        }
-        form->value_alloc = TRUE;
-      }
-      post = AddHttpPost(form->name, form->namelength,
-                         form->value, form->contentslength,
-                         form->buffer, form->bufferlength,
-                         form->contenttype, form->flags,
-                         form->contentheader, form->showfilename,
-                         form->userp,
-                         post, httppost,
-                         last_post);
-
-      if(!post) {
-        return_value = CURL_FORMADD_MEMORY;
-        break;
-      }
-
-      if(form->contenttype)
-        prevtype = form->contenttype;
-    }
-    if(CURL_FORMADD_OK != return_value) {
-      /* On error, free allocated fields for nodes of the FormInfo linked
-         list which are not already owned by the httppost linked list
-         without deallocating nodes. List nodes are deallocated later on */
-      struct FormInfo *ptr;
-      for(ptr = form; ptr != NULL; ptr = ptr->more) {
-        if(ptr->name_alloc) {
-          Curl_safefree(ptr->name);
-          ptr->name_alloc = FALSE;
-        }
-        if(ptr->value_alloc) {
-          Curl_safefree(ptr->value);
-          ptr->value_alloc = FALSE;
-        }
-        if(ptr->contenttype_alloc) {
-          Curl_safefree(ptr->contenttype);
-          ptr->contenttype_alloc = FALSE;
-        }
-        if(ptr->showfilename_alloc) {
-          Curl_safefree(ptr->showfilename);
-          ptr->showfilename_alloc = FALSE;
-        }
-      }
-    }
-  }
+    free_formlist(first_form);
 
   /* Always deallocate FormInfo linked list nodes without touching node
      fields given that these have either been deallocated or are owned
@@ -684,7 +652,19 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
     first_form = ptr;
   }
 
-  return return_value;
+  if(!retval) {
+    /* Only if all is fine, link the new chain into the provided list */
+    if(*last_post)
+      (*last_post)->next = newchain;
+    else
+      (*httppost) = newchain;
+
+    (*last_post) = lastnode;
+  }
+  else
+    free_chain(newchain);
+
+  return retval;
 }
 
 /*
diff --git a/extra/curl/curl-8.12.1/lib/formdata.h b/extra/curl/curl-8.14.1/lib/formdata.h
similarity index 94%
rename from extra/curl/curl-8.12.1/lib/formdata.h
rename to extra/curl/curl-8.14.1/lib/formdata.h
index 0e35e1892bf0..74f00bf4fcbc 100644
--- a/extra/curl/curl-8.12.1/lib/formdata.h
+++ b/extra/curl/curl-8.14.1/lib/formdata.h
@@ -35,7 +35,6 @@ struct FormInfo {
   char *value;
   curl_off_t contentslength;
   char *contenttype;
-  long flags;
   char *buffer;      /* pointer to existing buffer used for file upload */
   size_t bufferlength;
   char *showfilename; /* The filename to show. If not set, the actual
@@ -43,10 +42,11 @@ struct FormInfo {
   char *userp;        /* pointer for the read callback */
   struct curl_slist *contentheader;
   struct FormInfo *more;
-  bool name_alloc;
-  bool value_alloc;
-  bool contenttype_alloc;
-  bool showfilename_alloc;
+  unsigned char flags;
+  BIT(name_alloc);
+  BIT(value_alloc);
+  BIT(contenttype_alloc);
+  BIT(showfilename_alloc);
 };
 
 CURLcode Curl_getformdata(CURL *data,
diff --git a/extra/curl/curl-8.12.1/lib/ftp.c b/extra/curl/curl-8.14.1/lib/ftp.c
similarity index 82%
rename from extra/curl/curl-8.12.1/lib/ftp.c
rename to extra/curl/curl-8.14.1/lib/ftp.c
index 6c6a20704f02..2c6138e98020 100644
--- a/extra/curl/curl-8.12.1/lib/ftp.c
+++ b/extra/curl/curl-8.14.1/lib/ftp.c
@@ -54,7 +54,6 @@
 #include "ftplistparser.h"
 #include "curl_range.h"
 #include "curl_krb5.h"
-#include "strtoofft.h"
 #include "strcase.h"
 #include "vtls/vtls.h"
 #include "cfilters.h"
@@ -62,17 +61,18 @@
 #include "connect.h"
 #include "strerror.h"
 #include "inet_ntop.h"
-#include "inet_pton.h"
+#include "curlx/inet_pton.h"
 #include "select.h"
 #include "parsedate.h" /* for the week day and month names */
 #include "sockaddr.h" /* required for Curl_sockaddr_storage */
 #include "multiif.h"
 #include "url.h"
 #include "speedcheck.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
 #include "http_proxy.h"
 #include "socks.h"
 #include "strdup.h"
+#include "curlx/strparse.h"
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
@@ -95,8 +95,7 @@
 
 #ifdef CURL_DISABLE_VERBOSE_STRINGS
 #define ftp_pasv_verbose(a,b,c,d)  Curl_nop_stmt
-#define FTP_CSTATE(c)   ""
-#define FTP_DSTATE(d)   ""
+#define FTP_CSTATE(c)   ((void)(c), "")
 #else /* CURL_DISABLE_VERBOSE_STRINGS */
   /* for tracing purposes */
 static const char * const ftp_state_names[]={
@@ -136,23 +135,19 @@ static const char * const ftp_state_names[]={
   "STOR",
   "QUIT"
 };
-#define FTP_CSTATE(c)   ((c)? ftp_state_names[(c)->proto.ftpc.state] : "???")
-#define FTP_DSTATE(d)   (((d) && (d)->conn)? \
-                         ftp_state_names[(d)->conn->proto.ftpc.state] : "???")
+#define FTP_CSTATE(ftpc)   ((ftpc)? ftp_state_names[(ftpc)->state] : "???")
 
 #endif /* !CURL_DISABLE_VERBOSE_STRINGS */
 
 /* This is the ONLY way to change FTP state! */
 static void _ftp_state(struct Curl_easy *data,
+                       struct ftp_conn *ftpc,
                        ftpstate newstate
 #ifdef DEBUGBUILD
                        , int lineno
 #endif
   )
 {
-  struct connectdata *conn = data->conn;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
-
 #if defined(CURL_DISABLE_VERBOSE_STRINGS)
 #ifdef DEBUGBUILD
   (void)lineno;
@@ -160,10 +155,10 @@ static void _ftp_state(struct Curl_easy *data,
 #else /* CURL_DISABLE_VERBOSE_STRINGS */
   if(ftpc->state != newstate)
 #ifdef DEBUGBUILD
-    CURL_TRC_FTP(data, "[%s] -> [%s] (line %d)", FTP_DSTATE(data),
+    CURL_TRC_FTP(data, "[%s] -> [%s] (line %d)", FTP_CSTATE(ftpc),
                  ftp_state_names[newstate], lineno);
 #else
-    CURL_TRC_FTP(data, "[%s] -> [%s]", FTP_DSTATE(data),
+    CURL_TRC_FTP(data, "[%s] -> [%s]", FTP_CSTATE(ftpc),
                  ftp_state_names[newstate]);
 #endif
 #endif /* !CURL_DISABLE_VERBOSE_STRINGS */
@@ -174,32 +169,40 @@ static void _ftp_state(struct Curl_easy *data,
 
 /* Local API functions */
 #ifndef DEBUGBUILD
-#define ftp_state(x,y) _ftp_state(x,y)
+#define ftp_state(x,y,z) _ftp_state(x,y,z)
 #else /* !DEBUGBUILD */
-#define ftp_state(x,y) _ftp_state(x,y,__LINE__)
+#define ftp_state(x,y,z) _ftp_state(x,y,z,__LINE__)
 #endif /* DEBUGBUILD */
 
 static CURLcode ftp_sendquote(struct Curl_easy *data,
-                              struct connectdata *conn,
+                              struct ftp_conn *ftpc,
                               struct curl_slist *quote);
-static CURLcode ftp_quit(struct Curl_easy *data, struct connectdata *conn);
-static CURLcode ftp_parse_url_path(struct Curl_easy *data);
-static CURLcode ftp_regular_transfer(struct Curl_easy *data, bool *done);
+static CURLcode ftp_quit(struct Curl_easy *data, struct ftp_conn *ftpc);
+static CURLcode ftp_parse_url_path(struct Curl_easy *data,
+                                   struct ftp_conn *ftpc,
+                                   struct FTP *ftp);
+static CURLcode ftp_regular_transfer(struct Curl_easy *data,
+                                     struct ftp_conn *ftpc,
+                                     struct FTP *ftp,
+                                     bool *done);
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
 static void ftp_pasv_verbose(struct Curl_easy *data,
                              struct Curl_addrinfo *ai,
                              char *newhost, /* ASCII version */
                              int port);
 #endif
-static CURLcode ftp_state_prepare_transfer(struct Curl_easy *data);
-static CURLcode ftp_state_mdtm(struct Curl_easy *data);
+static CURLcode ftp_state_mdtm(struct Curl_easy *data,
+                               struct ftp_conn *ftpc,
+                               struct FTP *ftp);
 static CURLcode ftp_state_quote(struct Curl_easy *data,
+                                struct ftp_conn *ftpc,
+                                struct FTP *ftp,
                                 bool init, ftpstate instate);
 static CURLcode ftp_nb_type(struct Curl_easy *data,
-                            struct connectdata *conn,
+                            struct ftp_conn *ftpc,
+                            struct FTP *ftp,
                             bool ascii, ftpstate newstate);
-static int ftp_need_type(struct connectdata *conn,
-                         bool ascii);
+static int ftp_need_type(struct ftp_conn *ftpc, bool ascii);
 static CURLcode ftp_do(struct Curl_easy *data, bool *done);
 static CURLcode ftp_done(struct Curl_easy *data,
                          CURLcode, bool premature);
@@ -216,16 +219,26 @@ static CURLcode ftp_doing(struct Curl_easy *data,
                           bool *dophase_done);
 static CURLcode ftp_setup_connection(struct Curl_easy *data,
                                      struct connectdata *conn);
-static CURLcode init_wc_data(struct Curl_easy *data);
-static CURLcode wc_statemach(struct Curl_easy *data);
+static CURLcode init_wc_data(struct Curl_easy *data,
+                             struct ftp_conn *ftpc,
+                             struct FTP *ftp);
+static CURLcode wc_statemach(struct Curl_easy *data,
+                             struct ftp_conn *ftpc,
+                             struct FTP *ftp);
 static void wc_data_dtor(void *ptr);
-static CURLcode ftp_state_retr(struct Curl_easy *data, curl_off_t filesize);
+static CURLcode ftp_state_retr(struct Curl_easy *data,
+                               struct ftp_conn *ftpc,
+                               struct FTP *ftp,
+                               curl_off_t filesize);
 static CURLcode ftp_readresp(struct Curl_easy *data,
+                             struct ftp_conn *ftpc,
                              int sockindex,
                              struct pingpong *pp,
                              int *ftpcode,
                              size_t *size);
 static CURLcode ftp_dophase_done(struct Curl_easy *data,
+                                 struct ftp_conn *ftpc,
+                                 struct FTP *ftp,
                                  bool connected);
 
 /*
@@ -292,9 +305,11 @@ const struct Curl_handler Curl_handler_ftps = {
 };
 #endif
 
-static void close_secondarysocket(struct Curl_easy *data)
+static void close_secondarysocket(struct Curl_easy *data,
+                                  struct ftp_conn *ftpc)
 {
-  CURL_TRC_FTP(data, "[%s] closing DATA connection", FTP_DSTATE(data));
+  (void)ftpc;
+  CURL_TRC_FTP(data, "[%s] closing DATA connection", FTP_CSTATE(ftpc));
   Curl_conn_close(data, SECONDARYSOCKET);
   Curl_conn_cf_discard_all(data, data->conn, SECONDARYSOCKET);
 }
@@ -330,8 +345,7 @@ static void freedirs(struct ftp_conn *ftpc)
 }
 
 #ifdef CURL_PREFER_LF_LINEENDS
-/***********************************************************************
- *
+/*
  * Lineend Conversions
  * On ASCII transfers, e.g. directory listings, we might get lines
  * ending in '\r\n' and we prefer just '\n'.
@@ -348,9 +362,12 @@ static CURLcode ftp_cw_lc_write(struct Curl_easy *data,
 {
   static const char nl = '\n';
   struct ftp_cw_lc_ctx *ctx = writer->ctx;
+  struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN);
+
+  if(!ftpc)
+    return CURLE_FAILED_INIT;
 
-  if(!(type & CLIENTWRITE_BODY) ||
-     data->conn->proto.ftpc.transfertype != 'A')
+  if(!(type & CLIENTWRITE_BODY) || ftpc->transfertype != 'A')
     return Curl_cwriter_write(data, writer->next, type, buf, blen);
 
   /* ASCII mode BODY data, convert lineends */
@@ -424,18 +441,18 @@ static const struct Curl_cwtype ftp_cw_lc = {
  * ftp_check_ctrl_on_data_wait()
  *
  */
-static CURLcode ftp_check_ctrl_on_data_wait(struct Curl_easy *data)
+static CURLcode ftp_check_ctrl_on_data_wait(struct Curl_easy *data,
+                                            struct ftp_conn *ftpc)
 {
   struct connectdata *conn = data->conn;
   curl_socket_t ctrl_sock = conn->sock[FIRSTSOCKET];
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
   struct pingpong *pp = &ftpc->pp;
   ssize_t nread;
   int ftpcode;
   bool response = FALSE;
 
   /* First check whether there is a cached response from server */
-  if(Curl_dyn_len(&pp->recvbuf) && (*Curl_dyn_ptr(&pp->recvbuf) > '3')) {
+  if(curlx_dyn_len(&pp->recvbuf) && (*curlx_dyn_ptr(&pp->recvbuf) > '3')) {
     /* Data connection could not be established, let's return */
     infof(data, "There is negative response in cache while serv connect");
     (void)Curl_GetFTPResponse(data, &nread, &ftpcode);
@@ -464,16 +481,16 @@ static CURLcode ftp_check_ctrl_on_data_wait(struct Curl_easy *data)
   if(response) {
     infof(data, "Ctrl conn has data while waiting for data conn");
     if(pp->overflow > 3) {
-      char *r = Curl_dyn_ptr(&pp->recvbuf);
+      const char *r = curlx_dyn_ptr(&pp->recvbuf);
 
       DEBUGASSERT((pp->overflow + pp->nfinal) <=
-                  Curl_dyn_len(&pp->recvbuf));
+                  curlx_dyn_len(&pp->recvbuf));
       /* move over the most recently handled response line */
       r += pp->nfinal;
 
       if(LASTLINE(r)) {
-        int status = curlx_sltosi(strtol(r, NULL, 10));
-        if(status == 226) {
+        curl_off_t status;
+        if(!curlx_str_number(&r, &status, 999) && (status == 226)) {
           /* funny timing situation where we get the final message on the
              control connection before traffic on the data connection has been
              noticed. Leave the 226 in there and use this as a trigger to read
@@ -499,30 +516,30 @@ static CURLcode ftp_check_ctrl_on_data_wait(struct Curl_easy *data)
 
 /***********************************************************************
  *
- * InitiateTransfer()
+ * ftp_initiate_transfer()
  *
  * After connection from server is accepted this function is called to
  * setup transfer parameters and initiate the data transfer.
  *
  */
-static CURLcode InitiateTransfer(struct Curl_easy *data)
+static CURLcode ftp_initiate_transfer(struct Curl_easy *data,
+                                      struct ftp_conn *ftpc)
 {
   CURLcode result = CURLE_OK;
-  struct connectdata *conn = data->conn;
   bool connected;
 
-  CURL_TRC_FTP(data, "InitiateTransfer()");
+  CURL_TRC_FTP(data, "ftp_initiate_transfer()");
   result = Curl_conn_connect(data, SECONDARYSOCKET, TRUE, &connected);
   if(result || !connected)
     return result;
 
-  if(conn->proto.ftpc.state_saved == FTP_STOR) {
+  if(ftpc->state_saved == FTP_STOR) {
     /* When we know we are uploading a specified file, we can get the file
        size prior to the actual upload. */
     Curl_pgrsSetUploadSize(data, data->state.infilesize);
 
     /* set the SO_SNDBUF for the secondary socket for those who need it */
-    Curl_sndbuf_init(conn->sock[SECONDARYSOCKET]);
+    Curl_sndbuf_init(data->conn->sock[SECONDARYSOCKET]);
 
     /* FTP upload, shutdown DATA, ignore shutdown errors, as we rely
      * on the server response on the CONTROL connection. */
@@ -531,23 +548,24 @@ static CURLcode InitiateTransfer(struct Curl_easy *data)
   else {
     /* FTP download, shutdown, do not ignore errors */
     Curl_xfer_setup2(data, CURL_XFER_RECV,
-                     conn->proto.ftpc.retr_size_saved, TRUE, FALSE);
+                     ftpc->retr_size_saved, TRUE, FALSE);
   }
 
-  conn->proto.ftpc.pp.pending_resp = TRUE; /* expect server response */
-  ftp_state(data, FTP_STOP);
+  ftpc->pp.pending_resp = TRUE; /* expect server response */
+  ftp_state(data, ftpc, FTP_STOP);
 
   return CURLE_OK;
 }
 
 static bool ftp_endofresp(struct Curl_easy *data, struct connectdata *conn,
-                          char *line, size_t len, int *code)
+                          const char *line, size_t len, int *code)
 {
+  curl_off_t status;
   (void)data;
   (void)conn;
 
-  if((len > 3) && LASTLINE(line)) {
-    *code = curlx_sltosi(strtol(line, NULL, 10));
+  if((len > 3) && LASTLINE(line) && !curlx_str_number(&line, &status, 999)) {
+    *code = (int)status;
     return TRUE;
   }
 
@@ -555,6 +573,7 @@ static bool ftp_endofresp(struct Curl_easy *data, struct connectdata *conn,
 }
 
 static CURLcode ftp_readresp(struct Curl_easy *data,
+                             struct ftp_conn *ftpc,
                              int sockindex,
                              struct pingpong *pp,
                              int *ftpcode, /* return the ftp-code if done */
@@ -566,7 +585,7 @@ static CURLcode ftp_readresp(struct Curl_easy *data,
 #ifdef HAVE_GSSAPI
   {
     struct connectdata *conn = data->conn;
-    char * const buf = Curl_dyn_ptr(&data->conn->proto.ftpc.pp.recvbuf);
+    char * const buf = curlx_dyn_ptr(&ftpc->pp.recvbuf);
 
     /* handle the security-oriented responses 6xx ***/
     switch(code) {
@@ -586,8 +605,9 @@ static CURLcode ftp_readresp(struct Curl_easy *data,
   }
 #endif
 
-  /* store the latest code for later retrieval */
-  data->info.httpcode = code;
+  /* store the latest code for later retrieval, except during shutdown */
+  if(!ftpc->shutdown)
+    data->info.httpcode = code;
 
   if(ftpcode)
     *ftpcode = code;
@@ -601,7 +621,7 @@ static CURLcode ftp_readresp(struct Curl_easy *data,
      * generically is a good idea.
      */
     infof(data, "We got a 421 - timeout");
-    ftp_state(data, FTP_STOP);
+    ftp_state(data, ftpc, FTP_STOP);
     return CURLE_OPERATION_TIMEDOUT;
   }
 
@@ -630,21 +650,22 @@ CURLcode Curl_GetFTPResponse(struct Curl_easy *data,
   struct connectdata *conn = data->conn;
   curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
   CURLcode result = CURLE_OK;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
+  struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN);
   struct pingpong *pp = &ftpc->pp;
   size_t nread;
   int cache_skip = 0;
   int value_to_be_ignored = 0;
 
   CURL_TRC_FTP(data, "getFTPResponse start");
-
+  *nreadp = 0;
   if(ftpcode)
     *ftpcode = 0; /* 0 for errors */
   else
     /* make the pointer point to something for the rest of this function */
     ftpcode = &value_to_be_ignored;
 
-  *nreadp = 0;
+  if(!ftpc)
+    return CURLE_FAILED_INIT;
 
   while(!*ftpcode && !result) {
     /* check and reset timeout value every lap */
@@ -674,7 +695,7 @@ CURLcode Curl_GetFTPResponse(struct Curl_easy *data,
      *
      */
 
-    if(Curl_dyn_len(&pp->recvbuf) && (cache_skip < 2)) {
+    if(curlx_dyn_len(&pp->recvbuf) && (cache_skip < 2)) {
       /*
        * There is a cache left since before. We then skipping the wait for
        * socket action, unless this is the same cache like the previous round
@@ -704,11 +725,11 @@ CURLcode Curl_GetFTPResponse(struct Curl_easy *data,
         break;
     }
 
-    result = ftp_readresp(data, FIRSTSOCKET, pp, ftpcode, &nread);
+    result = ftp_readresp(data, ftpc, FIRSTSOCKET, pp, ftpcode, &nread);
     if(result)
       break;
 
-    if(!nread && Curl_dyn_len(&pp->recvbuf))
+    if(!nread && curlx_dyn_len(&pp->recvbuf))
       /* bump cache skip counter as on repeated skips we must wait for more
          data */
       cache_skip++;
@@ -729,25 +750,24 @@ CURLcode Curl_GetFTPResponse(struct Curl_easy *data,
 }
 
 static CURLcode ftp_state_user(struct Curl_easy *data,
+                               struct ftp_conn *ftpc,
                                struct connectdata *conn)
 {
-  CURLcode result = Curl_pp_sendf(data,
-                                  &conn->proto.ftpc.pp, "USER %s",
+  CURLcode result = Curl_pp_sendf(data, &ftpc->pp, "USER %s",
                                   conn->user ? conn->user : "");
   if(!result) {
-    struct ftp_conn *ftpc = &conn->proto.ftpc;
     ftpc->ftp_trying_alternative = FALSE;
-    ftp_state(data, FTP_USER);
+    ftp_state(data, ftpc, FTP_USER);
   }
   return result;
 }
 
 static CURLcode ftp_state_pwd(struct Curl_easy *data,
-                              struct connectdata *conn)
+                              struct ftp_conn *ftpc)
 {
-  CURLcode result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", "PWD");
+  CURLcode result = Curl_pp_sendf(data, &ftpc->pp, "%s", "PWD");
   if(!result)
-    ftp_state(data, FTP_PWD);
+    ftp_state(data, ftpc, FTP_PWD);
 
   return result;
 }
@@ -757,21 +777,25 @@ static int ftp_getsock(struct Curl_easy *data,
                        struct connectdata *conn,
                        curl_socket_t *socks)
 {
-  return Curl_pp_getsock(data, &conn->proto.ftpc.pp, socks);
+  struct ftp_conn *ftpc = Curl_conn_meta_get(conn, CURL_META_FTP_CONN);
+  return ftpc ? Curl_pp_getsock(data, &ftpc->pp, socks) : GETSOCK_BLANK;
 }
 
 /* For the FTP "DO_MORE" phase only */
 static int ftp_domore_getsock(struct Curl_easy *data,
                               struct connectdata *conn, curl_socket_t *socks)
 {
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
+  struct ftp_conn *ftpc = Curl_conn_meta_get(conn, CURL_META_FTP_CONN);
   (void)data;
 
+  if(!ftpc)
+    return GETSOCK_BLANK;
+
   /* When in DO_MORE state, we could be either waiting for us to connect to a
    * remote site, or we could wait for that site to connect to us. Or just
    * handle ordinary commands.
    */
-  CURL_TRC_FTP(data, "[%s] ftp_domore_getsock()", FTP_DSTATE(data));
+  CURL_TRC_FTP(data, "[%s] ftp_domore_getsock()", FTP_CSTATE(ftpc));
 
   if(FTP_STOP == ftpc->state) {
     /* if stopped and still in this state, then we are also waiting for a
@@ -784,7 +808,7 @@ static int ftp_domore_getsock(struct Curl_easy *data,
      * via its adjust_pollset() */
     return GETSOCK_READSOCK(0);
   }
-  return Curl_pp_getsock(data, &conn->proto.ftpc.pp, socks);
+  return Curl_pp_getsock(data, &ftpc->pp, socks);
 }
 
 /* This is called after the FTP_QUOTE state is passed.
@@ -794,14 +818,14 @@ static int ftp_domore_getsock(struct Curl_easy *data,
    missing ones, if that option is enabled.
 */
 static CURLcode ftp_state_cwd(struct Curl_easy *data,
-                              struct connectdata *conn)
+                              struct ftp_conn *ftpc,
+                              struct FTP *ftp)
 {
   CURLcode result = CURLE_OK;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
 
   if(ftpc->cwddone)
     /* already done and fine */
-    result = ftp_state_mdtm(data);
+    result = ftp_state_mdtm(data, ftpc, ftp);
   else {
     /* FTPFILE_NOCWD with full path: expect ftpc->cwddone! */
     DEBUGASSERT((data->set.ftp_filemethod != FTPFILE_NOCWD) ||
@@ -809,7 +833,7 @@ static CURLcode ftp_state_cwd(struct Curl_easy *data,
 
     ftpc->count2 = 0; /* count2 counts failed CWDs */
 
-    if(conn->bits.reuse && ftpc->entrypath &&
+    if(data->conn->bits.reuse && ftpc->entrypath &&
        /* no need to go to entrypath when we have an absolute path */
        !(ftpc->dirdepth && ftpc->dirs[0][0] == '/')) {
       /* This is a reused connection. Since we change directory to where the
@@ -819,7 +843,7 @@ static CURLcode ftp_state_cwd(struct Curl_easy *data,
                              for all upcoming ones in the ftp->dirs[] array */
       result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s", ftpc->entrypath);
       if(!result)
-        ftp_state(data, FTP_CWD);
+        ftp_state(data, ftpc, FTP_CWD);
     }
     else {
       if(ftpc->dirdepth) {
@@ -829,11 +853,11 @@ static CURLcode ftp_state_cwd(struct Curl_easy *data,
         result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s",
                                ftpc->dirs[ftpc->cwdcount -1]);
         if(!result)
-          ftp_state(data, FTP_CWD);
+          ftp_state(data, ftpc, FTP_CWD);
       }
       else {
         /* No CWD necessary */
-        result = ftp_state_mdtm(data);
+        result = ftp_state_mdtm(data, ftpc, ftp);
       }
     }
   }
@@ -847,11 +871,11 @@ typedef enum {
 } ftpport;
 
 static CURLcode ftp_state_use_port(struct Curl_easy *data,
+                                   struct ftp_conn *ftpc,
                                    ftpport fcmd) /* start with this */
 {
   CURLcode result = CURLE_FTP_PORT_FAILED;
   struct connectdata *conn = data->conn;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
   curl_socket_t portsock = CURL_SOCKET_BAD;
   char myhost[MAX_IPADR_LEN + 1] = "";
 
@@ -865,7 +889,6 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
   struct sockaddr_in6 * const sa6 = (void *)sa;
 #endif
   static const char mode[][5] = { "EPRT", "PORT" };
-  enum resolve_t rc;
   int error;
   char *host = NULL;
   char *string_ftpport = data->set.str[STRING_FTPPORT];
@@ -911,7 +934,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
           /* either ipv6 or (ipv4|domain|interface):port(-range) */
           addrlen = ip_end - string_ftpport;
 #ifdef USE_IPV6
-          if(Curl_inet_pton(AF_INET6, string_ftpport, &sa6->sin6_addr) == 1) {
+          if(curlx_inet_pton(AF_INET6, string_ftpport, &sa6->sin6_addr) == 1) {
             /* ipv6 */
             port_min = port_max = 0;
             ip_end = NULL; /* this got no port ! */
@@ -925,13 +948,20 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
 
     /* parse the port */
     if(ip_end) {
-      char *port_sep = NULL;
-      char *port_start = strchr(ip_end, ':');
-      if(port_start) {
-        port_min = curlx_ultous(strtoul(port_start + 1, NULL, 10));
-        port_sep = strchr(port_start, '-');
-        if(port_sep) {
-          port_max = curlx_ultous(strtoul(port_sep + 1, NULL, 10));
+      const char *portp = strchr(ip_end, ':');
+      if(portp) {
+        curl_off_t start;
+        curl_off_t end;
+        portp++;
+        if(!curlx_str_number(&portp, &start, 0xffff)) {
+          /* got the first number */
+          port_min = (unsigned short)start;
+          if(!curlx_str_single(&portp, '-')) {
+            /* got the dash */
+            if(!curlx_str_number(&portp, &end, 0xffff))
+              /* got the second number */
+              port_max = (unsigned short)end;
+          }
         }
         else
           port_max = port_min;
@@ -1005,14 +1035,12 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
   }
 
   /* resolv ip/host to ip */
-  rc = Curl_resolv(data, host, 0, FALSE, &dns_entry);
-  if(rc == CURLRESOLV_PENDING)
-    (void)Curl_resolver_wait_resolv(data, &dns_entry);
-  if(dns_entry) {
+  res = NULL;
+  result = Curl_resolv_blocking(data, host, 0, conn->ip_version, &dns_entry);
+  if(!result) {
+    DEBUGASSERT(dns_entry);
     res = dns_entry->addr;
   }
-  else
-    res = NULL; /* failure! */
 
   if(!res) {
     failf(data, "failed to resolve the address provided to PORT: %s", host);
@@ -1036,7 +1064,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
     goto out;
   }
   CURL_TRC_FTP(data, "[%s] ftp_state_use_port(), opened socket",
-               FTP_DSTATE(data));
+               FTP_CSTATE(ftpc));
 
   /* step 3, bind to a suitable local address */
 
@@ -1054,7 +1082,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
     if(bind(portsock, sa, sslen) ) {
       /* It failed. */
       error = SOCKERRNO;
-      if(possibly_non_local && (error == EADDRNOTAVAIL)) {
+      if(possibly_non_local && (error == SOCKEADDRNOTAVAIL)) {
         /* The requested bind address is not local. Use the address used for
          * the control connection instead and restart the port loop
          */
@@ -1071,7 +1099,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
         possibly_non_local = FALSE; /* do not try this again */
         continue;
       }
-      if(error != EADDRINUSE && error != EACCES) {
+      if(error != SOCKEADDRINUSE && error != SOCKEACCES) {
         failf(data, "bind(port=%hu) failed: %s", port,
               Curl_strerror(error, buffer, sizeof(buffer)));
         goto out;
@@ -1098,7 +1126,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
     goto out;
   }
   CURL_TRC_FTP(data, "[%s] ftp_state_use_port(), socket bound to port %d",
-               FTP_DSTATE(data), port);
+               FTP_CSTATE(ftpc), port);
 
   /* step 4, listen on the socket */
 
@@ -1108,7 +1136,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
     goto out;
   }
   CURL_TRC_FTP(data, "[%s] ftp_state_use_port(), listening on %d",
-               FTP_DSTATE(data), port);
+               FTP_CSTATE(ftpc), port);
 
   /* step 5, send the proper FTP command */
 
@@ -1118,7 +1146,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
 
 #ifdef USE_IPV6
   if(!conn->bits.ftp_use_eprt && conn->bits.ipv6)
-    /* EPRT is disabled but we are connected to a IPv6 host, so we ignore the
+    /* EPRT is disabled but we are connected to an IPv6 host, so we ignore the
        request and enable EPRT again! */
     conn->bits.ftp_use_eprt = TRUE;
 #endif
@@ -1195,7 +1223,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
 
   /* store which command was sent */
   ftpc->count1 = fcmd;
-  ftp_state(data, FTP_PORT);
+  ftp_state(data, ftpc, FTP_PORT);
 
   /* Replace any filter on SECONDARY with one listening on this socket */
   result = Curl_conn_tcp_listen_set(data, conn, SECONDARYSOCKET, &portsock);
@@ -1207,7 +1235,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
   if(dns_entry)
     Curl_resolv_unlink(data, &dns_entry);
   if(result) {
-    ftp_state(data, FTP_STOP);
+    ftp_state(data, ftpc, FTP_STOP);
   }
   else {
     /* successfully setup the list socket filter. Do we need more? */
@@ -1227,9 +1255,9 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
 }
 
 static CURLcode ftp_state_use_pasv(struct Curl_easy *data,
+                                   struct ftp_conn *ftpc,
                                    struct connectdata *conn)
 {
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
   CURLcode result = CURLE_OK;
   /*
     Here's the executive summary on what to do:
@@ -1250,7 +1278,7 @@ static CURLcode ftp_state_use_pasv(struct Curl_easy *data,
 
 #ifdef PF_INET6
   if(!conn->bits.ftp_use_epsv && conn->bits.ipv6)
-    /* EPSV is disabled but we are connected to a IPv6 host, so we ignore the
+    /* EPSV is disabled but we are connected to an IPv6 host, so we ignore the
        request and enable EPSV again! */
     conn->bits.ftp_use_epsv = TRUE;
 #endif
@@ -1260,7 +1288,7 @@ static CURLcode ftp_state_use_pasv(struct Curl_easy *data,
   result = Curl_pp_sendf(data, &ftpc->pp, "%s", mode[modeoff]);
   if(!result) {
     ftpc->count1 = modeoff;
-    ftp_state(data, FTP_PASV);
+    ftp_state(data, ftpc, FTP_PASV);
     infof(data, "Connect data stream passively");
   }
   return result;
@@ -1273,55 +1301,52 @@ static CURLcode ftp_state_use_pasv(struct Curl_easy *data,
  * request is made. Thus, if an actual transfer is to be made this is where we
  * take off for real.
  */
-static CURLcode ftp_state_prepare_transfer(struct Curl_easy *data)
+static CURLcode ftp_state_prepare_transfer(struct Curl_easy *data,
+                                           struct ftp_conn *ftpc,
+                                           struct FTP *ftp)
 {
   CURLcode result = CURLE_OK;
-  struct FTP *ftp = data->req.p.ftp;
   struct connectdata *conn = data->conn;
 
   if(ftp->transfer != PPTRANSFER_BODY) {
     /* does not transfer any data */
 
     /* still possibly do PRE QUOTE jobs */
-    ftp_state(data, FTP_RETR_PREQUOTE);
-    result = ftp_state_quote(data, TRUE, FTP_RETR_PREQUOTE);
+    ftp_state(data, ftpc, FTP_RETR_PREQUOTE);
+    result = ftp_state_quote(data, ftpc, ftp, TRUE, FTP_RETR_PREQUOTE);
   }
   else if(data->set.ftp_use_port) {
     /* We have chosen to use the PORT (or similar) command */
-    result = ftp_state_use_port(data, EPRT);
+    result = ftp_state_use_port(data, ftpc, EPRT);
   }
   else {
     /* We have chosen (this is default) to use the PASV (or similar) command */
     if(data->set.ftp_use_pret) {
       /* The user has requested that we send a PRET command
          to prepare the server for the upcoming PASV */
-      struct ftp_conn *ftpc = &conn->proto.ftpc;
-      if(!conn->proto.ftpc.file)
+      if(!ftpc->file)
         result = Curl_pp_sendf(data, &ftpc->pp, "PRET %s",
                                data->set.str[STRING_CUSTOMREQUEST] ?
                                data->set.str[STRING_CUSTOMREQUEST] :
                                (data->state.list_only ? "NLST" : "LIST"));
       else if(data->state.upload)
-        result = Curl_pp_sendf(data, &ftpc->pp, "PRET STOR %s",
-                               conn->proto.ftpc.file);
+        result = Curl_pp_sendf(data, &ftpc->pp, "PRET STOR %s", ftpc->file);
       else
-        result = Curl_pp_sendf(data, &ftpc->pp, "PRET RETR %s",
-                               conn->proto.ftpc.file);
+        result = Curl_pp_sendf(data, &ftpc->pp, "PRET RETR %s", ftpc->file);
       if(!result)
-        ftp_state(data, FTP_PRET);
+        ftp_state(data, ftpc, FTP_PRET);
     }
     else
-      result = ftp_state_use_pasv(data, conn);
+      result = ftp_state_use_pasv(data, ftpc, conn);
   }
   return result;
 }
 
 static CURLcode ftp_state_rest(struct Curl_easy *data,
-                               struct connectdata *conn)
+                               struct ftp_conn *ftpc,
+                               struct FTP *ftp)
 {
   CURLcode result = CURLE_OK;
-  struct FTP *ftp = data->req.p.ftp;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
 
   if((ftp->transfer != PPTRANSFER_BODY) && ftpc->file) {
     /* if a "head"-like request is being made (on a file) */
@@ -1330,20 +1355,19 @@ static CURLcode ftp_state_rest(struct Curl_easy *data,
        whether it supports range */
     result = Curl_pp_sendf(data, &ftpc->pp, "REST %d", 0);
     if(!result)
-      ftp_state(data, FTP_REST);
+      ftp_state(data, ftpc, FTP_REST);
   }
   else
-    result = ftp_state_prepare_transfer(data);
+    result = ftp_state_prepare_transfer(data, ftpc, ftp);
 
   return result;
 }
 
 static CURLcode ftp_state_size(struct Curl_easy *data,
-                               struct connectdata *conn)
+                               struct ftp_conn *ftpc,
+                               struct FTP *ftp)
 {
   CURLcode result = CURLE_OK;
-  struct FTP *ftp = data->req.p.ftp;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
 
   if((ftp->transfer == PPTRANSFER_INFO) && ftpc->file) {
     /* if a "head"-like request is being made (on a file) */
@@ -1351,19 +1375,19 @@ static CURLcode ftp_state_size(struct Curl_easy *data,
     /* we know ftpc->file is a valid pointer to a filename */
     result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file);
     if(!result)
-      ftp_state(data, FTP_SIZE);
+      ftp_state(data, ftpc, FTP_SIZE);
   }
   else
-    result = ftp_state_rest(data, conn);
+    result = ftp_state_rest(data, ftpc, ftp);
 
   return result;
 }
 
-static CURLcode ftp_state_list(struct Curl_easy *data)
+static CURLcode ftp_state_list(struct Curl_easy *data,
+                               struct ftp_conn *ftpc,
+                               struct FTP *ftp)
 {
   CURLcode result = CURLE_OK;
-  struct FTP *ftp = data->req.p.ftp;
-  struct connectdata *conn = data->conn;
 
   /* If this output is to be machine-parsed, the NLST command might be better
      to use, since the LIST command output is not specified or standard in any
@@ -1415,39 +1439,42 @@ static CURLcode ftp_state_list(struct Curl_easy *data)
   if(!cmd)
     return CURLE_OUT_OF_MEMORY;
 
-  result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", cmd);
+  result = Curl_pp_sendf(data, &ftpc->pp, "%s", cmd);
   free(cmd);
 
   if(!result)
-    ftp_state(data, FTP_LIST);
+    ftp_state(data, ftpc, FTP_LIST);
 
   return result;
 }
 
-static CURLcode ftp_state_retr_prequote(struct Curl_easy *data)
+static CURLcode ftp_state_retr_prequote(struct Curl_easy *data,
+                                        struct ftp_conn *ftpc,
+                                        struct FTP *ftp)
 {
   /* We have sent the TYPE, now we must send the list of prequote strings */
-  return ftp_state_quote(data, TRUE, FTP_RETR_PREQUOTE);
+  return ftp_state_quote(data, ftpc, ftp, TRUE, FTP_RETR_PREQUOTE);
 }
 
-static CURLcode ftp_state_stor_prequote(struct Curl_easy *data)
+static CURLcode ftp_state_stor_prequote(struct Curl_easy *data,
+                                        struct ftp_conn *ftpc,
+                                        struct FTP *ftp)
 {
   /* We have sent the TYPE, now we must send the list of prequote strings */
-  return ftp_state_quote(data, TRUE, FTP_STOR_PREQUOTE);
+  return ftp_state_quote(data, ftpc, ftp, TRUE, FTP_STOR_PREQUOTE);
 }
 
-static CURLcode ftp_state_type(struct Curl_easy *data)
+static CURLcode ftp_state_type(struct Curl_easy *data,
+                               struct ftp_conn *ftpc,
+                               struct FTP *ftp)
 {
   CURLcode result = CURLE_OK;
-  struct FTP *ftp = data->req.p.ftp;
-  struct connectdata *conn = data->conn;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
 
   /* If we have selected NOBODY and HEADER, it means that we only want file
      information. Which in FTP cannot be much more than the file size and
      date. */
   if(data->req.no_body && ftpc->file &&
-     ftp_need_type(conn, data->state.prefer_ascii)) {
+     ftp_need_type(ftpc, data->state.prefer_ascii)) {
     /* The SIZE command is _not_ RFC 959 specified, and therefore many servers
        may not support it! It is however the only way we have to get a file's
        size! */
@@ -1457,23 +1484,23 @@ static CURLcode ftp_state_type(struct Curl_easy *data)
 
     /* Some servers return different sizes for different modes, and thus we
        must set the proper type before we check the size */
-    result = ftp_nb_type(data, conn, data->state.prefer_ascii, FTP_TYPE);
+    result = ftp_nb_type(data, ftpc, ftp, data->state.prefer_ascii, FTP_TYPE);
     if(result)
       return result;
   }
   else
-    result = ftp_state_size(data, conn);
+    result = ftp_state_size(data, ftpc, ftp);
 
   return result;
 }
 
 /* This is called after the CWD commands have been done in the beginning of
    the DO phase */
-static CURLcode ftp_state_mdtm(struct Curl_easy *data)
+static CURLcode ftp_state_mdtm(struct Curl_easy *data,
+                               struct ftp_conn *ftpc,
+                               struct FTP *ftp)
 {
   CURLcode result = CURLE_OK;
-  struct connectdata *conn = data->conn;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
 
   /* Requested time of file or time-depended transfer? */
   if((data->set.get_filetime || data->set.timecondition) && ftpc->file) {
@@ -1483,10 +1510,10 @@ static CURLcode ftp_state_mdtm(struct Curl_easy *data)
     result = Curl_pp_sendf(data, &ftpc->pp, "MDTM %s", ftpc->file);
 
     if(!result)
-      ftp_state(data, FTP_MDTM);
+      ftp_state(data, ftpc, FTP_MDTM);
   }
   else
-    result = ftp_state_type(data);
+    result = ftp_state_type(data, ftpc, ftp);
 
   return result;
 }
@@ -1494,12 +1521,11 @@ static CURLcode ftp_state_mdtm(struct Curl_easy *data)
 
 /* This is called after the TYPE and possible quote commands have been sent */
 static CURLcode ftp_state_ul_setup(struct Curl_easy *data,
+                                   struct ftp_conn *ftpc,
+                                   struct FTP *ftp,
                                    bool sizechecked)
 {
   CURLcode result = CURLE_OK;
-  struct connectdata *conn = data->conn;
-  struct FTP *ftp = data->req.p.ftp;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
   bool append = data->set.remote_append;
 
   if((data->state.resume_from && !sizechecked) ||
@@ -1522,7 +1548,7 @@ static CURLcode ftp_state_ul_setup(struct Curl_easy *data,
       /* Got no given size to start from, figure it out */
       result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file);
       if(!result)
-        ftp_state(data, FTP_STOR_SIZE);
+        ftp_state(data, ftpc, FTP_STOR_SIZE);
       return result;
     }
 
@@ -1578,7 +1604,7 @@ static CURLcode ftp_state_ul_setup(struct Curl_easy *data,
          * ftp_done() because we did not transfer anything! */
         ftp->transfer = PPTRANSFER_NONE;
 
-        ftp_state(data, FTP_STOP);
+        ftp_state(data, ftpc, FTP_STOP);
         return CURLE_OK;
       }
     }
@@ -1588,19 +1614,18 @@ static CURLcode ftp_state_ul_setup(struct Curl_easy *data,
   result = Curl_pp_sendf(data, &ftpc->pp, append ? "APPE %s" : "STOR %s",
                          ftpc->file);
   if(!result)
-    ftp_state(data, FTP_STOR);
+    ftp_state(data, ftpc, FTP_STOR);
 
   return result;
 }
 
 static CURLcode ftp_state_quote(struct Curl_easy *data,
+                                struct ftp_conn *ftpc,
+                                struct FTP *ftp,
                                 bool init,
                                 ftpstate instate)
 {
   CURLcode result = CURLE_OK;
-  struct FTP *ftp = data->req.p.ftp;
-  struct connectdata *conn = data->conn;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
   bool quote = FALSE;
   struct curl_slist *item;
 
@@ -1649,7 +1674,7 @@ static CURLcode ftp_state_quote(struct Curl_easy *data,
       result = Curl_pp_sendf(data, &ftpc->pp, "%s", cmd);
       if(result)
         return result;
-      ftp_state(data, instate);
+      ftp_state(data, ftpc, instate);
       quote = TRUE;
     }
   }
@@ -1659,15 +1684,15 @@ static CURLcode ftp_state_quote(struct Curl_easy *data,
     switch(instate) {
     case FTP_QUOTE:
     default:
-      result = ftp_state_cwd(data, conn);
+      result = ftp_state_cwd(data, ftpc, ftp);
       break;
     case FTP_RETR_PREQUOTE:
       if(ftp->transfer != PPTRANSFER_BODY)
-        ftp_state(data, FTP_STOP);
+        ftp_state(data, ftpc, FTP_STOP);
       else {
         if(ftpc->known_filesize != -1) {
           Curl_pgrsSetDownloadSize(data, ftpc->known_filesize);
-          result = ftp_state_retr(data, ftpc->known_filesize);
+          result = ftp_state_retr(data, ftpc, ftp, ftpc->known_filesize);
         }
         else {
           if(data->set.ignorecl || data->state.prefer_ascii) {
@@ -1685,18 +1710,18 @@ static CURLcode ftp_state_quote(struct Curl_easy *data,
             */
             result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file);
             if(!result)
-              ftp_state(data, FTP_RETR);
+              ftp_state(data, ftpc, FTP_RETR);
           }
           else {
             result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file);
             if(!result)
-              ftp_state(data, FTP_RETR_SIZE);
+              ftp_state(data, ftpc, FTP_RETR_SIZE);
           }
         }
       }
       break;
     case FTP_STOR_PREQUOTE:
-      result = ftp_state_ul_setup(data, FALSE);
+      result = ftp_state_ul_setup(data, ftpc, ftp, FALSE);
       break;
     case FTP_POSTQUOTE:
       break;
@@ -1709,6 +1734,7 @@ static CURLcode ftp_state_quote(struct Curl_easy *data,
 /* called from ftp_state_pasv_resp to switch to PASV in case of EPSV
    problems */
 static CURLcode ftp_epsv_disable(struct Curl_easy *data,
+                                 struct ftp_conn *ftpc,
                                  struct connectdata *conn)
 {
   CURLcode result = CURLE_OK;
@@ -1726,15 +1752,14 @@ static CURLcode ftp_epsv_disable(struct Curl_easy *data,
   infof(data, "Failed EPSV attempt. Disabling EPSV");
   /* disable it for next transfer */
   conn->bits.ftp_use_epsv = FALSE;
-  Curl_conn_close(data, SECONDARYSOCKET);
-  Curl_conn_cf_discard_all(data, conn, SECONDARYSOCKET);
+  close_secondarysocket(data, ftpc);
   data->state.errorbuf = FALSE; /* allow error message to get
                                          rewritten */
-  result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", "PASV");
+  result = Curl_pp_sendf(data, &ftpc->pp, "%s", "PASV");
   if(!result) {
-    conn->proto.ftpc.count1++;
+    ftpc->count1++;
     /* remain in/go to the FTP_PASV state */
-    ftp_state(data, FTP_PASV);
+    ftp_state(data, ftpc, FTP_PASV);
   }
   return result;
 }
@@ -1758,36 +1783,30 @@ static bool match_pasv_6nums(const char *p,
 {
   int i;
   for(i = 0; i < 6; i++) {
-    unsigned long num;
-    char *endp;
+    curl_off_t num;
     if(i) {
       if(*p != ',')
         return FALSE;
       p++;
     }
-    if(!ISDIGIT(*p))
-      return FALSE;
-    num = strtoul(p, &endp, 10);
-    if(num > 255)
+    if(curlx_str_number(&p, &num, 0xff))
       return FALSE;
     array[i] = (unsigned int)num;
-    p = endp;
   }
   return TRUE;
 }
 
 static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
+                                    struct ftp_conn *ftpc,
                                     int ftpcode)
 {
   struct connectdata *conn = data->conn;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
   CURLcode result;
-  struct Curl_dns_entry *addr = NULL;
-  enum resolve_t rc;
+  struct Curl_dns_entry *dns = NULL;
   unsigned short connectport; /* the local port connect() should use! */
   struct pingpong *pp = &ftpc->pp;
   char *str =
-    Curl_dyn_ptr(&pp->recvbuf) + 4; /* start on the first letter */
+    curlx_dyn_ptr(&pp->recvbuf) + 4; /* start on the first letter */
 
   /* if we come here again, make sure the former name is cleared */
   Curl_safefree(ftpc->newhost);
@@ -1801,23 +1820,17 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
       ptr++;
       /* |||12345| */
       sep = ptr[0];
-      /* the ISDIGIT() check here is because strtoul() accepts leading minus
-         etc */
       if((ptr[1] == sep) && (ptr[2] == sep) && ISDIGIT(ptr[3])) {
-        char *endp;
-        unsigned long num = strtoul(&ptr[3], &endp, 10);
-        if(*endp != sep)
-          ptr = NULL;
-        else if(num > 0xffff) {
+        const char *p = &ptr[3];
+        curl_off_t num;
+        if(curlx_str_number(&p, &num, 0xffff) || (*p != sep)) {
           failf(data, "Illegal port number in EPSV reply");
           return CURLE_FTP_WEIRD_PASV_REPLY;
         }
-        if(ptr) {
-          ftpc->newport = (unsigned short)(num & 0xffff);
-          ftpc->newhost = strdup(control_address(conn));
-          if(!ftpc->newhost)
-            return CURLE_OUT_OF_MEMORY;
-        }
+        ftpc->newport = (unsigned short)num;
+        ftpc->newhost = strdup(control_address(conn));
+        if(!ftpc->newhost)
+          return CURLE_OUT_OF_MEMORY;
       }
       else
         ptr = NULL;
@@ -1871,7 +1884,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
   }
   else if(ftpc->count1 == 0) {
     /* EPSV failed, move on to PASV */
-    return ftp_epsv_disable(data, conn);
+    return ftp_epsv_disable(data, ftpc, conn);
   }
   else {
     failf(data, "Bad PASV/EPSV response: %03d", ftpcode);
@@ -1887,16 +1900,12 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
      */
     const char * const host_name = conn->bits.socksproxy ?
       conn->socks_proxy.host.name : conn->http_proxy.host.name;
-    rc = Curl_resolv(data, host_name, conn->primary.remote_port, FALSE, &addr);
-    if(rc == CURLRESOLV_PENDING)
-      /* BLOCKING, ignores the return code but 'addr' will be NULL in
-         case of failure */
-      (void)Curl_resolver_wait_resolv(data, &addr);
-
+    (void)Curl_resolv_blocking(data, host_name, conn->primary.remote_port,
+                               conn->ip_version, &dns);
     /* we connect to the proxy's port */
     connectport = (unsigned short)conn->primary.remote_port;
 
-    if(!addr) {
+    if(!dns) {
       failf(data, "cannot resolve proxy host %s:%hu", host_name, connectport);
       return CURLE_COULDNT_RESOLVE_PROXY;
     }
@@ -1909,34 +1918,30 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
 
     /* postponed address resolution in case of tcp fastopen */
     if(conn->bits.tcp_fastopen && !conn->bits.reuse && !ftpc->newhost[0]) {
-      Curl_safefree(ftpc->newhost);
+      free(ftpc->newhost);
       ftpc->newhost = strdup(control_address(conn));
       if(!ftpc->newhost)
         return CURLE_OUT_OF_MEMORY;
     }
 
-    rc = Curl_resolv(data, ftpc->newhost, ftpc->newport, FALSE, &addr);
-    if(rc == CURLRESOLV_PENDING)
-      /* BLOCKING */
-      (void)Curl_resolver_wait_resolv(data, &addr);
-
+    (void)Curl_resolv_blocking(data, ftpc->newhost, ftpc->newport,
+                               conn->ip_version, &dns);
     connectport = ftpc->newport; /* we connect to the remote port */
 
-    if(!addr) {
+    if(!dns) {
       failf(data, "cannot resolve new host %s:%hu",
             ftpc->newhost, connectport);
       return CURLE_FTP_CANT_GET_HOST;
     }
   }
 
-  result = Curl_conn_setup(data, conn, SECONDARYSOCKET, addr,
+  result = Curl_conn_setup(data, conn, SECONDARYSOCKET, dns,
                            conn->bits.ftp_use_data_ssl ?
                            CURL_CF_SSL_ENABLE : CURL_CF_SSL_DISABLE);
 
   if(result) {
-    Curl_resolv_unlink(data, &addr); /* we are done using this address */
     if(ftpc->count1 == 0 && ftpcode == 229)
-      return ftp_epsv_disable(data, conn);
+      return ftp_epsv_disable(data, ftpc, conn);
 
     return result;
   }
@@ -1950,27 +1955,26 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
 
   if(data->set.verbose)
     /* this just dumps information about this second connection */
-    ftp_pasv_verbose(data, addr->addr, ftpc->newhost, connectport);
+    ftp_pasv_verbose(data, dns->addr, ftpc->newhost, connectport);
 
-  Curl_resolv_unlink(data, &addr); /* we are done using this address */
-
-  Curl_safefree(conn->secondaryhostname);
+  free(conn->secondaryhostname);
   conn->secondary_port = ftpc->newport;
   conn->secondaryhostname = strdup(ftpc->newhost);
   if(!conn->secondaryhostname)
     return CURLE_OUT_OF_MEMORY;
 
   conn->bits.do_more = TRUE;
-  ftp_state(data, FTP_STOP); /* this phase is completed */
+  ftp_state(data, ftpc, FTP_STOP); /* this phase is completed */
 
   return result;
 }
 
 static CURLcode ftp_state_port_resp(struct Curl_easy *data,
+                                    struct ftp_conn *ftpc,
+                                    struct FTP *ftp,
                                     int ftpcode)
 {
   struct connectdata *conn = data->conn;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
   ftpport fcmd = (ftpport)ftpc->count1;
   CURLcode result = CURLE_OK;
 
@@ -1991,12 +1995,12 @@ static CURLcode ftp_state_port_resp(struct Curl_easy *data,
     }
     else
       /* try next */
-      result = ftp_state_use_port(data, fcmd);
+      result = ftp_state_use_port(data, ftpc, fcmd);
   }
   else {
     infof(data, "Connect data stream actively");
-    ftp_state(data, FTP_STOP); /* end of DO phase */
-    result = ftp_dophase_done(data, FALSE);
+    ftp_state(data, ftpc, FTP_STOP); /* end of DO phase */
+    result = ftp_dophase_done(data, ftpc, ftp, FALSE);
   }
 
   return result;
@@ -2052,12 +2056,11 @@ static CURLcode client_write_header(struct Curl_easy *data,
 }
 
 static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data,
+                                    struct ftp_conn *ftpc,
+                                    struct FTP *ftp,
                                     int ftpcode)
 {
   CURLcode result = CURLE_OK;
-  struct FTP *ftp = data->req.p.ftp;
-  struct connectdata *conn = data->conn;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
 
   switch(ftpcode) {
   case 213:
@@ -2066,7 +2069,7 @@ static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data,
          last .sss part is optional and means fractions of a second */
       int year, month, day, hour, minute, second;
       struct pingpong *pp = &ftpc->pp;
-      char *resp = Curl_dyn_ptr(&pp->recvbuf) + 4;
+      char *resp = curlx_dyn_ptr(&pp->recvbuf) + 4;
       if(ftp_213_date(resp, &year, &month, &day, &hour, &minute, &second)) {
         /* we have a time, reformat it */
         char timebuf[24];
@@ -2142,7 +2145,7 @@ static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data,
           infof(data, "The requested document is not new enough");
           ftp->transfer = PPTRANSFER_NONE; /* mark to not transfer data */
           data->info.timecond = TRUE;
-          ftp_state(data, FTP_STOP);
+          ftp_state(data, ftpc, FTP_STOP);
           return CURLE_OK;
         }
         break;
@@ -2151,7 +2154,7 @@ static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data,
           infof(data, "The requested document is not old enough");
           ftp->transfer = PPTRANSFER_NONE; /* mark to not transfer data */
           data->info.timecond = TRUE;
-          ftp_state(data, FTP_STOP);
+          ftp_state(data, ftpc, FTP_STOP);
           return CURLE_OK;
         }
         break;
@@ -2163,17 +2166,18 @@ static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data,
   }
 
   if(!result)
-    result = ftp_state_type(data);
+    result = ftp_state_type(data, ftpc, ftp);
 
   return result;
 }
 
 static CURLcode ftp_state_type_resp(struct Curl_easy *data,
+                                    struct ftp_conn *ftpc,
+                                    struct FTP *ftp,
                                     int ftpcode,
                                     ftpstate instate)
 {
   CURLcode result = CURLE_OK;
-  struct connectdata *conn = data->conn;
 
   if(ftpcode/100 != 2) {
     /* "sasserftpd" and "(u)r(x)bot ftpd" both responds with 226 after a
@@ -2187,26 +2191,25 @@ static CURLcode ftp_state_type_resp(struct Curl_easy *data,
           ftpcode);
 
   if(instate == FTP_TYPE)
-    result = ftp_state_size(data, conn);
+    result = ftp_state_size(data, ftpc, ftp);
   else if(instate == FTP_LIST_TYPE)
-    result = ftp_state_list(data);
+    result = ftp_state_list(data, ftpc, ftp);
   else if(instate == FTP_RETR_TYPE)
-    result = ftp_state_retr_prequote(data);
+    result = ftp_state_retr_prequote(data, ftpc, ftp);
   else if(instate == FTP_STOR_TYPE)
-    result = ftp_state_stor_prequote(data);
+    result = ftp_state_stor_prequote(data, ftpc, ftp);
 
   return result;
 }
 
 static CURLcode ftp_state_retr(struct Curl_easy *data,
+                               struct ftp_conn *ftpc,
+                               struct FTP *ftp,
                                curl_off_t filesize)
 {
   CURLcode result = CURLE_OK;
-  struct FTP *ftp = data->req.p.ftp;
-  struct connectdata *conn = data->conn;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
 
-  CURL_TRC_FTP(data, "[%s] ftp_state_retr()", FTP_DSTATE(data));
+  CURL_TRC_FTP(data, "[%s] ftp_state_retr()", FTP_CSTATE(ftpc));
   if(data->set.max_filesize && (filesize > data->set.max_filesize)) {
     failf(data, "Maximum file size exceeded");
     return CURLE_FILESIZE_EXCEEDED;
@@ -2259,7 +2262,7 @@ static CURLcode ftp_state_retr(struct Curl_easy *data,
       /* Set ->transfer so that we will not get any error in ftp_done()
        * because we did not transfer the any file */
       ftp->transfer = PPTRANSFER_NONE;
-      ftp_state(data, FTP_STOP);
+      ftp_state(data, ftpc, FTP_STOP);
       return CURLE_OK;
     }
 
@@ -2270,26 +2273,28 @@ static CURLcode ftp_state_retr(struct Curl_easy *data,
     result = Curl_pp_sendf(data, &ftpc->pp, "REST %" FMT_OFF_T,
                            data->state.resume_from);
     if(!result)
-      ftp_state(data, FTP_RETR_REST);
+      ftp_state(data, ftpc, FTP_RETR_REST);
   }
   else {
     /* no resume */
     result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file);
     if(!result)
-      ftp_state(data, FTP_RETR);
+      ftp_state(data, ftpc, FTP_RETR);
   }
 
   return result;
 }
 
 static CURLcode ftp_state_size_resp(struct Curl_easy *data,
+                                    struct ftp_conn *ftpc,
+                                    struct FTP *ftp,
                                     int ftpcode,
                                     ftpstate instate)
 {
   CURLcode result = CURLE_OK;
   curl_off_t filesize = -1;
-  char *buf = Curl_dyn_ptr(&data->conn->proto.ftpc.pp.recvbuf);
-  size_t len = data->conn->proto.ftpc.pp.nfinal;
+  char *buf = curlx_dyn_ptr(&ftpc->pp.recvbuf);
+  size_t len = ftpc->pp.nfinal;
 
   /* get the size from the ascii string: */
   if(ftpcode == 213) {
@@ -2297,7 +2302,7 @@ static CURLcode ftp_state_size_resp(struct Curl_easy *data,
        for all the digits at the end of the response and parse only those as a
        number. */
     char *start = &buf[4];
-    char *fdigit = memchr(start, '\r', len);
+    const char *fdigit = memchr(start, '\r', len);
     if(fdigit) {
       fdigit--;
       if(*fdigit == '\n')
@@ -2307,9 +2312,8 @@ static CURLcode ftp_state_size_resp(struct Curl_easy *data,
     }
     else
       fdigit = start;
-    /* ignores parsing errors, which will make the size remain unknown */
-    (void)curlx_strtoofft(fdigit, NULL, 10, &filesize);
-
+    if(curlx_str_number(&fdigit, &filesize, CURL_OFF_T_MAX))
+      filesize = -1; /* size remain unknown */
   }
   else if(ftpcode == 550) { /* "No such file or directory" */
     /* allow a SIZE failure for (resumed) uploads, when probing what command
@@ -2332,27 +2336,27 @@ static CURLcode ftp_state_size_resp(struct Curl_easy *data,
     }
 #endif
     Curl_pgrsSetDownloadSize(data, filesize);
-    result = ftp_state_rest(data, data->conn);
+    result = ftp_state_rest(data, ftpc, ftp);
   }
   else if(instate == FTP_RETR_SIZE) {
     Curl_pgrsSetDownloadSize(data, filesize);
-    result = ftp_state_retr(data, filesize);
+    result = ftp_state_retr(data, ftpc, ftp, filesize);
   }
   else if(instate == FTP_STOR_SIZE) {
     data->state.resume_from = filesize;
-    result = ftp_state_ul_setup(data, TRUE);
+    result = ftp_state_ul_setup(data, ftpc, ftp, TRUE);
   }
 
   return result;
 }
 
 static CURLcode ftp_state_rest_resp(struct Curl_easy *data,
-                                    struct connectdata *conn,
+                                    struct ftp_conn *ftpc,
+                                    struct FTP *ftp,
                                     int ftpcode,
                                     ftpstate instate)
 {
   CURLcode result = CURLE_OK;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
 
   switch(instate) {
   case FTP_REST:
@@ -2365,7 +2369,7 @@ static CURLcode ftp_state_rest_resp(struct Curl_easy *data,
         return result;
     }
 #endif
-    result = ftp_state_prepare_transfer(data);
+    result = ftp_state_prepare_transfer(data, ftpc, ftp);
     break;
 
   case FTP_RETR_REST:
@@ -2376,7 +2380,7 @@ static CURLcode ftp_state_rest_resp(struct Curl_easy *data,
     else {
       result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file);
       if(!result)
-        ftp_state(data, FTP_RETR);
+        ftp_state(data, ftpc, FTP_RETR);
     }
     break;
   }
@@ -2385,26 +2389,25 @@ static CURLcode ftp_state_rest_resp(struct Curl_easy *data,
 }
 
 static CURLcode ftp_state_stor_resp(struct Curl_easy *data,
+                                    struct ftp_conn *ftpc,
                                     int ftpcode, ftpstate instate)
 {
   CURLcode result = CURLE_OK;
-  struct connectdata *conn = data->conn;
 
   if(ftpcode >= 400) {
     failf(data, "Failed FTP upload: %0d", ftpcode);
-    ftp_state(data, FTP_STOP);
+    ftp_state(data, ftpc, FTP_STOP);
     /* oops, we never close the sockets! */
     return CURLE_UPLOAD_FAILED;
   }
 
-  conn->proto.ftpc.state_saved = instate;
+  ftpc->state_saved = instate;
 
   /* PORT means we are now awaiting the server to connect to us. */
   if(data->set.ftp_use_port) {
-    struct ftp_conn *ftpc = &conn->proto.ftpc;
     bool connected;
 
-    ftp_state(data, FTP_STOP); /* no longer in STOR state */
+    ftp_state(data, ftpc, FTP_STOP); /* no longer in STOR state */
 
     result = Curl_conn_connect(data, SECONDARYSOCKET, FALSE, &connected);
     if(result)
@@ -2413,21 +2416,21 @@ static CURLcode ftp_state_stor_resp(struct Curl_easy *data,
     if(!connected) {
       infof(data, "Data conn was not available immediately");
       ftpc->wait_data_conn = TRUE;
-      return ftp_check_ctrl_on_data_wait(data);
+      return ftp_check_ctrl_on_data_wait(data, ftpc);
     }
     ftpc->wait_data_conn = FALSE;
   }
-  return InitiateTransfer(data);
+  return ftp_initiate_transfer(data, ftpc);
 }
 
 /* for LIST and RETR responses */
 static CURLcode ftp_state_get_resp(struct Curl_easy *data,
+                                   struct ftp_conn *ftpc,
+                                   struct FTP *ftp,
                                    int ftpcode,
                                    ftpstate instate)
 {
   CURLcode result = CURLE_OK;
-  struct FTP *ftp = data->req.p.ftp;
-  struct connectdata *conn = data->conn;
 
   if((ftpcode == 150) || (ftpcode == 125)) {
 
@@ -2471,8 +2474,8 @@ static CURLcode ftp_state_get_resp(struct Curl_easy *data,
        * those cases only confuses us.
        *
        * Example D above makes this parsing a little tricky */
-      char *bytes;
-      char *buf = Curl_dyn_ptr(&conn->proto.ftpc.pp.recvbuf);
+      const char *bytes;
+      char *buf = curlx_dyn_ptr(&ftpc->pp.recvbuf);
       bytes = strstr(buf, " bytes");
       if(bytes) {
         long in = (long)(--bytes-buf);
@@ -2493,7 +2496,8 @@ static CURLcode ftp_state_get_resp(struct Curl_easy *data,
         if(bytes) {
           ++bytes;
           /* get the number! */
-          (void)curlx_strtoofft(bytes, NULL, 10, &size);
+          if(curlx_str_number(&bytes, &size, CURL_OFF_T_MAX))
+            size = 1;
         }
       }
     }
@@ -2511,11 +2515,10 @@ static CURLcode ftp_state_get_resp(struct Curl_easy *data,
       infof(data, "Getting file with size: %" FMT_OFF_T, size);
 
     /* FTP download: */
-    conn->proto.ftpc.state_saved = instate;
-    conn->proto.ftpc.retr_size_saved = size;
+    ftpc->state_saved = instate;
+    ftpc->retr_size_saved = size;
 
     if(data->set.ftp_use_port) {
-      struct ftp_conn *ftpc = &conn->proto.ftpc;
       bool connected;
 
       result = Curl_conn_connect(data, SECONDARYSOCKET, FALSE, &connected);
@@ -2524,19 +2527,19 @@ static CURLcode ftp_state_get_resp(struct Curl_easy *data,
 
       if(!connected) {
         infof(data, "Data conn was not available immediately");
-        ftp_state(data, FTP_STOP);
+        ftp_state(data, ftpc, FTP_STOP);
         ftpc->wait_data_conn = TRUE;
-        return ftp_check_ctrl_on_data_wait(data);
+        return ftp_check_ctrl_on_data_wait(data, ftpc);
       }
       ftpc->wait_data_conn = FALSE;
     }
-    return InitiateTransfer(data);
+    return ftp_initiate_transfer(data, ftpc);
   }
   else {
     if((instate == FTP_LIST) && (ftpcode == 450)) {
       /* simply no matching files in the dir listing */
       ftp->transfer = PPTRANSFER_NONE; /* do not download anything */
-      ftp_state(data, FTP_STOP); /* this phase is over */
+      ftp_state(data, ftpc, FTP_STOP); /* this phase is over */
     }
     else {
       failf(data, "RETR response: %03d", ftpcode);
@@ -2550,12 +2553,12 @@ static CURLcode ftp_state_get_resp(struct Curl_easy *data,
 }
 
 /* after USER, PASS and ACCT */
-static CURLcode ftp_state_loggedin(struct Curl_easy *data)
+static CURLcode ftp_state_loggedin(struct Curl_easy *data,
+                                   struct ftp_conn *ftpc)
 {
   CURLcode result = CURLE_OK;
-  struct connectdata *conn = data->conn;
 
-  if(conn->bits.ftp_use_control_ssl) {
+  if(data->conn->bits.ftp_use_control_ssl) {
     /* PBSZ = PROTECTION BUFFER SIZE.
 
     The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says:
@@ -2570,44 +2573,43 @@ static CURLcode ftp_state_loggedin(struct Curl_easy *data)
     parameter of '0' to indicate that no buffering is taking place
     and the data connection should not be encapsulated.
     */
-    result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "PBSZ %d", 0);
+    result = Curl_pp_sendf(data, &ftpc->pp, "PBSZ %d", 0);
     if(!result)
-      ftp_state(data, FTP_PBSZ);
+      ftp_state(data, ftpc, FTP_PBSZ);
   }
   else {
-    result = ftp_state_pwd(data, conn);
+    result = ftp_state_pwd(data, ftpc);
   }
   return result;
 }
 
 /* for USER and PASS responses */
 static CURLcode ftp_state_user_resp(struct Curl_easy *data,
+                                    struct ftp_conn *ftpc,
                                     int ftpcode)
 {
   CURLcode result = CURLE_OK;
-  struct connectdata *conn = data->conn;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
 
   /* some need password anyway, and others just return 2xx ignored */
   if((ftpcode == 331) && (ftpc->state == FTP_USER)) {
     /* 331 Password required for ...
        (the server requires to send the user's password too) */
     result = Curl_pp_sendf(data, &ftpc->pp, "PASS %s",
-                           conn->passwd ? conn->passwd : "");
+                           data->conn->passwd ? data->conn->passwd : "");
     if(!result)
-      ftp_state(data, FTP_PASS);
+      ftp_state(data, ftpc, FTP_PASS);
   }
   else if(ftpcode/100 == 2) {
     /* 230 User ... logged in.
        (the user logged in with or without password) */
-    result = ftp_state_loggedin(data);
+    result = ftp_state_loggedin(data, ftpc);
   }
   else if(ftpcode == 332) {
     if(data->set.str[STRING_FTP_ACCOUNT]) {
       result = Curl_pp_sendf(data, &ftpc->pp, "ACCT %s",
                              data->set.str[STRING_FTP_ACCOUNT]);
       if(!result)
-        ftp_state(data, FTP_ACCT);
+        ftp_state(data, ftpc, FTP_ACCT);
     }
     else {
       failf(data, "ACCT requested but none available");
@@ -2628,7 +2630,7 @@ static CURLcode ftp_state_user_resp(struct Curl_easy *data,
                       data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]);
       if(!result) {
         ftpc->ftp_trying_alternative = TRUE;
-        ftp_state(data, FTP_USER);
+        ftp_state(data, ftpc, FTP_USER);
       }
     }
     else {
@@ -2641,6 +2643,7 @@ static CURLcode ftp_state_user_resp(struct Curl_easy *data,
 
 /* for ACCT response */
 static CURLcode ftp_state_acct_resp(struct Curl_easy *data,
+                                    struct ftp_conn *ftpc,
                                     int ftpcode)
 {
   CURLcode result = CURLE_OK;
@@ -2649,26 +2652,30 @@ static CURLcode ftp_state_acct_resp(struct Curl_easy *data,
     result = CURLE_FTP_WEIRD_PASS_REPLY; /* FIX */
   }
   else
-    result = ftp_state_loggedin(data);
+    result = ftp_state_loggedin(data, ftpc);
 
   return result;
 }
 
 
-static CURLcode ftp_statemachine(struct Curl_easy *data,
-                                 struct connectdata *conn)
+static CURLcode ftp_pp_statemachine(struct Curl_easy *data,
+                                    struct connectdata *conn)
 {
   CURLcode result;
   int ftpcode;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
-  struct pingpong *pp = &ftpc->pp;
+  struct ftp_conn *ftpc = Curl_conn_meta_get(conn, CURL_META_FTP_CONN);
+  struct FTP *ftp = Curl_meta_get(data, CURL_META_FTP_EASY);
+  struct pingpong *pp;
   static const char * const ftpauth[] = { "SSL", "TLS" };
   size_t nread = 0;
 
+  if(!ftpc || !ftp)
+    return CURLE_FAILED_INIT;
+  pp = &ftpc->pp;
   if(pp->sendleft)
     return Curl_pp_flushsend(data, pp);
 
-  result = ftp_readresp(data, FIRSTSOCKET, pp, &ftpcode, &nread);
+  result = ftp_readresp(data, ftpc, FIRSTSOCKET, pp, &ftpcode, &nread);
   if(result)
     return result;
 
@@ -2680,7 +2687,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
         /* 230 User logged in - already! Take as 220 if TLS required. */
         if(data->set.use_ssl <= CURLUSESSL_TRY ||
            conn->bits.ftp_use_control_ssl)
-          return ftp_state_user_resp(data, ftpcode);
+          return ftp_state_user_resp(data, ftpc, ftpcode);
       }
       else if(ftpcode != 220) {
         failf(data, "Got a %03d ftp-server response when 220 was expected",
@@ -2708,8 +2715,8 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
 #endif
 
       if(data->set.use_ssl && !conn->bits.ftp_use_control_ssl) {
-        /* We do not have a SSL/TLS control connection yet, but FTPS is
-           requested. Try a FTPS connection now */
+        /* We do not have an SSL/TLS control connection yet, but FTPS is
+           requested. Try an FTPS connection now */
 
         ftpc->count3 = 0;
         switch(data->set.ftpsslauth) {
@@ -2730,10 +2737,10 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
         result = Curl_pp_sendf(data, &ftpc->pp, "AUTH %s",
                                ftpauth[ftpc->count1]);
         if(!result)
-          ftp_state(data, FTP_AUTH);
+          ftp_state(data, ftpc, FTP_AUTH);
       }
       else
-        result = ftp_state_user(data, conn);
+        result = ftp_state_user(data, ftpc, conn);
       break;
 
     case FTP_AUTH:
@@ -2763,7 +2770,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
         if(!result) {
           conn->bits.ftp_use_data_ssl = FALSE; /* clear-text data */
           conn->bits.ftp_use_control_ssl = TRUE; /* SSL on control */
-          result = ftp_state_user(data, conn);
+          result = ftp_state_user(data, ftpc, conn);
         }
       }
       else if(ftpc->count3 < 1) {
@@ -2779,17 +2786,17 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
           result = CURLE_USE_SSL_FAILED;
         else
           /* ignore the failure and continue */
-          result = ftp_state_user(data, conn);
+          result = ftp_state_user(data, ftpc, conn);
       }
       break;
 
     case FTP_USER:
     case FTP_PASS:
-      result = ftp_state_user_resp(data, ftpcode);
+      result = ftp_state_user_resp(data, ftpc, ftpcode);
       break;
 
     case FTP_ACCT:
-      result = ftp_state_acct_resp(data, ftpcode);
+      result = ftp_state_acct_resp(data, ftpc, ftpcode);
       break;
 
     case FTP_PBSZ:
@@ -2797,7 +2804,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
         Curl_pp_sendf(data, &ftpc->pp, "PROT %c",
                       data->set.use_ssl == CURLUSESSL_CONTROL ? 'C' : 'P');
       if(!result)
-        ftp_state(data, FTP_PROT);
+        ftp_state(data, ftpc, FTP_PROT);
       break;
 
     case FTP_PROT:
@@ -2816,10 +2823,10 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
          */
         result = Curl_pp_sendf(data, &ftpc->pp, "%s", "CCC");
         if(!result)
-          ftp_state(data, FTP_CCC);
+          ftp_state(data, ftpc, FTP_CCC);
       }
       else
-        result = ftp_state_pwd(data, conn);
+        result = ftp_state_pwd(data, ftpc);
       break;
 
     case FTP_CCC:
@@ -2838,16 +2845,16 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
       }
       if(!result)
         /* Then continue as normal */
-        result = ftp_state_pwd(data, conn);
+        result = ftp_state_pwd(data, ftpc);
       break;
 
     case FTP_PWD:
       if(ftpcode == 257) {
-        char *ptr = Curl_dyn_ptr(&pp->recvbuf) + 4; /* start on the first
-                                                       letter */
+        char *ptr = curlx_dyn_ptr(&pp->recvbuf) + 4; /* start on the first
+                                                        letter */
         bool entry_extracted = FALSE;
         struct dynbuf out;
-        Curl_dyn_init(&out, 1000);
+        curlx_dyn_init(&out, 1000);
 
         /* Reply format is like
            257<space>[rubbish]"<directory-name>"<space><commentary> and the
@@ -2868,18 +2875,18 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
             if('\"' == *ptr) {
               if('\"' == ptr[1]) {
                 /* "quote-doubling" */
-                result = Curl_dyn_addn(&out, &ptr[1], 1);
+                result = curlx_dyn_addn(&out, &ptr[1], 1);
                 ptr++;
               }
               else {
                 /* end of path */
-                if(Curl_dyn_len(&out))
+                if(curlx_dyn_len(&out))
                   entry_extracted = TRUE;
                 break; /* get out of this loop */
               }
             }
             else
-              result = Curl_dyn_addn(&out, ptr, 1);
+              result = curlx_dyn_addn(&out, ptr, 1);
             if(result)
               return result;
           }
@@ -2896,7 +2903,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
                The method used here is to check the server OS: we do it only
              if the path name looks strange to minimize overhead on other
              systems. */
-          char *dir = Curl_dyn_ptr(&out);
+          char *dir = curlx_dyn_ptr(&out);
 
           if(!ftpc->server_os && dir[0] != '/') {
             result = Curl_pp_sendf(data, &ftpc->pp, "%s", "SYST");
@@ -2904,34 +2911,40 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
               free(dir);
               return result;
             }
-            Curl_safefree(ftpc->entrypath);
+            free(ftpc->entrypath);
             ftpc->entrypath = dir; /* remember this */
             infof(data, "Entry path is '%s'", ftpc->entrypath);
             /* also save it where getinfo can access it: */
-            data->state.most_recent_ftp_entrypath = ftpc->entrypath;
-            ftp_state(data, FTP_SYST);
+            free(data->state.most_recent_ftp_entrypath);
+            data->state.most_recent_ftp_entrypath = strdup(ftpc->entrypath);
+            if(!data->state.most_recent_ftp_entrypath)
+              return CURLE_OUT_OF_MEMORY;
+            ftp_state(data, ftpc, FTP_SYST);
             break;
           }
 
-          Curl_safefree(ftpc->entrypath);
+          free(ftpc->entrypath);
           ftpc->entrypath = dir; /* remember this */
           infof(data, "Entry path is '%s'", ftpc->entrypath);
           /* also save it where getinfo can access it: */
-          data->state.most_recent_ftp_entrypath = ftpc->entrypath;
+          free(data->state.most_recent_ftp_entrypath);
+          data->state.most_recent_ftp_entrypath = strdup(ftpc->entrypath);
+          if(!data->state.most_recent_ftp_entrypath)
+            return CURLE_OUT_OF_MEMORY;
         }
         else {
           /* could not get the path */
-          Curl_dyn_free(&out);
+          curlx_dyn_free(&out);
           infof(data, "Failed to figure out path");
         }
       }
-      ftp_state(data, FTP_STOP); /* we are done with the CONNECT phase! */
-      CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_DSTATE(data));
+      ftp_state(data, ftpc, FTP_STOP); /* we are done with CONNECT phase! */
+      CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_CSTATE(ftpc));
       break;
 
     case FTP_SYST:
       if(ftpcode == 215) {
-        char *ptr = Curl_dyn_ptr(&pp->recvbuf) + 4; /* start on the first
+        char *ptr = curlx_dyn_ptr(&pp->recvbuf) + 4; /* start on the first
                                                        letter */
         char *os;
         char *start;
@@ -2956,33 +2969,33 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
             return result;
           }
           /* remember target server OS */
-          Curl_safefree(ftpc->server_os);
+          free(ftpc->server_os);
           ftpc->server_os = os;
-          ftp_state(data, FTP_NAMEFMT);
+          ftp_state(data, ftpc, FTP_NAMEFMT);
           break;
         }
         /* Nothing special for the target server. */
         /* remember target server OS */
-        Curl_safefree(ftpc->server_os);
+        free(ftpc->server_os);
         ftpc->server_os = os;
       }
       else {
         /* Cannot identify server OS. Continue anyway and cross fingers. */
       }
 
-      ftp_state(data, FTP_STOP); /* we are done with the CONNECT phase! */
-      CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_DSTATE(data));
+      ftp_state(data, ftpc, FTP_STOP); /* we are done with CONNECT phase! */
+      CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_CSTATE(ftpc));
       break;
 
     case FTP_NAMEFMT:
       if(ftpcode == 250) {
         /* Name format change successful: reload initial path. */
-        ftp_state_pwd(data, conn);
+        ftp_state_pwd(data, ftpc);
         break;
       }
 
-      ftp_state(data, FTP_STOP); /* we are done with the CONNECT phase! */
-      CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_DSTATE(data));
+      ftp_state(data, ftpc, FTP_STOP); /* we are done with CONNECT phase! */
+      CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_CSTATE(ftpc));
       break;
 
     case FTP_QUOTE:
@@ -2995,7 +3008,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
         result = CURLE_QUOTE_ERROR;
       }
       else
-        result = ftp_state_quote(data, FALSE, ftpc->state);
+        result = ftp_state_quote(data, ftpc, ftp, FALSE, ftpc->state);
       break;
 
     case FTP_CWD:
@@ -3014,7 +3027,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
           result = Curl_pp_sendf(data, &ftpc->pp, "MKD %s",
                                  ftpc->dirs[ftpc->cwdcount - 1]);
           if(!result)
-            ftp_state(data, FTP_MKD);
+            ftp_state(data, ftpc, FTP_MKD);
         }
         else {
           /* return failure */
@@ -3032,7 +3045,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
           result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s",
                                  ftpc->dirs[ftpc->cwdcount - 1]);
         else
-          result = ftp_state_mdtm(data);
+          result = ftp_state_mdtm(data, ftpc, ftp);
       }
       break;
 
@@ -3043,7 +3056,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
         result = CURLE_REMOTE_ACCESS_DENIED;
       }
       else {
-        ftp_state(data, FTP_CWD);
+        ftp_state(data, ftpc, FTP_CWD);
         /* send CWD */
         result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s",
                                ftpc->dirs[ftpc->cwdcount - 1]);
@@ -3051,25 +3064,25 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
       break;
 
     case FTP_MDTM:
-      result = ftp_state_mdtm_resp(data, ftpcode);
+      result = ftp_state_mdtm_resp(data, ftpc, ftp, ftpcode);
       break;
 
     case FTP_TYPE:
     case FTP_LIST_TYPE:
     case FTP_RETR_TYPE:
     case FTP_STOR_TYPE:
-      result = ftp_state_type_resp(data, ftpcode, ftpc->state);
+      result = ftp_state_type_resp(data, ftpc, ftp, ftpcode, ftpc->state);
       break;
 
     case FTP_SIZE:
     case FTP_RETR_SIZE:
     case FTP_STOR_SIZE:
-      result = ftp_state_size_resp(data, ftpcode, ftpc->state);
+      result = ftp_state_size_resp(data, ftpc, ftp, ftpcode, ftpc->state);
       break;
 
     case FTP_REST:
     case FTP_RETR_REST:
-      result = ftp_state_rest_resp(data, conn, ftpcode, ftpc->state);
+      result = ftp_state_rest_resp(data, ftpc, ftp, ftpcode, ftpc->state);
       break;
 
     case FTP_PRET:
@@ -3078,30 +3091,30 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
         failf(data, "PRET command not accepted: %03d", ftpcode);
         return CURLE_FTP_PRET_FAILED;
       }
-      result = ftp_state_use_pasv(data, conn);
+      result = ftp_state_use_pasv(data, ftpc, conn);
       break;
 
     case FTP_PASV:
-      result = ftp_state_pasv_resp(data, ftpcode);
+      result = ftp_state_pasv_resp(data, ftpc, ftpcode);
       break;
 
     case FTP_PORT:
-      result = ftp_state_port_resp(data, ftpcode);
+      result = ftp_state_port_resp(data, ftpc, ftp, ftpcode);
       break;
 
     case FTP_LIST:
     case FTP_RETR:
-      result = ftp_state_get_resp(data, ftpcode, ftpc->state);
+      result = ftp_state_get_resp(data, ftpc, ftp, ftpcode, ftpc->state);
       break;
 
     case FTP_STOR:
-      result = ftp_state_stor_resp(data, ftpcode, ftpc->state);
+      result = ftp_state_stor_resp(data, ftpc, ftpcode, ftpc->state);
       break;
 
     case FTP_QUIT:
     default:
       /* internal error */
-      ftp_state(data, FTP_STOP);
+      ftp_state(data, ftpc, FTP_STOP);
       break;
     }
   } /* if(ftpcode) */
@@ -3111,11 +3124,10 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
 
 
 /* called repeatedly until done from multi.c */
-static CURLcode ftp_multi_statemach(struct Curl_easy *data,
-                                    bool *done)
+static CURLcode ftp_statemach(struct Curl_easy *data,
+                               struct ftp_conn *ftpc,
+                               bool *done)
 {
-  struct connectdata *conn = data->conn;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
   CURLcode result = Curl_pp_statemach(data, &ftpc->pp, FALSE, FALSE);
 
   /* Check for the state outside of the Curl_socket_check() return code checks
@@ -3126,14 +3138,23 @@ static CURLcode ftp_multi_statemach(struct Curl_easy *data,
   return result;
 }
 
+/* called repeatedly until done from multi.c */
+static CURLcode ftp_multi_statemach(struct Curl_easy *data,
+                                    bool *done)
+{
+  struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN);
+  return ftpc ? ftp_statemach(data, ftpc, done) : CURLE_FAILED_INIT;
+}
+
 static CURLcode ftp_block_statemach(struct Curl_easy *data,
-                                    struct connectdata *conn)
+                                    struct ftp_conn *ftpc)
 {
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
   struct pingpong *pp = &ftpc->pp;
   CURLcode result = CURLE_OK;
 
   while(ftpc->state != FTP_STOP) {
+    if(ftpc->shutdown)
+      CURL_TRC_FTP(data, "in shutdown, waiting for server response");
     result = Curl_pp_statemach(data, pp, TRUE, TRUE /* disconnecting */);
     if(result)
       break;
@@ -3155,15 +3176,17 @@ static CURLcode ftp_connect(struct Curl_easy *data,
 {
   CURLcode result;
   struct connectdata *conn = data->conn;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
-  struct pingpong *pp = &ftpc->pp;
+  struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN);
+  struct pingpong *pp;
 
   *done = FALSE; /* default to not done yet */
-
+  if(!ftpc)
+    return CURLE_FAILED_INIT;
+  pp = &ftpc->pp;
   /* We always support persistent connections on ftp */
   connkeep(conn, "FTP default");
 
-  PINGPONG_SETUP(pp, ftp_statemachine, ftp_endofresp);
+  PINGPONG_SETUP(pp, ftp_pp_statemachine, ftp_endofresp);
 
   if(Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
     /* BLOCKING */
@@ -3177,9 +3200,9 @@ static CURLcode ftp_connect(struct Curl_easy *data,
 
   /* When we connect, we start in the state where we await the 220
      response */
-  ftp_state(data, FTP_WAIT220);
+  ftp_state(data, ftpc, FTP_WAIT220);
 
-  result = ftp_multi_statemach(data, done);
+  result = ftp_statemach(data, ftpc, done);
 
   return result;
 }
@@ -3197,18 +3220,19 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
                          bool premature)
 {
   struct connectdata *conn = data->conn;
-  struct FTP *ftp = data->req.p.ftp;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
-  struct pingpong *pp = &ftpc->pp;
+  struct FTP *ftp = Curl_meta_get(data, CURL_META_FTP_EASY);
+  struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN);
+  struct pingpong *pp;
   ssize_t nread;
   int ftpcode;
   CURLcode result = CURLE_OK;
   char *rawPath = NULL;
   size_t pathLen = 0;
 
-  if(!ftp)
+  if(!ftp || !ftpc)
     return CURLE_OK;
 
+  pp = &ftpc->pp;
   switch(status) {
   case CURLE_BAD_DOWNLOAD_RESUME:
   case CURLE_FTP_WEIRD_PASV_REPLY:
@@ -3293,11 +3317,11 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
 
   /* shut down the socket to inform the server we are done */
 
-#ifdef _WIN32_WCE
+#ifdef UNDER_CE
   shutdown(conn->sock[SECONDARYSOCKET], 2);  /* SD_BOTH */
 #endif
 
-  if(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) {
+  if(Curl_conn_is_setup(conn, SECONDARYSOCKET)) {
     if(!result && ftpc->dont_check && data->req.maxdownload > 0) {
       /* partial download completed */
       result = Curl_pp_sendf(data, pp, "%s", "ABOR");
@@ -3309,7 +3333,7 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
       }
     }
 
-    close_secondarysocket(data);
+    close_secondarysocket(data, ftpc);
   }
 
   if(!result && (ftp->transfer == PPTRANSFER_BODY) && ftpc->ctl_valid &&
@@ -3323,7 +3347,7 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
     timediff_t old_time = pp->response_time;
 
     pp->response_time = 60*1000; /* give it only a minute for now */
-    pp->response = Curl_now(); /* timeout relative now */
+    pp->response = curlx_now(); /* timeout relative now */
 
     result = Curl_GetFTPResponse(data, &nread, &ftpcode);
 
@@ -3335,10 +3359,8 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
       connclose(conn, "Timeout or similar in FTP DONE operation"); /* close */
     }
 
-    if(result) {
-      Curl_safefree(ftp->pathalloc);
+    if(result)
       return result;
-    }
 
     if(ftpc->dont_check && data->req.maxdownload > 0) {
       /* we have just sent ABOR and there is no reliable way to check if it was
@@ -3371,10 +3393,13 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
        use checking further */
     ;
   else if(data->state.upload) {
-    if((-1 != data->state.infilesize) &&
-       (data->state.infilesize != data->req.writebytecount) &&
-       !data->set.crlf &&
-       (ftp->transfer == PPTRANSFER_BODY)) {
+    if((ftp->transfer == PPTRANSFER_BODY) &&
+       (data->state.infilesize != -1) && /* upload with known size */
+       ((!data->set.crlf && !data->state.prefer_ascii && /* no conversion */
+         (data->state.infilesize != data->req.writebytecount)) ||
+        ((data->set.crlf || data->state.prefer_ascii) && /* maybe crlf conv */
+         (data->state.infilesize > data->req.writebytecount))
+       )) {
       failf(data, "Uploaded unaligned file size (%" FMT_OFF_T
             " out of %" FMT_OFF_T " bytes)",
             data->req.writebytecount, data->state.infilesize);
@@ -3403,9 +3428,8 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
 
   /* Send any post-transfer QUOTE strings? */
   if(!status && !result && !premature && data->set.postquote)
-    result = ftp_sendquote(data, conn, data->set.postquote);
-  CURL_TRC_FTP(data, "[%s] done, result=%d", FTP_DSTATE(data), result);
-  Curl_safefree(ftp->pathalloc);
+    result = ftp_sendquote(data, ftpc, data->set.postquote);
+  CURL_TRC_FTP(data, "[%s] done, result=%d", FTP_CSTATE(ftpc), result);
   return result;
 }
 
@@ -3421,10 +3445,10 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
 
 static
 CURLcode ftp_sendquote(struct Curl_easy *data,
-                       struct connectdata *conn, struct curl_slist *quote)
+                       struct ftp_conn *ftpc,
+                       struct curl_slist *quote)
 {
   struct curl_slist *item;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
   struct pingpong *pp = &ftpc->pp;
 
   item = quote;
@@ -3448,7 +3472,7 @@ CURLcode ftp_sendquote(struct Curl_easy *data,
 
       result = Curl_pp_sendf(data, &ftpc->pp, "%s", cmd);
       if(!result) {
-        pp->response = Curl_now(); /* timeout relative now */
+        pp->response = curlx_now(); /* timeout relative now */
         result = Curl_GetFTPResponse(data, &nread, &ftpcode);
       }
       if(result)
@@ -3472,10 +3496,10 @@ CURLcode ftp_sendquote(struct Curl_easy *data,
  *
  * Returns TRUE if we in the current situation should send TYPE
  */
-static int ftp_need_type(struct connectdata *conn,
+static int ftp_need_type(struct ftp_conn *ftpc,
                          bool ascii_wanted)
 {
-  return conn->proto.ftpc.transfertype != (ascii_wanted ? 'A' : 'I');
+  return ftpc->transfertype != (ascii_wanted ? 'A' : 'I');
 }
 
 /***********************************************************************
@@ -3487,21 +3511,21 @@ static int ftp_need_type(struct connectdata *conn,
  * If the transfer type is not sent, simulate on OK response in newstate
  */
 static CURLcode ftp_nb_type(struct Curl_easy *data,
-                            struct connectdata *conn,
+                            struct ftp_conn *ftpc,
+                            struct FTP *ftp,
                             bool ascii, ftpstate newstate)
 {
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
   CURLcode result;
   char want = (char)(ascii ? 'A' : 'I');
 
   if(ftpc->transfertype == want) {
-    ftp_state(data, newstate);
-    return ftp_state_type_resp(data, 200, newstate);
+    ftp_state(data, ftpc, newstate);
+    return ftp_state_type_resp(data, ftpc, ftp, 200, newstate);
   }
 
   result = Curl_pp_sendf(data, &ftpc->pp, "TYPE %c", want);
   if(!result) {
-    ftp_state(data, newstate);
+    ftp_state(data, ftpc, newstate);
 
     /* keep track of our current transfer type */
     ftpc->transfertype = want;
@@ -3545,16 +3569,17 @@ ftp_pasv_verbose(struct Curl_easy *data,
 static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
 {
   struct connectdata *conn = data->conn;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
+  struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN);
+  struct FTP *ftp = Curl_meta_get(data, CURL_META_FTP_EASY);
   CURLcode result = CURLE_OK;
   bool connected = FALSE;
   bool complete = FALSE;
-
   /* the ftp struct is inited in ftp_connect(). If we are connecting to an HTTP
    * proxy then the state will not be valid until after that connection is
    * complete */
-  struct FTP *ftp = NULL;
 
+  if(!ftpc || !ftp)
+    return CURLE_FAILED_INIT;
   /* if the second connection has been set up, try to connect it fully
    * to the remote host. This may not complete at this time, for several
    * reasons:
@@ -3571,20 +3596,17 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
       if(result && !is_eptr && (ftpc->count1 == 0)) {
         *completep = -1; /* go back to DOING please */
         /* this is a EPSV connect failing, try PASV instead */
-        return ftp_epsv_disable(data, conn);
+        return ftp_epsv_disable(data, ftpc, conn);
       }
       *completep = (int)complete;
       return result;
     }
   }
 
-  /* Curl_proxy_connect might have moved the protocol state */
-  ftp = data->req.p.ftp;
-
   if(ftpc->state) {
     /* already in a state so skip the initial commands.
        They are only done to kickstart the do_more state */
-    result = ftp_multi_statemach(data, &complete);
+    result = ftp_statemach(data, ftpc, &complete);
 
     *completep = (int)complete;
 
@@ -3606,14 +3628,14 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
     if(ftpc->wait_data_conn) {
       bool serv_conned;
 
-      result = Curl_conn_connect(data, SECONDARYSOCKET, TRUE, &serv_conned);
+      result = Curl_conn_connect(data, SECONDARYSOCKET, FALSE, &serv_conned);
       if(result)
         return result; /* Failed to accept data connection */
 
       if(serv_conned) {
         /* It looks data connection is established */
         ftpc->wait_data_conn = FALSE;
-        result = InitiateTransfer(data);
+        result = ftp_initiate_transfer(data, ftpc);
 
         if(result)
           return result;
@@ -3622,19 +3644,23 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
                            connected back to us */
       }
       else {
-        result = ftp_check_ctrl_on_data_wait(data);
+        result = ftp_check_ctrl_on_data_wait(data, ftpc);
         if(result)
           return result;
       }
     }
     else if(data->state.upload) {
-      result = ftp_nb_type(data, conn, data->state.prefer_ascii,
+      result = ftp_nb_type(data, ftpc, ftp, data->state.prefer_ascii,
                            FTP_STOR_TYPE);
       if(result)
         return result;
 
-      result = ftp_multi_statemach(data, &complete);
-      *completep = (int)complete;
+      result = ftp_statemach(data, ftpc, &complete);
+      /* ftp_nb_type() might have skipped sending `TYPE A|I` when not
+       * deemed necessary and directly sent `STORE name`. If this was
+       * then complete, but we are still waiting on the data connection,
+       * the transfer has not been initiated yet. */
+      *completep = (int)(ftpc->wait_data_conn ? 0 : complete);
     }
     else {
       /* download */
@@ -3656,20 +3682,20 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
 
         /* But only if a body transfer was requested. */
         if(ftp->transfer == PPTRANSFER_BODY) {
-          result = ftp_nb_type(data, conn, TRUE, FTP_LIST_TYPE);
+          result = ftp_nb_type(data, ftpc, ftp, TRUE, FTP_LIST_TYPE);
           if(result)
             return result;
         }
         /* otherwise just fall through */
       }
       else {
-        result = ftp_nb_type(data, conn, data->state.prefer_ascii,
+        result = ftp_nb_type(data, ftpc, ftp, data->state.prefer_ascii,
                              FTP_RETR_TYPE);
         if(result)
           return result;
       }
 
-      result = ftp_multi_statemach(data, &complete);
+      result = ftp_statemach(data, ftpc, &complete);
       *completep = (int)complete;
     }
     return result;
@@ -3681,7 +3707,7 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
   if(!ftpc->wait_data_conn) {
     /* no waiting for the data connection so this is now complete */
     *completep = 1;
-    CURL_TRC_FTP(data, "[%s] DO-MORE phase ends with %d", FTP_DSTATE(data),
+    CURL_TRC_FTP(data, "[%s] DO-MORE phase ends with %d", FTP_CSTATE(ftpc),
                  (int)result);
   }
 
@@ -3700,41 +3726,42 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
 
 static
 CURLcode ftp_perform(struct Curl_easy *data,
+                     struct ftp_conn *ftpc,
+                     struct FTP *ftp,
                      bool *connected,  /* connect status after PASV / PORT */
                      bool *dophase_done)
 {
   /* this is FTP and no proxy */
   CURLcode result = CURLE_OK;
 
-  CURL_TRC_FTP(data, "[%s] DO phase starts", FTP_DSTATE(data));
+  CURL_TRC_FTP(data, "[%s] DO phase starts", FTP_CSTATE(ftpc));
 
   if(data->req.no_body) {
     /* requested no body means no transfer... */
-    struct FTP *ftp = data->req.p.ftp;
     ftp->transfer = PPTRANSFER_INFO;
   }
 
   *dophase_done = FALSE; /* not done yet */
 
   /* start the first command in the DO phase */
-  result = ftp_state_quote(data, TRUE, FTP_QUOTE);
+  result = ftp_state_quote(data, ftpc, ftp, TRUE, FTP_QUOTE);
   if(result)
     return result;
 
   /* run the state-machine */
-  result = ftp_multi_statemach(data, dophase_done);
+  result = ftp_statemach(data, ftpc, dophase_done);
 
   *connected = Curl_conn_is_connected(data->conn, SECONDARYSOCKET);
 
   if(*connected)
     infof(data, "[FTP] [%s] perform, DATA connection established",
-          FTP_DSTATE(data));
+          FTP_CSTATE(ftpc));
   else
     CURL_TRC_FTP(data, "[%s] perform, awaiting DATA connect",
-                 FTP_DSTATE(data));
+                 FTP_CSTATE(ftpc));
 
   if(*dophase_done)
-    CURL_TRC_FTP(data, "[%s] DO phase is complete1", FTP_DSTATE(data));
+    CURL_TRC_FTP(data, "[%s] DO phase is complete1", FTP_CSTATE(ftpc));
 
   return result;
 }
@@ -3747,10 +3774,11 @@ static void wc_data_dtor(void *ptr)
   free(ftpwc);
 }
 
-static CURLcode init_wc_data(struct Curl_easy *data)
+static CURLcode init_wc_data(struct Curl_easy *data,
+                             struct ftp_conn *ftpc,
+                             struct FTP *ftp)
 {
   char *last_slash;
-  struct FTP *ftp = data->req.p.ftp;
   char *path = ftp->path;
   struct WildcardData *wildcard = data->wildcard;
   CURLcode result = CURLE_OK;
@@ -3761,7 +3789,7 @@ static CURLcode init_wc_data(struct Curl_easy *data)
     last_slash++;
     if(last_slash[0] == '\0') {
       wildcard->state = CURLWC_CLEAN;
-      return ftp_parse_url_path(data);
+      return ftp_parse_url_path(data, ftpc, ftp);
     }
     wildcard->pattern = strdup(last_slash);
     if(!wildcard->pattern)
@@ -3777,7 +3805,7 @@ static CURLcode init_wc_data(struct Curl_easy *data)
     }
     else { /* only list */
       wildcard->state = CURLWC_CLEAN;
-      return ftp_parse_url_path(data);
+      return ftp_parse_url_path(data, ftpc, ftp);
     }
   }
 
@@ -3806,7 +3834,7 @@ static CURLcode init_wc_data(struct Curl_easy *data)
     data->set.ftp_filemethod = FTPFILE_MULTICWD;
 
   /* try to parse ftp URL */
-  result = ftp_parse_url_path(data);
+  result = ftp_parse_url_path(data, ftpc, ftp);
   if(result) {
     goto fail;
   }
@@ -3840,16 +3868,17 @@ static CURLcode init_wc_data(struct Curl_easy *data)
   return result;
 }
 
-static CURLcode wc_statemach(struct Curl_easy *data)
+static CURLcode wc_statemach(struct Curl_easy *data,
+                             struct ftp_conn *ftpc,
+                             struct FTP *ftp)
 {
   struct WildcardData * const wildcard = data->wildcard;
-  struct connectdata *conn = data->conn;
   CURLcode result = CURLE_OK;
 
   for(;;) {
     switch(wildcard->state) {
     case CURLWC_INIT:
-      result = init_wc_data(data);
+      result = init_wc_data(data, ftpc, ftp);
       if(wildcard->state == CURLWC_CLEAN)
         /* only listing! */
         return result;
@@ -3881,10 +3910,8 @@ static CURLcode wc_statemach(struct Curl_easy *data)
 
     case CURLWC_DOWNLOADING: {
       /* filelist has at least one file, lets get first one */
-      struct ftp_conn *ftpc = &conn->proto.ftpc;
       struct Curl_llist_node *head = Curl_llist_head(&wildcard->filelist);
       struct curl_fileinfo *finfo = Curl_node_elem(head);
-      struct FTP *ftp = data->req.p.ftp;
 
       char *tmp_path = aprintf("%s%s", wildcard->path, finfo->filename);
       if(!tmp_path)
@@ -3921,7 +3948,7 @@ static CURLcode wc_statemach(struct Curl_easy *data)
       if(finfo->flags & CURLFINFOFLAG_KNOWN_SIZE)
         ftpc->known_filesize = finfo->size;
 
-      result = ftp_parse_url_path(data);
+      result = ftp_parse_url_path(data, ftpc, ftp);
       if(result)
         return result;
 
@@ -3985,10 +4012,12 @@ static CURLcode wc_statemach(struct Curl_easy *data)
 static CURLcode ftp_do(struct Curl_easy *data, bool *done)
 {
   CURLcode result = CURLE_OK;
-  struct connectdata *conn = data->conn;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
+  struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN);
+  struct FTP *ftp = Curl_meta_get(data, CURL_META_FTP_EASY);
 
   *done = FALSE; /* default to false */
+  if(!ftpc || !ftp)
+    return CURLE_FAILED_INIT;
   ftpc->wait_data_conn = FALSE; /* default to no such wait */
 
 #ifdef CURL_PREFER_LF_LINEENDS
@@ -4010,7 +4039,7 @@ static CURLcode ftp_do(struct Curl_easy *data, bool *done)
 #endif /* CURL_PREFER_LF_LINEENDS */
 
   if(data->state.wildcardmatch) {
-    result = wc_statemach(data);
+    result = wc_statemach(data, ftpc, ftp);
     if(data->wildcard->state == CURLWC_SKIP ||
        data->wildcard->state == CURLWC_DONE) {
       /* do not call ftp_regular_transfer */
@@ -4020,12 +4049,12 @@ static CURLcode ftp_do(struct Curl_easy *data, bool *done)
       return result;
   }
   else { /* no wildcard FSM needed */
-    result = ftp_parse_url_path(data);
+    result = ftp_parse_url_path(data, ftpc, ftp);
     if(result)
       return result;
   }
 
-  result = ftp_regular_transfer(data, done);
+  result = ftp_regular_transfer(data, ftpc, ftp, done);
 
   return result;
 }
@@ -4040,24 +4069,26 @@ static CURLcode ftp_do(struct Curl_easy *data, bool *done)
  * connection.
  *
  */
-static CURLcode ftp_quit(struct Curl_easy *data, struct connectdata *conn)
+static CURLcode ftp_quit(struct Curl_easy *data,
+                         struct ftp_conn *ftpc)
 {
   CURLcode result = CURLE_OK;
 
-  if(conn->proto.ftpc.ctl_valid) {
-    result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", "QUIT");
+  if(ftpc->ctl_valid) {
+    CURL_TRC_FTP(data, "sending QUIT to close session");
+    result = Curl_pp_sendf(data, &ftpc->pp, "%s", "QUIT");
     if(result) {
       failf(data, "Failure sending QUIT command: %s",
             curl_easy_strerror(result));
-      conn->proto.ftpc.ctl_valid = FALSE; /* mark control connection as bad */
-      connclose(conn, "QUIT command failed"); /* mark for connection closure */
-      ftp_state(data, FTP_STOP);
+      ftpc->ctl_valid = FALSE; /* mark control connection as bad */
+      connclose(data->conn, "QUIT command failed"); /* mark for closure */
+      ftp_state(data, ftpc, FTP_STOP);
       return result;
     }
 
-    ftp_state(data, FTP_QUIT);
+    ftp_state(data, ftpc, FTP_QUIT);
 
-    result = ftp_block_statemach(data, conn);
+    result = ftp_block_statemach(data, ftpc);
   }
 
   return result;
@@ -4074,9 +4105,10 @@ static CURLcode ftp_disconnect(struct Curl_easy *data,
                                struct connectdata *conn,
                                bool dead_connection)
 {
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
-  struct pingpong *pp = &ftpc->pp;
+  struct ftp_conn *ftpc = Curl_conn_meta_get(conn, CURL_META_FTP_CONN);
 
+  if(!ftpc)
+    return CURLE_FAILED_INIT;
   /* We cannot send quit unconditionally. If this connection is stale or
      bad in any way, sending quit and waiting around here will make the
      disconnect wait in vain and cause more problems than we need to.
@@ -4084,26 +4116,12 @@ static CURLcode ftp_disconnect(struct Curl_easy *data,
      ftp_quit() will check the state of ftp->ctl_valid. If it is ok it
      will try to send the QUIT command, otherwise it will just return.
   */
+  ftpc->shutdown = TRUE;
   if(dead_connection)
     ftpc->ctl_valid = FALSE;
 
   /* The FTP session may or may not have been allocated/setup at this point! */
-  (void)ftp_quit(data, conn); /* ignore errors on the QUIT */
-
-  if(ftpc->entrypath) {
-    if(data->state.most_recent_ftp_entrypath == ftpc->entrypath) {
-      data->state.most_recent_ftp_entrypath = NULL;
-    }
-    Curl_safefree(ftpc->entrypath);
-  }
-
-  freedirs(ftpc);
-  Curl_safefree(ftpc->account);
-  Curl_safefree(ftpc->alternative_to_user);
-  Curl_safefree(ftpc->prevpath);
-  Curl_safefree(ftpc->server_os);
-  Curl_pp_disconnect(pp);
-  Curl_sec_end(conn);
+  (void)ftp_quit(data, ftpc); /* ignore errors on the QUIT */
   return CURLE_OK;
 }
 
@@ -4115,12 +4133,10 @@ static CURLcode ftp_disconnect(struct Curl_easy *data,
  *
  */
 static
-CURLcode ftp_parse_url_path(struct Curl_easy *data)
+CURLcode ftp_parse_url_path(struct Curl_easy *data,
+                            struct ftp_conn *ftpc,
+                            struct FTP *ftp)
 {
-  /* the ftp struct is already inited in ftp_connect() */
-  struct FTP *ftp = data->req.p.ftp;
-  struct connectdata *conn = data->conn;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
   const char *slashPos = NULL;
   const char *fileName = NULL;
   CURLcode result = CURLE_OK;
@@ -4243,7 +4259,7 @@ CURLcode ftp_parse_url_path(struct Curl_easy *data)
   if((data->set.ftp_filemethod == FTPFILE_NOCWD) && (rawPath[0] == '/'))
     ftpc->cwddone = TRUE; /* skip CWD for absolute paths */
   else { /* newly created FTP connections are already in entry path */
-    const char *oldPath = conn->bits.reuse ? ftpc->prevpath : "";
+    const char *oldPath = data->conn->bits.reuse ? ftpc->prevpath : "";
     if(oldPath) {
       size_t n = pathLen;
       if(data->set.ftp_filemethod == FTPFILE_NOCWD)
@@ -4263,18 +4279,17 @@ CURLcode ftp_parse_url_path(struct Curl_easy *data)
 }
 
 /* call this when the DO phase has completed */
-static CURLcode ftp_dophase_done(struct Curl_easy *data, bool connected)
+static CURLcode ftp_dophase_done(struct Curl_easy *data,
+                                 struct ftp_conn *ftpc,
+                                 struct FTP *ftp,
+                                 bool connected)
 {
-  struct connectdata *conn = data->conn;
-  struct FTP *ftp = data->req.p.ftp;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
-
   if(connected) {
     int completed;
     CURLcode result = ftp_do_more(data, &completed);
 
     if(result) {
-      close_secondarysocket(data);
+      close_secondarysocket(data, ftpc);
       return result;
     }
   }
@@ -4284,7 +4299,7 @@ static CURLcode ftp_dophase_done(struct Curl_easy *data, bool connected)
     Curl_xfer_setup_nop(data);
   else if(!connected)
     /* since we did not connect now, we want do_more to get called */
-    conn->bits.do_more = TRUE;
+    data->conn->bits.do_more = TRUE;
 
   ftpc->ctl_valid = TRUE; /* seems good */
 
@@ -4295,14 +4310,20 @@ static CURLcode ftp_dophase_done(struct Curl_easy *data, bool connected)
 static CURLcode ftp_doing(struct Curl_easy *data,
                           bool *dophase_done)
 {
-  CURLcode result = ftp_multi_statemach(data, dophase_done);
+  struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN);
+  struct FTP *ftp = Curl_meta_get(data, CURL_META_FTP_EASY);
+  CURLcode result;
+
+  if(!ftpc || !ftp)
+    return CURLE_FAILED_INIT;
+  result = ftp_statemach(data, ftpc, dophase_done);
 
   if(result)
-    CURL_TRC_FTP(data, "[%s] DO phase failed", FTP_DSTATE(data));
+    CURL_TRC_FTP(data, "[%s] DO phase failed", FTP_CSTATE(ftpc));
   else if(*dophase_done) {
-    result = ftp_dophase_done(data, FALSE /* not connected */);
+    result = ftp_dophase_done(data, ftpc, ftp, FALSE /* not connected */);
 
-    CURL_TRC_FTP(data, "[%s] DO phase is complete2", FTP_DSTATE(data));
+    CURL_TRC_FTP(data, "[%s] DO phase is complete2", FTP_CSTATE(ftpc));
   }
   return result;
 }
@@ -4321,12 +4342,12 @@ static CURLcode ftp_doing(struct Curl_easy *data,
  */
 static
 CURLcode ftp_regular_transfer(struct Curl_easy *data,
+                              struct ftp_conn *ftpc,
+                              struct FTP *ftp,
                               bool *dophase_done)
 {
   CURLcode result = CURLE_OK;
   bool connected = FALSE;
-  struct connectdata *conn = data->conn;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
   data->req.size = -1; /* make sure this is unknown at this point */
 
   Curl_pgrsSetUploadCounter(data, 0);
@@ -4336,7 +4357,7 @@ CURLcode ftp_regular_transfer(struct Curl_easy *data,
 
   ftpc->ctl_valid = TRUE; /* starts good */
 
-  result = ftp_perform(data,
+  result = ftp_perform(data, ftpc, ftp,
                        &connected, /* have we connected after PASV/PORT */
                        dophase_done); /* all commands in the DO-phase done? */
 
@@ -4346,7 +4367,7 @@ CURLcode ftp_regular_transfer(struct Curl_easy *data,
       /* the DO phase has not completed yet */
       return CURLE_OK;
 
-    result = ftp_dophase_done(data, connected);
+    result = ftp_dophase_done(data, ftpc, ftp, connected);
 
     if(result)
       return result;
@@ -4357,23 +4378,53 @@ CURLcode ftp_regular_transfer(struct Curl_easy *data,
   return result;
 }
 
+static void ftp_easy_dtor(void *key, size_t klen, void *entry)
+{
+  struct FTP *ftp = entry;
+  (void)key;
+  (void)klen;
+  Curl_safefree(ftp->pathalloc);
+  free(ftp);
+}
+
+static void ftp_conn_dtor(void *key, size_t klen, void *entry)
+{
+  struct ftp_conn *ftpc = entry;
+  (void)key;
+  (void)klen;
+  freedirs(ftpc);
+  Curl_safefree(ftpc->account);
+  Curl_safefree(ftpc->alternative_to_user);
+  Curl_safefree(ftpc->entrypath);
+  Curl_safefree(ftpc->prevpath);
+  Curl_safefree(ftpc->server_os);
+  Curl_pp_disconnect(&ftpc->pp);
+  free(ftpc);
+}
+
 static CURLcode ftp_setup_connection(struct Curl_easy *data,
                                      struct connectdata *conn)
 {
   char *type;
   struct FTP *ftp;
   CURLcode result = CURLE_OK;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
+  struct ftp_conn *ftpc;
 
-  ftp = calloc(1, sizeof(struct FTP));
-  if(!ftp)
+  ftp = calloc(1, sizeof(*ftp));
+  if(!ftp ||
+     Curl_meta_set(data, CURL_META_FTP_EASY, ftp, ftp_easy_dtor))
+    return CURLE_OUT_OF_MEMORY;
+
+  ftpc = calloc(1, sizeof(*ftpc));
+  if(!ftpc ||
+     Curl_conn_meta_set(conn, CURL_META_FTP_CONN, ftpc, ftp_conn_dtor))
     return CURLE_OUT_OF_MEMORY;
 
   /* clone connection related data that is FTP specific */
   if(data->set.str[STRING_FTP_ACCOUNT]) {
     ftpc->account = strdup(data->set.str[STRING_FTP_ACCOUNT]);
     if(!ftpc->account) {
-      free(ftp);
+      Curl_conn_meta_remove(conn, CURL_META_FTP_CONN);
       return CURLE_OUT_OF_MEMORY;
     }
   }
@@ -4382,11 +4433,10 @@ static CURLcode ftp_setup_connection(struct Curl_easy *data,
       strdup(data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]);
     if(!ftpc->alternative_to_user) {
       Curl_safefree(ftpc->account);
-      free(ftp);
+      Curl_conn_meta_remove(conn, CURL_META_FTP_CONN);
       return CURLE_OUT_OF_MEMORY;
     }
   }
-  data->req.p.ftp = ftp;
 
   ftp->path = &data->state.up.path[1]; /* do not include the initial slash */
 
@@ -4426,8 +4476,23 @@ static CURLcode ftp_setup_connection(struct Curl_easy *data,
   ftpc->use_ssl = data->set.use_ssl;
   ftpc->ccc = data->set.ftp_ccc;
 
-  CURL_TRC_FTP(data, "[%s] setup connection -> %d", FTP_CSTATE(conn), result);
+  CURL_TRC_FTP(data, "[%s] setup connection -> %d", FTP_CSTATE(ftpc), result);
   return result;
 }
 
+bool ftp_conns_match(struct connectdata *needle, struct connectdata *conn)
+{
+  struct ftp_conn *nftpc = Curl_conn_meta_get(needle, CURL_META_FTP_CONN);
+  struct ftp_conn *cftpc = Curl_conn_meta_get(conn, CURL_META_FTP_CONN);
+  /* Also match ACCOUNT, ALTERNATIVE-TO-USER, USE_SSL and CCC options */
+  if(!nftpc || !cftpc ||
+     Curl_timestrcmp(nftpc->account, cftpc->account) ||
+     Curl_timestrcmp(nftpc->alternative_to_user,
+                     cftpc->alternative_to_user) ||
+     (nftpc->use_ssl != cftpc->use_ssl) ||
+     (nftpc->ccc != cftpc->ccc))
+    return FALSE;
+  return TRUE;
+}
+
 #endif /* CURL_DISABLE_FTP */
diff --git a/extra/curl/curl-8.12.1/lib/ftp.h b/extra/curl/curl-8.14.1/lib/ftp.h
similarity index 91%
rename from extra/curl/curl-8.12.1/lib/ftp.h
rename to extra/curl/curl-8.14.1/lib/ftp.h
index 3d0af0158786..c31aa9328617 100644
--- a/extra/curl/curl-8.12.1/lib/ftp.h
+++ b/extra/curl/curl-8.14.1/lib/ftp.h
@@ -37,6 +37,9 @@ extern const struct Curl_handler Curl_handler_ftps;
 
 CURLcode Curl_GetFTPResponse(struct Curl_easy *data, ssize_t *nread,
                              int *ftpcode);
+
+bool ftp_conns_match(struct connectdata *needle, struct connectdata *conn);
+
 #endif /* CURL_DISABLE_FTP */
 
 /****************************************************************************
@@ -125,7 +128,8 @@ struct ftp_conn {
   char *entrypath; /* the PWD reply when we logged on */
   char *file;    /* url-decoded filename (or path) */
   char **dirs;   /* realloc()ed array for path components */
-  char *newhost;
+  char *newhost; /* the (allocated) IP addr or hostname to connect the data
+                    connection to */
   char *prevpath;   /* url-decoded conn->path from the previous transfer */
   char transfertype; /* set by ftp_transfertype for use by Curl_client_write()a
                         and others (A/I or zero) */
@@ -139,9 +143,8 @@ struct ftp_conn {
   int count1; /* general purpose counter for the state machine */
   int count2; /* general purpose counter for the state machine */
   int count3; /* general purpose counter for the state machine */
-  /* newhost is the (allocated) IP addr or hostname to connect the data
-     connection to */
-  unsigned short newport;
+  unsigned short newport;  /* the port of 'newhost' to connect the data
+                              connection to */
   ftpstate state; /* always use ftp.c:state() to change state! */
   ftpstate state_saved; /* transfer type saved to be reloaded after data
                            connection is established */
@@ -160,8 +163,14 @@ struct ftp_conn {
   BIT(cwdfail);     /* set TRUE if a CWD command fails, as then we must prevent
                        caching the current directory */
   BIT(wait_data_conn); /* this is set TRUE if data connection is waited */
+  BIT(shutdown);    /* connection is being shutdown, e.g. QUIT */
 };
 
+/* meta key for storing `struct FTP` as easy meta data */
+#define CURL_META_FTP_EASY   "meta:proto:ftp:easy"
+/* meta key for storing `struct ftp_conn` as connection meta data */
+#define CURL_META_FTP_CONN   "meta:proto:ftp:conn"
+
 #define DEFAULT_ACCEPT_TIMEOUT   60000 /* milliseconds == one minute */
 
 #endif /* HEADER_CURL_FTP_H */
diff --git a/extra/curl/curl-8.14.1/lib/ftplistparser.c b/extra/curl/curl-8.14.1/lib/ftplistparser.c
new file mode 100644
index 000000000000..70939d6a76f7
--- /dev/null
+++ b/extra/curl/curl-8.14.1/lib/ftplistparser.c
@@ -0,0 +1,1012 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+/**
+ * Now implemented:
+ *
+ * 1) Unix version 1
+ * drwxr-xr-x 1 user01 ftp  512 Jan 29 23:32 prog
+ * 2) Unix version 2
+ * drwxr-xr-x 1 user01 ftp  512 Jan 29 1997  prog
+ * 3) Unix version 3
+ * drwxr-xr-x 1      1   1  512 Jan 29 23:32 prog
+ * 4) Unix symlink
+ * lrwxr-xr-x 1 user01 ftp  512 Jan 29 23:32 prog -> prog2000
+ * 5) DOS style
+ * 01-29-97 11:32PM <DIR> prog
+ */
+
+#include "curl_setup.h"
+
+#ifndef CURL_DISABLE_FTP
+
+#include <curl/curl.h>
+
+#include "urldata.h"
+#include "fileinfo.h"
+#include "llist.h"
+#include "ftp.h"
+#include "ftplistparser.h"
+#include "curl_fnmatch.h"
+#include "multiif.h"
+#include "curlx/strparse.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+typedef enum {
+  PL_UNIX_TOTALSIZE = 0,
+  PL_UNIX_FILETYPE,
+  PL_UNIX_PERMISSION,
+  PL_UNIX_HLINKS,
+  PL_UNIX_USER,
+  PL_UNIX_GROUP,
+  PL_UNIX_SIZE,
+  PL_UNIX_TIME,
+  PL_UNIX_FILENAME,
+  PL_UNIX_SYMLINK
+} pl_unix_mainstate;
+
+typedef union {
+  enum {
+    PL_UNIX_TOTALSIZE_INIT = 0,
+    PL_UNIX_TOTALSIZE_READING
+  } total_dirsize;
+
+  enum {
+    PL_UNIX_HLINKS_PRESPACE = 0,
+    PL_UNIX_HLINKS_NUMBER
+  } hlinks;
+
+  enum {
+    PL_UNIX_USER_PRESPACE = 0,
+    PL_UNIX_USER_PARSING
+  } user;
+
+  enum {
+    PL_UNIX_GROUP_PRESPACE = 0,
+    PL_UNIX_GROUP_NAME
+  } group;
+
+  enum {
+    PL_UNIX_SIZE_PRESPACE = 0,
+    PL_UNIX_SIZE_NUMBER
+  } size;
+
+  enum {
+    PL_UNIX_TIME_PREPART1 = 0,
+    PL_UNIX_TIME_PART1,
+    PL_UNIX_TIME_PREPART2,
+    PL_UNIX_TIME_PART2,
+    PL_UNIX_TIME_PREPART3,
+    PL_UNIX_TIME_PART3
+  } time;
+
+  enum {
+    PL_UNIX_FILENAME_PRESPACE = 0,
+    PL_UNIX_FILENAME_NAME,
+    PL_UNIX_FILENAME_WINDOWSEOL
+  } filename;
+
+  enum {
+    PL_UNIX_SYMLINK_PRESPACE = 0,
+    PL_UNIX_SYMLINK_NAME,
+    PL_UNIX_SYMLINK_PRETARGET1,
+    PL_UNIX_SYMLINK_PRETARGET2,
+    PL_UNIX_SYMLINK_PRETARGET3,
+    PL_UNIX_SYMLINK_PRETARGET4,
+    PL_UNIX_SYMLINK_TARGET,
+    PL_UNIX_SYMLINK_WINDOWSEOL
+  } symlink;
+} pl_unix_substate;
+
+typedef enum {
+  PL_WINNT_DATE = 0,
+  PL_WINNT_TIME,
+  PL_WINNT_DIRORSIZE,
+  PL_WINNT_FILENAME
+} pl_winNT_mainstate;
+
+typedef union {
+  enum {
+    PL_WINNT_TIME_PRESPACE = 0,
+    PL_WINNT_TIME_TIME
+  } time;
+  enum {
+    PL_WINNT_DIRORSIZE_PRESPACE = 0,
+    PL_WINNT_DIRORSIZE_CONTENT
+  } dirorsize;
+  enum {
+    PL_WINNT_FILENAME_PRESPACE = 0,
+    PL_WINNT_FILENAME_CONTENT,
+    PL_WINNT_FILENAME_WINEOL
+  } filename;
+} pl_winNT_substate;
+
+/* This struct is used in wildcard downloading - for parsing LIST response */
+struct ftp_parselist_data {
+  enum {
+    OS_TYPE_UNKNOWN = 0,
+    OS_TYPE_UNIX,
+    OS_TYPE_WIN_NT
+  } os_type;
+
+  union {
+    struct {
+      pl_unix_mainstate main;
+      pl_unix_substate sub;
+    } UNIX;
+
+    struct {
+      pl_winNT_mainstate main;
+      pl_winNT_substate sub;
+    } NT;
+  } state;
+
+  CURLcode error;
+  struct fileinfo *file_data;
+  unsigned int item_length;
+  size_t item_offset;
+  struct {
+    size_t filename;
+    size_t user;
+    size_t group;
+    size_t time;
+    size_t perm;
+    size_t symlink_target;
+  } offsets;
+};
+
+static void fileinfo_dtor(void *user, void *element)
+{
+  (void)user;
+  Curl_fileinfo_cleanup(element);
+}
+
+CURLcode Curl_wildcard_init(struct WildcardData *wc)
+{
+  Curl_llist_init(&wc->filelist, fileinfo_dtor);
+  wc->state = CURLWC_INIT;
+
+  return CURLE_OK;
+}
+
+void Curl_wildcard_dtor(struct WildcardData **wcp)
+{
+  struct WildcardData *wc = *wcp;
+  if(!wc)
+    return;
+
+  if(wc->dtor) {
+    wc->dtor(wc->ftpwc);
+    wc->dtor = ZERO_NULL;
+    wc->ftpwc = NULL;
+  }
+  DEBUGASSERT(wc->ftpwc == NULL);
+
+  Curl_llist_destroy(&wc->filelist, NULL);
+  free(wc->path);
+  wc->path = NULL;
+  free(wc->pattern);
+  wc->pattern = NULL;
+  wc->state = CURLWC_INIT;
+  free(wc);
+  *wcp = NULL;
+}
+
+struct ftp_parselist_data *Curl_ftp_parselist_data_alloc(void)
+{
+  return calloc(1, sizeof(struct ftp_parselist_data));
+}
+
+
+void Curl_ftp_parselist_data_free(struct ftp_parselist_data **parserp)
+{
+  struct ftp_parselist_data *parser = *parserp;
+  if(parser)
+    Curl_fileinfo_cleanup(parser->file_data);
+  free(parser);
+  *parserp = NULL;
+}
+
+
+CURLcode Curl_ftp_parselist_geterror(struct ftp_parselist_data *pl_data)
+{
+  return pl_data->error;
+}
+
+
+#define FTP_LP_MALFORMATED_PERM 0x01000000
+
+static unsigned int ftp_pl_get_permission(const char *str)
+{
+  unsigned int permissions = 0;
+  /* USER */
+  if(str[0] == 'r')
+    permissions |= 1 << 8;
+  else if(str[0] != '-')
+    permissions |= FTP_LP_MALFORMATED_PERM;
+  if(str[1] == 'w')
+    permissions |= 1 << 7;
+  else if(str[1] != '-')
+    permissions |= FTP_LP_MALFORMATED_PERM;
+
+  if(str[2] == 'x')
+    permissions |= 1 << 6;
+  else if(str[2] == 's') {
+    permissions |= 1 << 6;
+    permissions |= 1 << 11;
+  }
+  else if(str[2] == 'S')
+    permissions |= 1 << 11;
+  else if(str[2] != '-')
+    permissions |= FTP_LP_MALFORMATED_PERM;
+  /* GROUP */
+  if(str[3] == 'r')
+    permissions |= 1 << 5;
+  else if(str[3] != '-')
+    permissions |= FTP_LP_MALFORMATED_PERM;
+  if(str[4] == 'w')
+    permissions |= 1 << 4;
+  else if(str[4] != '-')
+    permissions |= FTP_LP_MALFORMATED_PERM;
+  if(str[5] == 'x')
+    permissions |= 1 << 3;
+  else if(str[5] == 's') {
+    permissions |= 1 << 3;
+    permissions |= 1 << 10;
+  }
+  else if(str[5] == 'S')
+    permissions |= 1 << 10;
+  else if(str[5] != '-')
+    permissions |= FTP_LP_MALFORMATED_PERM;
+  /* others */
+  if(str[6] == 'r')
+    permissions |= 1 << 2;
+  else if(str[6] != '-')
+    permissions |= FTP_LP_MALFORMATED_PERM;
+  if(str[7] == 'w')
+    permissions |= 1 << 1;
+  else if(str[7] != '-')
+      permissions |= FTP_LP_MALFORMATED_PERM;
+  if(str[8] == 'x')
+    permissions |= 1;
+  else if(str[8] == 't') {
+    permissions |= 1;
+    permissions |= 1 << 9;
+  }
+  else if(str[8] == 'T')
+    permissions |= 1 << 9;
+  else if(str[8] != '-')
+    permissions |= FTP_LP_MALFORMATED_PERM;
+
+  return permissions;
+}
+
+static CURLcode ftp_pl_insert_finfo(struct Curl_easy *data,
+                                    struct fileinfo *infop)
+{
+  curl_fnmatch_callback compare;
+  struct WildcardData *wc = data->wildcard;
+  struct ftp_wc *ftpwc = wc->ftpwc;
+  struct Curl_llist *llist = &wc->filelist;
+  struct ftp_parselist_data *parser = ftpwc->parser;
+  bool add = TRUE;
+  struct curl_fileinfo *finfo = &infop->info;
+
+  /* set the finfo pointers */
+  char *str = curlx_dyn_ptr(&infop->buf);
+  finfo->filename       = str + parser->offsets.filename;
+  finfo->strings.group  = parser->offsets.group ?
+                          str + parser->offsets.group : NULL;
+  finfo->strings.perm   = parser->offsets.perm ?
+                          str + parser->offsets.perm : NULL;
+  finfo->strings.target = parser->offsets.symlink_target ?
+                          str + parser->offsets.symlink_target : NULL;
+  finfo->strings.time   = str + parser->offsets.time;
+  finfo->strings.user   = parser->offsets.user ?
+                          str + parser->offsets.user : NULL;
+
+  /* get correct fnmatch callback */
+  compare = data->set.fnmatch;
+  if(!compare)
+    compare = Curl_fnmatch;
+
+  /* filter pattern-corresponding filenames */
+  Curl_set_in_callback(data, TRUE);
+  if(compare(data->set.fnmatch_data, wc->pattern,
+             finfo->filename) == 0) {
+    /* discard symlink which is containing multiple " -> " */
+    if((finfo->filetype == CURLFILETYPE_SYMLINK) && finfo->strings.target &&
+       (strstr(finfo->strings.target, " -> "))) {
+      add = FALSE;
+    }
+  }
+  else {
+    add = FALSE;
+  }
+  Curl_set_in_callback(data, FALSE);
+
+  if(add) {
+    Curl_llist_append(llist, finfo, &infop->list);
+  }
+  else {
+    Curl_fileinfo_cleanup(infop);
+  }
+
+  ftpwc->parser->file_data = NULL;
+  return CURLE_OK;
+}
+
+#define MAX_FTPLIST_BUFFER 10000 /* arbitrarily set */
+
+static CURLcode unix_filetype(const char c, curlfiletype *t)
+{
+  switch(c) {
+  case '-':
+    *t = CURLFILETYPE_FILE;
+    break;
+  case 'd':
+    *t = CURLFILETYPE_DIRECTORY;
+    break;
+  case 'l':
+    *t = CURLFILETYPE_SYMLINK;
+    break;
+  case 'p':
+    *t = CURLFILETYPE_NAMEDPIPE;
+    break;
+  case 's':
+    *t = CURLFILETYPE_SOCKET;
+    break;
+  case 'c':
+    *t = CURLFILETYPE_DEVICE_CHAR;
+    break;
+  case 'b':
+    *t = CURLFILETYPE_DEVICE_BLOCK;
+    break;
+  case 'D':
+    *t = CURLFILETYPE_DOOR;
+    break;
+  default:
+    return CURLE_FTP_BAD_FILE_LIST;
+  }
+  return CURLE_OK;
+}
+
+static CURLcode parse_unix(struct Curl_easy *data,
+                           struct ftp_parselist_data *parser,
+                           struct fileinfo *infop,
+                           const char c)
+{
+  struct curl_fileinfo *finfo = &infop->info;
+  size_t len = curlx_dyn_len(&infop->buf);
+  char *mem = curlx_dyn_ptr(&infop->buf);
+  CURLcode result = CURLE_OK;
+
+  switch(parser->state.UNIX.main) {
+  case PL_UNIX_TOTALSIZE:
+    switch(parser->state.UNIX.sub.total_dirsize) {
+    case PL_UNIX_TOTALSIZE_INIT:
+      if(c == 't') {
+        parser->state.UNIX.sub.total_dirsize = PL_UNIX_TOTALSIZE_READING;
+        parser->item_length++;
+      }
+      else {
+        parser->state.UNIX.main = PL_UNIX_FILETYPE;
+        /* continue to fall through */
+      }
+      break;
+    case PL_UNIX_TOTALSIZE_READING:
+      parser->item_length++;
+      if(c == '\r') {
+        parser->item_length--;
+        if(len)
+          curlx_dyn_setlen(&infop->buf, --len);
+      }
+      else if(c == '\n') {
+        mem[parser->item_length - 1] = 0;
+        if(!strncmp("total ", mem, 6)) {
+          const char *endptr = mem + 6;
+          /* here we can deal with directory size, pass the leading
+             whitespace and then the digits */
+          curlx_str_passblanks(&endptr);
+          while(ISDIGIT(*endptr))
+            endptr++;
+          if(*endptr) {
+            return CURLE_FTP_BAD_FILE_LIST;
+          }
+          parser->state.UNIX.main = PL_UNIX_FILETYPE;
+          curlx_dyn_reset(&infop->buf);
+        }
+        else
+          return CURLE_FTP_BAD_FILE_LIST;
+
+      }
+      break;
+    }
+    if(parser->state.UNIX.main != PL_UNIX_FILETYPE)
+      break;
+    FALLTHROUGH();
+  case PL_UNIX_FILETYPE:
+    result = unix_filetype(c, &finfo->filetype);
+    if(result)
+      return result;
+    parser->state.UNIX.main = PL_UNIX_PERMISSION;
+    parser->item_length = 0;
+    parser->item_offset = 1;
+    break;
+  case PL_UNIX_PERMISSION:
+    parser->item_length++;
+    if((parser->item_length <= 9) && !strchr("rwx-tTsS", c))
+      return CURLE_FTP_BAD_FILE_LIST;
+
+    else if(parser->item_length == 10) {
+      unsigned int perm;
+      if(c != ' ')
+        return CURLE_FTP_BAD_FILE_LIST;
+
+      mem[10] = 0; /* terminate permissions */
+      perm = ftp_pl_get_permission(mem + parser->item_offset);
+      if(perm & FTP_LP_MALFORMATED_PERM)
+        return CURLE_FTP_BAD_FILE_LIST;
+
+      parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_PERM;
+      parser->file_data->info.perm = perm;
+      parser->offsets.perm = parser->item_offset;
+
+      parser->item_length = 0;
+      parser->state.UNIX.main = PL_UNIX_HLINKS;
+      parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_PRESPACE;
+    }
+    break;
+  case PL_UNIX_HLINKS:
+    switch(parser->state.UNIX.sub.hlinks) {
+    case PL_UNIX_HLINKS_PRESPACE:
+      if(c != ' ') {
+        if(ISDIGIT(c) && len) {
+          parser->item_offset = len - 1;
+          parser->item_length = 1;
+          parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_NUMBER;
+        }
+        else
+          return CURLE_FTP_BAD_FILE_LIST;
+      }
+      break;
+    case PL_UNIX_HLINKS_NUMBER:
+      parser->item_length ++;
+      if(c == ' ') {
+        const char *p = &mem[parser->item_offset];
+        curl_off_t hlinks;
+        mem[parser->item_offset + parser->item_length - 1] = 0;
+
+        if(!curlx_str_number(&p, &hlinks, LONG_MAX)) {
+          parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_HLINKCOUNT;
+          parser->file_data->info.hardlinks = (long)hlinks;
+        }
+        parser->item_length = 0;
+        parser->item_offset = 0;
+        parser->state.UNIX.main = PL_UNIX_USER;
+        parser->state.UNIX.sub.user = PL_UNIX_USER_PRESPACE;
+      }
+      else if(!ISDIGIT(c))
+        return CURLE_FTP_BAD_FILE_LIST;
+
+      break;
+    }
+    break;
+  case PL_UNIX_USER:
+    switch(parser->state.UNIX.sub.user) {
+    case PL_UNIX_USER_PRESPACE:
+      if(c != ' ' && len) {
+        parser->item_offset = len - 1;
+        parser->item_length = 1;
+        parser->state.UNIX.sub.user = PL_UNIX_USER_PARSING;
+      }
+      break;
+    case PL_UNIX_USER_PARSING:
+      parser->item_length++;
+      if(c == ' ') {
+        mem[parser->item_offset + parser->item_length - 1] = 0;
+        parser->offsets.user = parser->item_offset;
+        parser->state.UNIX.main = PL_UNIX_GROUP;
+        parser->state.UNIX.sub.group = PL_UNIX_GROUP_PRESPACE;
+        parser->item_offset = 0;
+        parser->item_length = 0;
+      }
+      break;
+    }
+    break;
+  case PL_UNIX_GROUP:
+    switch(parser->state.UNIX.sub.group) {
+    case PL_UNIX_GROUP_PRESPACE:
+      if(c != ' ' && len) {
+        parser->item_offset = len - 1;
+        parser->item_length = 1;
+        parser->state.UNIX.sub.group = PL_UNIX_GROUP_NAME;
+      }
+      break;
+    case PL_UNIX_GROUP_NAME:
+      parser->item_length++;
+      if(c == ' ') {
+        mem[parser->item_offset + parser->item_length - 1] = 0;
+        parser->offsets.group = parser->item_offset;
+        parser->state.UNIX.main = PL_UNIX_SIZE;
+        parser->state.UNIX.sub.size = PL_UNIX_SIZE_PRESPACE;
+        parser->item_offset = 0;
+        parser->item_length = 0;
+      }
+      break;
+    }
+    break;
+  case PL_UNIX_SIZE:
+    switch(parser->state.UNIX.sub.size) {
+    case PL_UNIX_SIZE_PRESPACE:
+      if(c != ' ') {
+        if(ISDIGIT(c) && len) {
+          parser->item_offset = len - 1;
+          parser->item_length = 1;
+          parser->state.UNIX.sub.size = PL_UNIX_SIZE_NUMBER;
+        }
+        else
+          return CURLE_FTP_BAD_FILE_LIST;
+      }
+      break;
+    case PL_UNIX_SIZE_NUMBER:
+      parser->item_length++;
+      if(c == ' ') {
+        const char *p = mem + parser->item_offset;
+        curl_off_t fsize;
+        mem[parser->item_offset + parser->item_length - 1] = 0;
+        if(!curlx_str_numblanks(&p, &fsize)) {
+          if(p[0] == '\0' && fsize != CURL_OFF_T_MAX) {
+            parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_SIZE;
+            parser->file_data->info.size = fsize;
+          }
+          parser->item_length = 0;
+          parser->item_offset = 0;
+          parser->state.UNIX.main = PL_UNIX_TIME;
+          parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART1;
+        }
+      }
+      else if(!ISDIGIT(c))
+        return CURLE_FTP_BAD_FILE_LIST;
+
+      break;
+    }
+    break;
+  case PL_UNIX_TIME:
+    switch(parser->state.UNIX.sub.time) {
+    case PL_UNIX_TIME_PREPART1:
+      if(c != ' ') {
+        if(ISALNUM(c) && len) {
+          parser->item_offset = len -1;
+          parser->item_length = 1;
+          parser->state.UNIX.sub.time = PL_UNIX_TIME_PART1;
+        }
+        else
+          return CURLE_FTP_BAD_FILE_LIST;
+      }
+      break;
+    case PL_UNIX_TIME_PART1:
+      parser->item_length++;
+      if(c == ' ')
+        parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART2;
+
+      else if(!ISALNUM(c) && c != '.')
+        return CURLE_FTP_BAD_FILE_LIST;
+
+      break;
+    case PL_UNIX_TIME_PREPART2:
+      parser->item_length++;
+      if(c != ' ') {
+        if(ISALNUM(c))
+          parser->state.UNIX.sub.time = PL_UNIX_TIME_PART2;
+        else
+          return CURLE_FTP_BAD_FILE_LIST;
+      }
+      break;
+    case PL_UNIX_TIME_PART2:
+      parser->item_length++;
+      if(c == ' ')
+        parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART3;
+      else if(!ISALNUM(c) && c != '.')
+        return CURLE_FTP_BAD_FILE_LIST;
+      break;
+    case PL_UNIX_TIME_PREPART3:
+      parser->item_length++;
+      if(c != ' ') {
+        if(ISALNUM(c))
+          parser->state.UNIX.sub.time = PL_UNIX_TIME_PART3;
+        else
+          return CURLE_FTP_BAD_FILE_LIST;
+      }
+      break;
+    case PL_UNIX_TIME_PART3:
+      parser->item_length++;
+      if(c == ' ') {
+        mem[parser->item_offset + parser->item_length -1] = 0;
+        parser->offsets.time = parser->item_offset;
+        if(finfo->filetype == CURLFILETYPE_SYMLINK) {
+          parser->state.UNIX.main = PL_UNIX_SYMLINK;
+          parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRESPACE;
+        }
+        else {
+          parser->state.UNIX.main = PL_UNIX_FILENAME;
+          parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_PRESPACE;
+        }
+      }
+      else if(!ISALNUM(c) && c != '.' && c != ':')
+        return CURLE_FTP_BAD_FILE_LIST;
+      break;
+    }
+    break;
+  case PL_UNIX_FILENAME:
+    switch(parser->state.UNIX.sub.filename) {
+    case PL_UNIX_FILENAME_PRESPACE:
+      if(c != ' ' && len) {
+        parser->item_offset = len - 1;
+        parser->item_length = 1;
+        parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_NAME;
+      }
+      break;
+    case PL_UNIX_FILENAME_NAME:
+      parser->item_length++;
+      if(c == '\r')
+        parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_WINDOWSEOL;
+
+      else if(c == '\n') {
+        mem[parser->item_offset + parser->item_length - 1] = 0;
+        parser->offsets.filename = parser->item_offset;
+        parser->state.UNIX.main = PL_UNIX_FILETYPE;
+        result = ftp_pl_insert_finfo(data, infop);
+        if(result)
+          return result;
+      }
+      break;
+    case PL_UNIX_FILENAME_WINDOWSEOL:
+      if(c == '\n') {
+        mem[parser->item_offset + parser->item_length - 1] = 0;
+        parser->offsets.filename = parser->item_offset;
+        parser->state.UNIX.main = PL_UNIX_FILETYPE;
+        result = ftp_pl_insert_finfo(data, infop);
+        if(result)
+          return result;
+      }
+      else
+        return CURLE_FTP_BAD_FILE_LIST;
+
+      break;
+    }
+    break;
+  case PL_UNIX_SYMLINK:
+    switch(parser->state.UNIX.sub.symlink) {
+    case PL_UNIX_SYMLINK_PRESPACE:
+      if(c != ' ' && len) {
+        parser->item_offset = len - 1;
+        parser->item_length = 1;
+        parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
+      }
+      break;
+    case PL_UNIX_SYMLINK_NAME:
+      parser->item_length++;
+      if(c == ' ')
+        parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET1;
+
+      else if(c == '\r' || c == '\n')
+        return CURLE_FTP_BAD_FILE_LIST;
+
+      break;
+    case PL_UNIX_SYMLINK_PRETARGET1:
+      parser->item_length++;
+      if(c == '-')
+        parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET2;
+
+      else if(c == '\r' || c == '\n')
+        return CURLE_FTP_BAD_FILE_LIST;
+      else
+        parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
+      break;
+    case PL_UNIX_SYMLINK_PRETARGET2:
+      parser->item_length++;
+      if(c == '>')
+        parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET3;
+      else if(c == '\r' || c == '\n')
+        return CURLE_FTP_BAD_FILE_LIST;
+      else
+        parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
+
+      break;
+    case PL_UNIX_SYMLINK_PRETARGET3:
+      parser->item_length++;
+      if(c == ' ') {
+        parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET4;
+        /* now place where is symlink following */
+        mem[parser->item_offset + parser->item_length - 4] = 0;
+        parser->offsets.filename = parser->item_offset;
+        parser->item_length = 0;
+        parser->item_offset = 0;
+      }
+      else if(c == '\r' || c == '\n')
+        return CURLE_FTP_BAD_FILE_LIST;
+      else
+        parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
+      break;
+    case PL_UNIX_SYMLINK_PRETARGET4:
+      if(c != '\r' && c != '\n' && len) {
+        parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_TARGET;
+        parser->item_offset = len - 1;
+        parser->item_length = 1;
+      }
+      else
+        return CURLE_FTP_BAD_FILE_LIST;
+
+      break;
+    case PL_UNIX_SYMLINK_TARGET:
+      parser->item_length++;
+      if(c == '\r')
+        parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_WINDOWSEOL;
+
+      else if(c == '\n') {
+        mem[parser->item_offset + parser->item_length - 1] = 0;
+        parser->offsets.symlink_target = parser->item_offset;
+        result = ftp_pl_insert_finfo(data, infop);
+        if(result)
+          return result;
+
+        parser->state.UNIX.main = PL_UNIX_FILETYPE;
+      }
+      break;
+    case PL_UNIX_SYMLINK_WINDOWSEOL:
+      if(c == '\n') {
+        mem[parser->item_offset + parser->item_length - 1] = 0;
+        parser->offsets.symlink_target = parser->item_offset;
+        result = ftp_pl_insert_finfo(data, infop);
+        if(result)
+          return result;
+
+        parser->state.UNIX.main = PL_UNIX_FILETYPE;
+      }
+      else
+        return CURLE_FTP_BAD_FILE_LIST;
+
+      break;
+    }
+    break;
+  }
+  return CURLE_OK;
+}
+
+static CURLcode parse_winnt(struct Curl_easy *data,
+                            struct ftp_parselist_data *parser,
+                            struct fileinfo *infop,
+                            const char c)
+{
+  struct curl_fileinfo *finfo = &infop->info;
+  size_t len = curlx_dyn_len(&infop->buf);
+  char *mem = curlx_dyn_ptr(&infop->buf);
+  CURLcode result = CURLE_OK;
+
+  switch(parser->state.NT.main) {
+  case PL_WINNT_DATE:
+    parser->item_length++;
+    if(parser->item_length < 9) {
+      if(!strchr("0123456789-", c)) { /* only simple control */
+        return CURLE_FTP_BAD_FILE_LIST;
+      }
+    }
+    else if(parser->item_length == 9) {
+      if(c == ' ') {
+        parser->state.NT.main = PL_WINNT_TIME;
+        parser->state.NT.sub.time = PL_WINNT_TIME_PRESPACE;
+      }
+      else
+        return CURLE_FTP_BAD_FILE_LIST;
+    }
+    else
+      return CURLE_FTP_BAD_FILE_LIST;
+    break;
+  case PL_WINNT_TIME:
+    parser->item_length++;
+    switch(parser->state.NT.sub.time) {
+    case PL_WINNT_TIME_PRESPACE:
+      if(!ISBLANK(c))
+        parser->state.NT.sub.time = PL_WINNT_TIME_TIME;
+      break;
+    case PL_WINNT_TIME_TIME:
+      if(c == ' ') {
+        parser->offsets.time = parser->item_offset;
+        mem[parser->item_offset + parser->item_length -1] = 0;
+        parser->state.NT.main = PL_WINNT_DIRORSIZE;
+        parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_PRESPACE;
+        parser->item_length = 0;
+      }
+      else if(!strchr("APM0123456789:", c))
+        return CURLE_FTP_BAD_FILE_LIST;
+      break;
+    }
+    break;
+  case PL_WINNT_DIRORSIZE:
+    switch(parser->state.NT.sub.dirorsize) {
+    case PL_WINNT_DIRORSIZE_PRESPACE:
+      if(c != ' ' && len) {
+        parser->item_offset = len - 1;
+        parser->item_length = 1;
+        parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_CONTENT;
+      }
+      break;
+    case PL_WINNT_DIRORSIZE_CONTENT:
+      parser->item_length ++;
+      if(c == ' ') {
+        mem[parser->item_offset + parser->item_length - 1] = 0;
+        if(strcmp("<DIR>", mem + parser->item_offset) == 0) {
+          finfo->filetype = CURLFILETYPE_DIRECTORY;
+          finfo->size = 0;
+        }
+        else {
+          const char *p = mem + parser->item_offset;
+          if(curlx_str_numblanks(&p, &finfo->size)) {
+            return CURLE_FTP_BAD_FILE_LIST;
+          }
+          /* correct file type */
+          parser->file_data->info.filetype = CURLFILETYPE_FILE;
+        }
+
+        parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_SIZE;
+        parser->item_length = 0;
+        parser->state.NT.main = PL_WINNT_FILENAME;
+        parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
+      }
+      break;
+    }
+    break;
+  case PL_WINNT_FILENAME:
+    switch(parser->state.NT.sub.filename) {
+    case PL_WINNT_FILENAME_PRESPACE:
+      if(c != ' ' && len) {
+        parser->item_offset = len -1;
+        parser->item_length = 1;
+        parser->state.NT.sub.filename = PL_WINNT_FILENAME_CONTENT;
+      }
+      break;
+    case PL_WINNT_FILENAME_CONTENT:
+      parser->item_length++;
+      if(!len)
+        return CURLE_FTP_BAD_FILE_LIST;
+      if(c == '\r') {
+        parser->state.NT.sub.filename = PL_WINNT_FILENAME_WINEOL;
+        mem[len - 1] = 0;
+      }
+      else if(c == '\n') {
+        parser->offsets.filename = parser->item_offset;
+        mem[len - 1] = 0;
+        result = ftp_pl_insert_finfo(data, infop);
+        if(result)
+          return result;
+
+        parser->state.NT.main = PL_WINNT_DATE;
+        parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
+      }
+      break;
+    case PL_WINNT_FILENAME_WINEOL:
+      if(c == '\n') {
+        parser->offsets.filename = parser->item_offset;
+        result = ftp_pl_insert_finfo(data, infop);
+        if(result)
+          return result;
+
+        parser->state.NT.main = PL_WINNT_DATE;
+        parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
+      }
+      else
+        return CURLE_FTP_BAD_FILE_LIST;
+
+      break;
+    }
+    break;
+  }
+
+  return CURLE_OK;
+}
+
+size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb,
+                          void *connptr)
+{
+  size_t bufflen = size*nmemb;
+  struct Curl_easy *data = (struct Curl_easy *)connptr;
+  struct ftp_wc *ftpwc = data->wildcard->ftpwc;
+  struct ftp_parselist_data *parser = ftpwc->parser;
+  size_t i = 0;
+  CURLcode result;
+  size_t retsize = bufflen;
+
+  if(parser->error) { /* error in previous call */
+    /* scenario:
+     * 1. call => OK..
+     * 2. call => OUT_OF_MEMORY (or other error)
+     * 3. (last) call => is skipped RIGHT HERE and the error is handled later
+     *    in wc_statemach()
+     */
+    goto fail;
+  }
+
+  if(parser->os_type == OS_TYPE_UNKNOWN && bufflen > 0) {
+    /* considering info about FILE response format */
+    parser->os_type = ISDIGIT(buffer[0]) ? OS_TYPE_WIN_NT : OS_TYPE_UNIX;
+  }
+
+  while(i < bufflen) { /* FSM */
+    char c = buffer[i];
+    struct fileinfo *infop;
+    if(!parser->file_data) { /* tmp file data is not allocated yet */
+      parser->file_data = Curl_fileinfo_alloc();
+      if(!parser->file_data) {
+        parser->error = CURLE_OUT_OF_MEMORY;
+        goto fail;
+      }
+      parser->item_offset = 0;
+      parser->item_length = 0;
+      curlx_dyn_init(&parser->file_data->buf, MAX_FTPLIST_BUFFER);
+    }
+
+    infop = parser->file_data;
+
+    if(curlx_dyn_addn(&infop->buf, &c, 1)) {
+      parser->error = CURLE_OUT_OF_MEMORY;
+      goto fail;
+    }
+
+    switch(parser->os_type) {
+    case OS_TYPE_UNIX:
+      result = parse_unix(data, parser, infop, c);
+      break;
+    case OS_TYPE_WIN_NT:
+      result = parse_winnt(data, parser, infop, c);
+      break;
+    default:
+      retsize = bufflen + 1;
+      goto fail;
+    }
+    if(result) {
+      parser->error = result;
+      goto fail;
+    }
+
+    i++;
+  }
+  return retsize;
+
+fail:
+
+  /* Clean up any allocated memory. */
+  if(parser->file_data) {
+    Curl_fileinfo_cleanup(parser->file_data);
+    parser->file_data = NULL;
+  }
+
+  return retsize;
+}
+
+#endif /* CURL_DISABLE_FTP */
diff --git a/extra/curl/curl-8.12.1/lib/ftplistparser.h b/extra/curl/curl-8.14.1/lib/ftplistparser.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/ftplistparser.h
rename to extra/curl/curl-8.14.1/lib/ftplistparser.h
diff --git a/extra/curl/curl-8.12.1/lib/functypes.h b/extra/curl/curl-8.14.1/lib/functypes.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/functypes.h
rename to extra/curl/curl-8.14.1/lib/functypes.h
diff --git a/extra/curl/curl-8.12.1/lib/getenv.c b/extra/curl/curl-8.14.1/lib/getenv.c
similarity index 97%
rename from extra/curl/curl-8.12.1/lib/getenv.c
rename to extra/curl/curl-8.14.1/lib/getenv.c
index 63eaeda0f26e..3bfcf707a44b 100644
--- a/extra/curl/curl-8.12.1/lib/getenv.c
+++ b/extra/curl/curl-8.14.1/lib/getenv.c
@@ -31,7 +31,7 @@
 
 static char *GetEnv(const char *variable)
 {
-#if defined(_WIN32_WCE) || defined(CURL_WINDOWS_UWP) || \
+#if defined(CURL_WINDOWS_UWP) || defined(UNDER_CE) || \
   defined(__ORBIS__) || defined(__PROSPERO__) /* PlayStation 4 and 5 */
   (void)variable;
   return NULL;
diff --git a/extra/curl/curl-8.12.1/lib/getinfo.c b/extra/curl/curl-8.14.1/lib/getinfo.c
similarity index 96%
rename from extra/curl/curl-8.12.1/lib/getinfo.c
rename to extra/curl/curl-8.14.1/lib/getinfo.c
index ae6b3b8aa0f0..388646bf7514 100644
--- a/extra/curl/curl-8.12.1/lib/getinfo.c
+++ b/extra/curl/curl-8.14.1/lib/getinfo.c
@@ -28,10 +28,10 @@
 
 #include "urldata.h"
 #include "getinfo.h"
-
 #include "vtls/vtls.h"
 #include "connect.h" /* Curl_getconnectinfo() */
 #include "progress.h"
+#include "curlx/strparse.h"
 
 /* The last #include files should be: */
 #include "curl_memory.h"
@@ -98,7 +98,7 @@ static CURLcode getinfo_char(struct Curl_easy *data, CURLINFO info,
 {
   switch(info) {
   case CURLINFO_EFFECTIVE_URL:
-    *param_charp = data->state.url ? data->state.url : (char *)"";
+    *param_charp = data->state.url ? data->state.url : "";
     break;
   case CURLINFO_EFFECTIVE_METHOD: {
     const char *m = data->set.str[STRING_CUSTOMREQUEST];
@@ -204,9 +204,10 @@ static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info,
   } lptr;
 
 #ifdef DEBUGBUILD
-  char *timestr = getenv("CURL_TIME");
+  const char *timestr = getenv("CURL_TIME");
   if(timestr) {
-    unsigned long val = strtoul(timestr, NULL, 10);
+    curl_off_t val;
+    curlx_str_number(&timestr, &val, TIME_T_MAX);
     switch(info) {
     case CURLINFO_LOCAL_PORT:
       *param_longp = (long)val;
@@ -218,7 +219,8 @@ static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info,
   /* use another variable for this to allow different values */
   timestr = getenv("CURL_DEBUG_SIZE");
   if(timestr) {
-    unsigned long val = strtoul(timestr, NULL, 10);
+    curl_off_t val;
+    curlx_str_number(&timestr, &val, LONG_MAX);
     switch(info) {
     case CURLINFO_HEADER_SIZE:
     case CURLINFO_REQUEST_SIZE:
@@ -379,9 +381,11 @@ static CURLcode getinfo_offt(struct Curl_easy *data, CURLINFO info,
                              curl_off_t *param_offt)
 {
 #ifdef DEBUGBUILD
-  char *timestr = getenv("CURL_TIME");
+  const char *timestr = getenv("CURL_TIME");
   if(timestr) {
-    unsigned long val = strtoul(timestr, NULL, 10);
+    curl_off_t val;
+    curlx_str_number(&timestr, &val, CURL_OFF_T_MAX);
+
     switch(info) {
     case CURLINFO_TOTAL_TIME_T:
     case CURLINFO_NAMELOOKUP_TIME_T:
@@ -418,11 +422,11 @@ static CURLcode getinfo_offt(struct Curl_easy *data, CURLINFO info,
     *param_offt = data->progress.ul.speed;
     break;
   case CURLINFO_CONTENT_LENGTH_DOWNLOAD_T:
-    *param_offt = (data->progress.flags & PGRS_DL_SIZE_KNOWN) ?
+    *param_offt = data->progress.dl_size_known ?
       data->progress.dl.total_size : -1;
     break;
   case CURLINFO_CONTENT_LENGTH_UPLOAD_T:
-    *param_offt = (data->progress.flags & PGRS_UL_SIZE_KNOWN) ?
+    *param_offt = data->progress.ul_size_known ?
       data->progress.ul.total_size : -1;
     break;
    case CURLINFO_TOTAL_TIME_T:
@@ -476,9 +480,11 @@ static CURLcode getinfo_double(struct Curl_easy *data, CURLINFO info,
                                double *param_doublep)
 {
 #ifdef DEBUGBUILD
-  char *timestr = getenv("CURL_TIME");
+  const char *timestr = getenv("CURL_TIME");
   if(timestr) {
-    unsigned long val = strtoul(timestr, NULL, 10);
+    curl_off_t val;
+    curlx_str_number(&timestr, &val, CURL_OFF_T_MAX);
+
     switch(info) {
     case CURLINFO_TOTAL_TIME:
     case CURLINFO_NAMELOOKUP_TIME:
@@ -528,11 +534,11 @@ static CURLcode getinfo_double(struct Curl_easy *data, CURLINFO info,
     *param_doublep = (double)data->progress.ul.speed;
     break;
   case CURLINFO_CONTENT_LENGTH_DOWNLOAD:
-    *param_doublep = (data->progress.flags & PGRS_DL_SIZE_KNOWN) ?
+    *param_doublep = data->progress.dl_size_known ?
       (double)data->progress.dl.total_size : -1;
     break;
   case CURLINFO_CONTENT_LENGTH_UPLOAD:
-    *param_doublep = (data->progress.flags & PGRS_UL_SIZE_KNOWN) ?
+    *param_doublep = data->progress.ul_size_known ?
       (double)data->progress.ul.total_size : -1;
     break;
   case CURLINFO_REDIRECT_TIME:
diff --git a/extra/curl/curl-8.12.1/lib/getinfo.h b/extra/curl/curl-8.14.1/lib/getinfo.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/getinfo.h
rename to extra/curl/curl-8.14.1/lib/getinfo.h
diff --git a/extra/curl/curl-8.12.1/lib/gopher.c b/extra/curl/curl-8.14.1/lib/gopher.c
similarity index 98%
rename from extra/curl/curl-8.12.1/lib/gopher.c
rename to extra/curl/curl-8.14.1/lib/gopher.c
index 00d42f0ec7b2..68ccb59e408a 100644
--- a/extra/curl/curl-8.12.1/lib/gopher.c
+++ b/extra/curl/curl-8.14.1/lib/gopher.c
@@ -39,7 +39,7 @@
 #include "vtls/vtls.h"
 #include "url.h"
 #include "escape.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
 #include "curl_printf.h"
 #include "curl_memory.h"
 /* The last #include file should be: */
@@ -162,7 +162,7 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done)
 
   /* Create selector. Degenerate cases: / and /1 => convert to "" */
   if(strlen(gopherpath) <= 2) {
-    sel = (char *)"";
+    sel = (char *)CURL_UNCONST("");
     len = strlen(sel);
     free(gopherpath);
   }
@@ -236,7 +236,7 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done)
     failf(data, "Failed sending Gopher request");
     return result;
   }
-  result = Curl_client_write(data, CLIENTWRITE_HEADER, (char *)"\r\n", 2);
+  result = Curl_client_write(data, CLIENTWRITE_HEADER, "\r\n", 2);
   if(result)
     return result;
 
diff --git a/extra/curl/curl-8.12.1/lib/gopher.h b/extra/curl/curl-8.14.1/lib/gopher.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/gopher.h
rename to extra/curl/curl-8.14.1/lib/gopher.h
diff --git a/extra/curl/curl-8.12.1/lib/hash.c b/extra/curl/curl-8.14.1/lib/hash.c
similarity index 64%
rename from extra/curl/curl-8.12.1/lib/hash.c
rename to extra/curl/curl-8.14.1/lib/hash.c
index aa9904e7099e..8d13aae483af 100644
--- a/extra/curl/curl-8.12.1/lib/hash.c
+++ b/extra/curl/curl-8.14.1/lib/hash.c
@@ -34,29 +34,49 @@
 #include "memdebug.h"
 
 /* random patterns for API verification */
+#ifdef DEBUGBUILD
 #define HASHINIT 0x7017e781
 #define ITERINIT 0x5FEDCBA9
+#endif
 
-static void
-hash_element_dtor(void *user, void *element)
+
+#if 0 /* useful function for debugging hashes and their contents */
+void Curl_hash_print(struct Curl_hash *h,
+                     void (*func)(void *))
 {
-  struct Curl_hash *h = (struct Curl_hash *) user;
-  struct Curl_hash_element *e = (struct Curl_hash_element *) element;
-  DEBUGASSERT(h);
-  DEBUGASSERT(e);
+  struct Curl_hash_iterator iter;
+  struct Curl_hash_element *he;
+  size_t last_index = UINT_MAX;
 
-  if(e->ptr) {
-    if(e->dtor)
-      e->dtor(e->key, e->key_len, e->ptr);
-    else
-      h->dtor(e->ptr);
-    e->ptr = NULL;
-  }
+  if(!h)
+    return;
+
+  fprintf(stderr, "=Hash dump=\n");
+
+  Curl_hash_start_iterate(h, &iter);
+
+  he = Curl_hash_next_element(&iter);
+  while(he) {
+    if(iter.slot_index != last_index) {
+      fprintf(stderr, "index %d:", (int)iter.slot_index);
+      if(last_index != UINT_MAX) {
+        fprintf(stderr, "\n");
+      }
+      last_index = iter.slot_index;
+    }
 
-  e->key_len = 0;
+    if(func)
+      func(he->ptr);
+    else
+      fprintf(stderr, " [key=%.*s, he=%p, ptr=%p]",
+              (int)he->key_len, (char *)he->key,
+              (void *)he, (void *)he->ptr);
 
-  free(e);
+    he = Curl_hash_next_element(&iter);
+  }
+  fprintf(stderr, "\n");
 }
+#endif
 
 /* Initializes a hash structure.
  * Return 1 on error, 0 is fine.
@@ -89,62 +109,96 @@ Curl_hash_init(struct Curl_hash *h,
 }
 
 static struct Curl_hash_element *
-mk_hash_element(const void *key, size_t key_len, const void *p,
-                Curl_hash_elem_dtor dtor)
+hash_elem_create(const void *key, size_t key_len, const void *p,
+                 Curl_hash_elem_dtor dtor)
 {
+  struct Curl_hash_element *he;
+
   /* allocate the struct plus memory after it to store the key */
-  struct Curl_hash_element *he = malloc(sizeof(struct Curl_hash_element) +
-                                        key_len);
+  he = malloc(sizeof(struct Curl_hash_element) + key_len);
   if(he) {
+    he->next = NULL;
     /* copy the key */
     memcpy(he->key, key, key_len);
     he->key_len = key_len;
-    he->ptr = (void *) p;
+    he->ptr = CURL_UNCONST(p);
     he->dtor = dtor;
   }
   return he;
 }
 
-#define FETCH_LIST(x,y,z) &x->table[x->hash_func(y, z, x->slots)]
+static void hash_elem_clear_ptr(struct Curl_hash *h,
+                                struct Curl_hash_element *he)
+{
+  DEBUGASSERT(h);
+  DEBUGASSERT(he);
+  if(he->ptr) {
+    if(he->dtor)
+      he->dtor(he->key, he->key_len, he->ptr);
+    else
+      h->dtor(he->ptr);
+    he->ptr = NULL;
+  }
+}
+
+static void hash_elem_destroy(struct Curl_hash *h,
+                              struct Curl_hash_element *he)
+{
+  hash_elem_clear_ptr(h, he);
+  free(he);
+}
+
+static void hash_elem_unlink(struct Curl_hash *h,
+                             struct Curl_hash_element **he_anchor,
+                             struct Curl_hash_element *he)
+{
+  *he_anchor = he->next;
+  --h->size;
+}
+
+static void hash_elem_link(struct Curl_hash *h,
+                           struct Curl_hash_element **he_anchor,
+                           struct Curl_hash_element *he)
+{
+  he->next = *he_anchor;
+  *he_anchor = he;
+  ++h->size;
+}
+
+#define CURL_HASH_SLOT(x,y,z)      x->table[x->hash_func(y, z, x->slots)]
+#define CURL_HASH_SLOT_ADDR(x,y,z) &CURL_HASH_SLOT(x,y,z)
 
 void *Curl_hash_add2(struct Curl_hash *h, void *key, size_t key_len, void *p,
                      Curl_hash_elem_dtor dtor)
 {
-  struct Curl_hash_element  *he;
-  struct Curl_llist_node *le;
-  struct Curl_llist *l;
+  struct Curl_hash_element *he, **slot;
 
   DEBUGASSERT(h);
   DEBUGASSERT(h->slots);
   DEBUGASSERT(h->init == HASHINIT);
   if(!h->table) {
-    size_t i;
-    h->table = malloc(h->slots * sizeof(struct Curl_llist));
+    h->table = calloc(h->slots, sizeof(struct Curl_hash_element *));
     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 = Curl_llist_head(l); le; le = Curl_node_next(le)) {
-    he = (struct Curl_hash_element *) Curl_node_elem(le);
+  slot = CURL_HASH_SLOT_ADDR(h, key, key_len);
+  for(he = *slot; he; he = he->next) {
     if(h->comp_func(he->key, he->key_len, key, key_len)) {
-      Curl_node_uremove(le, (void *)h);
-      --h->size;
-      break;
+      /* existing key entry, overwrite by clearing old pointer */
+      hash_elem_clear_ptr(h, he);
+      he->ptr = (void *)p;
+      he->dtor = dtor;
+      return p;
     }
   }
 
-  he = mk_hash_element(key, key_len, p, dtor);
-  if(he) {
-    Curl_llist_append(l, he, &he->list);
-    ++h->size;
-    return p; /* return the new entry */
-  }
+  he = hash_elem_create(key, key_len, p, dtor);
+  if(!he)
+    return NULL; /* OOM */
 
-  return NULL; /* failure */
+  hash_elem_link(h, slot, he);
+  return p; /* return the new entry */
 }
 
 /* Insert the data in the hash. If there already was a match in the hash, that
@@ -172,16 +226,17 @@ int Curl_hash_delete(struct Curl_hash *h, void *key, size_t key_len)
   DEBUGASSERT(h->slots);
   DEBUGASSERT(h->init == HASHINIT);
   if(h->table) {
-    struct Curl_llist_node *le;
-    struct Curl_llist *l = FETCH_LIST(h, key, key_len);
+    struct Curl_hash_element *he, **he_anchor;
 
-    for(le = Curl_llist_head(l); le; le = Curl_node_next(le)) {
-      struct Curl_hash_element *he = Curl_node_elem(le);
+    he_anchor = CURL_HASH_SLOT_ADDR(h, key, key_len);
+    while(*he_anchor) {
+      he = *he_anchor;
       if(h->comp_func(he->key, he->key_len, key, key_len)) {
-        Curl_node_uremove(le, (void *) h);
-        --h->size;
+        hash_elem_unlink(h, he_anchor, he);
+        hash_elem_destroy(h, he);
         return 0;
       }
+      he_anchor = &he->next;
     }
   }
   return 1;
@@ -197,18 +252,16 @@ Curl_hash_pick(struct Curl_hash *h, void *key, size_t key_len)
   DEBUGASSERT(h);
   DEBUGASSERT(h->init == HASHINIT);
   if(h->table) {
-    struct Curl_llist_node *le;
-    struct Curl_llist *l;
+    struct Curl_hash_element *he;
     DEBUGASSERT(h->slots);
-    l = FETCH_LIST(h, key, key_len);
-    for(le = Curl_llist_head(l); le; le = Curl_node_next(le)) {
-      struct Curl_hash_element *he = Curl_node_elem(le);
+    he = CURL_HASH_SLOT(h, key, key_len);
+    while(he) {
       if(h->comp_func(he->key, he->key_len, key, key_len)) {
         return he->ptr;
       }
+      he = he->next;
     }
   }
-
   return NULL;
 }
 
@@ -224,13 +277,10 @@ Curl_hash_destroy(struct Curl_hash *h)
 {
   DEBUGASSERT(h->init == HASHINIT);
   if(h->table) {
-    size_t i;
-    for(i = 0; i < h->slots; ++i) {
-      Curl_llist_destroy(&h->table[i], (void *) h);
-    }
+    Curl_hash_clean(h);
     Curl_safefree(h->table);
   }
-  h->size = 0;
+  DEBUGASSERT(h->size == 0);
   h->slots = 0;
 }
 
@@ -238,10 +288,21 @@ Curl_hash_destroy(struct Curl_hash *h)
  *
  * @unittest: 1602
  */
-void
-Curl_hash_clean(struct Curl_hash *h)
+void Curl_hash_clean(struct Curl_hash *h)
 {
-  Curl_hash_clean_with_criterium(h, NULL, NULL);
+  if(h && h->table) {
+    struct Curl_hash_element *he, **he_anchor;
+    size_t i;
+    DEBUGASSERT(h->init == HASHINIT);
+    for(i = 0; i < h->slots; ++i) {
+      he_anchor = &h->table[i];
+      while(*he_anchor) {
+        he = *he_anchor;
+        hash_elem_unlink(h, he_anchor, he);
+        hash_elem_destroy(h, he);
+      }
+    }
+  }
 }
 
 size_t Curl_hash_count(struct Curl_hash *h)
@@ -262,18 +323,16 @@ Curl_hash_clean_with_criterium(struct Curl_hash *h, void *user,
 
   DEBUGASSERT(h->init == HASHINIT);
   for(i = 0; i < h->slots; ++i) {
-    struct Curl_llist *list = &h->table[i];
-    struct Curl_llist_node *le =
-      Curl_llist_head(list); /* get first list entry */
-    while(le) {
-      struct Curl_hash_element *he = Curl_node_elem(le);
-      struct Curl_llist_node *lnext = Curl_node_next(le);
+    struct Curl_hash_element *he, **he_anchor = &h->table[i];
+    while(*he_anchor) {
       /* ask the callback function if we shall remove this entry or not */
-      if(!comp || comp(user, he->ptr)) {
-        Curl_node_uremove(le, (void *) h);
-        --h->size; /* one less entry in the hash now */
+      if(!comp || comp(user, (*he_anchor)->ptr)) {
+        he = *he_anchor;
+        hash_elem_unlink(h, he_anchor, he);
+        hash_elem_destroy(h, he);
       }
-      le = lnext;
+      else
+        he_anchor = &(*he_anchor)->next;
     }
   }
 }
@@ -293,7 +352,7 @@ size_t Curl_hash_str(void *key, size_t key_length, size_t slots_num)
   return (h % slots_num);
 }
 
-size_t Curl_str_key_compare(void *k1, size_t key1_len,
+size_t curlx_str_key_compare(void *k1, size_t key1_len,
                             void *k2, size_t key2_len)
 {
   if((key1_len == key2_len) && !memcmp(k1, k2, key1_len))
@@ -308,7 +367,7 @@ void Curl_hash_start_iterate(struct Curl_hash *hash,
   DEBUGASSERT(hash->init == HASHINIT);
   iter->hash = hash;
   iter->slot_index = 0;
-  iter->current_element = NULL;
+  iter->current = NULL;
 #ifdef DEBUGBUILD
   iter->init = ITERINIT;
 #endif
@@ -324,82 +383,20 @@ Curl_hash_next_element(struct Curl_hash_iterator *iter)
     return NULL; /* empty hash, nothing to return */
 
   /* Get the next element in the current list, if any */
-  if(iter->current_element)
-    iter->current_element = Curl_node_next(iter->current_element);
+  if(iter->current)
+    iter->current = iter->current->next;
 
   /* If we have reached the end of the list, find the next one */
-  if(!iter->current_element) {
+  if(!iter->current) {
     size_t i;
     for(i = iter->slot_index; i < h->slots; i++) {
-      if(Curl_llist_head(&h->table[i])) {
-        iter->current_element = Curl_llist_head(&h->table[i]);
+      if(h->table[i]) {
+        iter->current = h->table[i];
         iter->slot_index = i + 1;
         break;
       }
     }
   }
 
-  if(iter->current_element) {
-    struct Curl_hash_element *he = Curl_node_elem(iter->current_element);
-    return he;
-  }
-  return NULL;
-}
-
-#if 0 /* useful function for debugging hashes and their contents */
-void Curl_hash_print(struct Curl_hash *h,
-                     void (*func)(void *))
-{
-  struct Curl_hash_iterator iter;
-  struct Curl_hash_element *he;
-  size_t last_index = ~0;
-
-  if(!h)
-    return;
-
-  fprintf(stderr, "=Hash dump=\n");
-
-  Curl_hash_start_iterate(h, &iter);
-
-  he = Curl_hash_next_element(&iter);
-  while(he) {
-    if(iter.slot_index != last_index) {
-      fprintf(stderr, "index %d:", iter.slot_index);
-      if(last_index != ~0) {
-        fprintf(stderr, "\n");
-      }
-      last_index = iter.slot_index;
-    }
-
-    if(func)
-      func(he->ptr);
-    else
-      fprintf(stderr, " [%p]", (void *)he->ptr);
-
-    he = Curl_hash_next_element(&iter);
-  }
-  fprintf(stderr, "\n");
-}
-#endif
-
-void Curl_hash_offt_init(struct Curl_hash *h,
-                         size_t slots,
-                         Curl_hash_dtor dtor)
-{
-  Curl_hash_init(h, slots, Curl_hash_str, Curl_str_key_compare, dtor);
-}
-
-void *Curl_hash_offt_set(struct Curl_hash *h, curl_off_t id, void *elem)
-{
-  return Curl_hash_add(h, &id, sizeof(id), elem);
-}
-
-int Curl_hash_offt_remove(struct Curl_hash *h, curl_off_t id)
-{
-  return Curl_hash_delete(h, &id, sizeof(id));
-}
-
-void *Curl_hash_offt_get(struct Curl_hash *h, curl_off_t id)
-{
-  return Curl_hash_pick(h, &id, sizeof(id));
+  return iter->current;
 }
diff --git a/extra/curl/curl-8.12.1/lib/hash.h b/extra/curl/curl-8.14.1/lib/hash.h
similarity index 86%
rename from extra/curl/curl-8.12.1/lib/hash.h
rename to extra/curl/curl-8.14.1/lib/hash.h
index b1603950245b..314b811e4a4d 100644
--- a/extra/curl/curl-8.12.1/lib/hash.h
+++ b/extra/curl/curl-8.14.1/lib/hash.h
@@ -45,14 +45,24 @@ typedef size_t (*comp_function) (void *key1,
 
 typedef void (*Curl_hash_dtor)(void *);
 
+typedef void (*Curl_hash_elem_dtor)(void *key, size_t key_len, void *p);
+
+struct Curl_hash_element {
+  struct Curl_hash_element *next;
+  void   *ptr;
+  Curl_hash_elem_dtor dtor;
+  size_t key_len;
+  char   key[1]; /* allocated memory following the struct */
+};
+
 struct Curl_hash {
-  struct Curl_llist *table;
+  struct Curl_hash_element **table;
 
   /* Hash function to be used for this hash table */
   hash_function hash_func;
-
   /* Comparator function to compare keys */
   comp_function comp_func;
+  /* General element construct, unless element itself carries one */
   Curl_hash_dtor   dtor;
   size_t slots;
   size_t size;
@@ -61,23 +71,10 @@ struct Curl_hash {
 #endif
 };
 
-typedef void (*Curl_hash_elem_dtor)(void *key, size_t key_len, void *p);
-
-struct Curl_hash_element {
-  struct Curl_llist_node list;
-  void   *ptr;
-  Curl_hash_elem_dtor dtor;
-  size_t key_len;
-#ifdef DEBUGBUILD
-  int init;
-#endif
-  char   key[1]; /* allocated memory following the struct */
-};
-
 struct Curl_hash_iterator {
   struct Curl_hash *hash;
   size_t slot_index;
-  struct Curl_llist_node *current_element;
+  struct Curl_hash_element *current;
 #ifdef DEBUGBUILD
   int init;
 #endif
@@ -101,7 +98,7 @@ void Curl_hash_clean(struct Curl_hash *h);
 void Curl_hash_clean_with_criterium(struct Curl_hash *h, void *user,
                                     int (*comp)(void *, void *));
 size_t Curl_hash_str(void *key, size_t key_length, size_t slots_num);
-size_t Curl_str_key_compare(void *k1, size_t key1_len, void *k2,
+size_t curlx_str_key_compare(void *k1, size_t key1_len, void *k2,
                             size_t key2_len);
 void Curl_hash_start_iterate(struct Curl_hash *hash,
                              struct Curl_hash_iterator *iter);
@@ -111,13 +108,4 @@ Curl_hash_next_element(struct Curl_hash_iterator *iter);
 void Curl_hash_print(struct Curl_hash *h,
                      void (*func)(void *));
 
-/* Hash for `curl_off_t` as key */
-void Curl_hash_offt_init(struct Curl_hash *h, size_t slots,
-                         Curl_hash_dtor dtor);
-
-void *Curl_hash_offt_set(struct Curl_hash *h, curl_off_t id, void *elem);
-int Curl_hash_offt_remove(struct Curl_hash *h, curl_off_t id);
-void *Curl_hash_offt_get(struct Curl_hash *h, curl_off_t id);
-
-
 #endif /* HEADER_CURL_HASH_H */
diff --git a/extra/curl/curl-8.12.1/lib/headers.c b/extra/curl/curl-8.14.1/lib/headers.c
similarity index 96%
rename from extra/curl/curl-8.12.1/lib/headers.c
rename to extra/curl/curl-8.14.1/lib/headers.c
index 2985e1e18549..71592a9c7376 100644
--- a/extra/curl/curl-8.12.1/lib/headers.c
+++ b/extra/curl/curl-8.14.1/lib/headers.c
@@ -29,6 +29,7 @@
 #include "strcase.h"
 #include "sendf.h"
 #include "headers.h"
+#include "curlx/strparse.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -208,15 +209,15 @@ static CURLcode namevalue(char *header, size_t hlen, unsigned int type,
   else
     return CURLE_BAD_FUNCTION_ARGUMENT;
 
-  /* skip all leading space letters */
-  while(*header && ISBLANK(*header))
+  /* skip all leading blank letters */
+  while(ISBLANK(*header))
     header++;
 
   *value = header;
 
   /* skip all trailing space letters */
-  while((end > header) && ISSPACE(*end))
-    *end-- = 0; /* nul terminate */
+  while((end > header) && ISBLANK(*end))
+    *end-- = 0; /* null-terminate */
   return CURLE_OK;
 }
 
@@ -235,7 +236,7 @@ static CURLcode unfold_value(struct Curl_easy *data, const char *value,
   oalloc = olen + offset + 1;
 
   /* skip all trailing space letters */
-  while(vlen && ISSPACE(value[vlen - 1]))
+  while(vlen && ISBLANK(value[vlen - 1]))
     vlen--;
 
   /* save only one leading space */
@@ -312,12 +313,17 @@ CURLcode Curl_headers_push(struct Curl_easy *data, const char *header,
         return CURLE_WEIRD_SERVER_REPLY;
     }
   }
+  if(Curl_llist_count(&data->state.httphdrs) >= MAX_HTTP_RESP_HEADER_COUNT) {
+    failf(data, "Too many response headers, %d is max",
+          MAX_HTTP_RESP_HEADER_COUNT);
+    return CURLE_TOO_LARGE;
+  }
 
   hs = calloc(1, sizeof(*hs) + hlen);
   if(!hs)
     return CURLE_OUT_OF_MEMORY;
   memcpy(hs->buffer, header, hlen);
-  hs->buffer[hlen] = 0; /* nul terminate */
+  hs->buffer[hlen] = 0; /* null-terminate */
 
   result = namevalue(hs->buffer, hlen, type, &name, &value);
   if(!result) {
@@ -330,8 +336,10 @@ CURLcode Curl_headers_push(struct Curl_easy *data, const char *header,
     Curl_llist_append(&data->state.httphdrs, hs, &hs->node);
     data->state.prevhead = hs;
   }
-  else
+  else {
+    failf(data, "Invalid response header");
     free(hs);
+  }
   return result;
 }
 
diff --git a/extra/curl/curl-8.12.1/lib/headers.h b/extra/curl/curl-8.14.1/lib/headers.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/headers.h
rename to extra/curl/curl-8.14.1/lib/headers.h
diff --git a/extra/curl/curl-8.12.1/lib/hmac.c b/extra/curl/curl-8.14.1/lib/hmac.c
similarity index 99%
rename from extra/curl/curl-8.12.1/lib/hmac.c
rename to extra/curl/curl-8.14.1/lib/hmac.c
index 7b8a29e69232..3af1f292dac2 100644
--- a/extra/curl/curl-8.12.1/lib/hmac.c
+++ b/extra/curl/curl-8.14.1/lib/hmac.c
@@ -34,7 +34,7 @@
 
 #include "curl_hmac.h"
 #include "curl_memory.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
 
 /* The last #include file should be: */
 #include "memdebug.h"
diff --git a/extra/curl/curl-8.12.1/lib/hostip.c b/extra/curl/curl-8.14.1/lib/hostip.c
similarity index 62%
rename from extra/curl/curl-8.12.1/lib/hostip.c
rename to extra/curl/curl-8.14.1/lib/hostip.c
index 5ab854d1ce8c..ca6724ed55dc 100644
--- a/extra/curl/curl-8.12.1/lib/hostip.c
+++ b/extra/curl/curl-8.14.1/lib/hostip.c
@@ -42,22 +42,28 @@
 #endif
 
 #include <setjmp.h>
+#ifndef UNDER_CE
 #include <signal.h>
+#endif
 
 #include "urldata.h"
 #include "sendf.h"
+#include "connect.h"
 #include "hostip.h"
 #include "hash.h"
 #include "rand.h"
 #include "share.h"
 #include "url.h"
 #include "inet_ntop.h"
-#include "inet_pton.h"
+#include "curlx/inet_pton.h"
 #include "multiif.h"
 #include "doh.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
+#include "select.h"
 #include "strcase.h"
 #include "easy_lock.h"
+#include "curlx/strparse.h"
+
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
@@ -102,20 +108,18 @@
  * The host*.c sources files are split up like this:
  *
  * hostip.c   - method-independent resolver functions and utility functions
- * hostasyn.c - functions for asynchronous name resolves
- * hostsyn.c  - functions for synchronous name resolves
  * hostip4.c  - IPv4 specific functions
  * hostip6.c  - IPv6 specific functions
- *
+ * asyn.h     - common functions for all async resolvers
  * The two asynchronous name resolver backends are implemented in:
- * asyn-ares.c   - functions for ares-using name resolves
- * asyn-thread.c - functions for threaded name resolves
-
+ * asyn-ares.c - async resolver using c-ares
+ * asyn-thread.c - async resolver using POSIX threads
+ *
  * The hostip.h is the united header file for all this. It defines the
  * CURLRES_* defines based on the config*.h and curl_setup.h defines.
  */
 
-static void hostcache_unlink_entry(void *entry);
+static void dnscache_entry_free(struct Curl_dns_entry *dns);
 
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
 static void show_resolve_info(struct Curl_easy *data,
@@ -162,9 +166,9 @@ void Curl_printable_address(const struct Curl_addrinfo *ai, char *buf,
  * the DNS caching. Without alloc. Return length of the id string.
  */
 static size_t
-create_hostcache_id(const char *name,
-                    size_t nlen, /* 0 or actual name length */
-                    int port, char *ptr, size_t buflen)
+create_dnscache_id(const char *name,
+                   size_t nlen, /* 0 or actual name length */
+                   int port, char *ptr, size_t buflen)
 {
   size_t len = nlen ? nlen : strlen(name);
   DEBUGASSERT(buflen >= MAX_HOSTCACHE_LEN);
@@ -175,7 +179,7 @@ create_hostcache_id(const char *name,
   return msnprintf(&ptr[len], 7, ":%u", port) + len;
 }
 
-struct hostcache_prune_data {
+struct dnscache_prune_data {
   time_t now;
   time_t oldest; /* oldest time in cache not pruned. */
   int max_age_sec;
@@ -189,10 +193,10 @@ struct hostcache_prune_data {
  * cache.
  */
 static int
-hostcache_entry_is_stale(void *datap, void *hc)
+dnscache_entry_is_stale(void *datap, void *hc)
 {
-  struct hostcache_prune_data *prune =
-    (struct hostcache_prune_data *) datap;
+  struct dnscache_prune_data *prune =
+    (struct dnscache_prune_data *) datap;
   struct Curl_dns_entry *dns = (struct Curl_dns_entry *) hc;
 
   if(dns->timestamp) {
@@ -211,10 +215,10 @@ hostcache_entry_is_stale(void *datap, void *hc)
  * Returns the 'age' of the oldest still kept entry.
  */
 static time_t
-hostcache_prune(struct Curl_hash *hostcache, int cache_timeout,
-                time_t now)
+dnscache_prune(struct Curl_hash *hostcache, int cache_timeout,
+               time_t now)
 {
-  struct hostcache_prune_data user;
+  struct dnscache_prune_data user;
 
   user.max_age_sec = cache_timeout;
   user.now = now;
@@ -222,33 +226,56 @@ hostcache_prune(struct Curl_hash *hostcache, int cache_timeout,
 
   Curl_hash_clean_with_criterium(hostcache,
                                  (void *) &user,
-                                 hostcache_entry_is_stale);
+                                 dnscache_entry_is_stale);
 
   return user.oldest;
 }
 
+static struct Curl_dnscache *dnscache_get(struct Curl_easy *data)
+{
+  if(data->share && data->share->specifier & (1 << CURL_LOCK_DATA_DNS))
+    return &data->share->dnscache;
+  if(data->multi)
+    return &data->multi->dnscache;
+  return NULL;
+}
+
+static void dnscache_lock(struct Curl_easy *data,
+                          struct Curl_dnscache *dnscache)
+{
+  if(data->share && dnscache == &data->share->dnscache)
+    Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
+}
+
+static void dnscache_unlock(struct Curl_easy *data,
+                            struct Curl_dnscache *dnscache)
+{
+  if(data->share && dnscache == &data->share->dnscache)
+    Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+}
+
 /*
  * Library-wide function for pruning the DNS cache. This function takes and
  * returns the appropriate locks.
  */
-void Curl_hostcache_prune(struct Curl_easy *data)
+void Curl_dnscache_prune(struct Curl_easy *data)
 {
+  struct Curl_dnscache *dnscache = dnscache_get(data);
   time_t now;
   /* the timeout may be set -1 (forever) */
   int timeout = data->set.dns_cache_timeout;
 
-  if(!data->dns.hostcache)
+  if(!dnscache)
     /* NULL hostcache means we cannot do it */
     return;
 
-  if(data->share)
-    Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
+  dnscache_lock(data, dnscache);
 
   now = time(NULL);
 
   do {
     /* Remove outdated and unused entries from the hostcache */
-    time_t oldest = hostcache_prune(data->dns.hostcache, timeout, now);
+    time_t oldest = dnscache_prune(&dnscache->entries, timeout, now);
 
     if(oldest < INT_MAX)
       timeout = (int)oldest; /* we know it fits */
@@ -258,10 +285,9 @@ void Curl_hostcache_prune(struct Curl_easy *data)
     /* if the cache size is still too big, use the oldest age as new
        prune limit */
   } while(timeout &&
-          (Curl_hash_count(data->dns.hostcache) > MAX_DNS_CACHE_SIZE));
+          (Curl_hash_count(&dnscache->entries) > MAX_DNS_CACHE_SIZE));
 
-  if(data->share)
-    Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+  dnscache_unlock(data, dnscache);
 }
 
 #ifdef USE_ALARM_TIMEOUT
@@ -274,50 +300,56 @@ static curl_simple_lock curl_jmpenv_lock;
 
 /* lookup address, returns entry if found and not stale */
 static struct Curl_dns_entry *fetch_addr(struct Curl_easy *data,
+                                         struct Curl_dnscache *dnscache,
                                          const char *hostname,
-                                         int port)
+                                         int port,
+                                         int ip_version)
 {
   struct Curl_dns_entry *dns = NULL;
   char entry_id[MAX_HOSTCACHE_LEN];
+  size_t entry_len;
+
+  if(!dnscache)
+    return NULL;
 
   /* Create an entry id, based upon the hostname and port */
-  size_t entry_len = create_hostcache_id(hostname, 0, port,
-                                         entry_id, sizeof(entry_id));
+  entry_len = create_dnscache_id(hostname, 0, port,
+                                 entry_id, sizeof(entry_id));
 
   /* See if it is already in our dns cache */
-  dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
+  dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1);
 
   /* No entry found in cache, check if we might have a wildcard entry */
   if(!dns && data->state.wildcard_resolve) {
-    entry_len = create_hostcache_id("*", 1, port, entry_id, sizeof(entry_id));
+    entry_len = create_dnscache_id("*", 1, port, entry_id, sizeof(entry_id));
 
     /* See if it is already in our dns cache */
-    dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
+    dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1);
   }
 
   if(dns && (data->set.dns_cache_timeout != -1)) {
     /* See whether the returned entry is stale. Done before we release lock */
-    struct hostcache_prune_data user;
+    struct dnscache_prune_data user;
 
     user.now = time(NULL);
     user.max_age_sec = data->set.dns_cache_timeout;
     user.oldest = 0;
 
-    if(hostcache_entry_is_stale(&user, dns)) {
+    if(dnscache_entry_is_stale(&user, dns)) {
       infof(data, "Hostname in DNS cache was stale, zapped");
       dns = NULL; /* the memory deallocation is being handled by the hash */
-      Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
+      Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1);
     }
   }
 
   /* See if the returned entry matches the required resolve mode */
-  if(dns && data->conn->ip_version != CURL_IPRESOLVE_WHATEVER) {
+  if(dns && ip_version != CURL_IPRESOLVE_WHATEVER) {
     int pf = PF_INET;
     bool found = FALSE;
     struct Curl_addrinfo *addr = dns->addr;
 
 #ifdef PF_INET6
-    if(data->conn->ip_version == CURL_IPRESOLVE_V6)
+    if(ip_version == CURL_IPRESOLVE_V6)
       pf = PF_INET6;
 #endif
 
@@ -332,14 +364,14 @@ static struct Curl_dns_entry *fetch_addr(struct Curl_easy *data,
     if(!found) {
       infof(data, "Hostname in DNS cache does not have needed family, zapped");
       dns = NULL; /* the memory deallocation is being handled by the hash */
-      Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
+      Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1);
     }
   }
   return dns;
 }
 
 /*
- * Curl_fetch_addr() fetches a 'Curl_dns_entry' already in the DNS cache.
+ * Curl_dnscache_get() fetches a 'Curl_dns_entry' already in the DNS cache.
  *
  * Curl_resolv() checks initially and multi_runsingle() checks each time
  * it discovers the handle in the state WAITRESOLVE whether the hostname
@@ -353,22 +385,21 @@ static struct Curl_dns_entry *fetch_addr(struct Curl_easy *data,
  * use, or we will leak memory!
  */
 struct Curl_dns_entry *
-Curl_fetch_addr(struct Curl_easy *data,
-                const char *hostname,
-                int port)
+Curl_dnscache_get(struct Curl_easy *data,
+                  const char *hostname,
+                  int port,
+                  int ip_version)
 {
+  struct Curl_dnscache *dnscache = dnscache_get(data);
   struct Curl_dns_entry *dns = NULL;
 
-  if(data->share)
-    Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
-
-  dns = fetch_addr(data, hostname, port);
+  dnscache_lock(data, dnscache);
 
+  dns = fetch_addr(data, dnscache, hostname, port, ip_version);
   if(dns)
     dns->refcount++; /* we use it! */
 
-  if(data->share)
-    Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+  dnscache_unlock(data, dnscache);
 
   return dns;
 }
@@ -455,34 +486,24 @@ UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data,
 }
 #endif
 
-/*
- * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
- *
- * When calling Curl_resolv() has resulted in a response with a returned
- * address, we call this function to store the information in the dns
- * cache etc
- *
- * Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
- */
 struct Curl_dns_entry *
-Curl_cache_addr(struct Curl_easy *data,
-                struct Curl_addrinfo *addr,
-                const char *hostname,
-                size_t hostlen, /* length or zero */
-                int port,
-                bool permanent)
+Curl_dnscache_mk_entry(struct Curl_easy *data,
+                       struct Curl_addrinfo *addr,
+                       const char *hostname,
+                       size_t hostlen, /* length or zero */
+                       int port,
+                       bool permanent)
 {
-  char entry_id[MAX_HOSTCACHE_LEN];
-  size_t entry_len;
   struct Curl_dns_entry *dns;
-  struct Curl_dns_entry *dns2;
 
 #ifndef CURL_DISABLE_SHUFFLE_DNS
   /* shuffle addresses if requested */
   if(data->set.dns_shuffle_addresses) {
     CURLcode result = Curl_shuffle_addr(data, &addr);
-    if(result)
+    if(result) {
+      Curl_freeaddrinfo(addr);
       return NULL;
+    }
   }
 #endif
   if(!hostlen)
@@ -491,13 +512,10 @@ Curl_cache_addr(struct Curl_easy *data,
   /* Create a new cache entry */
   dns = calloc(1, sizeof(struct Curl_dns_entry) + hostlen);
   if(!dns) {
+    Curl_freeaddrinfo(addr);
     return NULL;
   }
 
-  /* Create an entry id, based upon the hostname and port */
-  entry_len = create_hostcache_id(hostname, hostlen, port,
-                                  entry_id, sizeof(entry_id));
-
   dns->refcount = 1; /* the cache has the first reference */
   dns->addr = addr; /* this is the address(es) */
   if(permanent)
@@ -511,11 +529,36 @@ Curl_cache_addr(struct Curl_easy *data,
   if(hostlen)
     memcpy(dns->hostname, hostname, hostlen);
 
+  return dns;
+}
+
+static struct Curl_dns_entry *
+dnscache_add_addr(struct Curl_easy *data,
+                  struct Curl_dnscache *dnscache,
+                  struct Curl_addrinfo *addr,
+                  const char *hostname,
+                  size_t hlen, /* length or zero */
+                  int port,
+                  bool permanent)
+{
+  char entry_id[MAX_HOSTCACHE_LEN];
+  size_t entry_len;
+  struct Curl_dns_entry *dns;
+  struct Curl_dns_entry *dns2;
+
+  dns = Curl_dnscache_mk_entry(data, addr, hostname, hlen, port, permanent);
+  if(!dns)
+    return NULL;
+
+  /* Create an entry id, based upon the hostname and port */
+  entry_len = create_dnscache_id(hostname, hlen, port,
+                                 entry_id, sizeof(entry_id));
+
   /* Store the resolved data in our DNS cache. */
-  dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len + 1,
+  dns2 = Curl_hash_add(&dnscache->entries, entry_id, entry_len + 1,
                        (void *)dns);
   if(!dns2) {
-    free(dns);
+    dnscache_entry_free(dns);
     return NULL;
   }
 
@@ -524,6 +567,30 @@ Curl_cache_addr(struct Curl_easy *data,
   return dns;
 }
 
+CURLcode Curl_dnscache_add(struct Curl_easy *data,
+                           struct Curl_dns_entry *entry)
+{
+  struct Curl_dnscache *dnscache = dnscache_get(data);
+  char id[MAX_HOSTCACHE_LEN];
+  size_t idlen;
+
+  if(!dnscache)
+    return CURLE_FAILED_INIT;
+  /* Create an entry id, based upon the hostname and port */
+  idlen = create_dnscache_id(entry->hostname, 0, entry->hostport,
+                             id, sizeof(id));
+
+  /* Store the resolved data in our DNS cache and up ref count */
+  dnscache_lock(data, dnscache);
+  if(!Curl_hash_add(&dnscache->entries, id, idlen + 1, (void *)entry)) {
+    dnscache_unlock(data, dnscache);
+    return CURLE_OUT_OF_MEMORY;
+  }
+  entry->refcount++;
+  dnscache_unlock(data, dnscache);
+  return CURLE_OK;
+}
+
 #ifdef USE_IPV6
 /* return a static IPv6 ::1 for the name */
 static struct Curl_addrinfo *get_localhost6(int port, const char *name)
@@ -545,7 +612,7 @@ static struct Curl_addrinfo *get_localhost6(int port, const char *name)
   sa6.sin6_scope_id = 0;
 #endif
 
-  (void)Curl_inet_pton(AF_INET6, "::1", ipv6);
+  (void)curlx_inet_pton(AF_INET6, "::1", ipv6);
   memcpy(&sa6.sin6_addr, ipv6, sizeof(ipv6));
 
   ca->ai_flags     = 0;
@@ -579,7 +646,7 @@ static struct Curl_addrinfo *get_localhost(int port, const char *name)
   memset(&sa, 0, sizeof(sa));
   sa.sin_family = AF_INET;
   sa.sin_port = htons(port16);
-  if(Curl_inet_pton(AF_INET, "127.0.0.1", (char *)&ipv4) < 1)
+  if(curlx_inet_pton(AF_INET, "127.0.0.1", (char *)&ipv4) < 1)
     return NULL;
   memcpy(&sa.sin_addr, &ipv4, sizeof(ipv4));
 
@@ -647,9 +714,9 @@ bool Curl_host_is_ipnum(const char *hostname)
 #ifdef USE_IPV6
   struct in6_addr in6;
 #endif
-  if(Curl_inet_pton(AF_INET, hostname, &in) > 0
+  if(curlx_inet_pton(AF_INET, hostname, &in) > 0
 #ifdef USE_IPV6
-     || Curl_inet_pton(AF_INET6, hostname, &in6) > 0
+     || curlx_inet_pton(AF_INET6, hostname, &in6) > 0
 #endif
     )
     return TRUE;
@@ -658,15 +725,63 @@ bool Curl_host_is_ipnum(const char *hostname)
 
 
 /* return TRUE if 'part' is a case insensitive tail of 'full' */
-static bool tailmatch(const char *full, const char *part)
+static bool tailmatch(const char *full, size_t flen,
+                      const char *part, size_t plen)
 {
-  size_t plen = strlen(part);
-  size_t flen = strlen(full);
   if(plen > flen)
     return FALSE;
   return strncasecompare(part, &full[flen - plen], plen);
 }
 
+static struct Curl_addrinfo *
+convert_ipaddr_direct(const char *hostname, int port, bool *is_ipaddr)
+{
+  struct in_addr in;
+  *is_ipaddr = FALSE;
+  /* First check if this is an IPv4 address string */
+  if(curlx_inet_pton(AF_INET, hostname, &in) > 0) {
+    /* This is a dotted IP address 123.123.123.123-style */
+    *is_ipaddr = TRUE;
+#ifdef USE_RESOLVE_ON_IPS
+    (void)port;
+    return NULL;
+#else
+    return Curl_ip2addr(AF_INET, &in, hostname, port);
+#endif
+  }
+#ifdef USE_IPV6
+  else {
+    struct in6_addr in6;
+    /* check if this is an IPv6 address string */
+    if(curlx_inet_pton(AF_INET6, hostname, &in6) > 0) {
+      /* This is an IPv6 address literal */
+      *is_ipaddr = TRUE;
+#ifdef USE_RESOLVE_ON_IPS
+      return NULL;
+#else
+      return Curl_ip2addr(AF_INET6, &in6, hostname, port);
+#endif
+    }
+  }
+#endif /* USE_IPV6 */
+  return NULL;
+}
+
+static bool can_resolve_ip_version(struct Curl_easy *data, int ip_version)
+{
+#ifdef CURLRES_IPV6
+  if(ip_version == CURL_IPRESOLVE_V6 && !Curl_ipv6works(data))
+    return FALSE;
+#elif defined(CURLRES_IPV4)
+  (void)data;
+  if(ip_version == CURL_IPRESOLVE_V6)
+    return FALSE;
+#else
+#error either CURLRES_IPV6 or CURLRES_IPV4 need to be defined
+#endif
+  return TRUE;
+}
+
 /*
  * Curl_resolv() is the main name resolve function within libcurl. It resolves
  * a name and returns a pointer to the entry in the 'entry' argument (if one
@@ -678,184 +793,168 @@ static bool tailmatch(const char *full, const char *part)
  * done using this struct) to decrease the reference counter again.
  *
  * Return codes:
- *
- * CURLRESOLV_ERROR   (-1) = error, no pointer
- * CURLRESOLV_RESOLVED (0) = OK, pointer provided
- * CURLRESOLV_PENDING  (1) = waiting for response, no pointer
+ * CURLE_OK = success, *entry set to non-NULL
+ * CURLE_AGAIN = resolving in progress, *entry == NULL
+ * CURLE_COULDNT_RESOLVE_HOST = error, *entry == NULL
+ * CURLE_OPERATION_TIMEDOUT = timeout expired, *entry == NULL
  */
-
-enum resolve_t Curl_resolv(struct Curl_easy *data,
-                           const char *hostname,
-                           int port,
-                           bool allowDOH,
-                           struct Curl_dns_entry **entry)
+CURLcode Curl_resolv(struct Curl_easy *data,
+                     const char *hostname,
+                     int port,
+                     int ip_version,
+                     bool allowDOH,
+                     struct Curl_dns_entry **entry)
 {
+  struct Curl_dnscache *dnscache = dnscache_get(data);
   struct Curl_dns_entry *dns = NULL;
-  CURLcode result;
-  enum resolve_t rc = CURLRESOLV_ERROR; /* default to failure */
-  struct connectdata *conn = data->conn;
+  struct Curl_addrinfo *addr = NULL;
+  int respwait = 0;
+  bool is_ipaddr;
+  size_t hostname_len;
+
+#ifndef CURL_DISABLE_DOH
+  data->conn->bits.doh = FALSE; /* default is not */
+#else
+  (void)allowDOH;
+#endif
+  if(!dnscache)
+    goto error;
+
   /* We should intentionally error and not resolve .onion TLDs */
-  size_t hostname_len = strlen(hostname);
+  hostname_len = strlen(hostname);
   if(hostname_len >= 7 &&
      (curl_strequal(&hostname[hostname_len - 6], ".onion") ||
       curl_strequal(&hostname[hostname_len - 7], ".onion."))) {
     failf(data, "Not resolving .onion address (RFC 7686)");
-    return CURLRESOLV_ERROR;
+    goto error;
   }
-  *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);
-
-  dns = fetch_addr(data, hostname, port);
 
+  /* Let's check our DNS cache first */
+  dnscache_lock(data, dnscache);
+  dns = fetch_addr(data, dnscache, hostname, port, ip_version);
+  if(dns)
+    dns->refcount++; /* we pass out the reference. */
+  dnscache_unlock(data, dnscache);
   if(dns) {
     infof(data, "Hostname %s was found in DNS cache", hostname);
-    dns->refcount++; /* we use it! */
-    rc = CURLRESOLV_RESOLVED;
+    goto out;
   }
 
-  if(data->share)
-    Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
-
-  if(!dns) {
-    /* The entry was not in the cache. Resolve it to IP address */
-
-    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;
+  /* No luck, we need to resolve hostname. Notify user callback. */
+  if(data->set.resolver_start) {
+    void *resolver = NULL;
+    int st;
+#ifdef CURLRES_ASYNCH
+    if(Curl_async_get_impl(data, &resolver))
+      goto error;
 #endif
+    Curl_set_in_callback(data, TRUE);
+    st = data->set.resolver_start(resolver, NULL,
+                                  data->set.resolver_start_client);
+    Curl_set_in_callback(data, FALSE);
+    if(st)
+      goto error;
+  }
 
-    /* notify the resolver start callback */
-    if(data->set.resolver_start) {
-      int st;
-      Curl_set_in_callback(data, TRUE);
-      st = data->set.resolver_start(
-#ifdef USE_CURL_ASYNC
-        data->state.async.resolver,
-#else
-        NULL,
-#endif
-        NULL,
-        data->set.resolver_start_client);
-      Curl_set_in_callback(data, FALSE);
-      if(st)
-        return CURLRESOLV_ERROR;
-    }
+  /* shortcut literal IP addresses, if we are not told to resolve them. */
+  addr = convert_ipaddr_direct(hostname, port, &is_ipaddr);
+  if(addr)
+    goto out;
 
 #ifndef USE_RESOLVE_ON_IPS
-    /* 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 */
-      addr = Curl_ip2addr(AF_INET, &in, hostname, port);
-      if(!addr)
-        return CURLRESOLV_ERROR;
-    }
-#ifdef USE_IPV6
-    else {
-      struct in6_addr in6;
-      /* check if this is an IPv6 address string */
-      if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0) {
-        /* This is an IPv6 address literal */
-        addr = Curl_ip2addr(AF_INET6, &in6, hostname, port);
-        if(!addr)
-          return CURLRESOLV_ERROR;
-      }
-    }
-#endif /* USE_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 */
-      ipnum = TRUE;
-#ifdef USE_IPV6
-    else {
-      struct in6_addr in6;
-      /* check if this is an IPv6 address string */
-      if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0)
-        /* This is an IPv6 address literal */
-        ipnum = TRUE;
-    }
-#endif /* USE_IPV6 */
-#endif /* CURL_DISABLE_DOH */
-
-#endif /* !USE_RESOLVE_ON_IPS */
+  /* allowed to convert, hostname is IP address, then NULL means error */
+  if(is_ipaddr)
+    goto error;
+#endif
 
-    if(!addr) {
-      if(conn->ip_version == CURL_IPRESOLVE_V6 && !Curl_ipv6works(data))
-        return CURLRESOLV_ERROR;
+  /* Really need a resolver for hostname. */
+  if(ip_version == CURL_IPRESOLVE_V6 && !Curl_ipv6works(data))
+    goto error;
 
-      if(strcasecompare(hostname, "localhost") ||
-         strcasecompare(hostname, "localhost.") ||
-         tailmatch(hostname, ".localhost") ||
-         tailmatch(hostname, ".localhost."))
-        addr = get_localhost(port, hostname);
+  if(!is_ipaddr &&
+     (strcasecompare(hostname, "localhost") ||
+      strcasecompare(hostname, "localhost.") ||
+      tailmatch(hostname, hostname_len, STRCONST(".localhost")) ||
+      tailmatch(hostname, hostname_len, STRCONST(".localhost.")))) {
+    addr = get_localhost(port, hostname);
+  }
 #ifndef CURL_DISABLE_DOH
-      else if(allowDOH && data->set.doh && !ipnum)
-        addr = Curl_doh(data, hostname, port, &respwait);
+  else if(!is_ipaddr && allowDOH && data->set.doh) {
+    addr = Curl_doh(data, hostname, port, ip_version, &respwait);
+  }
 #endif
-      else {
-        /* Check what IP specifics the app has requested and if we can provide
-         * it. If not, bail out. */
-        if(!Curl_ipvalid(data, conn))
-          return CURLRESOLV_ERROR;
-        /* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a
-           non-zero value indicating that we need to wait for the response to
-           the resolve call */
-        addr = Curl_getaddrinfo(data, hostname, port, &respwait);
-      }
-    }
-    if(!addr) {
-      if(respwait) {
-        /* the response to our resolve call will come asynchronously at
-           a later time, good or bad */
-        /* First, check that we have not received the info by now */
-        result = Curl_resolv_check(data, &dns);
-        if(result) /* error detected */
-          return CURLRESOLV_ERROR;
-        if(dns)
-          rc = CURLRESOLV_RESOLVED; /* pointer provided */
-        else
-          rc = CURLRESOLV_PENDING; /* no info yet */
-      }
-    }
-    else {
-      if(data->share)
-        Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
-
-      /* we got a response, store it in the cache */
-      dns = Curl_cache_addr(data, addr, hostname, 0, port, FALSE);
+  else {
+    /* Can we provide the requested IP specifics in resolving? */
+    if(!can_resolve_ip_version(data, ip_version))
+      goto error;
 
-      if(data->share)
-        Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+#ifdef CURLRES_ASYNCH
+    addr = Curl_async_getaddrinfo(data, hostname, port, ip_version, &respwait);
+#else
+    respwait = 0; /* no async waiting here */
+    addr = Curl_sync_getaddrinfo(data, hostname, port, ip_version);
+#endif
+  }
 
-      if(!dns)
-        /* returned failure, bail out nicely */
-        Curl_freeaddrinfo(addr);
-      else {
-        rc = CURLRESOLV_RESOLVED;
-        show_resolve_info(data, dns);
-      }
+out:
+  /* We either have found a `dns` or looked up the `addr`
+   * or `respwait` is set for an async operation.
+   * Everything else is a failure to resolve. */
+  if(dns) {
+    *entry = dns;
+    return CURLE_OK;
+  }
+  else if(addr) {
+    /* we got a response, create a dns entry, add to cache, return */
+    dns = Curl_dnscache_mk_entry(data, addr, hostname, 0, port, FALSE);
+    if(!dns)
+      goto error;
+    if(Curl_dnscache_add(data, dns))
+      goto error;
+    show_resolve_info(data, dns);
+    *entry = dns;
+    return CURLE_OK;
+  }
+  else if(respwait) {
+    if(!Curl_resolv_check(data, &dns)) {
+      *entry = dns;
+      return dns ? CURLE_OK : CURLE_AGAIN;
     }
   }
+error:
+  if(dns)
+    Curl_resolv_unlink(data, &dns);
+  *entry = NULL;
+  Curl_async_shutdown(data);
+  return CURLE_COULDNT_RESOLVE_HOST;
+}
 
-  *entry = dns;
+CURLcode Curl_resolv_blocking(struct Curl_easy *data,
+                              const char *hostname,
+                              int port,
+                              int ip_version,
+                              struct Curl_dns_entry **dnsentry)
+{
+  CURLcode result;
 
-  return rc;
+  *dnsentry = NULL;
+  result = Curl_resolv(data, hostname, port, ip_version, FALSE, dnsentry);
+  switch(result) {
+  case CURLE_OK:
+    DEBUGASSERT(*dnsentry);
+    return CURLE_OK;
+  case CURLE_AGAIN:
+    DEBUGASSERT(!*dnsentry);
+    result = Curl_async_await(data, dnsentry);
+    if(result || !*dnsentry) {
+      /* close the connection, since we cannot return failure here without
+         cleaning up this connection properly. */
+      connclose(data->conn, "async resolve failed");
+    }
+    return result;
+  default:
+    return result;
+  }
 }
 
 #ifdef USE_ALARM_TIMEOUT
@@ -887,18 +986,18 @@ void alarmfunc(int sig)
  * is ignored.
  *
  * Return codes:
- *
- * CURLRESOLV_TIMEDOUT(-2) = warning, time too short or previous alarm expired
- * CURLRESOLV_ERROR   (-1) = error, no pointer
- * CURLRESOLV_RESOLVED (0) = OK, pointer provided
- * CURLRESOLV_PENDING  (1) = waiting for response, no pointer
+ * CURLE_OK = success, *entry set to non-NULL
+ * CURLE_AGAIN = resolving in progress, *entry == NULL
+ * CURLE_COULDNT_RESOLVE_HOST = error, *entry == NULL
+ * CURLE_OPERATION_TIMEDOUT = timeout expired, *entry == NULL
  */
 
-enum resolve_t Curl_resolv_timeout(struct Curl_easy *data,
-                                   const char *hostname,
-                                   int port,
-                                   struct Curl_dns_entry **entry,
-                                   timediff_t timeoutms)
+CURLcode Curl_resolv_timeout(struct Curl_easy *data,
+                             const char *hostname,
+                             int port,
+                             int ip_version,
+                             struct Curl_dns_entry **entry,
+                             timediff_t timeoutms)
 {
 #ifdef USE_ALARM_TIMEOUT
 #ifdef HAVE_SIGACTION
@@ -913,13 +1012,13 @@ enum resolve_t Curl_resolv_timeout(struct Curl_easy *data,
   volatile long timeout;
   volatile unsigned int prev_alarm = 0;
 #endif /* USE_ALARM_TIMEOUT */
-  enum resolve_t rc;
+  CURLcode result;
 
   *entry = NULL;
 
   if(timeoutms < 0)
     /* got an already expired timeout */
-    return CURLRESOLV_TIMEDOUT;
+    return CURLE_OPERATION_TIMEDOUT;
 
 #ifdef USE_ALARM_TIMEOUT
   if(data->set.no_signal)
@@ -928,9 +1027,14 @@ enum resolve_t Curl_resolv_timeout(struct Curl_easy *data,
   else
     timeout = (timeoutms > LONG_MAX) ? LONG_MAX : (long)timeoutms;
 
-  if(!timeout)
-    /* USE_ALARM_TIMEOUT defined, but no timeout actually requested */
-    return Curl_resolv(data, hostname, port, TRUE, entry);
+  if(!timeout
+#ifndef CURL_DISABLE_DOH
+     || data->set.doh
+#endif
+    )
+    /* USE_ALARM_TIMEOUT defined, but no timeout actually requested or resolve
+       done using DoH */
+    return Curl_resolv(data, hostname, port, ip_version, TRUE, entry);
 
   if(timeout < 1000) {
     /* The alarm() function only provides integer second resolution, so if
@@ -938,7 +1042,7 @@ enum resolve_t Curl_resolv_timeout(struct Curl_easy *data,
     failf(data,
         "remaining timeout of %ld too small to resolve via SIGALRM method",
         timeout);
-    return CURLRESOLV_TIMEDOUT;
+    return CURLE_OPERATION_TIMEDOUT;
   }
   /* This allows us to time-out from the name resolver, as the timeout
      will generate a signal and we will siglongjmp() from that here.
@@ -951,7 +1055,7 @@ enum resolve_t Curl_resolv_timeout(struct Curl_easy *data,
   if(sigsetjmp(curl_jmpenv, 1)) {
     /* this is coming from a siglongjmp() after an alarm signal */
     failf(data, "name lookup timed out");
-    rc = CURLRESOLV_ERROR;
+    result = CURLE_OPERATION_TIMEDOUT;
     goto clean_up;
   }
   else {
@@ -982,19 +1086,19 @@ enum resolve_t Curl_resolv_timeout(struct Curl_easy *data,
     prev_alarm = alarm(curlx_sltoui(timeout/1000L));
   }
 
-#else
+#else /* USE_ALARM_TIMEOUT */
 #ifndef CURLRES_ASYNCH
   if(timeoutms)
     infof(data, "timeout on name lookup is not supported");
 #else
   (void)timeoutms; /* timeoutms not used with an async resolver */
 #endif
-#endif /* USE_ALARM_TIMEOUT */
+#endif /* else USE_ALARM_TIMEOUT */
 
   /* Perform the actual name resolution. This might be interrupted by an
    * alarm if it takes too long.
    */
-  rc = Curl_resolv(data, hostname, port, TRUE, entry);
+  result = Curl_resolv(data, hostname, port, ip_version, TRUE, entry);
 
 #ifdef USE_ALARM_TIMEOUT
 clean_up:
@@ -1022,7 +1126,7 @@ enum resolve_t Curl_resolv_timeout(struct Curl_easy *data,
      the time we spent until now! */
   if(prev_alarm) {
     /* there was an alarm() set before us, now put it back */
-    timediff_t elapsed_secs = Curl_timediff(Curl_now(),
+    timediff_t elapsed_secs = curlx_timediff(curlx_now(),
                                             data->conn->created) / 1000;
 
     /* the alarm period is counted in even number of seconds */
@@ -1035,7 +1139,7 @@ enum resolve_t Curl_resolv_timeout(struct Curl_easy *data,
          will not, and zero would be to switch it off so we never set it to
          less than 1! */
       alarm(1);
-      rc = CURLRESOLV_TIMEDOUT;
+      result = CURLE_OPERATION_TIMEDOUT;
       failf(data, "Previous alarm fired off");
     }
     else
@@ -1043,7 +1147,19 @@ enum resolve_t Curl_resolv_timeout(struct Curl_easy *data,
   }
 #endif /* USE_ALARM_TIMEOUT */
 
-  return rc;
+  return result;
+}
+
+static void dnscache_entry_free(struct Curl_dns_entry *dns)
+{
+  Curl_freeaddrinfo(dns->addr);
+#ifdef USE_HTTPSRR
+  if(dns->hinfo) {
+    Curl_httpsrr_cleanup(dns->hinfo);
+    free(dns->hinfo);
+  }
+#endif
+  free(dns);
 }
 
 /*
@@ -1055,110 +1171,85 @@ enum resolve_t Curl_resolv_timeout(struct Curl_easy *data,
  */
 void Curl_resolv_unlink(struct Curl_easy *data, struct Curl_dns_entry **pdns)
 {
-  struct Curl_dns_entry *dns = *pdns;
-  *pdns = NULL;
-  if(data && data->share)
-    Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
-
-  hostcache_unlink_entry(dns);
-
-  if(data && data->share)
-    Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+  if(*pdns) {
+    struct Curl_dnscache *dnscache = dnscache_get(data);
+    struct Curl_dns_entry *dns = *pdns;
+    *pdns = NULL;
+    dnscache_lock(data, dnscache);
+    dns->refcount--;
+    if(dns->refcount == 0)
+      dnscache_entry_free(dns);
+    dnscache_unlock(data, dnscache);
+  }
 }
 
-/*
- * File-internal: release cache dns entry reference, free if inuse drops to 0
- */
-static void hostcache_unlink_entry(void *entry)
+static void dnscache_entry_dtor(void *entry)
 {
   struct Curl_dns_entry *dns = (struct Curl_dns_entry *) entry;
   DEBUGASSERT(dns && (dns->refcount > 0));
-
   dns->refcount--;
-  if(dns->refcount == 0) {
-    Curl_freeaddrinfo(dns->addr);
-#ifdef USE_HTTPSRR
-    if(dns->hinfo) {
-      free(dns->hinfo->target);
-      free(dns->hinfo->ipv4hints);
-      free(dns->hinfo->echconfiglist);
-      free(dns->hinfo->ipv6hints);
-      free(dns->hinfo);
-    }
-#endif
-    free(dns);
-  }
+  if(dns->refcount == 0)
+    dnscache_entry_free(dns);
 }
 
 /*
- * Curl_init_dnscache() inits a new DNS cache.
+ * Curl_dnscache_init() inits a new DNS cache.
  */
-void Curl_init_dnscache(struct Curl_hash *hash, size_t size)
+void Curl_dnscache_init(struct Curl_dnscache *dns, size_t size)
 {
-  Curl_hash_init(hash, size, Curl_hash_str, Curl_str_key_compare,
-                 hostcache_unlink_entry);
+  Curl_hash_init(&dns->entries, size, Curl_hash_str, curlx_str_key_compare,
+                 dnscache_entry_dtor);
 }
 
-/*
- * Curl_hostcache_clean()
- *
- * This _can_ be called with 'data' == NULL but then of course no locking
- * can be done!
- */
-
-void Curl_hostcache_clean(struct Curl_easy *data,
-                          struct Curl_hash *hash)
+void Curl_dnscache_destroy(struct Curl_dnscache *dns)
 {
-  if(data && data->share)
-    Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
-
-  Curl_hash_clean(hash);
-
-  if(data && data->share)
-    Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+  Curl_hash_destroy(&dns->entries);
 }
 
-
 CURLcode Curl_loadhostpairs(struct Curl_easy *data)
 {
+  struct Curl_dnscache *dnscache = dnscache_get(data);
   struct curl_slist *hostp;
-  char *host_end;
+
+  if(!dnscache)
+    return CURLE_FAILED_INIT;
 
   /* Default is no wildcard found */
   data->state.wildcard_resolve = FALSE;
 
   for(hostp = data->state.resolve; hostp; hostp = hostp->next) {
     char entry_id[MAX_HOSTCACHE_LEN];
-    if(!hostp->data)
+    const char *host = hostp->data;
+    struct Curl_str source;
+    if(!host)
       continue;
-    if(hostp->data[0] == '-') {
-      unsigned long num = 0;
+    if(*host == '-') {
+      curl_off_t num = 0;
       size_t entry_len;
-      size_t hlen = 0;
-      host_end = strchr(&hostp->data[1], ':');
-
-      if(host_end) {
-        hlen = host_end - &hostp->data[1];
-        num = strtoul(++host_end, NULL, 10);
-        if(!hlen || (num > 0xffff))
-          host_end = NULL;
+      host++;
+      if(!curlx_str_single(&host, '[')) {
+        if(curlx_str_until(&host, &source, MAX_IPADR_LEN, ']') ||
+           curlx_str_single(&host, ']') ||
+           curlx_str_single(&host, ':'))
+          continue;
       }
-      if(!host_end) {
-        infof(data, "Bad syntax CURLOPT_RESOLVE removal entry '%s'",
-              hostp->data);
-        continue;
+      else {
+        if(curlx_str_until(&host, &source, 4096, ':') ||
+           curlx_str_single(&host, ':')) {
+          continue;
+        }
       }
-      /* Create an entry id, based upon the hostname and port */
-      entry_len = create_hostcache_id(&hostp->data[1], hlen, (int)num,
-                                      entry_id, sizeof(entry_id));
-      if(data->share)
-        Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
 
-      /* delete entry, ignore if it did not exist */
-      Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
-
-      if(data->share)
-        Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+      if(!curlx_str_number(&host, &num, 0xffff)) {
+        /* Create an entry id, based upon the hostname and port */
+        entry_len = create_dnscache_id(curlx_str(&source),
+                                       curlx_strlen(&source), (int)num,
+                                       entry_id, sizeof(entry_id));
+        dnscache_lock(data, dnscache);
+        /* delete entry, ignore if it did not exist */
+        Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1);
+        dnscache_unlock(data, dnscache);
+      }
     }
     else {
       struct Curl_dns_entry *dns;
@@ -1166,75 +1257,69 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data)
       size_t entry_len;
       char address[64];
 #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
-      char *addresses = NULL;
+      const char *addresses = NULL;
 #endif
-      char *addr_begin;
-      char *addr_end;
-      char *port_ptr;
-      int port = 0;
-      char *end_ptr;
+      curl_off_t port = 0;
       bool permanent = TRUE;
-      unsigned long tmp_port;
       bool error = TRUE;
-      char *host_begin = hostp->data;
-      size_t hlen = 0;
 
-      if(host_begin[0] == '+') {
-        host_begin++;
+      if(*host == '+') {
+        host++;
         permanent = FALSE;
       }
-      host_end = strchr(host_begin, ':');
-      if(!host_end)
-        goto err;
-      hlen = host_end - host_begin;
-
-      port_ptr = host_end + 1;
-      tmp_port = strtoul(port_ptr, &end_ptr, 10);
-      if(tmp_port > USHRT_MAX || end_ptr == port_ptr || *end_ptr != ':')
+      if(!curlx_str_single(&host, '[')) {
+        if(curlx_str_until(&host, &source, MAX_IPADR_LEN, ']') ||
+           curlx_str_single(&host, ']'))
+          continue;
+      }
+      else {
+        if(curlx_str_until(&host, &source, 4096, ':'))
+          continue;
+      }
+      if(curlx_str_single(&host, ':') ||
+         curlx_str_number(&host, &port, 0xffff) ||
+         curlx_str_single(&host, ':'))
         goto err;
 
-      port = (int)tmp_port;
 #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
-      addresses = end_ptr + 1;
+      addresses = host;
 #endif
 
-      while(*end_ptr) {
-        size_t alen;
+      /* start the address section */
+      while(*host) {
+        struct Curl_str target;
         struct Curl_addrinfo *ai;
 
-        addr_begin = end_ptr + 1;
-        addr_end = strchr(addr_begin, ',');
-        if(!addr_end)
-          addr_end = addr_begin + strlen(addr_begin);
-        end_ptr = addr_end;
-
-        /* allow IP(v6) address within [brackets] */
-        if(*addr_begin == '[') {
-          if(addr_end == addr_begin || *(addr_end - 1) != ']')
+        if(!curlx_str_single(&host, '[')) {
+          if(curlx_str_until(&host, &target, MAX_IPADR_LEN, ']') ||
+             curlx_str_single(&host, ']'))
             goto err;
-          ++addr_begin;
-          --addr_end;
         }
-
-        alen = addr_end - addr_begin;
-        if(!alen)
-          continue;
-
-        if(alen >= sizeof(address))
-          goto err;
-
-        memcpy(address, addr_begin, alen);
-        address[alen] = '\0';
-
+        else {
+          if(curlx_str_until(&host, &target, 4096, ',')) {
+            if(curlx_str_single(&host, ','))
+              goto err;
+            /* survive nothing but just a comma */
+            continue;
+          }
+        }
 #ifndef USE_IPV6
-        if(strchr(address, ':')) {
+        if(memchr(target.str, ':', target.len)) {
           infof(data, "Ignoring resolve address '%s', missing IPv6 support.",
                 address);
+          if(curlx_str_single(&host, ','))
+            goto err;
           continue;
         }
 #endif
 
-        ai = Curl_str2addr(address, port);
+        if(curlx_strlen(&target) >= sizeof(address))
+          goto err;
+
+        memcpy(address, curlx_str(&target), curlx_strlen(&target));
+        address[curlx_strlen(&target)] = '\0';
+
+        ai = Curl_str2addr(address, (int)port);
         if(!ai) {
           infof(data, "Resolve address '%s' found illegal", address);
           goto err;
@@ -1247,6 +1332,8 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data)
         else {
           head = tail = ai;
         }
+        if(curlx_str_single(&host, ','))
+          break;
       }
 
       if(!head)
@@ -1262,18 +1349,19 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data)
       }
 
       /* Create an entry id, based upon the hostname and port */
-      entry_len = create_hostcache_id(host_begin, hlen, port,
-                                      entry_id, sizeof(entry_id));
+      entry_len = create_dnscache_id(curlx_str(&source), curlx_strlen(&source),
+                                     (int)port,
+                                     entry_id, sizeof(entry_id));
 
-      if(data->share)
-        Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
+      dnscache_lock(data, dnscache);
 
       /* See if it is already in our dns cache */
-      dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
+      dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1);
 
       if(dns) {
-        infof(data, "RESOLVE %.*s:%d - old addresses discarded",
-              (int)hlen, host_begin, port);
+        infof(data, "RESOLVE %.*s:%" CURL_FORMAT_CURL_OFF_T
+              " - old addresses discarded", (int)curlx_strlen(&source),
+              curlx_str(&source), port);
         /* delete old entry, there are two reasons for this
          1. old entry may have different addresses.
          2. even if entry with correct addresses is already in the cache,
@@ -1285,33 +1373,33 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data)
          4. when adding a non-permanent entry, we want it to get a "fresh"
             timeout that starts _now_. */
 
-        Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
+        Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1);
       }
 
       /* put this new host in the cache */
-      dns = Curl_cache_addr(data, head, host_begin, hlen, port, permanent);
+      dns = dnscache_add_addr(data, dnscache, head, curlx_str(&source),
+                              curlx_strlen(&source), (int)port, permanent);
       if(dns) {
         /* release the returned reference; the cache itself will keep the
          * entry alive: */
         dns->refcount--;
       }
 
-      if(data->share)
-        Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+      dnscache_unlock(data, dnscache);
 
-      if(!dns) {
-        Curl_freeaddrinfo(head);
+      if(!dns)
         return CURLE_OUT_OF_MEMORY;
-      }
+
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
-      infof(data, "Added %.*s:%d:%s to DNS cache%s",
-            (int)hlen, host_begin, port, addresses,
+      infof(data, "Added %.*s:%" CURL_FORMAT_CURL_OFF_T ":%s to DNS cache%s",
+            (int)curlx_strlen(&source), curlx_str(&source), port, addresses,
             permanent ? "" : " (non-permanent)");
 #endif
 
       /* Wildcard hostname */
-      if((hlen == 1) && (host_begin[0] == '*')) {
-        infof(data, "RESOLVE *:%d using wildcard", port);
+      if(curlx_str_casecompare(&source, "*")) {
+        infof(data, "RESOLVE *:%" CURL_FORMAT_CURL_OFF_T " using wildcard",
+              port);
         data->state.wildcard_resolve = TRUE;
       }
     }
@@ -1345,9 +1433,9 @@ static void show_resolve_info(struct Curl_easy *data,
   infof(data, "Host %s:%d was resolved.",
         (dns->hostname[0] ? dns->hostname : "(none)"), dns->hostport);
 
-  Curl_dyn_init(&out[0], 1024);
+  curlx_dyn_init(&out[0], 1024);
 #ifdef CURLRES_IPV6
-  Curl_dyn_init(&out[1], 1024);
+  curlx_dyn_init(&out[1], 1024);
 #endif
 
   while(a) {
@@ -1359,10 +1447,10 @@ static void show_resolve_info(struct Curl_easy *data,
       char buf[MAX_IPADR_LEN];
       struct dynbuf *d = &out[(a->ai_family != PF_INET)];
       Curl_printable_address(a, buf, sizeof(buf));
-      if(Curl_dyn_len(d))
-        result = Curl_dyn_addn(d, ", ", 2);
+      if(curlx_dyn_len(d))
+        result = curlx_dyn_addn(d, ", ", 2);
       if(!result)
-        result = Curl_dyn_add(d, buf);
+        result = curlx_dyn_add(d, buf);
       if(result) {
         infof(data, "too many IP, cannot show");
         goto fail;
@@ -1373,38 +1461,55 @@ static void show_resolve_info(struct Curl_easy *data,
 
 #ifdef CURLRES_IPV6
   infof(data, "IPv6: %s",
-        (Curl_dyn_len(&out[1]) ? Curl_dyn_ptr(&out[1]) : "(none)"));
+        (curlx_dyn_len(&out[1]) ? curlx_dyn_ptr(&out[1]) : "(none)"));
 #endif
   infof(data, "IPv4: %s",
-        (Curl_dyn_len(&out[0]) ? Curl_dyn_ptr(&out[0]) : "(none)"));
+        (curlx_dyn_len(&out[0]) ? curlx_dyn_ptr(&out[0]) : "(none)"));
 
 fail:
-  Curl_dyn_free(&out[0]);
+  curlx_dyn_free(&out[0]);
 #ifdef CURLRES_IPV6
-  Curl_dyn_free(&out[1]);
+  curlx_dyn_free(&out[1]);
 #endif
 }
 #endif
 
+#ifdef USE_CURL_ASYNC
 CURLcode Curl_resolv_check(struct Curl_easy *data,
                            struct Curl_dns_entry **dns)
 {
   CURLcode result;
-#if defined(CURL_DISABLE_DOH) && !defined(CURLRES_ASYNCH)
-  (void)data;
-  (void)dns;
-#endif
+
+  /* If async resolving is ongoing, this must be set */
+  if(!data->state.async.hostname)
+    return CURLE_FAILED_INIT;
+
+  /* check if we have the name resolved by now (from someone else) */
+  *dns = Curl_dnscache_get(data, data->state.async.hostname,
+                           data->state.async.port,
+                           data->state.async.ip_version);
+  if(*dns) {
+    /* Tell a possibly async resolver we no longer need the results. */
+    infof(data, "Hostname '%s' was found in DNS cache",
+          data->state.async.hostname);
+    Curl_async_shutdown(data);
+    data->state.async.dns = *dns;
+    data->state.async.done = TRUE;
+    return CURLE_OK;
+  }
+
 #ifndef CURL_DISABLE_DOH
   if(data->conn->bits.doh) {
     result = Curl_doh_is_resolved(data, dns);
   }
   else
 #endif
-  result = Curl_resolver_is_resolved(data, dns);
+  result = Curl_async_is_resolved(data, dns);
   if(*dns)
     show_resolve_info(data, *dns);
   return result;
 }
+#endif
 
 int Curl_resolv_getsock(struct Curl_easy *data,
                         curl_socket_t *socks)
@@ -1416,7 +1521,7 @@ int Curl_resolv_getsock(struct Curl_easy *data,
        sockets */
     return GETSOCK_BLANK;
 #endif
-  return Curl_resolver_getsock(data, socks);
+  return Curl_async_getsock(data, socks);
 #else
   (void)data;
   (void)socks;
@@ -1429,23 +1534,25 @@ int Curl_resolv_getsock(struct Curl_easy *data,
 
    Note: this function disconnects and frees the conn data in case of
    resolve failure */
-CURLcode Curl_once_resolved(struct Curl_easy *data, bool *protocol_done)
+CURLcode Curl_once_resolved(struct Curl_easy *data,
+                            struct Curl_dns_entry *dns,
+                            bool *protocol_done)
 {
   CURLcode result;
   struct connectdata *conn = data->conn;
 
 #ifdef USE_CURL_ASYNC
   if(data->state.async.dns) {
-    conn->dns_entry = data->state.async.dns;
+    DEBUGASSERT(data->state.async.dns == dns);
     data->state.async.dns = NULL;
   }
 #endif
 
-  result = Curl_setup_conn(data, protocol_done);
+  result = Curl_setup_conn(data, dns, protocol_done);
 
   if(result) {
     Curl_detach_connection(data);
-    Curl_cpool_disconnect(data, conn, TRUE);
+    Curl_conn_terminate(data, conn, TRUE);
   }
   return result;
 }
@@ -1458,25 +1565,21 @@ CURLcode Curl_once_resolved(struct Curl_easy *data, bool *protocol_done)
 #ifdef USE_CURL_ASYNC
 CURLcode Curl_resolver_error(struct Curl_easy *data)
 {
-  const char *host_or_proxy;
-  CURLcode result;
+  struct connectdata *conn = data->conn;
+  const char *host_or_proxy = "host";
+  const char *name = conn->host.dispname;
+  CURLcode result = CURLE_COULDNT_RESOLVE_HOST;
 
 #ifndef CURL_DISABLE_PROXY
-  struct connectdata *conn = data->conn;
-  if(conn->bits.httpproxy) {
+  if(conn->bits.proxy) {
     host_or_proxy = "proxy";
     result = CURLE_COULDNT_RESOLVE_PROXY;
+    name = conn->socks_proxy.host.name ? conn->socks_proxy.host.dispname :
+      conn->http_proxy.host.dispname;
   }
-  else
 #endif
-  {
-    host_or_proxy = "host";
-    result = CURLE_COULDNT_RESOLVE_HOST;
-  }
-
-  failf(data, "Could not resolve %s: %s", host_or_proxy,
-        data->state.async.hostname);
 
+  failf(data, "Could not resolve %s: %s", host_or_proxy, name);
   return result;
 }
 #endif /* USE_CURL_ASYNC */
diff --git a/extra/curl/curl-8.12.1/lib/hostip.h b/extra/curl/curl-8.14.1/lib/hostip.h
similarity index 52%
rename from extra/curl/curl-8.12.1/lib/hostip.h
rename to extra/curl/curl-8.14.1/lib/hostip.h
index 10f70b2ba82e..cd3d957e1e7a 100644
--- a/extra/curl/curl-8.12.1/lib/hostip.h
+++ b/extra/curl/curl-8.14.1/lib/hostip.h
@@ -27,7 +27,7 @@
 #include "curl_setup.h"
 #include "hash.h"
 #include "curl_addrinfo.h"
-#include "timeval.h" /* for timediff_t */
+#include "curlx/timeval.h" /* for timediff_t */
 #include "asyn.h"
 #include "httpsrr.h"
 
@@ -61,15 +61,6 @@ enum alpnid {
   ALPN_h3 = CURLALTSVC_H3
 };
 
-/*
- * Curl_global_host_cache_init() initializes and sets up a global DNS cache.
- * Global DNS cache is general badness. Do not use. This will be removed in
- * a future version. Use the share interface instead!
- *
- * Returns a struct Curl_hash pointer on success, NULL on failure.
- */
-struct Curl_hash *Curl_global_host_cache_init(void);
-
 struct Curl_dns_entry {
   struct Curl_addrinfo *addr;
 #ifdef USE_HTTPSRR
@@ -85,6 +76,10 @@ struct Curl_dns_entry {
   char hostname[1];
 };
 
+struct Curl_dnscache {
+  struct Curl_hash entries;
+};
+
 bool Curl_host_is_ipnum(const char *hostname);
 
 /*
@@ -94,22 +89,24 @@ bool Curl_host_is_ipnum(const char *hostname);
  * The returned data *MUST* be "released" with Curl_resolv_unlink() after
  * use, or we will leak memory!
  */
-/* return codes */
-enum resolve_t {
-  CURLRESOLV_TIMEDOUT = -2,
-  CURLRESOLV_ERROR    = -1,
-  CURLRESOLV_RESOLVED =  0,
-  CURLRESOLV_PENDING  =  1
-};
-enum resolve_t Curl_resolv(struct Curl_easy *data,
-                           const char *hostname,
-                           int port,
-                           bool allowDOH,
-                           struct Curl_dns_entry **dnsentry);
-enum resolve_t Curl_resolv_timeout(struct Curl_easy *data,
-                                   const char *hostname, int port,
-                                   struct Curl_dns_entry **dnsentry,
-                                   timediff_t timeoutms);
+CURLcode Curl_resolv(struct Curl_easy *data,
+                     const char *hostname,
+                     int port,
+                     int ip_version,
+                     bool allowDOH,
+                     struct Curl_dns_entry **dnsentry);
+
+CURLcode Curl_resolv_blocking(struct Curl_easy *data,
+                              const char *hostname,
+                              int port,
+                              int ip_version,
+                              struct Curl_dns_entry **dnsentry);
+
+CURLcode Curl_resolv_timeout(struct Curl_easy *data,
+                             const char *hostname, int port,
+                             int ip_version,
+                             struct Curl_dns_entry **dnsentry,
+                             timediff_t timeoutms);
 
 #ifdef USE_IPV6
 /*
@@ -120,49 +117,25 @@ bool Curl_ipv6works(struct Curl_easy *data);
 #define Curl_ipv6works(x) FALSE
 #endif
 
-/*
- * Curl_ipvalid() checks what CURL_IPRESOLVE_* requirements that might've
- * been set and returns TRUE if they are OK.
- */
-bool Curl_ipvalid(struct Curl_easy *data, struct connectdata *conn);
-
-
-/*
- * Curl_getaddrinfo() is the generic low-level name resolve API within this
- * source file. There are several versions of this function - for different
- * name resolve layers (selected at build-time). They all take this same set
- * of arguments
- */
-struct Curl_addrinfo *Curl_getaddrinfo(struct Curl_easy *data,
-                                       const char *hostname,
-                                       int port,
-                                       int *waitp);
-
 
 /* unlink a dns entry, potentially shared with a cache */
 void Curl_resolv_unlink(struct Curl_easy *data,
                         struct Curl_dns_entry **pdns);
 
 /* init a new dns cache */
-void Curl_init_dnscache(struct Curl_hash *hash, size_t hashsize);
+void Curl_dnscache_init(struct Curl_dnscache *dns, size_t hashsize);
+
+void Curl_dnscache_destroy(struct Curl_dnscache *dns);
 
 /* prune old entries from the DNS cache */
-void Curl_hostcache_prune(struct Curl_easy *data);
+void Curl_dnscache_prune(struct Curl_easy *data);
 
 /* IPv4 threadsafe resolve function used for synch and asynch builds */
 struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, int port);
 
-CURLcode Curl_once_resolved(struct Curl_easy *data, bool *protocol_connect);
-
-/*
- * Curl_addrinfo_callback() is used when we build with any asynch specialty.
- * Handles end of async request processing. Inserts ai into hostcache when
- * status is CURL_ASYNC_SUCCESS. Twiddles fields in conn to indicate async
- * request completed whether successful or failed.
- */
-CURLcode Curl_addrinfo_callback(struct Curl_easy *data,
-                                int status,
-                                struct Curl_addrinfo *ai);
+CURLcode Curl_once_resolved(struct Curl_easy *data,
+                            struct Curl_dns_entry *dns,
+                            bool *protocol_connect);
 
 /*
  * Curl_printable_address() returns a printable version of the 1st address
@@ -173,73 +146,72 @@ void Curl_printable_address(const struct Curl_addrinfo *ip,
                             char *buf, size_t bufsize);
 
 /*
- * Curl_fetch_addr() fetches a 'Curl_dns_entry' already in the DNS cache.
+ * Make a `Curl_dns_entry`.
+ * Creates a dnscache entry *without* adding it to a dnscache. This allows
+ * further modifications of the entry *before* then adding it to a cache.
  *
- * Returns the Curl_dns_entry entry pointer or NULL if not in the cache.
+ * The entry is created with a reference count of 1.
+ * Use `Curl_resolv_unlink()` to release your hold on it.
  *
- * The returned data *MUST* be "released" with Curl_resolv_unlink() after
- * use, or we will leak memory!
+ * The call takes ownership of `addr`and makes a copy of `hostname`.
+ *
+ * Returns entry or NULL on OOM.
  */
 struct Curl_dns_entry *
-Curl_fetch_addr(struct Curl_easy *data,
-                const char *hostname,
-                int port);
+Curl_dnscache_mk_entry(struct Curl_easy *data,
+                       struct Curl_addrinfo *addr,
+                       const char *hostname,
+                       size_t hostlen, /* length or zero */
+                       int port,
+                       bool permanent);
 
 /*
- * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
- * @param permanent   iff TRUE, entry will never become stale
- * Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
+ * Curl_dnscache_get() fetches a 'Curl_dns_entry' already in the DNS cache.
+ *
+ * Returns the Curl_dns_entry entry pointer or NULL if not in the cache.
+ *
+ * The returned data *MUST* be "released" with Curl_resolv_unlink() after
+ * use, or we will leak memory!
  */
 struct Curl_dns_entry *
-Curl_cache_addr(struct Curl_easy *data, struct Curl_addrinfo *addr,
-                const char *hostname, size_t hostlen, int port,
-                bool permanent);
-
-#ifndef INADDR_NONE
-#define CURL_INADDR_NONE (in_addr_t) ~0
-#else
-#define CURL_INADDR_NONE INADDR_NONE
-#endif
-
-/*
- * Function provided by the resolver backend to set DNS servers to use.
- */
-CURLcode Curl_set_dns_servers(struct Curl_easy *data, char *servers);
-
-/*
- * Function provided by the resolver backend to set
- * outgoing interface to use for DNS requests
- */
-CURLcode Curl_set_dns_interface(struct Curl_easy *data,
-                                const char *interf);
-
-/*
- * Function provided by the resolver backend to set
- * local IPv4 address to use as source address for DNS requests
- */
-CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
-                                const char *local_ip4);
+Curl_dnscache_get(struct Curl_easy *data,
+                  const char *hostname,
+                  int port, int ip_version);
 
 /*
- * Function provided by the resolver backend to set
- * local IPv6 address to use as source address for DNS requests
+ * Curl_dnscache_addr() adds `entry` to the cache, increasing its
+ * reference count on success.
  */
-CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
-                                const char *local_ip6);
-
-/*
- * Clean off entries from the cache
- */
-void Curl_hostcache_clean(struct Curl_easy *data, struct Curl_hash *hash);
+CURLcode Curl_dnscache_add(struct Curl_easy *data,
+                           struct Curl_dns_entry *entry);
 
 /*
  * Populate the cache with specified entries from CURLOPT_RESOLVE.
  */
 CURLcode Curl_loadhostpairs(struct Curl_easy *data);
+
+#ifdef USE_CURL_ASYNC
 CURLcode Curl_resolv_check(struct Curl_easy *data,
                            struct Curl_dns_entry **dns);
+#else
+#define Curl_resolv_check(x,y) CURLE_NOT_BUILT_IN
+#endif
 int Curl_resolv_getsock(struct Curl_easy *data,
                         curl_socket_t *socks);
 
 CURLcode Curl_resolver_error(struct Curl_easy *data);
+
+#ifdef CURLRES_SYNCH
+/*
+ * Curl_sync_getaddrinfo() is the non-async low-level name resolve API.
+ * There are several versions of this function - depending on IPV6
+ * support and platform.
+ */
+struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data,
+                                            const char *hostname,
+                                            int port,
+                                            int ip_version);
+
+#endif
+
 #endif /* HEADER_CURL_HOSTIP_H */
diff --git a/extra/curl/curl-8.12.1/lib/hostip4.c b/extra/curl/curl-8.14.1/lib/hostip4.c
similarity index 92%
rename from extra/curl/curl-8.12.1/lib/hostip4.c
rename to extra/curl/curl-8.14.1/lib/hostip4.c
index 58333fbc6076..14e4d98a8662 100644
--- a/extra/curl/curl-8.12.1/lib/hostip4.c
+++ b/extra/curl/curl-8.14.1/lib/hostip4.c
@@ -54,24 +54,11 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
-/*
- * Curl_ipvalid() checks what CURL_IPRESOLVE_* requirements that might've
- * been set and returns TRUE if they are OK.
- */
-bool Curl_ipvalid(struct Curl_easy *data, struct connectdata *conn)
-{
-  (void)data;
-  if(conn->ip_version == CURL_IPRESOLVE_V6)
-    /* An IPv6 address was requested and we cannot get/use one */
-    return FALSE;
-
-  return TRUE; /* OK, proceed */
-}
 
 #ifdef CURLRES_SYNCH
 
 /*
- * Curl_getaddrinfo() - the IPv4 synchronous version.
+ * Curl_sync_getaddrinfo() - the IPv4 synchronous version.
  *
  * The original code to this function was from the Dancer source code, written
  * by Bjorn Reese, it has since been patched and modified considerably.
@@ -86,19 +73,18 @@ bool Curl_ipvalid(struct Curl_easy *data, struct connectdata *conn)
  * flavours have thread-safe versions of the plain gethostbyname() etc.
  *
  */
-struct Curl_addrinfo *Curl_getaddrinfo(struct Curl_easy *data,
-                                       const char *hostname,
-                                       int port,
-                                       int *waitp)
+struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data,
+                                            const char *hostname,
+                                            int port,
+                                            int ip_version)
 {
   struct Curl_addrinfo *ai = NULL;
 
+  (void)ip_version;
 #ifdef CURL_DISABLE_VERBOSE_STRINGS
   (void)data;
 #endif
 
-  *waitp = 0; /* synchronous response only */
-
   ai = Curl_ipv4_resolve_r(hostname, port);
   if(!ai)
     infof(data, "Curl_ipv4_resolve_r failed for %s", hostname);
@@ -286,7 +272,7 @@ struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname,
    * getaddrinfo() nor gethostbyname_r() function or for which
    * gethostbyname() is the preferred one.
    */
-  h = gethostbyname((void *)hostname);
+  h = gethostbyname(CURL_UNCONST(hostname));
 #endif /* (HAVE_GETADDRINFO && HAVE_GETADDRINFO_THREADSAFE) ||
            HAVE_GETHOSTBYNAME_R */
 
diff --git a/extra/curl/curl-8.12.1/lib/hostip6.c b/extra/curl/curl-8.14.1/lib/hostip6.c
similarity index 80%
rename from extra/curl/curl-8.12.1/lib/hostip6.c
rename to extra/curl/curl-8.14.1/lib/hostip6.c
index c16ddfe58d1d..35cc2d737bb3 100644
--- a/extra/curl/curl-8.12.1/lib/hostip6.c
+++ b/extra/curl/curl-8.14.1/lib/hostip6.c
@@ -49,25 +49,13 @@
 #include "hash.h"
 #include "share.h"
 #include "url.h"
-#include "inet_pton.h"
+#include "curlx/inet_pton.h"
 #include "connect.h"
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
 #include "memdebug.h"
 
-/*
- * Curl_ipvalid() checks what CURL_IPRESOLVE_* requirements that might've
- * been set and returns TRUE if they are OK.
- */
-bool Curl_ipvalid(struct Curl_easy *data, struct connectdata *conn)
-{
-  if(conn->ip_version == CURL_IPRESOLVE_V6)
-    return Curl_ipv6works(data);
-
-  return TRUE;
-}
-
 #if defined(CURLRES_SYNCH)
 
 #ifdef DEBUG_ADDRINFO
@@ -87,7 +75,7 @@ static void dump_addrinfo(const struct Curl_addrinfo *ai)
 #endif
 
 /*
- * Curl_getaddrinfo() when built IPv6-enabled (non-threading and
+ * Curl_sync_getaddrinfo() when built IPv6-enabled (non-threading and
  * non-ares version).
  *
  * Returns name information about the given hostname and port number. If
@@ -95,10 +83,10 @@ static void dump_addrinfo(const struct Curl_addrinfo *ai)
  * to memory we need to free after use. That memory *MUST* be freed with
  * Curl_freeaddrinfo(), nothing else.
  */
-struct Curl_addrinfo *Curl_getaddrinfo(struct Curl_easy *data,
-                                       const char *hostname,
-                                       int port,
-                                       int *waitp)
+struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data,
+                                            const char *hostname,
+                                            int port,
+                                            int ip_version)
 {
   struct addrinfo hints;
   struct Curl_addrinfo *res;
@@ -110,9 +98,7 @@ struct Curl_addrinfo *Curl_getaddrinfo(struct Curl_easy *data,
 #endif
   int pf = PF_INET;
 
-  *waitp = 0; /* synchronous response only */
-
-  if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data))
+  if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data))
     /* The stack seems to be IPv6-enabled */
     pf = PF_UNSPEC;
 
@@ -126,8 +112,8 @@ struct Curl_addrinfo *Curl_getaddrinfo(struct Curl_easy *data,
    * The AI_NUMERICHOST must not be set to get synthesized IPv6 address from
    * an IPv4 address on iOS and macOS.
    */
-  if((1 == Curl_inet_pton(AF_INET, hostname, addrbuf)) ||
-     (1 == Curl_inet_pton(AF_INET6, hostname, addrbuf))) {
+  if((1 == curlx_inet_pton(AF_INET, hostname, addrbuf)) ||
+     (1 == curlx_inet_pton(AF_INET6, hostname, addrbuf))) {
     /* the given address is numerical only, prevent a reverse lookup */
     hints.ai_flags = AI_NUMERICHOST;
   }
diff --git a/extra/curl/curl-8.12.1/lib/hsts.c b/extra/curl/curl-8.14.1/lib/hsts.c
similarity index 89%
rename from extra/curl/curl-8.12.1/lib/hsts.c
rename to extra/curl/curl-8.14.1/lib/hsts.c
index 99452b61a6ab..62a3f890589c 100644
--- a/extra/curl/curl-8.12.1/lib/hsts.c
+++ b/extra/curl/curl-8.14.1/lib/hsts.c
@@ -35,13 +35,12 @@
 #include "curl_get_line.h"
 #include "strcase.h"
 #include "sendf.h"
-#include "strtoofft.h"
 #include "parsedate.h"
 #include "fopen.h"
 #include "rename.h"
 #include "share.h"
 #include "strdup.h"
-#include "strparse.h"
+#include "curlx/strparse.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -59,13 +58,12 @@
 time_t deltatime; /* allow for "adjustments" for unit test purposes */
 static time_t hsts_debugtime(void *unused)
 {
-  char *timestr = getenv("CURL_TIME");
+  const char *timestr = getenv("CURL_TIME");
   (void)unused;
   if(timestr) {
     curl_off_t val;
-    (void)curlx_strtoofft(timestr, NULL, 10, &val);
-
-    val += (curl_off_t)deltatime;
+    if(!curlx_str_number(&timestr, &val, TIME_T_MAX))
+      val += (curl_off_t)deltatime;
     return (time_t)val;
   }
   return time(NULL);
@@ -85,7 +83,7 @@ struct hsts *Curl_hsts_init(void)
 
 static void hsts_free(struct stsentry *e)
 {
-  free((char *)e->host);
+  free(CURL_UNCONST(e->host));
   free(e);
 }
 
@@ -156,35 +154,30 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname,
     return CURLE_OK;
 
   do {
-    while(*p && ISBLANK(*p))
-      p++;
+    curlx_str_passblanks(&p);
     if(strncasecompare("max-age", p, 7)) {
       bool quoted = FALSE;
-      CURLofft offt;
-      char *endp;
+      int rc;
 
       if(gotma)
         return CURLE_BAD_FUNCTION_ARGUMENT;
 
       p += 7;
-      while(*p && ISBLANK(*p))
-        p++;
-      if(*p++ != '=')
+      curlx_str_passblanks(&p);
+      if(curlx_str_single(&p, '='))
         return CURLE_BAD_FUNCTION_ARGUMENT;
-      while(*p && ISBLANK(*p))
-        p++;
+      curlx_str_passblanks(&p);
 
-      if(*p == '\"') {
-        p++;
+      if(!curlx_str_single(&p, '\"'))
         quoted = TRUE;
-      }
-      offt = curlx_strtoofft(p, &endp, 10, &expires);
-      if(offt == CURL_OFFT_FLOW)
+
+      rc = curlx_str_number(&p, &expires, TIME_T_MAX);
+      if(rc == STRE_OVERFLOW)
         expires = CURL_OFF_T_MAX;
-      else if(offt)
+      else if(rc)
         /* invalid max-age */
         return CURLE_BAD_FUNCTION_ARGUMENT;
-      p = endp;
+
       if(quoted) {
         if(*p != '\"')
           return CURLE_BAD_FUNCTION_ARGUMENT;
@@ -205,8 +198,7 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname,
         p++;
     }
 
-    while(*p && ISBLANK(*p))
-      p++;
+    curlx_str_passblanks(&p);
     if(*p == ';')
       p++;
   } while(*p);
@@ -287,7 +279,7 @@ struct stsentry *Curl_hsts(struct hsts *h, const char *hostname,
           blen = ntail;
         }
       }
-      /* avoid strcasecompare because the host name is not null terminated */
+      /* avoid strcasecompare because the host name is not null-terminated */
       if((hlen == ntail) && strncasecompare(hostname, sts->host, hlen))
         return sts;
     }
@@ -308,7 +300,7 @@ static CURLcode hsts_push(struct Curl_easy *data,
   struct tm stamp;
   CURLcode result;
 
-  e.name = (char *)sts->host;
+  e.name = (char *)CURL_UNCONST(sts->host);
   e.namelen = strlen(sts->host);
   e.includeSubDomains = sts->includeSubDomains;
 
@@ -416,7 +408,7 @@ CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h,
 }
 
 /* only returns SERIOUS errors */
-static CURLcode hsts_add(struct hsts *h, char *line)
+static CURLcode hsts_add(struct hsts *h, const char *line)
 {
   /* Example lines:
      example.com "20191231 10:00:00"
@@ -425,10 +417,10 @@ static CURLcode hsts_add(struct hsts *h, char *line)
   struct Curl_str host;
   struct Curl_str date;
 
-  if(Curl_str_word(&line, &host, MAX_HSTS_HOSTLEN) ||
-     Curl_str_singlespace(&line) ||
-     Curl_str_quotedword(&line, &date, MAX_HSTS_DATELEN) ||
-     Curl_str_newline(&line))
+  if(curlx_str_word(&line, &host, MAX_HSTS_HOSTLEN) ||
+     curlx_str_singlespace(&line) ||
+     curlx_str_quotedword(&line, &date, MAX_HSTS_DATELEN) ||
+     curlx_str_newline(&line))
     ;
   else {
     CURLcode result = CURLE_OK;
@@ -436,26 +428,26 @@ static CURLcode hsts_add(struct hsts *h, char *line)
     struct stsentry *e;
     char dbuf[MAX_HSTS_DATELEN + 1];
     time_t expires;
+    const char *hp = curlx_str(&host);
 
-    /* The date parser works on a null terminated string. The maximum length
-       is upheld by Curl_str_quotedword(). */
-    memcpy(dbuf, date.str, date.len);
-    dbuf[date.len] = 0;
+    /* The date parser works on a null-terminated string. The maximum length
+       is upheld by curlx_str_quotedword(). */
+    memcpy(dbuf, curlx_str(&date), curlx_strlen(&date));
+    dbuf[curlx_strlen(&date)] = 0;
 
     expires = strcmp(dbuf, UNLIMITED) ? Curl_getdate_capped(dbuf) :
       TIME_T_MAX;
 
-    if(host.str[0] == '.') {
-      host.str++;
-      host.len--;
+    if(hp[0] == '.') {
+      curlx_str_nudge(&host, 1);
       subdomain = TRUE;
     }
     /* only add it if not already present */
-    e = Curl_hsts(h, host.str, host.len, subdomain);
+    e = Curl_hsts(h, curlx_str(&host), curlx_strlen(&host), subdomain);
     if(!e)
-      result = hsts_create(h, host.str, host.len, subdomain, expires);
-    else if((strlen(e->host) == host.len) &&
-            strncasecompare(host.str, e->host, host.len)) {
+      result = hsts_create(h, curlx_str(&host), curlx_strlen(&host),
+                           subdomain, expires);
+    else if(curlx_str_casecompare(&host, e->host)) {
       /* the same hostname, use the largest expire time */
       if(expires > e->expires)
         e->expires = expires;
@@ -534,11 +526,11 @@ static CURLcode hsts_load(struct hsts *h, const char *file)
   fp = fopen(file, FOPEN_READTEXT);
   if(fp) {
     struct dynbuf buf;
-    Curl_dyn_init(&buf, MAX_HSTS_LINE);
+    curlx_dyn_init(&buf, MAX_HSTS_LINE);
     while(Curl_get_line(&buf, fp)) {
-      char *lineptr = Curl_dyn_ptr(&buf);
-      while(*lineptr && ISBLANK(*lineptr))
-        lineptr++;
+      const char *lineptr = curlx_dyn_ptr(&buf);
+      curlx_str_passblanks(&lineptr);
+
       /*
        * Skip empty or commented lines, since we know the line will have a
        * trailing newline from Curl_get_line we can treat length 1 as empty.
@@ -548,7 +540,7 @@ static CURLcode hsts_load(struct hsts *h, const char *file)
 
       hsts_add(h, lineptr);
     }
-    Curl_dyn_free(&buf); /* free the line buffer */
+    curlx_dyn_free(&buf); /* free the line buffer */
     fclose(fp);
   }
   return result;
diff --git a/extra/curl/curl-8.12.1/lib/hsts.h b/extra/curl/curl-8.14.1/lib/hsts.h
similarity index 98%
rename from extra/curl/curl-8.12.1/lib/hsts.h
rename to extra/curl/curl-8.14.1/lib/hsts.h
index e8d0f9d55235..8ec9637cb0c0 100644
--- a/extra/curl/curl-8.12.1/lib/hsts.h
+++ b/extra/curl/curl-8.14.1/lib/hsts.h
@@ -36,8 +36,8 @@ extern time_t deltatime;
 struct stsentry {
   struct Curl_llist_node node;
   const char *host;
-  bool includeSubDomains;
   curl_off_t expires; /* the timestamp of this entry's expiry */
+  BIT(includeSubDomains);
 };
 
 /* The HSTS cache. Needs to be able to tailmatch hostnames. */
diff --git a/extra/curl/curl-8.12.1/lib/http.c b/extra/curl/curl-8.14.1/lib/http.c
similarity index 85%
rename from extra/curl/curl-8.12.1/lib/http.c
rename to extra/curl/curl-8.14.1/lib/http.c
index 5bd2bd416633..5942d313b87a 100644
--- a/extra/curl/curl-8.12.1/lib/http.c
+++ b/extra/curl/curl-8.14.1/lib/http.c
@@ -54,7 +54,7 @@
 #include "formdata.h"
 #include "mime.h"
 #include "progress.h"
-#include "curl_base64.h"
+#include "curlx/base64.h"
 #include "cookie.h"
 #include "vauth/vauth.h"
 #include "vtls/vtls.h"
@@ -72,12 +72,11 @@
 #include "headers.h"
 #include "select.h"
 #include "parsedate.h" /* for the week day and month names */
-#include "strtoofft.h"
 #include "multiif.h"
 #include "strcase.h"
 #include "content_encoding.h"
 #include "http_proxy.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
 #include "http2.h"
 #include "cfilters.h"
 #include "connect.h"
@@ -86,6 +85,7 @@
 #include "hsts.h"
 #include "ws.h"
 #include "curl_ctype.h"
+#include "curlx/strparse.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -116,7 +116,7 @@ static CURLcode http_req_set_reader(struct Curl_easy *data,
                                     const char **tep);
 static CURLcode http_size(struct Curl_easy *data);
 static CURLcode http_statusline(struct Curl_easy *data,
-                                     struct connectdata *conn);
+                                struct connectdata *conn);
 static CURLcode http_target(struct Curl_easy *data, struct connectdata *conn,
                             struct dynbuf *req);
 static CURLcode http_useragent(struct Curl_easy *data);
@@ -186,19 +186,56 @@ const struct Curl_handler Curl_handler_https = {
 
 #endif
 
+void Curl_http_neg_init(struct Curl_easy *data, struct http_negotiation *neg)
+{
+  memset(neg, 0, sizeof(*neg));
+  neg->accept_09 = data->set.http09_allowed;
+  switch(data->set.httpwant) {
+  case CURL_HTTP_VERSION_1_0:
+    neg->wanted = neg->allowed = (CURL_HTTP_V1x);
+    neg->only_10 = TRUE;
+    break;
+  case CURL_HTTP_VERSION_1_1:
+    neg->wanted = neg->allowed = (CURL_HTTP_V1x);
+    break;
+  case CURL_HTTP_VERSION_2_0:
+    neg->wanted = neg->allowed = (CURL_HTTP_V1x | CURL_HTTP_V2x);
+    neg->h2_upgrade = TRUE;
+    break;
+  case CURL_HTTP_VERSION_2TLS:
+    neg->wanted = neg->allowed = (CURL_HTTP_V1x | CURL_HTTP_V2x);
+    break;
+  case CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE:
+    neg->wanted = neg->allowed = (CURL_HTTP_V2x);
+    data->state.http_neg.h2_prior_knowledge = TRUE;
+    break;
+  case CURL_HTTP_VERSION_3:
+    neg->wanted = (CURL_HTTP_V1x | CURL_HTTP_V2x | CURL_HTTP_V3x);
+    neg->allowed = neg->wanted;
+    break;
+  case CURL_HTTP_VERSION_3ONLY:
+    neg->wanted = neg->allowed = (CURL_HTTP_V3x);
+    break;
+  case CURL_HTTP_VERSION_NONE:
+  default:
+    neg->wanted = (CURL_HTTP_V1x | CURL_HTTP_V2x);
+    neg->allowed = (CURL_HTTP_V1x | CURL_HTTP_V2x | CURL_HTTP_V3x);
+    break;
+  }
+}
+
 CURLcode Curl_http_setup_conn(struct Curl_easy *data,
                               struct connectdata *conn)
 {
   /* allocate the HTTP-specific struct for the Curl_easy, only to survive
      during this request */
   connkeep(conn, "HTTP default");
-
-  if(data->state.httpwant == CURL_HTTP_VERSION_3ONLY) {
+  if(data->state.http_neg.wanted == CURL_HTTP_V3x) {
+    /* only HTTP/3, needs to work */
     CURLcode result = Curl_conn_may_http3(data, conn);
     if(result)
       return result;
   }
-
   return CURLE_OK;
 }
 
@@ -235,46 +272,28 @@ char *Curl_checkProxyheaders(struct Curl_easy *data,
 #endif
 
 /*
- * Strip off leading and trailing whitespace from the value in the
- * given HTTP header line and return a strdupped copy. Returns NULL in
- * case of allocation failure. Returns an empty string if the header value
- * consists entirely of whitespace.
+ * Strip off leading and trailing whitespace from the value in the given HTTP
+ * header line and return a strdup()ed copy. Returns NULL in case of
+ * allocation failure or bad input. Returns an empty string if the header
+ * value consists entirely of whitespace.
+ *
+ * If the header is provided as "name;", ending with a semicolon, it must
+ * return a blank string.
  */
 char *Curl_copy_header_value(const char *header)
 {
-  const char *start;
-  const char *end;
-  size_t len;
-
-  /* Find the end of the header name */
-  while(*header && (*header != ':'))
-    ++header;
-
-  if(*header)
-    /* Skip over colon */
-    ++header;
-
-  /* Find the first non-space letter */
-  start = header;
-  while(*start && ISSPACE(*start))
-    start++;
-
-  end = strchr(start, '\r');
-  if(!end)
-    end = strchr(start, '\n');
-  if(!end)
-    end = strchr(start, '\0');
-  if(!end)
-    return NULL;
-
-  /* skip all trailing space letters */
-  while((end > start) && ISSPACE(*end))
-    end--;
+  struct Curl_str out;
 
-  /* get length of the type */
-  len = end - start + 1;
+  /* find the end of the header name */
+  if(!curlx_str_cspn(&header, &out, ";:") &&
+     (!curlx_str_single(&header, ':') || !curlx_str_single(&header, ';'))) {
+    curlx_str_untilnl(&header, &out, MAX_HTTP_RESP_HEADER_SIZE);
+    curlx_str_trimblanks(&out);
 
-  return Curl_memdup0(start, len);
+    return Curl_memdup0(curlx_str(&out), curlx_strlen(&out));
+  }
+  /* bad input */
+  return NULL;
 }
 
 #ifndef CURL_DISABLE_HTTP_AUTH
@@ -317,7 +336,7 @@ static CURLcode http_output_basic(struct Curl_easy *data, bool proxy)
   if(!out)
     return CURLE_OUT_OF_MEMORY;
 
-  result = Curl_base64_encode(out, strlen(out), &authorization, &size);
+  result = curlx_base64_encode(out, strlen(out), &authorization, &size);
   if(result)
     goto fail;
 
@@ -458,7 +477,7 @@ static CURLcode http_perhapsrewind(struct Curl_easy *data,
 #if defined(USE_NTLM)
     if((data->state.authproxy.picked == CURLAUTH_NTLM) ||
        (data->state.authhost.picked == CURLAUTH_NTLM)) {
-      ongoing_auth = "NTML";
+      ongoing_auth = "NTLM";
       if((conn->http_ntlm_state != NTLMSTATE_NONE) ||
          (conn->proxy_ntlm_state != NTLMSTATE_NONE)) {
         /* The NTLM-negotiation has started, keep on sending.
@@ -537,7 +556,8 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data)
        (data->req.httpversion_sent > 11)) {
       infof(data, "Forcing HTTP/1.1 for NTLM");
       connclose(conn, "Force HTTP/1.1 connection");
-      data->state.httpwant = CURL_HTTP_VERSION_1_1;
+      data->state.http_neg.wanted = CURL_HTTP_V1x;
+      data->state.http_neg.allowed = CURL_HTTP_V1x;
     }
   }
 #ifndef CURL_DISABLE_PROXY
@@ -562,7 +582,7 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data)
     /* In case this is GSS auth, the newurl field is already allocated so
        we must make sure to free it before allocating a new one. As figured
        out in bug #2284386 */
-    Curl_safefree(data->req.newurl);
+    free(data->req.newurl);
     data->req.newurl = strdup(data->state.url); /* clone URL */
     if(!data->req.newurl)
       return CURLE_OUT_OF_MEMORY;
@@ -613,9 +633,10 @@ output_auth_headers(struct Curl_easy *data,
   (void)path;
 #endif
 #ifndef CURL_DISABLE_AWS
-  if(authstatus->picked == CURLAUTH_AWS_SIGV4) {
+  if((authstatus->picked == CURLAUTH_AWS_SIGV4) && !proxy) {
+    /* this method is never for proxy */
     auth = "AWS_SIGV4";
-    result = Curl_output_aws_sigv4(data, proxy);
+    result = Curl_output_aws_sigv4(data);
     if(result)
       return result;
   }
@@ -747,12 +768,12 @@ Curl_http_output_auth(struct Curl_easy *data,
 #ifndef CURL_DISABLE_PROXY
     (conn->bits.httpproxy && conn->bits.proxy_user_passwd) ||
 #endif
-     data->state.aptr.user ||
+    data->state.aptr.user ||
 #ifdef USE_SPNEGO
-     authhost->want & CURLAUTH_NEGOTIATE ||
-     authproxy->want & CURLAUTH_NEGOTIATE ||
+    authhost->want & CURLAUTH_NEGOTIATE ||
+    authproxy->want & CURLAUTH_NEGOTIATE ||
 #endif
-     data->set.str[STRING_BEARER])
+    data->set.str[STRING_BEARER])
     /* continue please */;
   else {
     authhost->done = TRUE;
@@ -837,9 +858,137 @@ Curl_http_output_auth(struct Curl_easy *data,
   !defined(CURL_DISABLE_DIGEST_AUTH) || \
   !defined(CURL_DISABLE_BASIC_AUTH) || \
   !defined(CURL_DISABLE_BEARER_AUTH)
-static int is_valid_auth_separator(char ch)
+static bool authcmp(const char *auth, const char *line)
+{
+  /* the auth string must not have an alnum following */
+  size_t n = strlen(auth);
+  return strncasecompare(auth, line, n) && !ISALNUM(line[n]);
+}
+#endif
+
+#ifdef USE_SPNEGO
+static CURLcode auth_spnego(struct Curl_easy *data,
+                            bool proxy,
+                            const char *auth,
+                            struct auth *authp,
+                            unsigned long *availp)
+{
+  if((authp->avail & CURLAUTH_NEGOTIATE) || Curl_auth_is_spnego_supported()) {
+    *availp |= CURLAUTH_NEGOTIATE;
+    authp->avail |= CURLAUTH_NEGOTIATE;
+
+    if(authp->picked == CURLAUTH_NEGOTIATE) {
+      struct connectdata *conn = data->conn;
+      CURLcode result = Curl_input_negotiate(data, conn, proxy, auth);
+      curlnegotiate *negstate = proxy ? &conn->proxy_negotiate_state :
+        &conn->http_negotiate_state;
+      if(!result) {
+        free(data->req.newurl);
+        data->req.newurl = strdup(data->state.url);
+        if(!data->req.newurl)
+          return CURLE_OUT_OF_MEMORY;
+        data->state.authproblem = FALSE;
+        /* we received a GSS auth token and we dealt with it fine */
+        *negstate = GSS_AUTHRECV;
+      }
+      else
+        data->state.authproblem = TRUE;
+    }
+  }
+  return CURLE_OK;
+}
+#endif
+
+#ifdef USE_NTLM
+static CURLcode auth_ntlm(struct Curl_easy *data,
+                          bool proxy,
+                          const char *auth,
+                          struct auth *authp,
+                          unsigned long *availp)
+{
+  /* NTLM support requires the SSL crypto libs */
+  if((authp->avail & CURLAUTH_NTLM) || Curl_auth_is_ntlm_supported()) {
+    *availp |= CURLAUTH_NTLM;
+    authp->avail |= CURLAUTH_NTLM;
+
+    if(authp->picked == CURLAUTH_NTLM) {
+      /* NTLM authentication is picked and activated */
+      CURLcode result = Curl_input_ntlm(data, proxy, auth);
+      if(!result)
+        data->state.authproblem = FALSE;
+      else {
+        infof(data, "NTLM authentication problem, ignoring.");
+        data->state.authproblem = TRUE;
+      }
+    }
+  }
+  return CURLE_OK;
+}
+#endif
+
+#ifndef CURL_DISABLE_DIGEST_AUTH
+static CURLcode auth_digest(struct Curl_easy *data,
+                            bool proxy,
+                            const char *auth,
+                            struct auth *authp,
+                            unsigned long *availp)
+{
+  if(authp->avail & CURLAUTH_DIGEST)
+    infof(data, "Ignoring duplicate digest auth header.");
+  else if(Curl_auth_is_digest_supported()) {
+    CURLcode result;
+
+    *availp |= CURLAUTH_DIGEST;
+    authp->avail |= CURLAUTH_DIGEST;
+
+    /* We call this function on input Digest headers even if Digest
+     * authentication is not activated yet, as we need to store the
+     * incoming data from this header in case we are going to use
+     * Digest */
+    result = Curl_input_digest(data, proxy, auth);
+    if(result) {
+      infof(data, "Digest authentication problem, ignoring.");
+      data->state.authproblem = TRUE;
+    }
+  }
+  return CURLE_OK;
+}
+#endif
+
+#ifndef CURL_DISABLE_BASIC_AUTH
+static CURLcode auth_basic(struct Curl_easy *data,
+                           struct auth *authp,
+                           unsigned long *availp)
+{
+  *availp |= CURLAUTH_BASIC;
+  authp->avail |= CURLAUTH_BASIC;
+  if(authp->picked == CURLAUTH_BASIC) {
+    /* We asked for Basic authentication but got a 40X back
+       anyway, which basically means our name+password is not
+       valid. */
+    authp->avail = CURLAUTH_NONE;
+    infof(data, "Basic authentication problem, ignoring.");
+    data->state.authproblem = TRUE;
+  }
+  return CURLE_OK;
+}
+#endif
+
+#ifndef CURL_DISABLE_BEARER_AUTH
+static CURLcode auth_bearer(struct Curl_easy *data,
+                            struct auth *authp,
+                            unsigned long *availp)
 {
-  return ch == '\0' || ch == ',' || ISSPACE(ch);
+  *availp |= CURLAUTH_BEARER;
+  authp->avail |= CURLAUTH_BEARER;
+  if(authp->picked == CURLAUTH_BEARER) {
+    /* We asked for Bearer authentication but got a 40X back
+       anyway, which basically means our token is not valid. */
+    authp->avail = CURLAUTH_NONE;
+    infof(data, "Bearer authentication problem, ignoring.");
+    data->state.authproblem = TRUE;
+  }
+  return CURLE_OK;
 }
 #endif
 
@@ -847,6 +996,8 @@ static int is_valid_auth_separator(char ch)
  * Curl_http_input_auth() deals with Proxy-Authenticate: and WWW-Authenticate:
  * headers. They are dealt with both in the transfer.c main loop and in the
  * proxy CONNECT loop.
+ *
+ * The 'auth' line ends with a null byte without CR or LF present.
  */
 CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
                               const char *auth) /* the first non-space */
@@ -854,11 +1005,6 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
   /*
    * This resource requires authentication
    */
-  struct connectdata *conn = data->conn;
-#ifdef USE_SPNEGO
-  curlnegotiate *negstate = proxy ? &conn->proxy_negotiate_state :
-    &conn->http_negotiate_state;
-#endif
 #if defined(USE_SPNEGO) ||                      \
   defined(USE_NTLM) ||                          \
   !defined(CURL_DISABLE_DIGEST_AUTH) ||         \
@@ -867,6 +1013,9 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
 
   unsigned long *availp;
   struct auth *authp;
+  CURLcode result = CURLE_OK;
+  DEBUGASSERT(auth);
+  DEBUGASSERT(data);
 
   if(proxy) {
     availp = &data->info.proxyauthavail;
@@ -876,11 +1025,6 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
     availp = &data->info.httpauthavail;
     authp = &data->state.authhost;
   }
-#else
-  (void) proxy;
-#endif
-
-  (void) conn; /* In case conditionals make it unused. */
 
   /*
    * Here we check if we want the specific single authentication (using ==) and
@@ -900,126 +1044,43 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
 
   while(*auth) {
 #ifdef USE_SPNEGO
-    if(checkprefix("Negotiate", auth) && is_valid_auth_separator(auth[9])) {
-      if((authp->avail & CURLAUTH_NEGOTIATE) ||
-         Curl_auth_is_spnego_supported()) {
-        *availp |= CURLAUTH_NEGOTIATE;
-        authp->avail |= CURLAUTH_NEGOTIATE;
-
-        if(authp->picked == CURLAUTH_NEGOTIATE) {
-          CURLcode result = Curl_input_negotiate(data, conn, proxy, auth);
-          if(!result) {
-            free(data->req.newurl);
-            data->req.newurl = strdup(data->state.url);
-            if(!data->req.newurl)
-              return CURLE_OUT_OF_MEMORY;
-            data->state.authproblem = FALSE;
-            /* we received a GSS auth token and we dealt with it fine */
-            *negstate = GSS_AUTHRECV;
-          }
-          else
-            data->state.authproblem = TRUE;
-        }
-      }
-    }
-    else
+    if(authcmp("Negotiate", auth))
+      result = auth_spnego(data, proxy, auth, authp, availp);
 #endif
 #ifdef USE_NTLM
-      /* NTLM support requires the SSL crypto libs */
-      if(checkprefix("NTLM", auth) && is_valid_auth_separator(auth[4])) {
-        if((authp->avail & CURLAUTH_NTLM) ||
-           Curl_auth_is_ntlm_supported()) {
-          *availp |= CURLAUTH_NTLM;
-          authp->avail |= CURLAUTH_NTLM;
-
-          if(authp->picked == CURLAUTH_NTLM) {
-            /* NTLM authentication is picked and activated */
-            CURLcode result = Curl_input_ntlm(data, proxy, auth);
-            if(!result) {
-              data->state.authproblem = FALSE;
-            }
-            else {
-              infof(data, "Authentication problem. Ignoring this.");
-              data->state.authproblem = TRUE;
-            }
-          }
-        }
-      }
-      else
+    if(!result && authcmp("NTLM", auth))
+      result = auth_ntlm(data, proxy, auth, authp, availp);
 #endif
 #ifndef CURL_DISABLE_DIGEST_AUTH
-        if(checkprefix("Digest", auth) && is_valid_auth_separator(auth[6])) {
-          if((authp->avail & CURLAUTH_DIGEST) != 0)
-            infof(data, "Ignoring duplicate digest auth header.");
-          else if(Curl_auth_is_digest_supported()) {
-            CURLcode result;
-
-            *availp |= CURLAUTH_DIGEST;
-            authp->avail |= CURLAUTH_DIGEST;
-
-            /* We call this function on input Digest headers even if Digest
-             * authentication is not activated yet, as we need to store the
-             * incoming data from this header in case we are going to use
-             * Digest */
-            result = Curl_input_digest(data, proxy, auth);
-            if(result) {
-              infof(data, "Authentication problem. Ignoring this.");
-              data->state.authproblem = TRUE;
-            }
-          }
-        }
-        else
+    if(!result && authcmp("Digest", auth))
+      result = auth_digest(data, proxy, auth, authp, availp);
 #endif
 #ifndef CURL_DISABLE_BASIC_AUTH
-          if(checkprefix("Basic", auth) &&
-             is_valid_auth_separator(auth[5])) {
-            *availp |= CURLAUTH_BASIC;
-            authp->avail |= CURLAUTH_BASIC;
-            if(authp->picked == CURLAUTH_BASIC) {
-              /* We asked for Basic authentication but got a 40X back
-                 anyway, which basically means our name+password is not
-                 valid. */
-              authp->avail = CURLAUTH_NONE;
-              infof(data, "Authentication problem. Ignoring this.");
-              data->state.authproblem = TRUE;
-            }
-          }
-          else
+    if(!result && authcmp("Basic", auth))
+      result = auth_basic(data, authp, availp);
 #endif
 #ifndef CURL_DISABLE_BEARER_AUTH
-            if(checkprefix("Bearer", auth) &&
-               is_valid_auth_separator(auth[6])) {
-              *availp |= CURLAUTH_BEARER;
-              authp->avail |= CURLAUTH_BEARER;
-              if(authp->picked == CURLAUTH_BEARER) {
-                /* We asked for Bearer authentication but got a 40X back
-                   anyway, which basically means our token is not valid. */
-                authp->avail = CURLAUTH_NONE;
-                infof(data, "Authentication problem. Ignoring this.");
-                data->state.authproblem = TRUE;
-              }
-            }
-#else
-            {
-              /*
-               * Empty block to terminate the if-else chain correctly.
-               *
-               * A semicolon would yield the same result here, but can cause a
-               * compiler warning when -Wextra is enabled.
-               */
-            }
+    if(authcmp("Bearer", auth))
+      result = auth_bearer(data, authp, availp);
 #endif
 
+    if(result)
+      break;
+
     /* there may be multiple methods on one line, so keep reading */
-    while(*auth && *auth != ',') /* read up to the next comma */
-      auth++;
-    if(*auth == ',') /* if we are on a comma, skip it */
-      auth++;
-    while(*auth && ISSPACE(*auth))
+    auth = strchr(auth, ',');
+    if(auth) /* if we are on a comma, skip it */
       auth++;
+    else
+      break;
+    curlx_str_passblanks(&auth);
   }
-
+  return result;
+#else
+  (void) proxy;
+  /* nothing to do when disabled */
   return CURLE_OK;
+#endif
 }
 
 /**
@@ -1093,6 +1154,21 @@ static bool http_should_fail(struct Curl_easy *data, int httpcode)
   return data->state.authproblem;
 }
 
+static void http_switch_to_get(struct Curl_easy *data, int code)
+{
+  const char *req = data->set.str[STRING_CUSTOMREQUEST];
+  if((req || data->state.httpreq != HTTPREQ_GET) &&
+     (data->set.http_follow_mode == CURLFOLLOW_OBEYCODE)) {
+    infof(data, "Switch to GET because of %d response", code);
+    data->state.http_ignorecustom = TRUE;
+  }
+  else if(req && (data->set.http_follow_mode != CURLFOLLOW_FIRSTONLY))
+    infof(data, "Stick to %s instead of GET", req);
+
+  data->state.httpreq = HTTPREQ_GET;
+  Curl_creader_set_rewind(data, FALSE);
+}
+
 CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl,
                           followtype type)
 {
@@ -1100,6 +1176,8 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl,
   bool reachedmax = FALSE;
   char *follow_url = NULL;
   CURLUcode uc;
+  CURLcode rewind_result;
+  bool switch_to_get = FALSE;
 
   DEBUGASSERT(type != FOLLOW_NONE);
 
@@ -1257,8 +1335,14 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl,
 
   data->state.url = follow_url;
   data->state.url_alloc = TRUE;
-  Curl_req_soft_reset(&data->req, data);
+  rewind_result = Curl_req_soft_reset(&data->req, data);
   infof(data, "Issue another request to this URL: '%s'", data->state.url);
+  if((data->set.http_follow_mode == CURLFOLLOW_FIRSTONLY) &&
+     data->set.str[STRING_CUSTOMREQUEST] &&
+     !data->state.http_ignorecustom) {
+    data->state.http_ignorecustom = TRUE;
+    infof(data, "Drop custom request method for next request");
+  }
 
   /*
    * We get here when the HTTP code is 300-399 (and 401). We need to perform
@@ -1301,9 +1385,8 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl,
         || data->state.httpreq == HTTPREQ_POST_FORM
         || data->state.httpreq == HTTPREQ_POST_MIME)
        && !(data->set.keep_post & CURL_REDIR_POST_301)) {
-      infof(data, "Switch from POST to GET");
-      data->state.httpreq = HTTPREQ_GET;
-      Curl_creader_set_rewind(data, FALSE);
+      http_switch_to_get(data, 301);
+      switch_to_get = TRUE;
     }
     break;
   case 302: /* Found */
@@ -1327,9 +1410,8 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl,
         || data->state.httpreq == HTTPREQ_POST_FORM
         || data->state.httpreq == HTTPREQ_POST_MIME)
        && !(data->set.keep_post & CURL_REDIR_POST_302)) {
-      infof(data, "Switch from POST to GET");
-      data->state.httpreq = HTTPREQ_GET;
-      Curl_creader_set_rewind(data, FALSE);
+      http_switch_to_get(data, 302);
+      switch_to_get = TRUE;
     }
     break;
 
@@ -1344,9 +1426,8 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl,
          data->state.httpreq != HTTPREQ_POST_FORM &&
          data->state.httpreq != HTTPREQ_POST_MIME) ||
         !(data->set.keep_post & CURL_REDIR_POST_303))) {
-      data->state.httpreq = HTTPREQ_GET;
-      infof(data, "Switch to %s",
-            data->req.no_body ? "HEAD" : "GET");
+      http_switch_to_get(data, 303);
+      switch_to_get = TRUE;
     }
     break;
   case 304: /* Not Modified */
@@ -1364,6 +1445,12 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl,
      */
     break;
   }
+
+  /* When rewind of upload data failed and we are not switching to GET,
+   * we need to fail the follow, as we cannot send the data again. */
+  if(rewind_result && !switch_to_get)
+    return rewind_result;
+
   Curl_pgrsTime(data, TIMER_REDIRECT);
   Curl_pgrsResetTransferSizes(data);
 
@@ -1388,9 +1475,8 @@ Curl_compareheader(const char *headerline, /* line to check */
    * The field value MAY be preceded by any amount of LWS, though a single SP
    * is preferred." */
 
-  size_t len;
-  const char *start;
-  const char *end;
+  const char *p;
+  struct Curl_str val;
   DEBUGASSERT(hlen);
   DEBUGASSERT(clen);
   DEBUGASSERT(header);
@@ -1400,31 +1486,21 @@ Curl_compareheader(const char *headerline, /* line to check */
     return FALSE; /* does not start with header */
 
   /* pass the header */
-  start = &headerline[hlen];
-
-  /* pass all whitespace */
-  while(*start && ISSPACE(*start))
-    start++;
+  p = &headerline[hlen];
 
-  /* find the end of the header line */
-  end = strchr(start, '\r'); /* lines end with CRLF */
-  if(!end) {
-    /* in case there is a non-standard compliant line here */
-    end = strchr(start, '\n');
-
-    if(!end)
-      /* hm, there is no line ending here, use the zero byte! */
-      end = strchr(start, '\0');
-  }
-
-  len = end-start; /* length of the content part of the input line */
+  if(curlx_str_untilnl(&p, &val, MAX_HTTP_RESP_HEADER_SIZE))
+    return FALSE;
+  curlx_str_trimblanks(&val);
 
   /* find the content string in the rest of the line */
-  for(; len >= clen; len--, start++) {
-    if(strncasecompare(start, content, clen))
-      return TRUE; /* match! */
+  if(curlx_strlen(&val) >= clen) {
+    size_t len;
+    p = curlx_str(&val);
+    for(len = curlx_strlen(&val); len >= curlx_strlen(&val); len--, p++) {
+      if(strncasecompare(p, content, clen))
+        return TRUE; /* match! */
+    }
   }
-
   return FALSE; /* no match */
 }
 
@@ -1471,7 +1547,7 @@ CURLcode Curl_http_done(struct Curl_easy *data,
   data->state.authhost.multipass = FALSE;
   data->state.authproxy.multipass = FALSE;
 
-  Curl_dyn_reset(&data->state.headerb);
+  curlx_dyn_reset(&data->state.headerb);
 
   if(status)
     return status;
@@ -1501,29 +1577,28 @@ static bool http_may_use_1_1(const struct Curl_easy *data)
   const struct connectdata *conn = data->conn;
   /* We have seen a previous response for *this* transfer with 1.0,
    * on another connection or the same one. */
-  if(data->state.httpversion == 10)
+  if(data->state.http_neg.rcvd_min == 10)
     return FALSE;
   /* We have seen a previous response on *this* connection with 1.0. */
-  if(conn->httpversion_seen == 10)
+  if(conn && conn->httpversion_seen == 10)
     return FALSE;
   /* We want 1.0 and have seen no previous response on *this* connection
      with a higher version (maybe no response at all yet). */
-  if((data->state.httpwant == CURL_HTTP_VERSION_1_0) &&
-     (conn->httpversion_seen <= 10))
+  if((data->state.http_neg.only_10) &&
+     (!conn || conn->httpversion_seen <= 10))
     return FALSE;
-  /* We want something newer than 1.0 or have no preferences. */
-  return (data->state.httpwant == CURL_HTTP_VERSION_NONE) ||
-         (data->state.httpwant >= CURL_HTTP_VERSION_1_1);
+  /* We are not restricted to use 1.0 only. */
+  return !data->state.http_neg.only_10;
 }
 
 static unsigned char http_request_version(struct Curl_easy *data)
 {
-  unsigned char httpversion = Curl_conn_http_version(data);
-  if(!httpversion) {
+  unsigned char v = Curl_conn_http_version(data, data->conn);
+  if(!v) {
     /* No specific HTTP connection filter installed. */
-    httpversion = http_may_use_1_1(data) ? 11 : 10;
+    v = http_may_use_1_1(data) ? 11 : 10;
   }
-  return httpversion;
+  return v;
 }
 
 static const char *get_http_string(int httpversion)
@@ -1544,7 +1619,6 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data,
                                  bool is_connect, int httpversion,
                                  struct dynbuf *req)
 {
-  char *ptr;
   struct curl_slist *h[2];
   struct curl_slist *headers;
   int numlists = 1; /* by default */
@@ -1584,98 +1658,81 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data,
 
   /* loop through one or two lists */
   for(i = 0; i < numlists; i++) {
-    headers = h[i];
-
-    while(headers) {
-      char *semicolonp = NULL;
-      ptr = strchr(headers->data, ':');
-      if(!ptr) {
-        char *optr;
-        /* no colon, semicolon? */
-        ptr = strchr(headers->data, ';');
-        if(ptr) {
-          optr = ptr;
-          ptr++; /* pass the semicolon */
-          while(*ptr && ISSPACE(*ptr))
-            ptr++;
-
-          if(*ptr) {
-            /* this may be used for something else in the future */
-            optr = NULL;
-          }
-          else {
-            if(*(--ptr) == ';') {
-              /* copy the source */
-              semicolonp = strdup(headers->data);
-              if(!semicolonp) {
-                Curl_dyn_free(req);
-                return CURLE_OUT_OF_MEMORY;
-              }
-              /* put a colon where the semicolon is */
-              semicolonp[ptr - headers->data] = ':';
-              /* point at the colon */
-              optr = &semicolonp [ptr - headers->data];
-            }
-          }
-          ptr = optr;
-        }
-      }
-      if(ptr && (ptr != headers->data)) {
-        /* we require a colon for this to be a true header */
-
-        ptr++; /* pass the colon */
-        while(*ptr && ISSPACE(*ptr))
-          ptr++;
-
-        if(*ptr || semicolonp) {
-          /* only send this if the contents was non-blank or done special */
-          CURLcode result = CURLE_OK;
-          char *compare = semicolonp ? semicolonp : headers->data;
-
-          if(data->state.aptr.host &&
-             /* a Host: header was sent already, do not pass on any custom
-                Host: header as that will produce *two* in the same
-                request! */
-             checkprefix("Host:", compare))
-            ;
-          else if(data->state.httpreq == HTTPREQ_POST_FORM &&
-                  /* this header (extended by formdata.c) is sent later */
-                  checkprefix("Content-Type:", compare))
-            ;
-          else if(data->state.httpreq == HTTPREQ_POST_MIME &&
-                  /* this header is sent later */
-                  checkprefix("Content-Type:", compare))
-            ;
-          else if(data->req.authneg &&
-                  /* while doing auth neg, do not allow the custom length since
-                     we will force length zero then */
-                  checkprefix("Content-Length:", compare))
-            ;
-          else if(data->state.aptr.te &&
-                  /* when asking for Transfer-Encoding, do not pass on a custom
-                     Connection: */
-                  checkprefix("Connection:", compare))
-            ;
-          else if((httpversion >= 20) &&
-                  checkprefix("Transfer-Encoding:", compare))
-            /* HTTP/2 does not support chunked requests */
-            ;
-          else if((checkprefix("Authorization:", compare) ||
-                   checkprefix("Cookie:", compare)) &&
-                  /* be careful of sending this potentially sensitive header to
-                     other hosts */
-                  !Curl_auth_allowed_to_host(data))
-            ;
-          else {
-            result = Curl_dyn_addf(req, "%s\r\n", compare);
-          }
-          if(semicolonp)
-            free(semicolonp);
-          if(result)
-            return result;
+    for(headers = h[i]; headers; headers = headers->next) {
+      CURLcode result = CURLE_OK;
+      bool blankheader = FALSE;
+      struct Curl_str name;
+      const char *p = headers->data;
+      const char *origp = p;
+
+      /* explicitly asked to send header without content is done by a header
+         that ends with a semicolon, but there must be no colon present in the
+         name */
+      if(!curlx_str_until(&p, &name, MAX_HTTP_RESP_HEADER_SIZE, ';') &&
+         !curlx_str_single(&p, ';') &&
+         !curlx_str_single(&p, '\0') &&
+         !memchr(curlx_str(&name), ':', curlx_strlen(&name)))
+        blankheader = TRUE;
+      else {
+        p = origp;
+        if(!curlx_str_until(&p, &name, MAX_HTTP_RESP_HEADER_SIZE, ':') &&
+           !curlx_str_single(&p, ':')) {
+          struct Curl_str val;
+          curlx_str_untilnl(&p, &val, MAX_HTTP_RESP_HEADER_SIZE);
+          curlx_str_trimblanks(&val);
+          if(!curlx_strlen(&val))
+            /* no content, don't send this */
+            continue;
         }
+        else
+          /* no colon */
+          continue;
       }
-      headers = headers->next;
+
+      /* only send this if the contents was non-blank or done special */
+
+      if(data->state.aptr.host &&
+         /* a Host: header was sent already, do not pass on any custom
+            Host: header as that will produce *two* in the same
+            request! */
+         curlx_str_casecompare(&name, "Host"))
+        ;
+      else if(data->state.httpreq == HTTPREQ_POST_FORM &&
+              /* this header (extended by formdata.c) is sent later */
+              curlx_str_casecompare(&name, "Content-Type"))
+        ;
+      else if(data->state.httpreq == HTTPREQ_POST_MIME &&
+              /* this header is sent later */
+              curlx_str_casecompare(&name, "Content-Type"))
+        ;
+      else if(data->req.authneg &&
+              /* while doing auth neg, do not allow the custom length since
+                 we will force length zero then */
+              curlx_str_casecompare(&name, "Content-Length"))
+        ;
+      else if(data->state.aptr.te &&
+              /* when asking for Transfer-Encoding, do not pass on a custom
+                 Connection: */
+              curlx_str_casecompare(&name, "Connection"))
+        ;
+      else if((httpversion >= 20) &&
+              curlx_str_casecompare(&name, "Transfer-Encoding"))
+        /* HTTP/2 does not support chunked requests */
+        ;
+      else if((curlx_str_casecompare(&name, "Authorization") ||
+               curlx_str_casecompare(&name, "Cookie")) &&
+              /* be careful of sending this potentially sensitive header to
+                 other hosts */
+              !Curl_auth_allowed_to_host(data))
+        ;
+      else if(blankheader)
+        result = curlx_dyn_addf(req, "%.*s:\r\n", (int)curlx_strlen(&name),
+                                curlx_str(&name));
+      else
+        result = curlx_dyn_addf(req, "%s\r\n", origp);
+
+      if(result)
+        return result;
     }
   }
 
@@ -1747,7 +1804,7 @@ CURLcode Curl_add_timecondition(struct Curl_easy *data,
             tm->tm_min,
             tm->tm_sec);
 
-  result = Curl_dyn_add(req, datestr);
+  result = curlx_dyn_add(req, datestr);
   return result;
 }
 #else
@@ -1771,8 +1828,10 @@ void Curl_http_method(struct Curl_easy *data, struct connectdata *conn,
     httpreq = HTTPREQ_PUT;
 
   /* Now set the 'request' pointer to the proper request string */
-  if(data->set.str[STRING_CUSTOMREQUEST])
+  if(data->set.str[STRING_CUSTOMREQUEST] &&
+     !data->state.http_ignorecustom) {
     request = data->set.str[STRING_CUSTOMREQUEST];
+  }
   else {
     if(data->req.no_body)
       request = "HEAD";
@@ -1865,7 +1924,7 @@ static CURLcode http_host(struct Curl_easy *data, struct connectdata *conn)
         if(colon)
           *colon = 0; /* The host must not include an embedded port number */
       }
-      Curl_safefree(aptr->cookiehost);
+      free(aptr->cookiehost);
       aptr->cookiehost = cookiehost;
     }
 #endif
@@ -1969,7 +2028,7 @@ static CURLcode http_target(struct Curl_easy *data,
     curl_url_cleanup(h);
 
     /* target or URL */
-    result = Curl_dyn_add(r, data->set.str[STRING_TARGET] ?
+    result = curlx_dyn_add(r, data->set.str[STRING_TARGET] ?
       data->set.str[STRING_TARGET] : url);
     free(url);
     if(result)
@@ -1990,8 +2049,8 @@ static CURLcode http_target(struct Curl_easy *data,
           }
         }
         if(!type) {
-          result = Curl_dyn_addf(r, ";type=%c",
-                                 data->state.prefer_ascii ? 'a' : 'i');
+          result = curlx_dyn_addf(r, ";type=%c",
+                                  data->state.prefer_ascii ? 'a' : 'i');
           if(result)
             return result;
         }
@@ -2004,11 +2063,11 @@ static CURLcode http_target(struct Curl_easy *data,
     (void)conn; /* not used in disabled-proxy builds */
 #endif
   {
-    result = Curl_dyn_add(r, path);
+    result = curlx_dyn_add(r, path);
     if(result)
       return result;
     if(query)
-      result = Curl_dyn_addf(r, "?%s", query);
+      result = curlx_dyn_addf(r, "?%s", query);
   }
 
   return result;
@@ -2266,7 +2325,7 @@ static CURLcode addexpect(struct Curl_easy *data, struct dynbuf *r,
        operations (as there is one packet coming back from the web server) */
     curl_off_t client_len = Curl_creader_client_length(data);
     if(client_len > EXPECT_100_THRESHOLD || client_len < 0) {
-      result = Curl_dyn_addn(r, STRCONST("Expect: 100-continue\r\n"));
+      result = curlx_dyn_addn(r, STRCONST("Expect: 100-continue\r\n"));
       if(result)
         return result;
       *announced_exp100 = TRUE;
@@ -2310,8 +2369,8 @@ static CURLcode http_req_complete(struct Curl_easy *data,
         !Curl_checkheaders(data, STRCONST("Content-Length")))) {
       /* we allow replacing this header if not during auth negotiation,
          although it is not very wise to actually set your own */
-      result = Curl_dyn_addf(r, "Content-Length: %" FMT_OFF_T "\r\n",
-                             req_clen);
+      result = curlx_dyn_addf(r, "Content-Length: %" FMT_OFF_T "\r\n",
+                              req_clen);
     }
     if(result)
       goto out;
@@ -2323,7 +2382,7 @@ static CURLcode http_req_complete(struct Curl_easy *data,
       struct curl_slist *hdr;
 
       for(hdr = data->state.mimepost->curlheaders; hdr; hdr = hdr->next) {
-        result = Curl_dyn_addf(r, "%s\r\n", hdr->data);
+        result = curlx_dyn_addf(r, "%s\r\n", hdr->data);
         if(result)
           goto out;
       }
@@ -2331,8 +2390,8 @@ static CURLcode http_req_complete(struct Curl_easy *data,
 #endif
     if(httpreq == HTTPREQ_POST) {
       if(!Curl_checkheaders(data, STRCONST("Content-Type"))) {
-        result = Curl_dyn_addn(r, STRCONST("Content-Type: application/"
-                                           "x-www-form-urlencoded\r\n"));
+        result = curlx_dyn_addn(r, STRCONST("Content-Type: application/"
+                                            "x-www-form-urlencoded\r\n"));
         if(result)
           goto out;
       }
@@ -2346,7 +2405,7 @@ static CURLcode http_req_complete(struct Curl_easy *data,
   }
 
   /* end of headers */
-  result = Curl_dyn_addn(r, STRCONST("\r\n"));
+  result = curlx_dyn_addn(r, STRCONST("\r\n"));
   if(!result) {
     Curl_pgrsSetUploadSize(data, req_clen);
     if(announced_exp100)
@@ -2402,7 +2461,7 @@ static CURLcode http_cookies(struct Curl_easy *data,
         if(co->value) {
           size_t add;
           if(!count) {
-            result = Curl_dyn_addn(r, STRCONST("Cookie: "));
+            result = curlx_dyn_addn(r, STRCONST("Cookie: "));
             if(result)
               break;
           }
@@ -2413,8 +2472,8 @@ static CURLcode http_cookies(struct Curl_easy *data,
             linecap = TRUE;
             break;
           }
-          result = Curl_dyn_addf(r, "%s%s=%s", count ? "; " : "",
-                                 co->name, co->value);
+          result = curlx_dyn_addf(r, "%s%s=%s", count ? "; " : "",
+                                  co->name, co->value);
           if(result)
             break;
           clen += add + (count ? 2 : 0);
@@ -2425,14 +2484,14 @@ static CURLcode http_cookies(struct Curl_easy *data,
     }
     if(addcookies && !result && !linecap) {
       if(!count)
-        result = Curl_dyn_addn(r, STRCONST("Cookie: "));
+        result = curlx_dyn_addn(r, STRCONST("Cookie: "));
       if(!result) {
-        result = Curl_dyn_addf(r, "%s%s", count ? "; " : "", addcookies);
+        result = curlx_dyn_addf(r, "%s%s", count ? "; " : "", addcookies);
         count++;
       }
     }
     if(count && !result)
-      result = Curl_dyn_addn(r, STRCONST("\r\n"));
+      result = curlx_dyn_addn(r, STRCONST("\r\n"));
 
     if(result)
       return result;
@@ -2620,11 +2679,11 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
 
   switch(conn->alpn) {
   case CURL_HTTP_VERSION_3:
-    DEBUGASSERT(Curl_conn_http_version(data) == 30);
+    DEBUGASSERT(Curl_conn_http_version(data, conn) == 30);
     break;
   case CURL_HTTP_VERSION_2:
 #ifndef CURL_DISABLE_PROXY
-    if((Curl_conn_http_version(data) != 20) &&
+    if((Curl_conn_http_version(data, conn) != 20) &&
        conn->bits.proxy && !conn->bits.tunnel_proxy
       ) {
       result = Curl_http2_switch(data);
@@ -2633,7 +2692,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
     }
     else
 #endif
-      DEBUGASSERT(Curl_conn_http_version(data) == 20);
+      DEBUGASSERT(Curl_conn_http_version(data, conn) == 20);
     break;
   case CURL_HTTP_VERSION_1_1:
     /* continue with HTTP/1.x when explicitly requested */
@@ -2690,7 +2749,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
 
   if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) &&
      data->set.str[STRING_ENCODING]) {
-    Curl_safefree(data->state.aptr.accept_encoding);
+    free(data->state.aptr.accept_encoding);
     data->state.aptr.accept_encoding =
       aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]);
     if(!data->state.aptr.accept_encoding)
@@ -2721,19 +2780,19 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
     goto fail;
 
   /* initialize a dynamic send-buffer */
-  Curl_dyn_init(&req, DYN_HTTP_REQUEST);
+  curlx_dyn_init(&req, DYN_HTTP_REQUEST);
 
   /* make sure the header buffer is reset - if there are leftovers from a
      previous transfer */
-  Curl_dyn_reset(&data->state.headerb);
+  curlx_dyn_reset(&data->state.headerb);
 
   /* add the main request stuff */
   /* GET/HEAD/POST/PUT */
-  result = Curl_dyn_addf(&req, "%s ", request);
+  result = curlx_dyn_addf(&req, "%s ", request);
   if(!result)
     result = http_target(data, conn, &req);
   if(result) {
-    Curl_dyn_free(&req);
+    curlx_dyn_free(&req);
     goto fail;
   }
 
@@ -2742,62 +2801,62 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
     altused = aprintf("Alt-Used: %s:%d\r\n",
                       conn->conn_to_host.name, conn->conn_to_port);
     if(!altused) {
-      Curl_dyn_free(&req);
+      curlx_dyn_free(&req);
       return CURLE_OUT_OF_MEMORY;
     }
   }
 #endif
   result =
-    Curl_dyn_addf(&req,
-                  " HTTP/%s\r\n" /* HTTP version */
-                  "%s" /* host */
-                  "%s" /* proxyuserpwd */
-                  "%s" /* userpwd */
-                  "%s" /* range */
-                  "%s" /* user agent */
-                  "%s" /* accept */
-                  "%s" /* TE: */
-                  "%s" /* accept-encoding */
-                  "%s" /* referer */
-                  "%s" /* Proxy-Connection */
-                  "%s" /* transfer-encoding */
-                  "%s",/* Alt-Used */
-
-                  httpstring,
-                  (data->state.aptr.host ? data->state.aptr.host : ""),
+    curlx_dyn_addf(&req,
+                   " HTTP/%s\r\n" /* HTTP version */
+                   "%s" /* host */
+                   "%s" /* proxyuserpwd */
+                   "%s" /* userpwd */
+                   "%s" /* range */
+                   "%s" /* user agent */
+                   "%s" /* accept */
+                   "%s" /* TE: */
+                   "%s" /* accept-encoding */
+                   "%s" /* referer */
+                   "%s" /* Proxy-Connection */
+                   "%s" /* transfer-encoding */
+                   "%s",/* Alt-Used */
+
+                   httpstring,
+                   (data->state.aptr.host ? data->state.aptr.host : ""),
 #ifndef CURL_DISABLE_PROXY
-                  data->state.aptr.proxyuserpwd ?
-                  data->state.aptr.proxyuserpwd : "",
+                   data->state.aptr.proxyuserpwd ?
+                   data->state.aptr.proxyuserpwd : "",
 #else
-                  "",
+                   "",
 #endif
-                  data->state.aptr.userpwd ? data->state.aptr.userpwd : "",
-                  (data->state.use_range && data->state.aptr.rangeline) ?
-                  data->state.aptr.rangeline : "",
-                  (data->set.str[STRING_USERAGENT] &&
-                   *data->set.str[STRING_USERAGENT] &&
-                   data->state.aptr.uagent) ?
-                  data->state.aptr.uagent : "",
-                  p_accept ? p_accept : "",
-                  data->state.aptr.te ? data->state.aptr.te : "",
-                  (data->set.str[STRING_ENCODING] &&
-                   *data->set.str[STRING_ENCODING] &&
-                   data->state.aptr.accept_encoding) ?
-                  data->state.aptr.accept_encoding : "",
-                  (data->state.referer && data->state.aptr.ref) ?
-                  data->state.aptr.ref : "" /* Referer: <data> */,
+                   data->state.aptr.userpwd ? data->state.aptr.userpwd : "",
+                   (data->state.use_range && data->state.aptr.rangeline) ?
+                   data->state.aptr.rangeline : "",
+                   (data->set.str[STRING_USERAGENT] &&
+                    *data->set.str[STRING_USERAGENT] &&
+                    data->state.aptr.uagent) ?
+                   data->state.aptr.uagent : "",
+                   p_accept ? p_accept : "",
+                   data->state.aptr.te ? data->state.aptr.te : "",
+                   (data->set.str[STRING_ENCODING] &&
+                    *data->set.str[STRING_ENCODING] &&
+                    data->state.aptr.accept_encoding) ?
+                   data->state.aptr.accept_encoding : "",
+                   (data->state.referer && data->state.aptr.ref) ?
+                   data->state.aptr.ref : "" /* Referer: <data> */,
 #ifndef CURL_DISABLE_PROXY
-                  (conn->bits.httpproxy &&
-                   !conn->bits.tunnel_proxy &&
-                   !Curl_checkheaders(data, STRCONST("Proxy-Connection")) &&
-                   !Curl_checkProxyheaders(data, conn,
-                                           STRCONST("Proxy-Connection"))) ?
-                  "Proxy-Connection: Keep-Alive\r\n":"",
+                   (conn->bits.httpproxy &&
+                    !conn->bits.tunnel_proxy &&
+                    !Curl_checkheaders(data, STRCONST("Proxy-Connection")) &&
+                    !Curl_checkProxyheaders(data, conn,
+                                            STRCONST("Proxy-Connection"))) ?
+                   "Proxy-Connection: Keep-Alive\r\n":"",
 #else
-                  "",
+                   "",
 #endif
-                  te,
-                  altused ? altused : ""
+                   te,
+                   altused ? altused : ""
       );
 
   /* clear userpwd and proxyuserpwd to avoid reusing old credentials
@@ -2809,17 +2868,18 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
   free(altused);
 
   if(result) {
-    Curl_dyn_free(&req);
+    curlx_dyn_free(&req);
     goto fail;
   }
 
   if(!Curl_conn_is_ssl(conn, FIRSTSOCKET) && (httpversion < 20) &&
-     (data->state.httpwant == CURL_HTTP_VERSION_2)) {
+     (data->state.http_neg.wanted & CURL_HTTP_V2x) &&
+     data->state.http_neg.h2_upgrade) {
     /* append HTTP2 upgrade magic stuff to the HTTP request if it is not done
        over SSL */
     result = Curl_http2_request_upgrade(&req, data);
     if(result) {
-      Curl_dyn_free(&req);
+      curlx_dyn_free(&req);
       return result;
     }
   }
@@ -2840,7 +2900,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
     if(!result)
       result = Curl_req_send(data, &req, httpversion);
   }
-  Curl_dyn_free(&req);
+  curlx_dyn_free(&req);
   if(result)
     goto fail;
 
@@ -2980,13 +3040,13 @@ static CURLcode http_header(struct Curl_easy *data,
       HD_VAL(hd, hdlen, "Content-Length:") : NULL;
     if(v) {
       curl_off_t contentlength;
-      CURLofft offt = curlx_strtoofft(v, NULL, 10, &contentlength);
+      int offt = curlx_str_numblanks(&v, &contentlength);
 
-      if(offt == CURL_OFFT_OK) {
+      if(offt == STRE_OK) {
         k->size = contentlength;
         k->maxdownload = k->size;
       }
-      else if(offt == CURL_OFFT_FLOW) {
+      else if(offt == STRE_OVERFLOW) {
         /* out of range */
         if(data->set.max_filesize) {
           failf(data, "Maximum file size exceeded");
@@ -3024,7 +3084,7 @@ static CURLcode http_header(struct Curl_easy *data,
         /* ignore empty data */
         free(contenttype);
       else {
-        Curl_safefree(data->info.contenttype);
+        free(data->info.contenttype);
         data->info.contenttype = contenttype;
       }
       return CURLE_OK;
@@ -3072,11 +3132,10 @@ static CURLcode http_header(struct Curl_easy *data,
 
       /* if it truly stopped on a digit */
       if(ISDIGIT(*ptr)) {
-        if(!curlx_strtoofft(ptr, NULL, 10, &k->offset)) {
-          if(data->state.resume_from == k->offset)
-            /* we asked for a resume and we got it */
-            k->content_range = TRUE;
-        }
+        if(!curlx_str_number(&ptr, &k->offset, CURL_OFF_T_MAX) &&
+           (data->state.resume_from == k->offset))
+          /* we asked for a resume and we got it */
+          k->content_range = TRUE;
       }
       else if(k->httpcode < 300)
         data->state.resume_from = 0; /* get everything */
@@ -3106,7 +3165,7 @@ static CURLcode http_header(struct Curl_easy *data,
       else {
         data->req.location = location;
 
-        if(data->set.http_follow_location) {
+        if(data->set.http_follow_mode) {
           DEBUGASSERT(!data->req.newurl);
           data->req.newurl = strdup(data->req.location); /* clone */
           if(!data->req.newurl)
@@ -3183,18 +3242,22 @@ static CURLcode http_header(struct Curl_easy *data,
     if(v) {
       /* Retry-After = HTTP-date / delay-seconds */
       curl_off_t retry_after = 0; /* zero for unknown or "now" */
-      /* Try it as a decimal number, if it works it is not a date */
-      (void)curlx_strtoofft(v, NULL, 10, &retry_after);
-      if(!retry_after) {
-        time_t date = Curl_getdate_capped(v);
+      time_t date;
+      curlx_str_passblanks(&v);
+
+      /* try it as a date first, because a date can otherwise start with and
+         get treated as a number */
+      date = Curl_getdate_capped(v);
+
+      if((time_t)-1 != date) {
         time_t current = time(NULL);
-        if((time_t)-1 != date && date > current) {
+        if(date >= current)
           /* convert date to number of seconds into the future */
           retry_after = date - current;
-        }
       }
-      if(retry_after < 0)
-        retry_after = 0;
+      else
+        /* Try it as a decimal number */
+        curlx_str_number(&v, &retry_after, CURL_OFF_T_MAX);
       /* limit to 6 hours max. this is not documented so that it can be changed
          in the future if necessary. */
       if(retry_after > 21600)
@@ -3346,9 +3409,10 @@ static CURLcode http_statusline(struct Curl_easy *data,
   data->info.httpversion = k->httpversion;
   conn->httpversion_seen = (unsigned char)k->httpversion;
 
-  if(!data->state.httpversion || data->state.httpversion > k->httpversion)
+  if(!data->state.http_neg.rcvd_min ||
+     data->state.http_neg.rcvd_min > k->httpversion)
     /* store the lowest server version we encounter */
-    data->state.httpversion = (unsigned char)k->httpversion;
+    data->state.http_neg.rcvd_min = (unsigned char)k->httpversion;
 
   /*
    * This code executes as part of processing the header. As a
@@ -3491,7 +3555,7 @@ static CURLcode http_write_header(struct Curl_easy *data,
 
   /* now, only output this if the header AND body are requested:
    */
-  Curl_debug(data, CURLINFO_HEADER_IN, (char *)hd, hdlen);
+  Curl_debug(data, CURLINFO_HEADER_IN, hd, hdlen);
 
   writetype = CLIENTWRITE_HEADER |
     ((data->req.httpcode/100 == 1) ? CLIENTWRITE_1XX : 0);
@@ -3574,7 +3638,7 @@ static CURLcode http_on_response(struct Curl_easy *data,
         /* We expect more response from HTTP/2 later */
         k->header = TRUE;
         k->headerline = 0; /* restart the header line counter */
-        k->httpversion_sent = 20; /* It's a HTTP/2 request now */
+        k->httpversion_sent = 20; /* It's an HTTP/2 request now */
         /* Any remaining `buf` bytes are already HTTP/2 and passed to
          * be processed. */
         result = Curl_http2_upgrade(data, conn, FIRSTSOCKET, buf, blen);
@@ -3808,8 +3872,8 @@ static CURLcode http_rw_hd(struct Curl_easy *data,
     struct dynbuf last_header;
     size_t consumed;
 
-    Curl_dyn_init(&last_header, hdlen + 1);
-    result = Curl_dyn_addn(&last_header, hd, hdlen);
+    curlx_dyn_init(&last_header, hdlen + 1);
+    result = curlx_dyn_addn(&last_header, hd, hdlen);
     if(result)
       return result;
 
@@ -3817,12 +3881,12 @@ static CURLcode http_rw_hd(struct Curl_easy *data,
     /* Caveat: we clear anything in the header brigade, because a
      * response might switch HTTP version which may call use recursively.
      * Not nice, but that is currently the way of things. */
-    Curl_dyn_reset(&data->state.headerb);
-    result = http_on_response(data, Curl_dyn_ptr(&last_header),
-                              Curl_dyn_len(&last_header),
+    curlx_dyn_reset(&data->state.headerb);
+    result = http_on_response(data, curlx_dyn_ptr(&last_header),
+                              curlx_dyn_len(&last_header),
                               buf_remain, blen, &consumed);
     *pconsumed += consumed;
-    Curl_dyn_free(&last_header);
+    curlx_dyn_free(&last_header);
     return result;
   }
 
@@ -3847,8 +3911,7 @@ static CURLcode http_rw_hd(struct Curl_easy *data,
        */
       const char *p = hd;
 
-      while(*p && ISBLANK(*p))
-        p++;
+      curlx_str_passblanks(&p);
       if(!strncmp(p, "HTTP/", 5)) {
         p += 5;
         switch(*p) {
@@ -3861,9 +3924,9 @@ static CURLcode http_rw_hd(struct Curl_easy *data,
               if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) {
                 k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 +
                   (p[2] - '0');
-                p += 3;
-                if(ISSPACE(*p))
-                  fine_statusline = TRUE;
+                /* RFC 9112 requires a single space following the status code,
+                   but the browsers don't so let's not insist */
+                fine_statusline = TRUE;
               }
             }
           }
@@ -3882,7 +3945,7 @@ static CURLcode http_rw_hd(struct Curl_easy *data,
             k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 +
               (p[2] - '0');
             p += 3;
-            if(!ISSPACE(*p))
+            if(!ISBLANK(*p))
               break;
             fine_statusline = TRUE;
           }
@@ -3907,30 +3970,22 @@ static CURLcode http_rw_hd(struct Curl_easy *data,
     }
     else if(data->conn->handler->protocol & CURLPROTO_RTSP) {
       const char *p = hd;
-      while(*p && ISBLANK(*p))
-        p++;
-      if(!strncmp(p, "RTSP/", 5)) {
-        p += 5;
-        if(ISDIGIT(*p)) {
-          p++;
-          if((p[0] == '.') && ISDIGIT(p[1])) {
-            if(ISBLANK(p[2])) {
-              p += 3;
-              if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) {
-                k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 +
-                  (p[2] - '0');
-                p += 3;
-                if(ISSPACE(*p)) {
-                  fine_statusline = TRUE;
-                  k->httpversion = 11; /* RTSP acts like HTTP 1.1 */
-                }
-              }
-            }
-          }
+      struct Curl_str ver;
+      curl_off_t status;
+      /* we set the max string a little excessive to forgive some leading
+         spaces */
+      if(!curlx_str_until(&p, &ver, 32, ' ') &&
+         !curlx_str_single(&p, ' ') &&
+         !curlx_str_number(&p, &status, 999)) {
+        curlx_str_trimblanks(&ver);
+        if(curlx_str_cmp(&ver, "RTSP/1.0")) {
+          k->httpcode = (int)status;
+          fine_statusline = TRUE;
+          k->httpversion = 11; /* RTSP acts like HTTP 1.1 */
         }
-        if(!fine_statusline)
-          return CURLE_WEIRD_SERVER_REPLY;
       }
+      if(!fine_statusline)
+        return CURLE_WEIRD_SERVER_REPLY;
     }
 
     if(fine_statusline) {
@@ -3956,7 +4011,7 @@ static CURLcode http_rw_hd(struct Curl_easy *data,
   /*
    * Taken in one (more) header. Write it to the client.
    */
-  Curl_debug(data, CURLINFO_HEADER_IN, (char *)hd, hdlen);
+  Curl_debug(data, CURLINFO_HEADER_IN, hd, hdlen);
 
   if(k->httpcode/100 == 1)
     writetype |= CLIENTWRITE_1XX;
@@ -3993,7 +4048,7 @@ static CURLcode http_parse_headers(struct Curl_easy *data,
     if(!end_ptr) {
       /* Not a complete header line within buffer, append the data to
          the end of the headerbuff. */
-      result = Curl_dyn_addn(&data->state.headerb, buf, blen);
+      result = curlx_dyn_addn(&data->state.headerb, buf, blen);
       if(result)
         return result;
       *pconsumed += blen;
@@ -4002,8 +4057,8 @@ static CURLcode http_parse_headers(struct Curl_easy *data,
         /* check if this looks like a protocol header */
         statusline st =
           checkprotoprefix(data, conn,
-                           Curl_dyn_ptr(&data->state.headerb),
-                           Curl_dyn_len(&data->state.headerb));
+                           curlx_dyn_ptr(&data->state.headerb),
+                           curlx_dyn_len(&data->state.headerb));
 
         if(st == STATUS_BAD) {
           /* this is not the beginning of a protocol first header line.
@@ -4014,7 +4069,7 @@ static CURLcode http_parse_headers(struct Curl_easy *data,
             failf(data, "Invalid status line");
             return CURLE_WEIRD_SERVER_REPLY;
           }
-          if(!data->set.http09_allowed) {
+          if(!data->state.http_neg.accept_09) {
             failf(data, "Received HTTP/0.9 when not allowed");
             return CURLE_UNSUPPORTED_PROTOCOL;
           }
@@ -4027,7 +4082,7 @@ static CURLcode http_parse_headers(struct Curl_easy *data,
 
     /* decrease the size of the remaining (supposed) header line */
     consumed = (end_ptr - buf) + 1;
-    result = Curl_dyn_addn(&data->state.headerb, buf, consumed);
+    result = curlx_dyn_addn(&data->state.headerb, buf, consumed);
     if(result)
       return result;
     blen -= consumed;
@@ -4041,8 +4096,8 @@ static CURLcode http_parse_headers(struct Curl_easy *data,
     if(!k->headerline) {
       /* the first read header */
       statusline st = checkprotoprefix(data, conn,
-                                       Curl_dyn_ptr(&data->state.headerb),
-                                       Curl_dyn_len(&data->state.headerb));
+                                       curlx_dyn_ptr(&data->state.headerb),
+                                       curlx_dyn_len(&data->state.headerb));
       if(st == STATUS_BAD) {
         streamclose(conn, "bad HTTP: No end-of-message indicator");
         /* this is not the beginning of a protocol first header line.
@@ -4051,7 +4106,7 @@ static CURLcode http_parse_headers(struct Curl_easy *data,
           failf(data, "Invalid status line");
           return CURLE_WEIRD_SERVER_REPLY;
         }
-        if(!data->set.http09_allowed) {
+        if(!data->state.http_neg.accept_09) {
           failf(data, "Received HTTP/0.9 when not allowed");
           return CURLE_UNSUPPORTED_PROTOCOL;
         }
@@ -4061,13 +4116,13 @@ static CURLcode http_parse_headers(struct Curl_easy *data,
       }
     }
 
-    result = http_rw_hd(data, Curl_dyn_ptr(&data->state.headerb),
-                        Curl_dyn_len(&data->state.headerb),
+    result = http_rw_hd(data, curlx_dyn_ptr(&data->state.headerb),
+                        curlx_dyn_len(&data->state.headerb),
                         buf, blen, &consumed);
     /* We are done with this line. We reset because response
      * processing might switch to HTTP/2 and that might call us
      * directly again. */
-    Curl_dyn_reset(&data->state.headerb);
+    curlx_dyn_reset(&data->state.headerb);
     if(consumed) {
       blen -= consumed;
       buf += consumed;
@@ -4082,7 +4137,7 @@ static CURLcode http_parse_headers(struct Curl_easy *data,
      buffer. */
 out:
   if(!k->header && !leftover_body) {
-    Curl_dyn_free(&data->state.headerb);
+    curlx_dyn_free(&data->state.headerb);
   }
   return CURLE_OK;
 }
@@ -4120,15 +4175,15 @@ CURLcode Curl_http_write_resp_hds(struct Curl_easy *data,
 
     result = http_parse_headers(data, buf, blen, pconsumed);
     if(!result && !data->req.header) {
-      if(!data->req.no_body && Curl_dyn_len(&data->state.headerb)) {
+      if(!data->req.no_body && curlx_dyn_len(&data->state.headerb)) {
         /* leftover from parsing something that turned out not
          * to be a header, only happens if we allow for
          * HTTP/0.9 like responses */
         result = Curl_client_write(data, CLIENTWRITE_BODY,
-                                   Curl_dyn_ptr(&data->state.headerb),
-                                   Curl_dyn_len(&data->state.headerb));
+                                   curlx_dyn_ptr(&data->state.headerb),
+                                   curlx_dyn_len(&data->state.headerb));
       }
-      Curl_dyn_free(&data->state.headerb);
+      curlx_dyn_free(&data->state.headerb);
     }
     return result;
   }
@@ -4157,7 +4212,7 @@ CURLcode Curl_http_write_resp(struct Curl_easy *data,
     flags = CLIENTWRITE_BODY;
     if(is_eos)
       flags |= CLIENTWRITE_EOS;
-    result = Curl_client_write(data, flags, (char *)buf, blen);
+    result = Curl_client_write(data, flags, buf, blen);
   }
 out:
   return result;
@@ -4197,11 +4252,9 @@ CURLcode Curl_http_req_make(struct httpreq **preq,
   struct httpreq *req;
   CURLcode result = CURLE_OUT_OF_MEMORY;
 
-  DEBUGASSERT(method);
-  if(m_len + 1 > sizeof(req->method))
-    return CURLE_BAD_FUNCTION_ARGUMENT;
+  DEBUGASSERT(method && m_len);
 
-  req = calloc(1, sizeof(*req));
+  req = calloc(1, sizeof(*req) + m_len);
   if(!req)
     goto out;
   memcpy(req->method, method, m_len);
@@ -4239,7 +4292,7 @@ static CURLcode req_assign_url_authority(struct httpreq *req, CURLU *url)
   CURLcode result = CURLE_URL_MALFORMAT;
 
   user = pass = host = port = NULL;
-  Curl_dyn_init(&buf, DYN_HTTP_REQUEST);
+  curlx_dyn_init(&buf, DYN_HTTP_REQUEST);
 
   uc = curl_url_get(url, CURLUPART_HOST, &host, 0);
   if(uc && uc != CURLUE_NO_HOST)
@@ -4263,27 +4316,27 @@ static CURLcode req_assign_url_authority(struct httpreq *req, CURLU *url)
   }
 
   if(user) {
-    result = Curl_dyn_add(&buf, user);
+    result = curlx_dyn_add(&buf, user);
     if(result)
       goto out;
     if(pass) {
-      result = Curl_dyn_addf(&buf, ":%s", pass);
+      result = curlx_dyn_addf(&buf, ":%s", pass);
       if(result)
         goto out;
     }
-    result = Curl_dyn_add(&buf, "@");
+    result = curlx_dyn_add(&buf, "@");
     if(result)
       goto out;
   }
-  result = Curl_dyn_add(&buf, host);
+  result = curlx_dyn_add(&buf, host);
   if(result)
     goto out;
   if(port) {
-    result = Curl_dyn_addf(&buf, ":%s", port);
+    result = curlx_dyn_addf(&buf, ":%s", port);
     if(result)
       goto out;
   }
-  req->authority = strdup(Curl_dyn_ptr(&buf));
+  req->authority = strdup(curlx_dyn_ptr(&buf));
   if(!req->authority)
     goto out;
   result = CURLE_OK;
@@ -4293,7 +4346,7 @@ static CURLcode req_assign_url_authority(struct httpreq *req, CURLU *url)
   free(pass);
   free(host);
   free(port);
-  Curl_dyn_free(&buf);
+  curlx_dyn_free(&buf);
   return result;
 }
 
@@ -4305,7 +4358,7 @@ static CURLcode req_assign_url_path(struct httpreq *req, CURLU *url)
   CURLcode result = CURLE_URL_MALFORMAT;
 
   path = query = NULL;
-  Curl_dyn_init(&buf, DYN_HTTP_REQUEST);
+  curlx_dyn_init(&buf, DYN_HTTP_REQUEST);
 
   uc = curl_url_get(url, CURLUPART_PATH, &path, CURLU_PATH_AS_IS);
   if(uc)
@@ -4323,16 +4376,16 @@ static CURLcode req_assign_url_path(struct httpreq *req, CURLU *url)
   }
   else {
     if(path) {
-      result = Curl_dyn_add(&buf, path);
+      result = curlx_dyn_add(&buf, path);
       if(result)
         goto out;
     }
     if(query) {
-      result = Curl_dyn_addf(&buf, "?%s", query);
+      result = curlx_dyn_addf(&buf, "?%s", query);
       if(result)
         goto out;
     }
-    req->path = strdup(Curl_dyn_ptr(&buf));
+    req->path = strdup(curlx_dyn_ptr(&buf));
     if(!req->path)
       goto out;
   }
@@ -4341,7 +4394,7 @@ static CURLcode req_assign_url_path(struct httpreq *req, CURLU *url)
 out:
   free(path);
   free(query);
-  Curl_dyn_free(&buf);
+  curlx_dyn_free(&buf);
   return result;
 }
 
@@ -4353,11 +4406,9 @@ CURLcode Curl_http_req_make2(struct httpreq **preq,
   CURLcode result = CURLE_OUT_OF_MEMORY;
   CURLUcode uc;
 
-  DEBUGASSERT(method);
-  if(m_len + 1 > sizeof(req->method))
-    return CURLE_BAD_FUNCTION_ARGUMENT;
+  DEBUGASSERT(method && m_len);
 
-  req = calloc(1, sizeof(*req));
+  req = calloc(1, sizeof(*req) + m_len);
   if(!req)
     goto out;
   memcpy(req->method, method, m_len);
@@ -4408,7 +4459,6 @@ struct name_const {
 
 /* keep them sorted by length! */
 static struct name_const H2_NON_FIELD[] = {
-  { STRCONST("TE") },
   { STRCONST("Host") },
   { STRCONST("Upgrade") },
   { STRCONST("Connection") },
@@ -4417,15 +4467,44 @@ static struct name_const H2_NON_FIELD[] = {
   { STRCONST("Transfer-Encoding") },
 };
 
-static bool h2_non_field(const char *name, size_t namelen)
+static bool h2_permissible_field(struct dynhds_entry *e)
 {
   size_t i;
-  for(i = 0; i < sizeof(H2_NON_FIELD)/sizeof(H2_NON_FIELD[0]); ++i) {
-    if(namelen < H2_NON_FIELD[i].namelen)
+  for(i = 0; i < CURL_ARRAYSIZE(H2_NON_FIELD); ++i) {
+    if(e->namelen < H2_NON_FIELD[i].namelen)
+      return TRUE;
+    if(e->namelen == H2_NON_FIELD[i].namelen &&
+       strcasecompare(H2_NON_FIELD[i].name, e->name))
       return FALSE;
-    if(namelen == H2_NON_FIELD[i].namelen &&
-       strcasecompare(H2_NON_FIELD[i].name, name))
+  }
+  return TRUE;
+}
+
+static bool http_TE_has_token(const char *fvalue, const char *token)
+{
+  while(*fvalue) {
+    struct Curl_str name;
+
+    /* skip to first token */
+    while(ISBLANK(*fvalue) || *fvalue == ',')
+      fvalue++;
+    if(curlx_str_cspn(&fvalue, &name, " \t\r;,"))
+      return FALSE;
+    if(curlx_str_casecompare(&name, token))
       return TRUE;
+
+    /* skip any remainder after token, e.g. parameters with quoted strings */
+    while(*fvalue && *fvalue != ',') {
+      if(*fvalue == '"') {
+        struct Curl_str qw;
+        /* if we do not cleanly find a quoted word here, the header value
+         * does not follow HTTP syntax and we reject */
+        if(curlx_str_quotedword(&fvalue, &qw, CURL_MAX_HTTP_HEADER))
+          return FALSE;
+      }
+      else
+        fvalue++;
+    }
   }
   return FALSE;
 }
@@ -4448,8 +4527,7 @@ CURLcode Curl_http_req_to_h2(struct dynhds *h2_headers,
     scheme = Curl_checkheaders(data, STRCONST(HTTP_PSEUDO_SCHEME));
     if(scheme) {
       scheme += sizeof(HTTP_PSEUDO_SCHEME);
-      while(*scheme && ISBLANK(*scheme))
-        scheme++;
+      curlx_str_passblanks(&scheme);
       infof(data, "set pseudo header %s to %s", HTTP_PSEUDO_SCHEME, scheme);
     }
     else {
@@ -4485,7 +4563,14 @@ CURLcode Curl_http_req_to_h2(struct dynhds *h2_headers,
   }
   for(i = 0; !result && i < Curl_dynhds_count(&req->headers); ++i) {
     e = Curl_dynhds_getn(&req->headers, i);
-    if(!h2_non_field(e->name, e->namelen)) {
+    /* "TE" is special in that it is only permissible when it
+     * has only value "trailers". RFC 9113 ch. 8.2.2 */
+    if(e->namelen == 2 && strcasecompare("TE", e->name)) {
+      if(http_TE_has_token(e->value, "trailers"))
+        result = Curl_dynhds_add(h2_headers, e->name, e->namelen,
+                                 "trailers", sizeof("trailers") - 1);
+    }
+    else if(h2_permissible_field(e)) {
       result = Curl_dynhds_add(h2_headers, e->name, e->namelen,
                                e->value, e->valuelen);
     }
@@ -4577,7 +4662,7 @@ static CURLcode cr_exp100_read(struct Curl_easy *data,
     DEBUGF(infof(data, "cr_exp100_read, start AWAITING_CONTINUE, "
            "timeout %ldms", data->set.expect_100_timeout));
     ctx->state = EXP100_AWAITING_CONTINUE;
-    ctx->start = Curl_now();
+    ctx->start = curlx_now();
     Curl_expire(data, data->set.expect_100_timeout, EXPIRE_100_TIMEOUT);
     data->req.keepon &= ~KEEP_SEND;
     data->req.keepon |= KEEP_SEND_TIMED;
@@ -4590,7 +4675,7 @@ static CURLcode cr_exp100_read(struct Curl_easy *data,
     *eos = FALSE;
     return CURLE_READ_ERROR;
   case EXP100_AWAITING_CONTINUE:
-    ms = Curl_timediff(Curl_now(), ctx->start);
+    ms = curlx_timediff(curlx_now(), ctx->start);
     if(ms < data->set.expect_100_timeout) {
       DEBUGF(infof(data, "cr_exp100_read, AWAITING_CONTINUE, not expired"));
       data->req.keepon &= ~KEEP_SEND;
diff --git a/extra/curl/curl-8.12.1/lib/http.h b/extra/curl/curl-8.14.1/lib/http.h
similarity index 90%
rename from extra/curl/curl-8.12.1/lib/http.h
rename to extra/curl/curl-8.14.1/lib/http.h
index a15a982356a3..8876f380447b 100644
--- a/extra/curl/curl-8.12.1/lib/http.h
+++ b/extra/curl/curl-8.14.1/lib/http.h
@@ -53,6 +53,12 @@ typedef enum {
   FOLLOW_REDIR /* a full true redirect */
 } followtype;
 
+#define CURL_HTTP_V1x   (1 << 0)
+#define CURL_HTTP_V2x   (1 << 1)
+#define CURL_HTTP_V3x   (1 << 2)
+/* bitmask of CURL_HTTP_V* values */
+typedef unsigned char http_majors;
+
 
 #ifndef CURL_DISABLE_HTTP
 
@@ -68,6 +74,18 @@ extern const struct Curl_handler Curl_handler_https;
 
 struct dynhds;
 
+struct http_negotiation {
+  unsigned char rcvd_min; /* minimum version seen in responses, 09, 10, 11 */
+  http_majors wanted;  /* wanted major versions when talking to server */
+  http_majors allowed; /* allowed major versions when talking to server */
+  BIT(h2_upgrade);  /* Do HTTP Upgrade from 1.1 to 2 */
+  BIT(h2_prior_knowledge); /* Directly do HTTP/2 without ALPN/SSL */
+  BIT(accept_09); /* Accept an HTTP/0.9 response */
+  BIT(only_10); /* When using major version 1x, use only 1.0 */
+};
+
+void Curl_http_neg_init(struct Curl_easy *data, struct http_negotiation *neg);
+
 CURLcode Curl_bump_headersize(struct Curl_easy *data,
                               size_t delta,
                               bool connect_only);
@@ -113,6 +131,7 @@ CURLcode Curl_http_write_resp_hd(struct Curl_easy *data,
 /* These functions are in http.c */
 CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
                               const char *auth);
+
 CURLcode Curl_http_auth_act(struct Curl_easy *data);
 
 /* follow a redirect or not */
@@ -155,6 +174,10 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl,
    version. This count includes CONNECT response headers. */
 #define MAX_HTTP_RESP_HEADER_SIZE (300*1024)
 
+/* MAX_HTTP_RESP_HEADER_COUNT is the maximum number of response headers that
+   libcurl allows for a single HTTP response, including CONNECT and
+   redirects. */
+#define MAX_HTTP_RESP_HEADER_COUNT 5000
 
 #endif /* CURL_DISABLE_HTTP */
 
@@ -199,12 +222,12 @@ CURLcode Curl_http_decode_status(int *pstatus, const char *s, size_t len);
  * All about a core HTTP request, excluding body and trailers
  */
 struct httpreq {
-  char method[24];
+  struct dynhds headers;
+  struct dynhds trailers;
   char *scheme;
   char *authority;
   char *path;
-  struct dynhds headers;
-  struct dynhds trailers;
+  char method[1];
 };
 
 /**
diff --git a/extra/curl/curl-8.12.1/lib/http1.c b/extra/curl/curl-8.14.1/lib/http1.c
similarity index 89%
rename from extra/curl/curl-8.12.1/lib/http1.c
rename to extra/curl/curl-8.14.1/lib/http1.c
index 9d2461e81355..537e6db2ff98 100644
--- a/extra/curl/curl-8.12.1/lib/http1.c
+++ b/extra/curl/curl-8.14.1/lib/http1.c
@@ -44,14 +44,14 @@ void Curl_h1_req_parse_init(struct h1_req_parser *parser, size_t max_line_len)
 {
   memset(parser, 0, sizeof(*parser));
   parser->max_line_len = max_line_len;
-  Curl_dyn_init(&parser->scratch, max_line_len);
+  curlx_dyn_init(&parser->scratch, max_line_len);
 }
 
 void Curl_h1_req_parse_free(struct h1_req_parser *parser)
 {
   if(parser) {
     Curl_http_req_free(parser->req);
-    Curl_dyn_free(&parser->scratch);
+    curlx_dyn_free(&parser->scratch);
     parser->req = NULL;
     parser->done = FALSE;
   }
@@ -108,18 +108,18 @@ static ssize_t next_line(struct h1_req_parser *parser,
   if(parser->line) {
     parser->line = NULL;
     parser->line_len = 0;
-    Curl_dyn_reset(&parser->scratch);
+    curlx_dyn_reset(&parser->scratch);
   }
 
   nread = detect_line(parser, buf, buflen, err);
   if(nread >= 0) {
-    if(Curl_dyn_len(&parser->scratch)) {
+    if(curlx_dyn_len(&parser->scratch)) {
       /* append detected line to scratch to have the complete line */
-      *err = Curl_dyn_addn(&parser->scratch, parser->line, parser->line_len);
+      *err = curlx_dyn_addn(&parser->scratch, parser->line, parser->line_len);
       if(*err)
         return -1;
-      parser->line = Curl_dyn_ptr(&parser->scratch);
-      parser->line_len = Curl_dyn_len(&parser->scratch);
+      parser->line = curlx_dyn_ptr(&parser->scratch);
+      parser->line_len = curlx_dyn_len(&parser->scratch);
     }
     *err = trim_line(parser, options);
     if(*err)
@@ -127,7 +127,8 @@ static ssize_t next_line(struct h1_req_parser *parser,
   }
   else if(*err == CURLE_AGAIN) {
     /* no line end in `buf`, add it to our scratch */
-    *err = Curl_dyn_addn(&parser->scratch, (const unsigned char *)buf, buflen);
+    *err = curlx_dyn_addn(&parser->scratch, (const unsigned char *)buf,
+                          buflen);
     nread = (*err) ? -1 : (ssize_t)buflen;
   }
   return nread;
@@ -208,7 +209,7 @@ static CURLcode start_req(struct h1_req_parser *parser,
     path = target;
     path_len = target_len;
 
-    /* URL parser wants 0-termination */
+    /* URL parser wants null-termination */
     if(target_len >= sizeof(tmp))
       goto out;
     memcpy(tmp, target, target_len);
@@ -299,7 +300,7 @@ ssize_t Curl_h1_req_parse_read(struct h1_req_parser *parser,
         goto out;
       }
       parser->done = TRUE;
-      Curl_dyn_reset(&parser->scratch);
+      curlx_dyn_reset(&parser->scratch);
       /* last chance adjustments */
     }
     else {
@@ -321,13 +322,13 @@ CURLcode Curl_h1_req_write_head(struct httpreq *req, int http_minor,
 {
   CURLcode result;
 
-  result = Curl_dyn_addf(dbuf, "%s %s%s%s%s HTTP/1.%d\r\n",
-                         req->method,
-                         req->scheme ? req->scheme : "",
-                         req->scheme ? "://" : "",
-                         req->authority ? req->authority : "",
-                         req->path ? req->path : "",
-                         http_minor);
+  result = curlx_dyn_addf(dbuf, "%s %s%s%s%s HTTP/1.%d\r\n",
+                          req->method,
+                          req->scheme ? req->scheme : "",
+                          req->scheme ? "://" : "",
+                          req->authority ? req->authority : "",
+                          req->path ? req->path : "",
+                          http_minor);
   if(result)
     goto out;
 
@@ -335,7 +336,7 @@ CURLcode Curl_h1_req_write_head(struct httpreq *req, int http_minor,
   if(result)
     goto out;
 
-  result = Curl_dyn_addn(dbuf, STRCONST("\r\n"));
+  result = curlx_dyn_addn(dbuf, STRCONST("\r\n"));
 
 out:
   return result;
diff --git a/extra/curl/curl-8.12.1/lib/http1.h b/extra/curl/curl-8.14.1/lib/http1.h
similarity index 99%
rename from extra/curl/curl-8.12.1/lib/http1.h
rename to extra/curl/curl-8.14.1/lib/http1.h
index 2de302f1f6c8..b38b32f59115 100644
--- a/extra/curl/curl-8.12.1/lib/http1.h
+++ b/extra/curl/curl-8.14.1/lib/http1.h
@@ -42,7 +42,7 @@ struct h1_req_parser {
   const char *line;
   size_t max_line_len;
   size_t line_len;
-  bool done;
+  BIT(done);
 };
 
 void Curl_h1_req_parse_init(struct h1_req_parser *parser, size_t max_line_len);
diff --git a/extra/curl/curl-8.12.1/lib/http2.c b/extra/curl/curl-8.14.1/lib/http2.c
similarity index 91%
rename from extra/curl/curl-8.12.1/lib/http2.c
rename to extra/curl/curl-8.14.1/lib/http2.c
index 821f2889dd70..dd82aa963ec3 100644
--- a/extra/curl/curl-8.12.1/lib/http2.c
+++ b/extra/curl/curl-8.14.1/lib/http2.c
@@ -29,13 +29,13 @@
 #include <nghttp2/nghttp2.h>
 #include "urldata.h"
 #include "bufq.h"
-#include "hash.h"
+#include "uint-hash.h"
 #include "http1.h"
 #include "http2.h"
 #include "http.h"
 #include "sendf.h"
 #include "select.h"
-#include "curl_base64.h"
+#include "curlx/base64.h"
 #include "strcase.h"
 #include "multiif.h"
 #include "url.h"
@@ -43,10 +43,10 @@
 #include "cfilters.h"
 #include "connect.h"
 #include "rand.h"
-#include "strtoofft.h"
 #include "strdup.h"
+#include "curlx/strparse.h"
 #include "transfer.h"
-#include "dynbuf.h"
+#include "curlx/dynbuf.h"
 #include "headers.h"
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -136,12 +136,15 @@ struct cf_h2_ctx {
   struct bufc_pool stream_bufcp; /* spares for stream buffers */
   struct dynbuf scratch;        /* scratch buffer for temp use */
 
-  struct Curl_hash streams; /* hash of `data->mid` to `h2_stream_ctx` */
+  struct uint_hash streams; /* hash of `data->mid` to `h2_stream_ctx` */
   size_t drain_total; /* sum of all stream's UrlState drain */
   uint32_t max_concurrent_streams;
   uint32_t goaway_error;        /* goaway error code from server */
   int32_t remote_max_sid;       /* max id processed by server */
   int32_t local_max_sid;        /* max id processed by us */
+#ifdef DEBUGBUILD
+  int32_t stream_win_max;       /* max h2 stream window size */
+#endif
   BIT(initialized);
   BIT(via_h1_upgrade);
   BIT(conn_closed);
@@ -156,17 +159,29 @@ struct cf_h2_ctx {
 #define CF_CTX_CALL_DATA(cf)  \
   ((struct cf_h2_ctx *)(cf)->ctx)->call_data
 
-static void h2_stream_hash_free(void *stream);
+static void h2_stream_hash_free(unsigned int id, void *stream);
 
 static void cf_h2_ctx_init(struct cf_h2_ctx *ctx, bool via_h1_upgrade)
 {
   Curl_bufcp_init(&ctx->stream_bufcp, H2_CHUNK_SIZE, H2_STREAM_POOL_SPARES);
   Curl_bufq_initp(&ctx->inbufq, &ctx->stream_bufcp, H2_NW_RECV_CHUNKS, 0);
   Curl_bufq_initp(&ctx->outbufq, &ctx->stream_bufcp, H2_NW_SEND_CHUNKS, 0);
-  Curl_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER);
-  Curl_hash_offt_init(&ctx->streams, 63, h2_stream_hash_free);
+  curlx_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER);
+  Curl_uint_hash_init(&ctx->streams, 63, h2_stream_hash_free);
   ctx->remote_max_sid = 2147483647;
   ctx->via_h1_upgrade = via_h1_upgrade;
+#ifdef DEBUGBUILD
+  {
+    const char *p = getenv("CURL_H2_STREAM_WIN_MAX");
+
+    ctx->stream_win_max = H2_STREAM_WINDOW_SIZE_MAX;
+    if(p) {
+      curl_off_t l;
+      if(!curlx_str_number(&p, &l, INT_MAX))
+        ctx->stream_win_max = (int32_t)l;
+    }
+  }
+#endif
   ctx->initialized = TRUE;
 }
 
@@ -176,9 +191,8 @@ static void cf_h2_ctx_free(struct cf_h2_ctx *ctx)
     Curl_bufq_free(&ctx->inbufq);
     Curl_bufq_free(&ctx->outbufq);
     Curl_bufcp_free(&ctx->stream_bufcp);
-    Curl_dyn_free(&ctx->scratch);
-    Curl_hash_clean(&ctx->streams);
-    Curl_hash_destroy(&ctx->streams);
+    curlx_dyn_free(&ctx->scratch);
+    Curl_uint_hash_destroy(&ctx->streams);
     memset(ctx, 0, sizeof(*ctx));
   }
   free(ctx);
@@ -192,7 +206,7 @@ static void cf_h2_ctx_close(struct cf_h2_ctx *ctx)
 }
 
 static CURLcode h2_progress_egress(struct Curl_cfilter *cf,
-                                  struct Curl_easy *data);
+                                   struct Curl_easy *data);
 
 /**
  * All about the H2 internals of a stream
@@ -220,10 +234,12 @@ struct h2_stream_ctx {
   BIT(bodystarted);
   BIT(body_eos);    /* the complete body has been added to `sendbuf` and
                      * is being/has been processed from there. */
+  BIT(write_paused);  /* stream write is paused */
 };
 
-#define H2_STREAM_CTX(ctx,data)   ((struct h2_stream_ctx *)(\
-            data? Curl_hash_offt_get(&(ctx)->streams, (data)->mid) : NULL))
+#define H2_STREAM_CTX(ctx,data)                                         \
+  ((struct h2_stream_ctx *)(                                            \
+    data? Curl_uint_hash_get(&(ctx)->streams, (data)->mid) : NULL))
 
 static struct h2_stream_ctx *h2_stream_ctx_create(struct cf_h2_ctx *ctx)
 {
@@ -267,8 +283,9 @@ static void h2_stream_ctx_free(struct h2_stream_ctx *stream)
   free(stream);
 }
 
-static void h2_stream_hash_free(void *stream)
+static void h2_stream_hash_free(unsigned int id, void *stream)
 {
+  (void)id;
   DEBUGASSERT(stream);
   h2_stream_ctx_free((struct h2_stream_ctx *)stream);
 }
@@ -285,23 +302,38 @@ static int32_t cf_h2_get_desired_local_win(struct Curl_cfilter *cf,
      * This gets less precise the higher the latency. */
     return (int32_t)data->set.max_recv_speed;
   }
+#ifdef DEBUGBUILD
+  else {
+    struct cf_h2_ctx *ctx = cf->ctx;
+    CURL_TRC_CF(data, cf, "stream_win_max=%d", ctx->stream_win_max);
+    return ctx->stream_win_max;
+  }
+#else
   return H2_STREAM_WINDOW_SIZE_MAX;
+#endif
 }
 
 static CURLcode cf_h2_update_local_win(struct Curl_cfilter *cf,
                                        struct Curl_easy *data,
-                                       struct h2_stream_ctx *stream,
-                                       bool paused)
+                                       struct h2_stream_ctx *stream)
 {
   struct cf_h2_ctx *ctx = cf->ctx;
   int32_t dwsize;
   int rv;
 
-  dwsize = paused ? 0 : cf_h2_get_desired_local_win(cf, data);
+  dwsize = (stream->write_paused || stream->xfer_result) ?
+           0 : cf_h2_get_desired_local_win(cf, data);
   if(dwsize != stream->local_window_size) {
     int32_t wsize = nghttp2_session_get_stream_effective_local_window_size(
                       ctx->h2, stream->id);
     if(dwsize > wsize) {
+      rv = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE,
+                                                 stream->id, dwsize);
+      if(rv) {
+        failf(data, "[%d] nghttp2 set_local_window_size(%d) failed: "
+              "%s(%d)", stream->id, dwsize, nghttp2_strerror(rv), rv);
+        return CURLE_HTTP2;
+      }
       rv = nghttp2_submit_window_update(ctx->h2, NGHTTP2_FLAG_NONE,
                                         stream->id, dwsize - wsize);
       if(rv) {
@@ -333,13 +365,11 @@ static CURLcode cf_h2_update_local_win(struct Curl_cfilter *cf,
 
 static CURLcode cf_h2_update_local_win(struct Curl_cfilter *cf,
                                        struct Curl_easy *data,
-                                       struct h2_stream_ctx *stream,
-                                       bool paused)
+                                       struct h2_stream_ctx *stream)
 {
   (void)cf;
   (void)data;
   (void)stream;
-  (void)paused;
   return CURLE_OK;
 }
 #endif /* !NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE */
@@ -385,7 +415,7 @@ static CURLcode http2_data_setup(struct Curl_cfilter *cf,
   if(!stream)
     return CURLE_OUT_OF_MEMORY;
 
-  if(!Curl_hash_offt_set(&ctx->streams, data->mid, stream)) {
+  if(!Curl_uint_hash_set(&ctx->streams, data->mid, stream)) {
     h2_stream_ctx_free(stream);
     return CURLE_OUT_OF_MEMORY;
   }
@@ -423,7 +453,7 @@ static void http2_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
       nghttp2_session_send(ctx->h2);
   }
 
-  Curl_hash_offt_remove(&ctx->streams, data->mid);
+  Curl_uint_hash_remove(&ctx->streams, data->mid);
 }
 
 static int h2_client_new(struct Curl_cfilter *cf,
@@ -432,7 +462,7 @@ static int h2_client_new(struct Curl_cfilter *cf,
   struct cf_h2_ctx *ctx = cf->ctx;
   nghttp2_option *o;
   nghttp2_mem mem = {NULL, Curl_nghttp2_malloc, Curl_nghttp2_free,
-                     Curl_nghttp2_calloc, Curl_nghttp2_realloc};
+    Curl_nghttp2_calloc, Curl_nghttp2_realloc};
 
   int rc = nghttp2_option_new(&o);
   if(rc)
@@ -482,6 +512,10 @@ static ssize_t send_callback(nghttp2_session *h2,
                              void *userp);
 static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
                          void *userp);
+static int cf_h2_on_invalid_frame_recv(nghttp2_session *session,
+                                       const nghttp2_frame *frame,
+                                       int lib_error_code,
+                                       void *user_data);
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
 static int on_frame_send(nghttp2_session *session, const nghttp2_frame *frame,
                          void *userp);
@@ -522,6 +556,8 @@ static CURLcode cf_h2_ctx_open(struct Curl_cfilter *cf,
 
   nghttp2_session_callbacks_set_send_callback(cbs, send_callback);
   nghttp2_session_callbacks_set_on_frame_recv_callback(cbs, on_frame_recv);
+  nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(cbs,
+    cf_h2_on_invalid_frame_recv);
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
   nghttp2_session_callbacks_set_on_frame_send_callback(cbs, on_frame_send);
 #endif
@@ -644,10 +680,8 @@ static int h2_process_pending_input(struct Curl_cfilter *cf,
 
     rv = nghttp2_session_mem_recv(ctx->h2, (const uint8_t *)buf, blen);
     if(rv < 0) {
-      failf(data,
-            "process_pending_input: nghttp2_session_mem_recv() returned "
-            "%zd:%s", rv, nghttp2_strerror((int)rv));
-      *err = CURLE_RECV_ERROR;
+      failf(data, "nghttp2 recv error %zd: %s", rv, nghttp2_strerror((int)rv));
+      *err = CURLE_HTTP2;
       return -1;
     }
     Curl_bufq_skip(&ctx->inbufq, (size_t)rv);
@@ -965,9 +999,6 @@ static int push_promise(struct Curl_cfilter *cf,
       goto fail;
     }
 
-    /* ask the application */
-    CURL_TRC_CF(data, cf, "Got PUSH_PROMISE, ask application");
-
     stream = H2_STREAM_CTX(ctx, data);
     if(!stream) {
       failf(data, "Internal NULL stream");
@@ -982,20 +1013,13 @@ static int push_promise(struct Curl_cfilter *cf,
 
     rv = set_transfer_url(newhandle, &heads);
     if(rv) {
+      CURL_TRC_CF(data, cf, "[%d] PUSH_PROMISE, failed to set url -> %d",
+                  frame->promised_stream_id, rv);
       discard_newhandle(cf, newhandle);
       rv = CURL_PUSH_DENY;
       goto fail;
     }
 
-    result = http2_data_setup(cf, newhandle, &newstream);
-    if(result) {
-      failf(data, "error setting up stream: %d", result);
-      discard_newhandle(cf, newhandle);
-      rv = CURL_PUSH_DENY;
-      goto fail;
-    }
-    DEBUGASSERT(stream);
-
     Curl_set_in_callback(data, TRUE);
     rv = data->multi->push_cb(data, newhandle,
                               stream->push_headers_used, &heads,
@@ -1008,16 +1032,15 @@ static int push_promise(struct Curl_cfilter *cf,
     if(rv) {
       DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT));
       /* denied, kill off the new handle again */
+      CURL_TRC_CF(data, cf, "[%d] PUSH_PROMISE, denied by application -> %d",
+                  frame->promised_stream_id, rv);
       discard_newhandle(cf, newhandle);
       goto fail;
     }
 
-    newstream->id = frame->promised_stream_id;
-    newhandle->req.maxdownload = -1;
-    newhandle->req.size = -1;
-
-    /* approved, add to the multi handle and immediately switch to PERFORM
-       state with the given connection !*/
+    /* approved, add to the multi handle for processing. This
+     * assigns newhandle->mid. For the new `mid` we assign the
+     * h2_stream instance and remember the stream_id already known. */
     rc = Curl_multi_add_perform(data->multi, newhandle, cf->conn);
     if(rc) {
       infof(data, "failed to add handle to multi");
@@ -1026,6 +1049,21 @@ static int push_promise(struct Curl_cfilter *cf,
       goto fail;
     }
 
+    result = http2_data_setup(cf, newhandle, &newstream);
+    if(result) {
+      failf(data, "error setting up stream: %d", result);
+      discard_newhandle(cf, newhandle);
+      rv = CURL_PUSH_DENY;
+      goto fail;
+    }
+
+    DEBUGASSERT(newstream);
+    newstream->id = frame->promised_stream_id;
+    newhandle->req.maxdownload = -1;
+    newhandle->req.size = -1;
+
+    CURL_TRC_CF(data, cf, "promise easy handle added to multi, mid=%u",
+                newhandle->mid);
     rv = nghttp2_session_set_stream_user_data(ctx->h2,
                                               newstream->id,
                                               newhandle);
@@ -1059,7 +1097,7 @@ static void h2_xfer_write_resp_hd(struct Curl_cfilter *cf,
   if(!stream->xfer_result) {
     stream->xfer_result = Curl_xfer_write_resp_hd(data, buf, blen, eos);
     if(!stream->xfer_result && !eos)
-      stream->xfer_result = cf_h2_update_local_win(cf, data, stream, FALSE);
+      stream->xfer_result = cf_h2_update_local_win(cf, data, stream);
     if(stream->xfer_result)
       CURL_TRC_CF(data, cf, "[%d] error %d writing %zu bytes of headers",
                   stream->id, stream->xfer_result, blen);
@@ -1075,8 +1113,6 @@ static void h2_xfer_write_resp(struct Curl_cfilter *cf,
   /* If we already encountered an error, skip further writes */
   if(!stream->xfer_result)
     stream->xfer_result = Curl_xfer_write_resp(data, buf, blen, eos);
-  if(!stream->xfer_result && !eos)
-    stream->xfer_result = cf_h2_update_local_win(cf, data, stream, FALSE);
   /* If the transfer write is errored, we do not want any more data */
   if(stream->xfer_result) {
     struct cf_h2_ctx *ctx = cf->ctx;
@@ -1086,6 +1122,17 @@ static void h2_xfer_write_resp(struct Curl_cfilter *cf,
     nghttp2_submit_rst_stream(ctx->h2, 0, stream->id,
                               (uint32_t)NGHTTP2_ERR_CALLBACK_FAILURE);
   }
+  else if(!stream->write_paused && Curl_xfer_write_is_paused(data)) {
+    CURL_TRC_CF(data, cf, "[%d] stream output paused", stream->id);
+    stream->write_paused = TRUE;
+  }
+  else if(stream->write_paused && !Curl_xfer_write_is_paused(data)) {
+    CURL_TRC_CF(data, cf, "[%d] stream output unpaused", stream->id);
+    stream->write_paused = FALSE;
+  }
+
+  if(!stream->xfer_result && !eos)
+    stream->xfer_result = cf_h2_update_local_win(cf, data, stream);
 }
 
 static CURLcode on_stream_frame(struct Curl_cfilter *cf,
@@ -1253,7 +1300,7 @@ static int fr_print(const nghttp2_frame *frame, char *buffer, size_t blen)
     }
     case NGHTTP2_GOAWAY: {
       char scratch[128];
-      size_t s_len = sizeof(scratch)/sizeof(scratch[0]);
+      size_t s_len = CURL_ARRAYSIZE(scratch);
       size_t len = (frame->goaway.opaque_data_len < s_len) ?
         frame->goaway.opaque_data_len : s_len-1;
       if(len)
@@ -1279,6 +1326,7 @@ static int on_frame_send(nghttp2_session *session, const nghttp2_frame *frame,
                          void *userp)
 {
   struct Curl_cfilter *cf = userp;
+  struct cf_h2_ctx *ctx = cf->ctx;
   struct Curl_easy *data = CF_DATA_CURRENT(cf);
 
   (void)session;
@@ -1290,6 +1338,13 @@ static int on_frame_send(nghttp2_session *session, const nghttp2_frame *frame,
     buffer[len] = 0;
     CURL_TRC_CF(data, cf, "[%d] -> %s", frame->hd.stream_id, buffer);
   }
+  if((frame->hd.type == NGHTTP2_GOAWAY) && !ctx->sent_goaway) {
+    /* A GOAWAY not initiated by us, but by nghttp2 itself on detecting
+     * a protocol error on the connection */
+    failf(data, "nghttp2 shuts down connection with error %d: %s",
+          frame->goaway.error_code,
+          nghttp2_http2_strerror(frame->goaway.error_code));
+  }
   return 0;
 }
 #endif /* !CURL_DISABLE_VERBOSE_STRINGS */
@@ -1372,6 +1427,39 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
   return on_stream_frame(cf, data_s, frame) ? NGHTTP2_ERR_CALLBACK_FAILURE : 0;
 }
 
+static int cf_h2_on_invalid_frame_recv(nghttp2_session *session,
+                                       const nghttp2_frame *frame,
+                                       int ngerr, void *userp)
+{
+  struct Curl_cfilter *cf = userp;
+  struct cf_h2_ctx *ctx = cf->ctx;
+  struct Curl_easy *data;
+  int32_t stream_id = frame->hd.stream_id;
+
+  data = nghttp2_session_get_stream_user_data(session, stream_id);
+  if(data) {
+    struct h2_stream_ctx *stream;
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+    char buffer[256];
+    int len;
+    len = fr_print(frame, buffer, sizeof(buffer)-1);
+    buffer[len] = 0;
+    failf(data, "[HTTP2] [%d] received invalid frame: %s, error %d: %s",
+          stream_id, buffer, ngerr, nghttp2_strerror(ngerr));
+#endif /* !CURL_DISABLE_VERBOSE_STRINGS */
+    stream = H2_STREAM_CTX(ctx, data);
+    if(stream) {
+      nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE,
+                                stream->id, NGHTTP2_STREAM_CLOSED);
+      stream->error = ngerr;
+      stream->closed = TRUE;
+      stream->reset = TRUE;
+      return 0;  /* keep the connection alive */
+    }
+  }
+  return NGHTTP2_ERR_CALLBACK_FAILURE;
+}
+
 static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
                               int32_t stream_id,
                               const uint8_t *mem, size_t len, void *userp)
@@ -1402,7 +1490,7 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
   if(!stream)
     return NGHTTP2_ERR_CALLBACK_FAILURE;
 
-  h2_xfer_write_resp(cf, data_s, stream, (char *)mem, len, FALSE);
+  h2_xfer_write_resp(cf, data_s, stream, (const char *)mem, len, FALSE);
 
   nghttp2_session_consume(ctx->h2, stream_id, len);
   stream->nrcvd_data += (curl_off_t)len;
@@ -1493,6 +1581,23 @@ static int on_begin_headers(nghttp2_session *session,
   return 0;
 }
 
+static void cf_h2_header_error(struct Curl_cfilter *cf,
+                               struct Curl_easy *data,
+                               struct h2_stream_ctx *stream,
+                               CURLcode result)
+{
+  struct cf_h2_ctx *ctx = cf->ctx;
+
+  failf(data, "Error receiving HTTP2 header: %d(%s)", result,
+        curl_easy_strerror(result));
+  if(stream) {
+    nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE,
+                              stream->id, NGHTTP2_STREAM_CLOSED);
+    stream->closed = TRUE;
+    stream->reset = TRUE;
+  }
+}
+
 /* frame->hd.type is either NGHTTP2_HEADERS or NGHTTP2_PUSH_PROMISE */
 static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
                      const uint8_t *name, size_t namelen,
@@ -1512,7 +1617,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
 
   /* get the stream from the hash based on Stream ID */
   data_s = nghttp2_session_get_stream_user_data(session, stream_id);
-  if(!data_s)
+  if(!GOOD_EASY_HANDLE(data_s))
     /* Receiving a Stream ID not in the hash should not happen, this is an
        internal error more than anything else! */
     return NGHTTP2_ERR_CALLBACK_FAILURE;
@@ -1592,8 +1697,10 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
     result = Curl_dynhds_add(&stream->resp_trailers,
                              (const char *)name, namelen,
                              (const char *)value, valuelen);
-    if(result)
+    if(result) {
+      cf_h2_header_error(cf, data_s, stream, result);
       return NGHTTP2_ERR_CALLBACK_FAILURE;
+    }
 
     return 0;
   }
@@ -1604,24 +1711,30 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
     char buffer[32];
     result = Curl_http_decode_status(&stream->status_code,
                                      (const char *)value, valuelen);
-    if(result)
+    if(result) {
+      cf_h2_header_error(cf, data_s, stream, result);
       return NGHTTP2_ERR_CALLBACK_FAILURE;
+    }
     msnprintf(buffer, sizeof(buffer), HTTP_PSEUDO_STATUS ":%u\r",
               stream->status_code);
     result = Curl_headers_push(data_s, buffer, CURLH_PSEUDO);
-    if(result)
+    if(result) {
+      cf_h2_header_error(cf, data_s, stream, result);
       return NGHTTP2_ERR_CALLBACK_FAILURE;
-    Curl_dyn_reset(&ctx->scratch);
-    result = Curl_dyn_addn(&ctx->scratch, STRCONST("HTTP/2 "));
+    }
+    curlx_dyn_reset(&ctx->scratch);
+    result = curlx_dyn_addn(&ctx->scratch, STRCONST("HTTP/2 "));
     if(!result)
-      result = Curl_dyn_addn(&ctx->scratch, value, valuelen);
+      result = curlx_dyn_addn(&ctx->scratch, value, valuelen);
     if(!result)
-      result = Curl_dyn_addn(&ctx->scratch, STRCONST(" \r\n"));
+      result = curlx_dyn_addn(&ctx->scratch, STRCONST(" \r\n"));
     if(!result)
-      h2_xfer_write_resp_hd(cf, data_s, stream, Curl_dyn_ptr(&ctx->scratch),
-                            Curl_dyn_len(&ctx->scratch), FALSE);
-    if(result)
+      h2_xfer_write_resp_hd(cf, data_s, stream, curlx_dyn_ptr(&ctx->scratch),
+                            curlx_dyn_len(&ctx->scratch), FALSE);
+    if(result) {
+      cf_h2_header_error(cf, data_s, stream, result);
       return NGHTTP2_ERR_CALLBACK_FAILURE;
+    }
     /* if we receive data for another handle, wake that up */
     if(CF_DATA_CURRENT(cf) != data_s)
       Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
@@ -1634,19 +1747,21 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
   /* nghttp2 guarantees that namelen > 0, and :status was already
      received, and this is not pseudo-header field . */
   /* convert to an HTTP1-style header */
-  Curl_dyn_reset(&ctx->scratch);
-  result = Curl_dyn_addn(&ctx->scratch, (const char *)name, namelen);
+  curlx_dyn_reset(&ctx->scratch);
+  result = curlx_dyn_addn(&ctx->scratch, (const char *)name, namelen);
   if(!result)
-    result = Curl_dyn_addn(&ctx->scratch, STRCONST(": "));
+    result = curlx_dyn_addn(&ctx->scratch, STRCONST(": "));
   if(!result)
-    result = Curl_dyn_addn(&ctx->scratch, (const char *)value, valuelen);
+    result = curlx_dyn_addn(&ctx->scratch, (const char *)value, valuelen);
   if(!result)
-    result = Curl_dyn_addn(&ctx->scratch, STRCONST("\r\n"));
+    result = curlx_dyn_addn(&ctx->scratch, STRCONST("\r\n"));
   if(!result)
-    h2_xfer_write_resp_hd(cf, data_s, stream, Curl_dyn_ptr(&ctx->scratch),
-                          Curl_dyn_len(&ctx->scratch), FALSE);
-  if(result)
+    h2_xfer_write_resp_hd(cf, data_s, stream, curlx_dyn_ptr(&ctx->scratch),
+                          curlx_dyn_len(&ctx->scratch), FALSE);
+  if(result) {
+    cf_h2_header_error(cf, data_s, stream, result);
     return NGHTTP2_ERR_CALLBACK_FAILURE;
+  }
   /* if we receive data for another handle, wake that up */
   if(CF_DATA_CURRENT(cf) != data_s)
     Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
@@ -1735,22 +1850,22 @@ CURLcode Curl_http2_request_upgrade(struct dynbuf *req,
   binlen = populate_binsettings(binsettings, data);
   if(binlen <= 0) {
     failf(data, "nghttp2 unexpectedly failed on pack_settings_payload");
-    Curl_dyn_free(req);
+    curlx_dyn_free(req);
     return CURLE_FAILED_INIT;
   }
 
-  result = Curl_base64url_encode((const char *)binsettings, (size_t)binlen,
-                                 &base64, &blen);
+  result = curlx_base64url_encode((const char *)binsettings, (size_t)binlen,
+                                  &base64, &blen);
   if(result) {
-    Curl_dyn_free(req);
+    curlx_dyn_free(req);
     return result;
   }
 
-  result = Curl_dyn_addf(req,
-                         "Connection: Upgrade, HTTP2-Settings\r\n"
-                         "Upgrade: %s\r\n"
-                         "HTTP2-Settings: %s\r\n",
-                         NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, base64);
+  result = curlx_dyn_addf(req,
+                          "Connection: Upgrade, HTTP2-Settings\r\n"
+                          "Upgrade: %s\r\n"
+                          "HTTP2-Settings: %s\r\n",
+                          NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, base64);
   free(base64);
 
   k->upgr101 = UPGR101_H2;
@@ -1810,25 +1925,25 @@ static ssize_t http2_handle_stream_close(struct Curl_cfilter *cf,
     size_t i;
 
     *err = CURLE_OK;
-    Curl_dyn_init(&dbuf, DYN_TRAILERS);
+    curlx_dyn_init(&dbuf, DYN_TRAILERS);
     for(i = 0; i < Curl_dynhds_count(&stream->resp_trailers); ++i) {
       e = Curl_dynhds_getn(&stream->resp_trailers, i);
       if(!e)
         break;
-      Curl_dyn_reset(&dbuf);
-      *err = Curl_dyn_addf(&dbuf, "%.*s: %.*s\x0d\x0a",
-                          (int)e->namelen, e->name,
-                          (int)e->valuelen, e->value);
+      curlx_dyn_reset(&dbuf);
+      *err = curlx_dyn_addf(&dbuf, "%.*s: %.*s\x0d\x0a",
+                            (int)e->namelen, e->name,
+                            (int)e->valuelen, e->value);
       if(*err)
         break;
-      Curl_debug(data, CURLINFO_HEADER_IN, Curl_dyn_ptr(&dbuf),
-                 Curl_dyn_len(&dbuf));
+      Curl_debug(data, CURLINFO_HEADER_IN, curlx_dyn_ptr(&dbuf),
+                 curlx_dyn_len(&dbuf));
       *err = Curl_client_write(data, CLIENTWRITE_HEADER|CLIENTWRITE_TRAILER,
-                               Curl_dyn_ptr(&dbuf), Curl_dyn_len(&dbuf));
+                               curlx_dyn_ptr(&dbuf), curlx_dyn_len(&dbuf));
       if(*err)
         break;
     }
-    Curl_dyn_free(&dbuf);
+    curlx_dyn_free(&dbuf);
     if(*err)
       goto out;
   }
@@ -1963,6 +2078,11 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf,
   CURLcode result = CURLE_OK;
   ssize_t nread;
 
+  if(should_close_session(ctx)) {
+    CURL_TRC_CF(data, cf, "progress ingress, session is closed");
+    return CURLE_HTTP2;
+  }
+
   /* Process network input buffer fist */
   if(!Curl_bufq_is_empty(&ctx->inbufq)) {
     CURL_TRC_CF(data, cf, "Process %zu bytes in connection buffer",
@@ -2035,7 +2155,7 @@ static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
      * a read() is called anyway. It is not clear what the calling sequence
      * is for such a case. */
     failf(data, "http/2 recv on a transfer never opened "
-          "or already cleared, mid=%" FMT_OFF_T, data->mid);
+          "or already cleared, mid=%u", data->mid);
     *err = CURLE_HTTP2;
     return -1;
   }
@@ -2442,7 +2562,7 @@ static void cf_h2_adjust_pollset(struct Curl_cfilter *cf,
 
 static CURLcode cf_h2_connect(struct Curl_cfilter *cf,
                               struct Curl_easy *data,
-                              bool blocking, bool *done)
+                              bool *done)
 {
   struct cf_h2_ctx *ctx = cf->ctx;
   CURLcode result = CURLE_OK;
@@ -2456,7 +2576,7 @@ static CURLcode cf_h2_connect(struct Curl_cfilter *cf,
 
   /* Connect the lower filters first */
   if(!cf->next->connected) {
-    result = Curl_conn_cf_connect(cf->next, data, blocking, done);
+    result = Curl_conn_cf_connect(cf->next, data, done);
     if(result || !*done)
       return result;
   }
@@ -2537,6 +2657,7 @@ static CURLcode cf_h2_shutdown(struct Curl_cfilter *cf,
   CF_DATA_SAVE(save, cf, data);
 
   if(!ctx->sent_goaway) {
+    ctx->sent_goaway = TRUE;
     rv = nghttp2_submit_goaway(ctx->h2, NGHTTP2_FLAG_NONE,
                                ctx->local_max_sid, 0,
                                (const uint8_t *)"shutdown",
@@ -2547,7 +2668,6 @@ static CURLcode cf_h2_shutdown(struct Curl_cfilter *cf,
       result = CURLE_SEND_ERROR;
       goto out;
     }
-    ctx->sent_goaway = TRUE;
   }
   /* GOAWAY submitted, process egress and ingress until nghttp2 is done. */
   result = CURLE_OK;
@@ -2580,7 +2700,10 @@ static CURLcode http2_data_pause(struct Curl_cfilter *cf,
 
   DEBUGASSERT(data);
   if(ctx && ctx->h2 && stream) {
-    CURLcode result = cf_h2_update_local_win(cf, data, stream, pause);
+    CURLcode result;
+
+    stream->write_paused = pause;
+    result = cf_h2_update_local_win(cf, data, stream);
     if(result)
       return result;
 
@@ -2684,7 +2807,7 @@ static CURLcode cf_h2_query(struct Curl_cfilter *cf,
     CF_DATA_SAVE(save, cf, data);
     if(nghttp2_session_check_request_allowed(ctx->h2) == 0) {
       /* the limit is what we have in use right now */
-      effective_max = CONN_INUSE(cf->conn);
+      effective_max = CONN_ATTACHED(cf->conn);
     }
     else {
       effective_max = ctx->max_concurrent_streams;
@@ -2795,8 +2918,9 @@ static CURLcode http2_cfilter_insert_after(struct Curl_cfilter *cf,
 
 bool Curl_http2_may_switch(struct Curl_easy *data)
 {
-  if(Curl_conn_http_version(data) < 20 &&
-     data->state.httpwant == CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) {
+  if(Curl_conn_http_version(data, data->conn) < 20 &&
+     (data->state.http_neg.wanted & CURL_HTTP_V2x) &&
+     data->state.http_neg.h2_prior_knowledge) {
 #ifndef CURL_DISABLE_PROXY
     if(data->conn->bits.httpproxy && !data->conn->bits.tunnel_proxy) {
       /* We do not support HTTP/2 proxies yet. Also it is debatable
@@ -2815,7 +2939,7 @@ CURLcode Curl_http2_switch(struct Curl_easy *data)
   struct Curl_cfilter *cf;
   CURLcode result;
 
-  DEBUGASSERT(Curl_conn_http_version(data) < 20);
+  DEBUGASSERT(Curl_conn_http_version(data, data->conn) < 20);
 
   result = http2_cfilter_add(&cf, data, data->conn, FIRSTSOCKET, FALSE);
   if(result)
@@ -2827,7 +2951,7 @@ CURLcode Curl_http2_switch(struct Curl_easy *data)
 
   if(cf->next) {
     bool done;
-    return Curl_conn_cf_connect(cf, data, FALSE, &done);
+    return Curl_conn_cf_connect(cf, data, &done);
   }
   return CURLE_OK;
 }
@@ -2837,7 +2961,7 @@ CURLcode Curl_http2_switch_at(struct Curl_cfilter *cf, struct Curl_easy *data)
   struct Curl_cfilter *cf_h2;
   CURLcode result;
 
-  DEBUGASSERT(Curl_conn_http_version(data) < 20);
+  DEBUGASSERT(Curl_conn_http_version(data, data->conn) < 20);
 
   result = http2_cfilter_insert_after(cf, data, FALSE);
   if(result)
@@ -2849,7 +2973,7 @@ CURLcode Curl_http2_switch_at(struct Curl_cfilter *cf, struct Curl_easy *data)
 
   if(cf_h2->next) {
     bool done;
-    return Curl_conn_cf_connect(cf_h2, data, FALSE, &done);
+    return Curl_conn_cf_connect(cf_h2, data, &done);
   }
   return CURLE_OK;
 }
@@ -2862,7 +2986,7 @@ CURLcode Curl_http2_upgrade(struct Curl_easy *data,
   struct cf_h2_ctx *ctx;
   CURLcode result;
 
-  DEBUGASSERT(Curl_conn_http_version(data) <  20);
+  DEBUGASSERT(Curl_conn_http_version(data, conn) <  20);
   DEBUGASSERT(data->req.upgr101 == UPGR101_RECEIVED);
 
   result = http2_cfilter_add(&cf, data, conn, sockindex, TRUE);
@@ -2900,7 +3024,7 @@ CURLcode Curl_http2_upgrade(struct Curl_easy *data,
 
   if(cf->next) {
     bool done;
-    return Curl_conn_cf_connect(cf, data, FALSE, &done);
+    return Curl_conn_cf_connect(cf, data, &done);
   }
   return CURLE_OK;
 }
@@ -2909,7 +3033,7 @@ CURLcode Curl_http2_upgrade(struct Curl_easy *data,
    CURLE_HTTP2_STREAM error! */
 bool Curl_h2_http_1_1_error(struct Curl_easy *data)
 {
-  if(Curl_conn_http_version(data) == 20) {
+  if(Curl_conn_http_version(data, data->conn) == 20) {
     int err = Curl_conn_get_stream_error(data, data->conn, FIRSTSOCKET);
     return err == NGHTTP2_HTTP_1_1_REQUIRED;
   }
diff --git a/extra/curl/curl-8.12.1/lib/http2.h b/extra/curl/curl-8.14.1/lib/http2.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/http2.h
rename to extra/curl/curl-8.14.1/lib/http2.h
diff --git a/extra/curl/curl-8.12.1/lib/http_aws_sigv4.c b/extra/curl/curl-8.14.1/lib/http_aws_sigv4.c
similarity index 52%
rename from extra/curl/curl-8.12.1/lib/http_aws_sigv4.c
rename to extra/curl/curl-8.14.1/lib/http_aws_sigv4.c
index c217d0d8e6e7..74253823591d 100644
--- a/extra/curl/curl-8.12.1/lib/http_aws_sigv4.c
+++ b/extra/curl/curl-8.14.1/lib/http_aws_sigv4.c
@@ -9,7 +9,7 @@
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
- * are also available at https://curl.haxx.se/docs/copyright.html.
+ * are also available at https://curl.se/docs/copyright.html.
  *
  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
  * copies of the Software, and permit persons to whom the Software is
@@ -35,7 +35,7 @@
 #include "parsedate.h"
 #include "sendf.h"
 #include "escape.h"
-#include "strparse.h"
+#include "curlx/strparse.h"
 
 #include <time.h>
 
@@ -46,16 +46,16 @@
 
 #include "slist.h"
 
-#define HMAC_SHA256(k, kl, d, dl, o)           \
-  do {                                         \
-    result = Curl_hmacit(&Curl_HMAC_SHA256,    \
-                         (unsigned char *)k,   \
-                         kl,                   \
-                         (unsigned char *)d,   \
-                         dl, o);               \
-    if(result) {                               \
-      goto fail;                               \
-    }                                          \
+#define HMAC_SHA256(k, kl, d, dl, o)                \
+  do {                                              \
+    result = Curl_hmacit(&Curl_HMAC_SHA256,         \
+                         (const unsigned char *)k,  \
+                         kl,                        \
+                         (const unsigned char *)d,  \
+                         dl, o);                    \
+    if(result) {                                    \
+      goto fail;                                    \
+    }                                               \
   } while(0)
 
 #define TIMESTAMP_SIZE 17
@@ -63,6 +63,27 @@
 /* hex-encoded with trailing null */
 #define SHA256_HEX_LENGTH (2 * CURL_SHA256_DIGEST_LENGTH + 1)
 
+#define MAX_QUERY_COMPONENTS 128
+
+struct pair {
+  struct dynbuf key;
+  struct dynbuf value;
+};
+
+static void dyn_array_free(struct dynbuf *db, size_t num_elements);
+static void pair_array_free(struct pair *pair_array, size_t num_elements);
+static CURLcode split_to_dyn_array(const char *source,
+                                   struct dynbuf db[MAX_QUERY_COMPONENTS],
+                                   size_t *num_splits);
+static bool is_reserved_char(const char c);
+static CURLcode uri_encode_path(struct Curl_str *original_path,
+                                struct dynbuf *new_path);
+static CURLcode encode_query_component(char *component, size_t len,
+                                       struct dynbuf *db);
+static CURLcode http_aws_decode_encode(const char *in, size_t in_len,
+                                       struct dynbuf *out);
+static bool should_urlencode(struct Curl_str *service_name);
+
 static void sha256_to_hex(char *dst, unsigned char *sha)
 {
   Curl_hexencode(sha, CURL_SHA256_DIGEST_LENGTH,
@@ -83,7 +104,7 @@ static void trim_headers(struct curl_slist *head)
 {
   struct curl_slist *l;
   for(l = head; l; l = l->next) {
-    char *value; /* to read from */
+    const char *value; /* to read from */
     char *store;
     size_t colon = strcspn(l->data, ":");
     Curl_strntolower(l->data, l->data, colon);
@@ -92,15 +113,14 @@ static void trim_headers(struct curl_slist *head)
     if(!*value)
       continue;
     ++value;
-    store = value;
+    store = (char *)CURL_UNCONST(value);
 
     /* skip leading whitespace */
-    while(*value && ISBLANK(*value))
-      value++;
+    curlx_str_passblanks(&value);
 
     while(*value) {
       int space = 0;
-      while(*value && ISBLANK(*value)) {
+      while(ISBLANK(*value)) {
         value++;
         space++;
       }
@@ -113,7 +133,7 @@ static void trim_headers(struct curl_slist *head)
       else
         *store++ = *value++;
     }
-    *store = 0; /* null terminate */
+    *store = 0; /* null-terminate */
   }
 }
 
@@ -155,6 +175,57 @@ static int compare_header_names(const char *a, const char *b)
   return cmp;
 }
 
+/* Merge duplicate header definitions by comma delimiting their values
+   in the order defined the headers are defined, expecting headers to
+   be alpha-sorted and use ':' at this point */
+static CURLcode merge_duplicate_headers(struct curl_slist *head)
+{
+  struct curl_slist *curr = head;
+  CURLcode result = CURLE_OK;
+
+  while(curr) {
+    struct curl_slist *next = curr->next;
+    if(!next)
+      break;
+
+    if(compare_header_names(curr->data, next->data) == 0) {
+      struct dynbuf buf;
+      char *colon_next;
+      char *val_next;
+
+      curlx_dyn_init(&buf, CURL_MAX_HTTP_HEADER);
+
+      result = curlx_dyn_add(&buf, curr->data);
+      if(result)
+        return result;
+
+      colon_next = strchr(next->data, ':');
+      DEBUGASSERT(colon_next);
+      val_next = colon_next + 1;
+
+      result = curlx_dyn_addn(&buf, ",", 1);
+      if(result)
+        return result;
+
+      result = curlx_dyn_add(&buf, val_next);
+      if(result)
+        return result;
+
+      free(curr->data);
+      curr->data = curlx_dyn_ptr(&buf);
+
+      curr->next = next->next;
+      free(next->data);
+      free(next);
+    }
+    else {
+      curr = curr->next;
+    }
+  }
+
+  return CURLE_OK;
+}
+
 /* timestamp should point to a buffer of at last TIMESTAMP_SIZE bytes */
 static CURLcode make_headers(struct Curl_easy *data,
                              const char *hostname,
@@ -234,7 +305,7 @@ static CURLcode make_headers(struct Curl_easy *data,
       sep = strchr(l->data, ';');
     if(!sep || (*sep == ':' && !*(sep + 1)))
       continue;
-    for(ptr = sep + 1; ISSPACE(*ptr); ++ptr)
+    for(ptr = sep + 1; ISBLANK(*ptr); ++ptr)
       ;
     if(!*ptr && ptr != sep + 1) /* a value of whitespace only */
       continue;
@@ -261,16 +332,15 @@ static CURLcode make_headers(struct Curl_easy *data,
     *date_header = aprintf("%s: %s\r\n", date_hdr_key, timestamp);
   }
   else {
-    char *value;
-    char *endp;
+    const char *value;
+    const char *endp;
     value = strchr(*date_header, ':');
     if(!value) {
       *date_header = NULL;
       goto fail;
     }
     ++value;
-    while(ISBLANK(*value))
-      ++value;
+    curlx_str_passblanks(&value);
     endp = value;
     while(*endp && ISALNUM(*endp))
       ++endp;
@@ -301,12 +371,16 @@ static CURLcode make_headers(struct Curl_easy *data,
     }
   } while(again);
 
+  ret = merge_duplicate_headers(head);
+  if(ret)
+    goto fail;
+
   for(l = head; l; l = l->next) {
     char *tmp;
 
-    if(Curl_dyn_add(canonical_headers, l->data))
+    if(curlx_dyn_add(canonical_headers, l->data))
       goto fail;
-    if(Curl_dyn_add(canonical_headers, "\n"))
+    if(curlx_dyn_add(canonical_headers, "\n"))
       goto fail;
 
     tmp = strchr(l->data, ':');
@@ -314,10 +388,10 @@ static CURLcode make_headers(struct Curl_easy *data,
       *tmp = 0;
 
     if(l != head) {
-      if(Curl_dyn_add(signed_headers, ";"))
+      if(curlx_dyn_add(signed_headers, ";"))
         goto fail;
     }
-    if(Curl_dyn_add(signed_headers, l->data))
+    if(curlx_dyn_add(signed_headers, l->data))
       goto fail;
   }
 
@@ -334,14 +408,13 @@ static CURLcode make_headers(struct Curl_easy *data,
                                 SHA256_HEX_LENGTH)
 
 /* try to parse a payload hash from the content-sha256 header */
-static char *parse_content_sha_hdr(struct Curl_easy *data,
-                                   const char *provider1,
-                                   size_t plen,
-                                   size_t *value_len)
-{
+static const char *parse_content_sha_hdr(struct Curl_easy *data,
+                                         const char *provider1,
+                                         size_t plen,
+                                         size_t *value_len) {
   char key[CONTENT_SHA256_KEY_LEN];
   size_t key_len;
-  char *value;
+  const char *value;
   size_t len;
 
   key_len = msnprintf(key, sizeof(key), "x-%.*s-content-sha256",
@@ -356,8 +429,7 @@ static char *parse_content_sha_hdr(struct Curl_easy *data,
     return NULL;
   ++value;
 
-  while(*value && ISBLANK(*value))
-    ++value;
+  curlx_str_passblanks(&value);
 
   len = strlen(value);
   while(len > 0 && ISBLANK(value[len-1]))
@@ -390,7 +462,8 @@ static CURLcode calc_payload_hash(struct Curl_easy *data,
 #define S3_UNSIGNED_PAYLOAD "UNSIGNED-PAYLOAD"
 
 static CURLcode calc_s3_payload_hash(struct Curl_easy *data,
-                                     Curl_HttpReq httpreq, char *provider1,
+                                     Curl_HttpReq httpreq,
+                                     const char *provider1,
                                      size_t plen,
                                      unsigned char *sha_hash,
                                      char *sha_hex, char *header)
@@ -425,158 +498,181 @@ static CURLcode calc_s3_payload_hash(struct Curl_easy *data,
   return ret;
 }
 
-struct pair {
-  const char *p;
-  size_t len;
-};
-
 static int compare_func(const void *a, const void *b)
 {
+
   const struct pair *aa = a;
   const struct pair *bb = b;
+  const size_t aa_key_len = curlx_dyn_len(&aa->key);
+  const size_t bb_key_len = curlx_dyn_len(&bb->key);
+  const size_t aa_value_len = curlx_dyn_len(&aa->value);
+  const size_t bb_value_len = curlx_dyn_len(&bb->value);
+  int compare;
+
   /* If one element is empty, the other is always sorted higher */
-  if(aa->len == 0 && bb->len == 0)
+
+  /* Compare keys */
+  if((aa_key_len == 0) && (bb_key_len == 0))
     return 0;
-  if(aa->len == 0)
+  if(aa_key_len == 0)
     return -1;
-  if(bb->len == 0)
+  if(bb_key_len == 0)
     return 1;
-  return strncmp(aa->p, bb->p, aa->len < bb->len ? aa->len : bb->len);
-}
+  compare = strcmp(curlx_dyn_ptr(&aa->key), curlx_dyn_ptr(&bb->key));
+  if(compare) {
+    return compare;
+  }
+
+  /* Compare values */
+  if((aa_value_len == 0) && (bb_value_len == 0))
+    return 0;
+  if(aa_value_len == 0)
+    return -1;
+  if(bb_value_len == 0)
+    return 1;
+  compare = strcmp(curlx_dyn_ptr(&aa->value), curlx_dyn_ptr(&bb->value));
 
-#define MAX_QUERYPAIRS 64
+  return compare;
 
-/**
- * found_equals have a double meaning,
- * detect if an equal have been found when called from canon_query,
- * and mark that this function is called to compute the path,
- * if found_equals is NULL.
- */
-static CURLcode canon_string(const char *q, size_t len,
-                             struct dynbuf *dq, bool *found_equals)
+}
+
+UNITTEST CURLcode canon_path(const char *q, size_t len,
+                              struct dynbuf *new_path,
+                              bool do_uri_encode)
 {
   CURLcode result = CURLE_OK;
 
-  for(; len && !result; q++, len--) {
-    if(ISALNUM(*q))
-      result = Curl_dyn_addn(dq, q, 1);
-    else {
-      switch(*q) {
-      case '-':
-      case '.':
-      case '_':
-      case '~':
-        /* allowed as-is */
-        result = Curl_dyn_addn(dq, q, 1);
-        break;
-      case '%':
-        /* uppercase the following if hexadecimal */
-        if(ISXDIGIT(q[1]) && ISXDIGIT(q[2])) {
-          char tmp[3]="%";
-          tmp[1] = Curl_raw_toupper(q[1]);
-          tmp[2] = Curl_raw_toupper(q[2]);
-          result = Curl_dyn_addn(dq, tmp, 3);
-          q += 2;
-          len -= 2;
-        }
-        else
-          /* '%' without a following two-digit hex, encode it */
-          result = Curl_dyn_addn(dq, "%25", 3);
-        break;
-      default: {
-        const char hex[] = "0123456789ABCDEF";
-        char out[3]={'%'};
-
-        if(!found_equals) {
-          /* if found_equals is NULL assuming, been in path */
-          if(*q == '/') {
-            /* allowed as if */
-            result = Curl_dyn_addn(dq, q, 1);
-            break;
-          }
-        }
-        else {
-          /* allowed as-is */
-          if(*q == '=') {
-            result = Curl_dyn_addn(dq, q, 1);
-            *found_equals = TRUE;
-            break;
-          }
-        }
-        /* URL encode */
-        out[1] = hex[((unsigned char)*q) >> 4];
-        out[2] = hex[*q & 0xf];
-        result = Curl_dyn_addn(dq, out, 3);
-        break;
-      }
-      }
-    }
+  struct Curl_str original_path;
+
+  curlx_str_assign(&original_path, q, len);
+
+  /* Normalized path will be either the same or shorter than the original
+   * path, plus trailing slash */
+
+  if(do_uri_encode)
+    result = uri_encode_path(&original_path, new_path);
+  else
+    result = curlx_dyn_addn(new_path, q, len);
+
+  if(!result) {
+    if(curlx_dyn_len(new_path) == 0)
+      result = curlx_dyn_add(new_path, "/");
   }
+
   return result;
 }
 
-
-static CURLcode canon_query(struct Curl_easy *data,
-                            const char *query, struct dynbuf *dq)
+UNITTEST CURLcode canon_query(const char *query, struct dynbuf *dq)
 {
   CURLcode result = CURLE_OK;
-  int entry = 0;
-  int i;
-  const char *p = query;
-  struct pair array[MAX_QUERYPAIRS];
-  struct pair *ap = &array[0];
+
+  struct dynbuf query_array[MAX_QUERY_COMPONENTS];
+  struct pair encoded_query_array[MAX_QUERY_COMPONENTS];
+  size_t num_query_components;
+  size_t counted_query_components = 0;
+  size_t index;
+
   if(!query)
     return result;
 
-  /* sort the name=value pairs first */
-  do {
-    char *amp;
-    entry++;
-    ap->p = p;
-    amp = strchr(p, '&');
-    if(amp)
-      ap->len = amp - p; /* excluding the ampersand */
+  result = split_to_dyn_array(query, &query_array[0],
+                              &num_query_components);
+  if(result) {
+    goto fail;
+  }
+
+  /* Create list of pairs, each pair containing an encoded query
+    * component */
+
+  for(index = 0; index < num_query_components; index++) {
+    const char *in_key;
+    size_t in_key_len;
+    char *offset;
+    size_t query_part_len = curlx_dyn_len(&query_array[index]);
+    char *query_part = curlx_dyn_ptr(&query_array[index]);
+
+    in_key = query_part;
+
+    offset = strchr(query_part, '=');
+    /* If there is no equals, this key has no value */
+    if(!offset) {
+      in_key_len = strlen(in_key);
+    }
     else {
-      ap->len = strlen(p);
-      break;
+      in_key_len = offset - in_key;
     }
-    ap++;
-    p = amp + 1;
-  } while(entry < MAX_QUERYPAIRS);
-  if(entry == MAX_QUERYPAIRS) {
-    /* too many query pairs for us */
-    failf(data, "aws-sigv4: too many query pairs in URL");
-    return CURLE_URL_MALFORMAT;
-  }
-
-  qsort(&array[0], entry, sizeof(struct pair), compare_func);
-
-  ap = &array[0];
-  for(i = 0; !result && (i < entry); i++, ap++) {
-    const char *q = ap->p;
-    bool found_equals = FALSE;
-    if(!ap->len)
-      continue;
-    result = canon_string(q, ap->len, dq, &found_equals);
-    if(!result && !found_equals) {
-      /* queries without value still need an equals */
-      result = Curl_dyn_addn(dq, "=", 1);
+
+    curlx_dyn_init(&encoded_query_array[index].key, query_part_len*3 + 1);
+    curlx_dyn_init(&encoded_query_array[index].value, query_part_len*3 + 1);
+    counted_query_components++;
+
+    /* Decode/encode the key */
+    result = http_aws_decode_encode(in_key, in_key_len,
+                                    &encoded_query_array[index].key);
+    if(result) {
+      goto fail;
+    }
+
+    /* Decode/encode the value if it exists */
+    if(offset && offset != (query_part + query_part_len - 1)) {
+      size_t in_value_len;
+      const char *in_value = offset + 1;
+      in_value_len = query_part + query_part_len - (offset + 1);
+      result = http_aws_decode_encode(in_value, in_value_len,
+                                      &encoded_query_array[index].value);
+      if(result) {
+        goto fail;
+      }
     }
-    if(!result && i < entry - 1) {
-      /* insert ampersands between query pairs */
-      result = Curl_dyn_addn(dq, "&", 1);
+    else {
+      /* If there is no value, the value is an empty string */
+      curlx_dyn_init(&encoded_query_array[index].value, 2);
+      result = curlx_dyn_addn(&encoded_query_array[index].value, "", 1);
+    }
+
+    if(result) {
+      goto fail;
     }
   }
+
+  /* Sort the encoded query components by key and value */
+  qsort(&encoded_query_array, num_query_components,
+        sizeof(struct pair), compare_func);
+
+  /* Append the query components together to make a full query string */
+  for(index = 0; index < num_query_components; index++) {
+
+    if(index)
+      result = curlx_dyn_addn(dq, "&", 1);
+    if(!result) {
+      char *key_ptr = curlx_dyn_ptr(&encoded_query_array[index].key);
+      char *value_ptr = curlx_dyn_ptr(&encoded_query_array[index].value);
+      size_t vlen = curlx_dyn_len(&encoded_query_array[index].value);
+      if(value_ptr && vlen) {
+        result = curlx_dyn_addf(dq, "%s=%s", key_ptr, value_ptr);
+      }
+      else {
+        /* Empty value is always encoded to key= */
+        result = curlx_dyn_addf(dq, "%s=", key_ptr);
+      }
+    }
+    if(result)
+      break;
+  }
+
+fail:
+  if(counted_query_components)
+    /* the encoded_query_array might not be initialized yet */
+    pair_array_free(&encoded_query_array[0], counted_query_components);
+  dyn_array_free(&query_array[0], num_query_components);
   return result;
 }
 
-
-CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
+CURLcode Curl_output_aws_sigv4(struct Curl_easy *data)
 {
   CURLcode result = CURLE_OUT_OF_MEMORY;
   struct connectdata *conn = data->conn;
-  size_t len;
-  char *line;
+  const char *line;
   struct Curl_str provider0;
   struct Curl_str provider1;
   struct Curl_str region = { NULL, 0};
@@ -593,7 +689,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
   char *date_header = NULL;
   Curl_HttpReq httpreq;
   const char *method = NULL;
-  char *payload_hash = NULL;
+  const char *payload_hash = NULL;
   size_t payload_hash_len = 0;
   unsigned char sha_hash[CURL_SHA256_DIGEST_LENGTH];
   char sha_hex[SHA256_HEX_LENGTH];
@@ -608,8 +704,10 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
   unsigned char sign1[CURL_SHA256_DIGEST_LENGTH] = {0};
   char *auth_headers = NULL;
 
-  DEBUGASSERT(!proxy);
-  (void)proxy;
+  if(data->set.path_as_is) {
+    failf(data, "Cannot use sigv4 authentication with path-as-is flag");
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+  }
 
   if(Curl_checkheaders(data, STRCONST("Authorization"))) {
     /* Authorization already present, Bailing out */
@@ -617,10 +715,10 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
   }
 
   /* we init those buffers here, so goto fail will free initialized dynbuf */
-  Curl_dyn_init(&canonical_headers, CURL_MAX_HTTP_HEADER);
-  Curl_dyn_init(&canonical_query, CURL_MAX_HTTP_HEADER);
-  Curl_dyn_init(&signed_headers, CURL_MAX_HTTP_HEADER);
-  Curl_dyn_init(&canonical_path, CURL_MAX_HTTP_HEADER);
+  curlx_dyn_init(&canonical_headers, CURL_MAX_HTTP_HEADER);
+  curlx_dyn_init(&canonical_query, CURL_MAX_HTTP_HEADER);
+  curlx_dyn_init(&signed_headers, CURL_MAX_HTTP_HEADER);
+  curlx_dyn_init(&canonical_path, CURL_MAX_HTTP_HEADER);
 
   /*
    * Parameters parsing
@@ -629,86 +727,70 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
    * AWS is the default because most of non-amazon providers
    * are still using aws:amz as a prefix.
    */
-  line = data->set.str[STRING_AWS_SIGV4] ?
-    data->set.str[STRING_AWS_SIGV4] : (char *)"aws:amz";
+  line = data->set.str[STRING_AWS_SIGV4];
+  if(!line || !*line)
+    line = "aws:amz";
 
   /* provider0[:provider1[:region[:service]]]
 
      No string can be longer than N bytes of non-whitespace
   */
-  if(Curl_str_until(&line, &provider0, MAX_SIGV4_LEN, ':')) {
+  if(curlx_str_until(&line, &provider0, MAX_SIGV4_LEN, ':')) {
     failf(data, "first aws-sigv4 provider cannot be empty");
     result = CURLE_BAD_FUNCTION_ARGUMENT;
     goto fail;
   }
-  if(Curl_str_single(&line, ':') ||
-     Curl_str_until(&line, &provider1, MAX_SIGV4_LEN, ':')) {
-    provider1.str = provider0.str;
-    provider1.len = provider0.len;
+  if(curlx_str_single(&line, ':') ||
+     curlx_str_until(&line, &provider1, MAX_SIGV4_LEN, ':')) {
+    provider1 = provider0;
   }
-  else if(Curl_str_single(&line, ':') ||
-          Curl_str_until(&line, &region, MAX_SIGV4_LEN, ':') ||
-          Curl_str_single(&line, ':') ||
-          Curl_str_until(&line, &service, MAX_SIGV4_LEN, ':')) {
+  else if(curlx_str_single(&line, ':') ||
+          curlx_str_until(&line, &region, MAX_SIGV4_LEN, ':') ||
+          curlx_str_single(&line, ':') ||
+          curlx_str_until(&line, &service, MAX_SIGV4_LEN, ':')) {
     /* nothing to do */
   }
 
-  if(!service.len) {
-    char *hostdot = strchr(hostname, '.');
-    if(!hostdot) {
+  if(!curlx_strlen(&service)) {
+    const char *p = hostname;
+    if(curlx_str_until(&p, &service, MAX_SIGV4_LEN, '.') ||
+       curlx_str_single(&p, '.')) {
       failf(data, "aws-sigv4: service missing in parameters and hostname");
       result = CURLE_URL_MALFORMAT;
       goto fail;
     }
-    len = hostdot - hostname;
-    if(len > MAX_SIGV4_LEN) {
-      failf(data, "aws-sigv4: service too long in hostname");
-      result = CURLE_URL_MALFORMAT;
-      goto fail;
-    }
-    service.str = (char *)hostname;
-    service.len = len;
 
     infof(data, "aws_sigv4: picked service %.*s from host",
-          (int)service.len, service.str);
+          (int)curlx_strlen(&service), curlx_str(&service));
 
-    if(!region.len) {
-      const char *reg = hostdot + 1;
-      const char *hostreg = strchr(reg, '.');
-      if(!hostreg) {
+    if(!curlx_strlen(&region)) {
+      if(curlx_str_until(&p, &region, MAX_SIGV4_LEN, '.') ||
+         curlx_str_single(&p, '.')) {
         failf(data, "aws-sigv4: region missing in parameters and hostname");
         result = CURLE_URL_MALFORMAT;
         goto fail;
       }
-      len = hostreg - reg;
-      if(len > MAX_SIGV4_LEN) {
-        failf(data, "aws-sigv4: region too long in hostname");
-        result = CURLE_URL_MALFORMAT;
-        goto fail;
-      }
-      region.str = (char *)reg;
-      region.len = len;
       infof(data, "aws_sigv4: picked region %.*s from host",
-            (int)region.len, region.str);
+            (int)curlx_strlen(&region), curlx_str(&region));
     }
   }
 
   Curl_http_method(data, conn, &method, &httpreq);
 
-  payload_hash = parse_content_sha_hdr(data, provider1.str, provider1.len,
-                                       &payload_hash_len);
+  payload_hash =
+    parse_content_sha_hdr(data, curlx_str(&provider1),
+                          curlx_strlen(&provider1), &payload_hash_len);
 
   if(!payload_hash) {
     /* AWS S3 requires a x-amz-content-sha256 header, and supports special
      * values like UNSIGNED-PAYLOAD */
-    bool sign_as_s3 = ((provider0.len == 3) &&
-                       strncasecompare(provider0.str, "aws", 3)) &&
-      ((service.len == 2) && strncasecompare(service.str, "s3", 2));
+    bool sign_as_s3 = curlx_str_casecompare(&provider0, "aws") &&
+      curlx_str_casecompare(&service, "s3");
 
     if(sign_as_s3)
-      result = calc_s3_payload_hash(data, httpreq,
-                                    provider1.str, provider1.len,
-                                    sha_hash, sha_hex, content_sha256_hdr);
+      result = calc_s3_payload_hash(data, httpreq, curlx_str(&provider1),
+                                    curlx_strlen(&provider1), sha_hash,
+                                    sha_hex, content_sha256_hdr);
     else
       result = calc_payload_hash(data, sha_hash, sha_hex);
     if(result)
@@ -740,7 +822,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
   }
 
   result = make_headers(data, hostname, timestamp,
-                        provider1.str, provider1.len,
+                        curlx_str(&provider1), curlx_strlen(&provider1),
                         &date_header, content_sha256_hdr,
                         &canonical_headers, &signed_headers);
   if(result)
@@ -756,12 +838,13 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
   memcpy(date, timestamp, sizeof(date));
   date[sizeof(date) - 1] = 0;
 
-  result = canon_query(data, data->state.up.query, &canonical_query);
+  result = canon_query(data->state.up.query, &canonical_query);
   if(result)
     goto fail;
 
-  result = canon_string(data->state.up.path, strlen(data->state.up.path),
-                        &canonical_path, NULL);
+  result = canon_path(data->state.up.path, strlen(data->state.up.path),
+                        &canonical_path,
+                        should_urlencode(&service));
   if(result)
     goto fail;
   result = CURLE_OUT_OF_MEMORY;
@@ -774,28 +857,30 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
             "%s\n" /* SignedHeaders */
             "%.*s",  /* HashedRequestPayload in hex */
             method,
-            Curl_dyn_ptr(&canonical_path),
-            Curl_dyn_ptr(&canonical_query) ?
-            Curl_dyn_ptr(&canonical_query) : "",
-            Curl_dyn_ptr(&canonical_headers),
-            Curl_dyn_ptr(&signed_headers),
+            curlx_dyn_ptr(&canonical_path),
+            curlx_dyn_ptr(&canonical_query) ?
+            curlx_dyn_ptr(&canonical_query) : "",
+            curlx_dyn_ptr(&canonical_headers),
+            curlx_dyn_ptr(&signed_headers),
             (int)payload_hash_len, payload_hash);
   if(!canonical_request)
     goto fail;
 
-  DEBUGF(infof(data, "Canonical request: %s", canonical_request));
+  infof(data, "aws_sigv4: Canonical request (enclosed in []) - [%s]",
+    canonical_request);
 
-  request_type = aprintf("%.*s4_request", (int)provider0.len, provider0.str);
+  request_type = aprintf("%.*s4_request",
+                         (int)curlx_strlen(&provider0), curlx_str(&provider0));
   if(!request_type)
     goto fail;
 
   /* provider0 is lowercased *after* aprintf() so that the buffer can be
      written to */
-  Curl_strntolower(request_type, request_type, provider0.len);
+  Curl_strntolower(request_type, request_type, curlx_strlen(&provider0));
 
-  credential_scope = aprintf("%s/%.*s/%.*s/%s",
-                             date, (int)region.len, region.str,
-                             (int)service.len, service.str,
+  credential_scope = aprintf("%s/%.*s/%.*s/%s", date,
+                             (int)curlx_strlen(&region), curlx_str(&region),
+                             (int)curlx_strlen(&service), curlx_str(&service),
                              request_type);
   if(!credential_scope)
     goto fail;
@@ -814,7 +899,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
                         "%s\n" /* RequestDateTime */
                         "%s\n" /* CredentialScope */
                         "%s",  /* HashedCanonicalRequest in hex */
-                        (int)provider0.len, provider0.str,
+                        (int)curlx_strlen(&provider0), curlx_str(&provider0),
                         timestamp,
                         credential_scope,
                         sha_hex);
@@ -822,24 +907,32 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
     goto fail;
 
   /* make provider0 part done uppercase */
-  Curl_strntoupper(str_to_sign, provider0.str, provider0.len);
+  Curl_strntoupper(str_to_sign, curlx_str(&provider0),
+                   curlx_strlen(&provider0));
+
+  infof(data, "aws_sigv4: String to sign (enclosed in []) - [%s]",
+    str_to_sign);
 
-  secret = aprintf("%.*s4%s", (int)provider0.len, provider0.str,
-                   data->state.aptr.passwd ?
+  secret = aprintf("%.*s4%s", (int)curlx_strlen(&provider0),
+                   curlx_str(&provider0), data->state.aptr.passwd ?
                    data->state.aptr.passwd : "");
   if(!secret)
     goto fail;
   /* make provider0 part done uppercase */
-  Curl_strntoupper(secret, provider0.str, provider0.len);
+  Curl_strntoupper(secret, curlx_str(&provider0), curlx_strlen(&provider0));
 
   HMAC_SHA256(secret, strlen(secret), date, strlen(date), sign0);
-  HMAC_SHA256(sign0, sizeof(sign0), region.str, region.len, sign1);
-  HMAC_SHA256(sign1, sizeof(sign1), service.str, service.len, sign0);
+  HMAC_SHA256(sign0, sizeof(sign0),
+              curlx_str(&region), curlx_strlen(&region), sign1);
+  HMAC_SHA256(sign1, sizeof(sign1),
+              curlx_str(&service), curlx_strlen(&service), sign0);
   HMAC_SHA256(sign0, sizeof(sign0), request_type, strlen(request_type), sign1);
   HMAC_SHA256(sign1, sizeof(sign1), str_to_sign, strlen(str_to_sign), sign0);
 
   sha256_to_hex(sha_hex, sign0);
 
+  infof(data, "aws_sigv4: Signature - %s", sha_hex);
+
   auth_headers = aprintf("Authorization: %.*s4-HMAC-SHA256 "
                          "Credential=%s/%s, "
                          "SignedHeaders=%s, "
@@ -851,10 +944,10 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
                           */
                          "%s"
                          "%s", /* optional sha256 header includes \r\n */
-                         (int)provider0.len, provider0.str,
+                         (int)curlx_strlen(&provider0), curlx_str(&provider0),
                          user,
                          credential_scope,
-                         Curl_dyn_ptr(&signed_headers),
+                         curlx_dyn_ptr(&signed_headers),
                          sha_hex,
                          date_header ? date_header : "",
                          content_sha256_hdr);
@@ -863,18 +956,18 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
   }
   /* provider 0 uppercase */
   Curl_strntoupper(&auth_headers[sizeof("Authorization: ") - 1],
-                   provider0.str, provider0.len);
+                   curlx_str(&provider0), curlx_strlen(&provider0));
 
-  Curl_safefree(data->state.aptr.userpwd);
+  free(data->state.aptr.userpwd);
   data->state.aptr.userpwd = auth_headers;
   data->state.authhost.done = TRUE;
   result = CURLE_OK;
 
 fail:
-  Curl_dyn_free(&canonical_query);
-  Curl_dyn_free(&canonical_path);
-  Curl_dyn_free(&canonical_headers);
-  Curl_dyn_free(&signed_headers);
+  curlx_dyn_free(&canonical_query);
+  curlx_dyn_free(&canonical_path);
+  curlx_dyn_free(&canonical_headers);
+  curlx_dyn_free(&signed_headers);
   free(canonical_request);
   free(request_type);
   free(credential_scope);
@@ -884,4 +977,176 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
   return result;
 }
 
+/*
+* Frees all allocated strings in a dynbuf pair array, and the dynbuf itself
+*/
+
+static void pair_array_free(struct pair *pair_array, size_t num_elements)
+{
+  size_t index;
+
+  for(index = 0; index != num_elements; index++) {
+    curlx_dyn_free(&pair_array[index].key);
+    curlx_dyn_free(&pair_array[index].value);
+  }
+
+}
+
+/*
+* Frees all allocated strings in a split dynbuf, and the dynbuf itself
+*/
+
+static void dyn_array_free(struct dynbuf *db, size_t num_elements)
+{
+  size_t index;
+
+  for(index = 0; index < num_elements; index++)
+    curlx_dyn_free((&db[index]));
+}
+
+/*
+* Splits source string by SPLIT_BY, and creates an array of dynbuf in db.
+* db is initialized by this function.
+* Caller is responsible for freeing the array elements with dyn_array_free
+*/
+
+#define SPLIT_BY '&'
+
+static CURLcode split_to_dyn_array(const char *source,
+                                   struct dynbuf db[MAX_QUERY_COMPONENTS],
+                                   size_t *num_splits_out)
+{
+  CURLcode result = CURLE_OK;
+  size_t len = strlen(source);
+  size_t pos;         /* Position in result buffer */
+  size_t start = 0;   /* Start of current segment */
+  size_t segment_length = 0;
+  size_t index = 0;
+  size_t num_splits = 0;
+
+  /* Split source_ptr on SPLIT_BY and store the segment offsets and length in
+   * array */
+  for(pos = 0; pos < len; pos++) {
+    if(source[pos] == SPLIT_BY) {
+      if(segment_length) {
+        curlx_dyn_init(&db[index], segment_length + 1);
+        result = curlx_dyn_addn(&db[index], &source[start],
+                                segment_length);
+        if(result)
+          goto fail;
+
+        segment_length = 0;
+        index++;
+        if(++num_splits == MAX_QUERY_COMPONENTS) {
+          result = CURLE_TOO_LARGE;
+          goto fail;
+        }
+      }
+      start = pos + 1;
+    }
+    else {
+      segment_length++;
+    }
+  }
+
+  if(segment_length) {
+    curlx_dyn_init(&db[index], segment_length + 1);
+    result = curlx_dyn_addn(&db[index], &source[start], segment_length);
+    if(!result) {
+      if(++num_splits == MAX_QUERY_COMPONENTS)
+        result = CURLE_TOO_LARGE;
+    }
+  }
+fail:
+  *num_splits_out = num_splits;
+  return result;
+}
+
+
+static bool is_reserved_char(const char c)
+{
+  return (ISALNUM(c) || ISURLPUNTCS(c));
+}
+
+static CURLcode uri_encode_path(struct Curl_str *original_path,
+                                struct dynbuf *new_path)
+{
+  const char *p = curlx_str(original_path);
+  size_t i;
+
+  for(i = 0; i < curlx_strlen(original_path); i++) {
+    /* Do not encode slashes or unreserved chars from RFC 3986 */
+    CURLcode result = CURLE_OK;
+    unsigned char c = p[i];
+    if(is_reserved_char(c) || c == '/')
+      result = curlx_dyn_addn(new_path, &c, 1);
+    else
+      result = curlx_dyn_addf(new_path, "%%%02X", c);
+    if(result)
+      return result;
+  }
+
+  return CURLE_OK;
+}
+
+
+static CURLcode encode_query_component(char *component, size_t len,
+                                       struct dynbuf *db)
+{
+  size_t i;
+  for(i = 0; i < len; i++) {
+    CURLcode result = CURLE_OK;
+    unsigned char this_char = component[i];
+
+    if(is_reserved_char(this_char))
+      /* Escape unreserved chars from RFC 3986 */
+      result = curlx_dyn_addn(db, &this_char, 1);
+    else if(this_char == '+')
+      /* Encode '+' as space */
+      result = curlx_dyn_add(db, "%20");
+    else
+      result = curlx_dyn_addf(db, "%%%02X", this_char);
+    if(result)
+      return result;
+  }
+
+  return CURLE_OK;
+}
+
+/*
+* Populates a dynbuf containing url_encode(url_decode(in))
+*/
+
+static CURLcode http_aws_decode_encode(const char *in, size_t in_len,
+                                       struct dynbuf *out)
+{
+  char *out_s;
+  size_t out_s_len;
+  CURLcode result =
+    Curl_urldecode(in, in_len, &out_s, &out_s_len, REJECT_NADA);
+
+  if(!result) {
+    result = encode_query_component(out_s, out_s_len, out);
+    Curl_safefree(out_s);
+  }
+  return result;
+}
+
+static bool should_urlencode(struct Curl_str *service_name)
+{
+  /*
+   * These services require unmodified (not additionally url encoded) URL
+   * paths.
+   * should_urlencode == true is equivalent to should_urlencode_uri_path
+   * from the AWS SDK. Urls are already normalized by the curl url parser
+   */
+
+  if(curlx_str_cmp(service_name, "s3") ||
+     curlx_str_cmp(service_name, "s3-express") ||
+     curlx_str_cmp(service_name, "s3-outposts")) {
+    return false;
+  }
+  return true;
+}
+
 #endif /* !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_AWS) */
diff --git a/extra/curl/curl-8.12.1/lib/file.h b/extra/curl/curl-8.14.1/lib/http_aws_sigv4.h
similarity index 63%
rename from extra/curl/curl-8.12.1/lib/file.h
rename to extra/curl/curl-8.14.1/lib/http_aws_sigv4.h
index 456552559202..9747c948a69d 100644
--- a/extra/curl/curl-8.12.1/lib/file.h
+++ b/extra/curl/curl-8.14.1/lib/http_aws_sigv4.h
@@ -1,5 +1,5 @@
-#ifndef HEADER_CURL_FILE_H
-#define HEADER_CURL_FILE_H
+#ifndef HEADER_CURL_HTTP_AWS_SIGV4_H
+#define HEADER_CURL_HTTP_AWS_SIGV4_H
 /***************************************************************************
  *                                  _   _ ____  _
  *  Project                     ___| | | |  _ \| |
@@ -23,20 +23,19 @@
  * SPDX-License-Identifier: curl
  *
  ***************************************************************************/
+#include "curl_setup.h"
+#include "curlx/dynbuf.h"
+#include "urldata.h"
+#include "curlx/strparse.h"
 
+/* this is for creating aws_sigv4 header output */
+CURLcode Curl_output_aws_sigv4(struct Curl_easy *data);
 
-/****************************************************************************
- * FILE unique setup
- ***************************************************************************/
-struct FILEPROTO {
-  char *path; /* the path we operate on */
-  char *freepath; /* pointer to the allocated block we must free, this might
-                     differ from the 'path' pointer */
-  int fd;     /* open file descriptor to read from! */
-};
-
-#ifndef CURL_DISABLE_FILE
-extern const struct Curl_handler Curl_handler_file;
+#ifdef UNITTESTS
+UNITTEST CURLcode canon_path(const char *q, size_t len,
+    struct dynbuf *new_path,
+    bool normalize);
+UNITTEST CURLcode canon_query(const char *query, struct dynbuf *dq);
 #endif
 
-#endif /* HEADER_CURL_FILE_H */
+#endif /* HEADER_CURL_HTTP_AWS_SIGV4_H */
diff --git a/extra/curl/curl-8.12.1/lib/http_chunks.c b/extra/curl/curl-8.14.1/lib/http_chunks.c
similarity index 95%
rename from extra/curl/curl-8.12.1/lib/http_chunks.c
rename to extra/curl/curl-8.14.1/lib/http_chunks.c
index aea84be98635..63e477c48a68 100644
--- a/extra/curl/curl-8.12.1/lib/http_chunks.c
+++ b/extra/curl/curl-8.14.1/lib/http_chunks.c
@@ -30,12 +30,12 @@
 #include "curl_printf.h"
 #include "curl_trc.h"
 #include "sendf.h"   /* for the client write stuff */
-#include "dynbuf.h"
+#include "curlx/dynbuf.h"
 #include "content_encoding.h"
 #include "http.h"
 #include "multiif.h"
-#include "strtoofft.h"
-#include "warnless.h"
+#include "curlx/strparse.h"
+#include "curlx/warnless.h"
 
 /* The last #include files should be: */
 #include "curl_memory.h"
@@ -85,7 +85,7 @@ void Curl_httpchunk_init(struct Curl_easy *data, struct Curl_chunker *ch,
   ch->hexindex = 0;      /* start at 0 */
   ch->state = CHUNK_HEX; /* we get hex first! */
   ch->last_code = CHUNKE_OK;
-  Curl_dyn_init(&ch->trailer, DYN_H1_TRAILER);
+  curlx_dyn_init(&ch->trailer, DYN_H1_TRAILER);
   ch->ignore_body = ignore_body;
 }
 
@@ -96,14 +96,14 @@ void Curl_httpchunk_reset(struct Curl_easy *data, struct Curl_chunker *ch,
   ch->hexindex = 0;      /* start at 0 */
   ch->state = CHUNK_HEX; /* we get hex first! */
   ch->last_code = CHUNKE_OK;
-  Curl_dyn_reset(&ch->trailer);
+  curlx_dyn_reset(&ch->trailer);
   ch->ignore_body = ignore_body;
 }
 
 void Curl_httpchunk_free(struct Curl_easy *data, struct Curl_chunker *ch)
 {
   (void)data;
-  Curl_dyn_free(&ch->trailer);
+  curlx_dyn_free(&ch->trailer);
 }
 
 bool Curl_httpchunk_is_done(struct Curl_easy *data, struct Curl_chunker *ch)
@@ -134,7 +134,7 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data,
     if(cw_next)
       result = Curl_cwriter_write(data, cw_next, CLIENTWRITE_BODY, buf, blen);
     else
-      result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)buf, blen);
+      result = Curl_client_write(data, CLIENTWRITE_BODY, buf, blen);
     if(result) {
       ch->state = CHUNK_FAILED;
       ch->last_code = CHUNKE_PASSTHRU_ERROR;
@@ -158,6 +158,7 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data,
         (*pconsumed)++;
       }
       else {
+        const char *p;
         if(0 == ch->hexindex) {
           /* This is illegal data, we received junk where we expected
              a hexadecimal digit. */
@@ -166,11 +167,11 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data,
           ch->last_code = CHUNKE_ILLEGAL_HEX;
           return CURLE_RECV_ERROR;
         }
-
         /* blen and buf are unmodified */
         ch->hexbuffer[ch->hexindex] = 0;
-        if(curlx_strtoofft(ch->hexbuffer, NULL, 16, &ch->datasize)) {
-          failf(data, "chunk hex-length not valid: '%s'", ch->hexbuffer);
+        p = &ch->hexbuffer[0];
+        if(curlx_str_hex(&p, &ch->datasize, CURL_OFF_T_MAX)) {
+          failf(data, "invalid chunk size: '%s'", ch->hexbuffer);
           ch->state = CHUNK_FAILED;
           ch->last_code = CHUNKE_ILLEGAL_HEX;
           return CURLE_RECV_ERROR;
@@ -212,8 +213,7 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data,
           result = Curl_cwriter_write(data, cw_next, CLIENTWRITE_BODY,
                                       buf, piece);
         else
-          result = Curl_client_write(data, CLIENTWRITE_BODY,
-                                    (char *)buf, piece);
+          result = Curl_client_write(data, CLIENTWRITE_BODY, buf, piece);
         if(result) {
           ch->state = CHUNK_FAILED;
           ch->last_code = CHUNKE_PASSTHRU_ERROR;
@@ -251,21 +251,20 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data,
 
     case CHUNK_TRAILER:
       if((*buf == 0x0d) || (*buf == 0x0a)) {
-        char *tr = Curl_dyn_ptr(&ch->trailer);
+        char *tr = curlx_dyn_ptr(&ch->trailer);
         /* this is the end of a trailer, but if the trailer was zero bytes
            there was no trailer and we move on */
 
         if(tr) {
-          size_t trlen;
-          result = Curl_dyn_addn(&ch->trailer, (char *)STRCONST("\x0d\x0a"));
+          result = curlx_dyn_addn(&ch->trailer, STRCONST("\x0d\x0a"));
           if(result) {
             ch->state = CHUNK_FAILED;
             ch->last_code = CHUNKE_OUT_OF_MEMORY;
             return result;
           }
-          tr = Curl_dyn_ptr(&ch->trailer);
-          trlen = Curl_dyn_len(&ch->trailer);
+          tr = curlx_dyn_ptr(&ch->trailer);
           if(!data->set.http_te_skip) {
+            size_t trlen = curlx_dyn_len(&ch->trailer);
             if(cw_next)
               result = Curl_cwriter_write(data, cw_next,
                                           CLIENTWRITE_HEADER|
@@ -282,7 +281,7 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data,
               return result;
             }
           }
-          Curl_dyn_reset(&ch->trailer);
+          curlx_dyn_reset(&ch->trailer);
           ch->state = CHUNK_TRAILER_CR;
           if(*buf == 0x0a)
             /* already on the LF */
@@ -295,7 +294,7 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data,
         }
       }
       else {
-        result = Curl_dyn_addn(&ch->trailer, buf, 1);
+        result = curlx_dyn_addn(&ch->trailer, buf, 1);
         if(result) {
           ch->state = CHUNK_FAILED;
           ch->last_code = CHUNKE_OUT_OF_MEMORY;
diff --git a/extra/curl/curl-8.12.1/lib/http_chunks.h b/extra/curl/curl-8.14.1/lib/http_chunks.h
similarity index 97%
rename from extra/curl/curl-8.12.1/lib/http_chunks.h
rename to extra/curl/curl-8.14.1/lib/http_chunks.h
index 34951ea0f41b..b84e479fcd61 100644
--- a/extra/curl/curl-8.12.1/lib/http_chunks.h
+++ b/extra/curl/curl-8.14.1/lib/http_chunks.h
@@ -26,7 +26,7 @@
 
 #ifndef CURL_DISABLE_HTTP
 
-#include "dynbuf.h"
+#include "curlx/dynbuf.h"
 
 struct connectdata;
 
@@ -49,7 +49,7 @@ typedef enum {
      POST_CR state. */
   CHUNK_DATA,
 
-  /* POSTLF should get a CR and then a LF and nothing else, then move back to
+  /* POSTLF should get a CR and then an LF and nothing else, then move back to
      HEX as the CRLF combination marks the end of a chunk. A missing CR is no
      big deal. */
   CHUNK_POSTLF,
@@ -64,7 +64,7 @@ typedef enum {
   CHUNK_TRAILER,
 
   /* A trailer CR has been found - next state is CHUNK_TRAILER_POSTCR.
-     Next char must be a LF */
+     Next char must be an LF */
   CHUNK_TRAILER_CR,
 
   /* A trailer LF must be found now, otherwise CHUNKE_BAD_CHUNK will be
diff --git a/extra/curl/curl-8.12.1/lib/http_digest.c b/extra/curl/curl-8.14.1/lib/http_digest.c
similarity index 95%
rename from extra/curl/curl-8.12.1/lib/http_digest.c
rename to extra/curl/curl-8.14.1/lib/http_digest.c
index a3ba17a51fa1..651bb834037c 100644
--- a/extra/curl/curl-8.12.1/lib/http_digest.c
+++ b/extra/curl/curl-8.14.1/lib/http_digest.c
@@ -30,6 +30,7 @@
 #include "strcase.h"
 #include "vauth/vauth.h"
 #include "http_digest.h"
+#include "curlx/strparse.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -62,8 +63,7 @@ CURLcode Curl_input_digest(struct Curl_easy *data,
     return CURLE_BAD_CONTENT_ENCODING;
 
   header += strlen("Digest");
-  while(*header && ISBLANK(*header))
-    header++;
+  curlx_str_passblanks(&header);
 
   return Curl_auth_decode_digest_http_message(header, digest);
 }
@@ -145,15 +145,15 @@ CURLcode Curl_output_digest(struct Curl_easy *data,
   */
 
   if(authp->iestyle) {
-    tmp = strchr((char *)uripath, '?');
+    tmp = strchr((const char *)uripath, '?');
     if(tmp) {
-      size_t urilen = tmp - (char *)uripath;
+      size_t urilen = tmp - (const char *)uripath;
       /* typecast is fine here since the value is always less than 32 bits */
       path = (unsigned char *) aprintf("%.*s", (int)urilen, uripath);
     }
   }
   if(!tmp)
-    path = (unsigned char *) strdup((char *) uripath);
+    path = (unsigned char *) strdup((const char *) uripath);
 
   if(!path)
     return CURLE_OUT_OF_MEMORY;
diff --git a/extra/curl/curl-8.12.1/lib/http_digest.h b/extra/curl/curl-8.14.1/lib/http_digest.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/http_digest.h
rename to extra/curl/curl-8.14.1/lib/http_digest.h
diff --git a/extra/curl/curl-8.12.1/lib/http_negotiate.c b/extra/curl/curl-8.14.1/lib/http_negotiate.c
similarity index 94%
rename from extra/curl/curl-8.12.1/lib/http_negotiate.c
rename to extra/curl/curl-8.14.1/lib/http_negotiate.c
index f031d0abc86f..5bec3b39ff5b 100644
--- a/extra/curl/curl-8.12.1/lib/http_negotiate.c
+++ b/extra/curl/curl-8.14.1/lib/http_negotiate.c
@@ -32,6 +32,7 @@
 #include "http_negotiate.h"
 #include "vauth/vauth.h"
 #include "vtls/vtls.h"
+#include "curlx/strparse.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -86,8 +87,7 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn,
 
   /* Obtain the input token, if any */
   header += strlen("Negotiate");
-  while(*header && ISBLANK(*header))
-    header++;
+  curlx_str_passblanks(&header);
 
   len = strlen(header);
   neg_ctx->havenegdata = len != 0;
@@ -109,9 +109,10 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn,
   neg_ctx->sslContext = conn->sslContext;
 #endif
   /* Check if the connection is using SSL and get the channel binding data */
-#if defined(USE_SSL) && defined(HAVE_GSSAPI)
+#ifdef HAVE_GSSAPI
+#ifdef USE_SSL
+  curlx_dyn_init(&neg_ctx->channel_binding_data, SSL_CB_MAX_SIZE + 1);
   if(Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
-    Curl_dyn_init(&neg_ctx->channel_binding_data, SSL_CB_MAX_SIZE + 1);
     result = Curl_ssl_get_channel_binding(
       data, FIRSTSOCKET, &neg_ctx->channel_binding_data);
     if(result) {
@@ -119,14 +120,17 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn,
       return result;
     }
   }
-#endif
+#else
+  curlx_dyn_init(&neg_ctx->channel_binding_data, 1);
+#endif /* USE_SSL */
+#endif /* HAVE_GSSAPI */
 
   /* Initialize the security context and decode our challenge */
   result = Curl_auth_decode_spnego_message(data, userp, passwdp, service,
                                            host, header, neg_ctx);
 
-#if defined(USE_SSL) && defined(HAVE_GSSAPI)
-  Curl_dyn_free(&neg_ctx->channel_binding_data);
+#ifdef HAVE_GSSAPI
+  curlx_dyn_free(&neg_ctx->channel_binding_data);
 #endif
 
   if(result)
@@ -203,12 +207,12 @@ CURLcode Curl_output_negotiate(struct Curl_easy *data,
 
     if(proxy) {
 #ifndef CURL_DISABLE_PROXY
-      Curl_safefree(data->state.aptr.proxyuserpwd);
+      free(data->state.aptr.proxyuserpwd);
       data->state.aptr.proxyuserpwd = userp;
 #endif
     }
     else {
-      Curl_safefree(data->state.aptr.userpwd);
+      free(data->state.aptr.userpwd);
       data->state.aptr.userpwd = userp;
     }
 
diff --git a/extra/curl/curl-8.12.1/lib/http_negotiate.h b/extra/curl/curl-8.14.1/lib/http_negotiate.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/http_negotiate.h
rename to extra/curl/curl-8.14.1/lib/http_negotiate.h
diff --git a/extra/curl/curl-8.12.1/lib/http_ntlm.c b/extra/curl/curl-8.14.1/lib/http_ntlm.c
similarity index 94%
rename from extra/curl/curl-8.12.1/lib/http_ntlm.c
rename to extra/curl/curl-8.14.1/lib/http_ntlm.c
index ab6f1dd9212e..e6a3b175a776 100644
--- a/extra/curl/curl-8.12.1/lib/http_ntlm.c
+++ b/extra/curl/curl-8.14.1/lib/http_ntlm.c
@@ -33,16 +33,15 @@
  * https://www.innovation.ch/java/ntlm.html
  */
 
-#define DEBUG_ME 0
-
 #include "urldata.h"
 #include "sendf.h"
 #include "strcase.h"
 #include "http_ntlm.h"
 #include "curl_ntlm_core.h"
-#include "curl_base64.h"
+#include "curlx/base64.h"
 #include "vauth/vauth.h"
 #include "url.h"
+#include "curlx/strparse.h"
 
 /* SSL backend-specific #if branches in this file must be kept in the order
    documented in curl_ntlm_core. */
@@ -55,12 +54,6 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
-#if DEBUG_ME
-# define DEBUG_OUT(x) x
-#else
-# define DEBUG_OUT(x) Curl_nop_stmt
-#endif
-
 CURLcode Curl_input_ntlm(struct Curl_easy *data,
                          bool proxy,         /* if proxy or not */
                          const char *header) /* rest of the www-authenticate:
@@ -78,14 +71,12 @@ CURLcode Curl_input_ntlm(struct Curl_easy *data,
   if(checkprefix("NTLM", header)) {
     header += strlen("NTLM");
 
-    while(*header && ISSPACE(*header))
-      header++;
-
+    curlx_str_passblanks(&header);
     if(*header) {
       unsigned char *hdr;
       size_t hdrlen;
 
-      result = Curl_base64_decode(header, &hdr, &hdrlen);
+      result = curlx_base64_decode(header, &hdr, &hdrlen);
       if(!result) {
         struct bufref hdrbuf;
 
@@ -214,7 +205,7 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy)
                                                  ntlm, &ntlmmsg);
     if(!result) {
       DEBUGASSERT(Curl_bufref_len(&ntlmmsg) != 0);
-      result = Curl_base64_encode((const char *) Curl_bufref_ptr(&ntlmmsg),
+      result = curlx_base64_encode((const char *) Curl_bufref_ptr(&ntlmmsg),
                                   Curl_bufref_len(&ntlmmsg), &base64, &len);
       if(!result) {
         free(*allocuserpwd);
@@ -233,8 +224,8 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy)
     result = Curl_auth_create_ntlm_type3_message(data, userp, passwdp,
                                                  ntlm, &ntlmmsg);
     if(!result && Curl_bufref_len(&ntlmmsg)) {
-      result = Curl_base64_encode((const char *) Curl_bufref_ptr(&ntlmmsg),
-                                  Curl_bufref_len(&ntlmmsg), &base64, &len);
+      result = curlx_base64_encode((const char *) Curl_bufref_ptr(&ntlmmsg),
+                                   Curl_bufref_len(&ntlmmsg), &base64, &len);
       if(!result) {
         free(*allocuserpwd);
         *allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n",
diff --git a/extra/curl/curl-8.12.1/lib/http_ntlm.h b/extra/curl/curl-8.14.1/lib/http_ntlm.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/http_ntlm.h
rename to extra/curl/curl-8.14.1/lib/http_ntlm.h
diff --git a/extra/curl/curl-8.12.1/lib/http_proxy.c b/extra/curl/curl-8.14.1/lib/http_proxy.c
similarity index 98%
rename from extra/curl/curl-8.12.1/lib/http_proxy.c
rename to extra/curl/curl-8.14.1/lib/http_proxy.c
index 3a2e9f2b05f4..3df6329c06f6 100644
--- a/extra/curl/curl-8.12.1/lib/http_proxy.c
+++ b/extra/curl/curl-8.14.1/lib/http_proxy.c
@@ -38,11 +38,12 @@
 #include "cf-h1-proxy.h"
 #include "cf-h2-proxy.h"
 #include "connect.h"
-#include "curlx.h"
+#include "strcase.h"
 #include "vtls/vtls.h"
 #include "transfer.h"
 #include "multiif.h"
 #include "vauth/vauth.h"
+#include "curlx/strparse.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -60,7 +61,7 @@ static CURLcode dynhds_add_custom(struct Curl_easy *data,
                                   struct dynhds *hds)
 {
   struct connectdata *conn = data->conn;
-  char *ptr;
+  const char *ptr;
   struct curl_slist *h[2];
   struct curl_slist *headers;
   int numlists = 1; /* by default */
@@ -108,8 +109,7 @@ static CURLcode dynhds_add_custom(struct Curl_easy *data,
         name = headers->data;
         namelen = ptr - headers->data;
         ptr++; /* pass the colon */
-        while(*ptr && ISSPACE(*ptr))
-          ptr++;
+        curlx_str_passblanks(&ptr);
         if(*ptr) {
           value = ptr;
           valuelen = strlen(value);
@@ -131,8 +131,7 @@ static CURLcode dynhds_add_custom(struct Curl_easy *data,
         name = headers->data;
         namelen = ptr - headers->data;
         ptr++; /* pass the semicolon */
-        while(*ptr && ISSPACE(*ptr))
-          ptr++;
+        curlx_str_passblanks(&ptr);
         if(!*ptr) {
           /* quirk #2, send an empty header */
           value = "";
@@ -307,7 +306,7 @@ CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq,
 
 static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf,
                                       struct Curl_easy *data,
-                                      bool blocking, bool *done)
+                                      bool *done)
 {
   struct cf_proxy_ctx *ctx = cf->ctx;
   CURLcode result;
@@ -319,7 +318,7 @@ static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf,
 
   CURL_TRC_CF(data, cf, "connect");
 connect_sub:
-  result = cf->next->cft->do_connect(cf->next, data, blocking, done);
+  result = cf->next->cft->do_connect(cf->next, data, done);
   if(result || !*done)
     return result;
 
diff --git a/extra/curl/curl-8.12.1/lib/http_proxy.h b/extra/curl/curl-8.14.1/lib/http_proxy.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/http_proxy.h
rename to extra/curl/curl-8.14.1/lib/http_proxy.h
diff --git a/extra/curl/curl-8.14.1/lib/httpsrr.c b/extra/curl/curl-8.14.1/lib/httpsrr.c
new file mode 100644
index 000000000000..df93ac34ac04
--- /dev/null
+++ b/extra/curl/curl-8.14.1/lib/httpsrr.c
@@ -0,0 +1,213 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef USE_HTTPSRR
+
+#include "urldata.h"
+#include "curl_addrinfo.h"
+#include "httpsrr.h"
+#include "connect.h"
+#include "sendf.h"
+#include "strdup.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#define MAX_ALPN_LENGTH 255
+
+static CURLcode httpsrr_decode_alpn(const char *cp, size_t len,
+                                    unsigned char *alpns)
+{
+  /*
+   * The wire-format value for "alpn" consists of at least one alpn-id
+   * prefixed by its length as a single octet, and these length-value pairs
+   * are concatenated to form the SvcParamValue. These pairs MUST exactly fill
+   * the SvcParamValue; otherwise, the SvcParamValue is malformed.
+   */
+  int idnum = 0;
+
+  while(len > 0) {
+    size_t tlen = (size_t) *cp++;
+    enum alpnid id;
+    len--;
+    if(tlen > len)
+      return CURLE_BAD_CONTENT_ENCODING;
+
+    /* we only store ALPN ids we know about */
+    id = Curl_alpn2alpnid(cp, tlen);
+    if(id != ALPN_none) {
+      if(idnum == MAX_HTTPSRR_ALPNS)
+        break;
+      if(idnum && memchr(alpns, id, idnum))
+        /* this ALPN id is already stored */
+        ;
+      else
+        alpns[idnum++] = (unsigned char)id;
+    }
+    cp += tlen;
+    len -= tlen;
+  }
+  if(idnum < MAX_HTTPSRR_ALPNS)
+    alpns[idnum] = ALPN_none; /* terminate the list */
+  return CURLE_OK;
+}
+
+CURLcode Curl_httpsrr_set(struct Curl_easy *data,
+                          struct Curl_https_rrinfo *hi,
+                          uint16_t rrkey, const uint8_t *val, size_t vlen)
+{
+  CURLcode result = CURLE_OK;
+  switch(rrkey) {
+  case HTTPS_RR_CODE_MANDATORY:
+    CURL_TRC_DNS(data, "HTTPS RR MANDATORY left to implement");
+    break;
+  case HTTPS_RR_CODE_ALPN: /* str_list */
+    result = httpsrr_decode_alpn((const char *)val, vlen, hi->alpns);
+    CURL_TRC_DNS(data, "HTTPS RR ALPN: %u %u %u %u",
+                 hi->alpns[0], hi->alpns[1], hi->alpns[2], hi->alpns[3]);
+    break;
+  case HTTPS_RR_CODE_NO_DEF_ALPN:
+    if(vlen) /* no data */
+      return CURLE_BAD_FUNCTION_ARGUMENT;
+    hi->no_def_alpn = TRUE;
+    CURL_TRC_DNS(data, "HTTPS RR no-def-alpn");
+    break;
+  case HTTPS_RR_CODE_IPV4: /* addr4 list */
+    if(!vlen || (vlen & 3)) /* the size must be 4-byte aligned */
+      return CURLE_BAD_FUNCTION_ARGUMENT;
+    hi->ipv4hints = Curl_memdup(val, vlen);
+    if(!hi->ipv4hints)
+      return CURLE_OUT_OF_MEMORY;
+    hi->ipv4hints_len = vlen;
+    CURL_TRC_DNS(data, "HTTPS RR IPv4");
+    break;
+  case HTTPS_RR_CODE_ECH:
+    if(!vlen)
+      return CURLE_BAD_FUNCTION_ARGUMENT;
+    hi->echconfiglist = Curl_memdup(val, vlen);
+    if(!hi->echconfiglist)
+      return CURLE_OUT_OF_MEMORY;
+    hi->echconfiglist_len = vlen;
+    CURL_TRC_DNS(data, "HTTPS RR ECH");
+    break;
+  case HTTPS_RR_CODE_IPV6: /* addr6 list */
+    if(!vlen || (vlen & 15)) /* the size must be 16-byte aligned */
+      return CURLE_BAD_FUNCTION_ARGUMENT;
+    hi->ipv6hints = Curl_memdup(val, vlen);
+    if(!hi->ipv6hints)
+      return CURLE_OUT_OF_MEMORY;
+    hi->ipv6hints_len = vlen;
+    CURL_TRC_DNS(data, "HTTPS RR IPv6");
+    break;
+  case HTTPS_RR_CODE_PORT:
+    if(vlen != 2)
+      return CURLE_BAD_FUNCTION_ARGUMENT;
+    hi->port = (unsigned short)((val[0] << 8) | val[1]);
+    CURL_TRC_DNS(data, "HTTPS RR port %u", hi->port);
+    break;
+  default:
+    CURL_TRC_DNS(data, "HTTPS RR unknown code");
+    break;
+  }
+  return result;
+}
+
+struct Curl_https_rrinfo *
+Curl_httpsrr_dup_move(struct Curl_https_rrinfo *rrinfo)
+{
+  struct Curl_https_rrinfo *dup = Curl_memdup(rrinfo, sizeof(*rrinfo));
+  if(dup)
+    memset(rrinfo, 0, sizeof(*rrinfo));
+  return dup;
+}
+
+void Curl_httpsrr_cleanup(struct Curl_https_rrinfo *rrinfo)
+{
+  Curl_safefree(rrinfo->target);
+  Curl_safefree(rrinfo->echconfiglist);
+  Curl_safefree(rrinfo->ipv4hints);
+  Curl_safefree(rrinfo->ipv6hints);
+}
+
+
+#ifdef USE_ARES
+
+static CURLcode httpsrr_opt(struct Curl_easy *data,
+                            const ares_dns_rr_t *rr,
+                            ares_dns_rr_key_t key, size_t idx,
+                            struct Curl_https_rrinfo *hinfo)
+{
+  const unsigned char *val = NULL;
+  unsigned short code;
+  size_t len = 0;
+
+  code  = ares_dns_rr_get_opt(rr, key, idx, &val, &len);
+  return Curl_httpsrr_set(data, hinfo, code, val, len);
+}
+
+CURLcode Curl_httpsrr_from_ares(struct Curl_easy *data,
+                                const ares_dns_record_t *dnsrec,
+                                struct Curl_https_rrinfo *hinfo)
+{
+  CURLcode result = CURLE_OK;
+  size_t i;
+
+  for(i = 0; i < ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ANSWER); i++) {
+    const char *target;
+    size_t opt;
+    const ares_dns_rr_t *rr =
+      ares_dns_record_rr_get_const(dnsrec, ARES_SECTION_ANSWER, i);
+    if(ares_dns_rr_get_type(rr) != ARES_REC_TYPE_HTTPS)
+      continue;
+    /* When SvcPriority is 0, the SVCB record is in AliasMode. Otherwise, it
+       is in ServiceMode */
+    target = ares_dns_rr_get_str(rr, ARES_RR_HTTPS_TARGET);
+    if(target && target[0]) {
+      hinfo->target = strdup(target);
+      if(!hinfo->target) {
+        result = CURLE_OUT_OF_MEMORY;
+        goto out;
+      }
+      CURL_TRC_DNS(data, "HTTPS RR target: %s", hinfo->target);
+    }
+    CURL_TRC_DNS(data, "HTTPS RR priority: %u",
+                 ares_dns_rr_get_u16(rr, ARES_RR_HTTPS_PRIORITY));
+    for(opt = 0; opt < ares_dns_rr_get_opt_cnt(rr, ARES_RR_HTTPS_PARAMS);
+        opt++) {
+      result = httpsrr_opt(data, rr, ARES_RR_HTTPS_PARAMS, opt, hinfo);
+      if(result)
+        break;
+    }
+  }
+out:
+  return result;
+}
+
+#endif /* USE_ARES */
+
+#endif /* USE_HTTPSRR */
diff --git a/extra/curl/curl-8.12.1/lib/httpsrr.h b/extra/curl/curl-8.14.1/lib/httpsrr.h
similarity index 76%
rename from extra/curl/curl-8.12.1/lib/httpsrr.h
rename to extra/curl/curl-8.14.1/lib/httpsrr.h
index ade2126f0f1d..83119dc6bde7 100644
--- a/extra/curl/curl-8.12.1/lib/httpsrr.h
+++ b/extra/curl/curl-8.14.1/lib/httpsrr.h
@@ -35,6 +35,8 @@
 #define CURL_MAXLEN_host_name 253
 #define MAX_HTTPSRR_ALPNS 4
 
+struct Curl_easy;
+
 struct Curl_https_rrinfo {
   /*
    * Fields from HTTPS RR. The only mandatory fields are priority and target.
@@ -51,13 +53,22 @@ struct Curl_https_rrinfo {
   /* store parsed alpnid entries in the array, end with ALPN_none */
   int port; /* -1 means not set */
   uint16_t priority;
-  bool no_def_alpn; /* keytag = 2 */
+  BIT(no_def_alpn); /* keytag = 2 */
 };
-#endif
+
+CURLcode Curl_httpsrr_set(struct Curl_easy *data,
+                          struct Curl_https_rrinfo *hi,
+                          uint16_t rrkey, const uint8_t *val, size_t vlen);
+
+struct Curl_https_rrinfo *
+Curl_httpsrr_dup_move(struct Curl_https_rrinfo *rrinfo);
+
+void Curl_httpsrr_cleanup(struct Curl_https_rrinfo *rrinfo);
 
 /*
  * Code points for DNS wire format SvcParams as per RFC 9460
  */
+#define HTTPS_RR_CODE_MANDATORY       0x00
 #define HTTPS_RR_CODE_ALPN            0x01
 #define HTTPS_RR_CODE_NO_DEF_ALPN     0x02
 #define HTTPS_RR_CODE_PORT            0x03
@@ -65,12 +76,11 @@ struct Curl_https_rrinfo {
 #define HTTPS_RR_CODE_ECH             0x05
 #define HTTPS_RR_CODE_IPV6            0x06
 
-CURLcode Curl_httpsrr_decode_alpn(const unsigned char *cp, size_t len,
-                                  unsigned char *alpns);
+#if defined(USE_ARES)
+CURLcode Curl_httpsrr_from_ares(struct Curl_easy *data,
+                                const ares_dns_record_t *dnsrec,
+                                struct Curl_https_rrinfo *hinfo);
+#endif /* USE_ARES */
+#endif /* USE_HTTPSRR */
 
-#if defined(USE_ARES) && defined(USE_HTTPSRR)
-void Curl_dnsrec_done_cb(void *arg, ares_status_t status,
-                         size_t timeouts,
-                         const ares_dns_record_t *dnsrec);
-#endif
 #endif /* HEADER_CURL_HTTPSRR_H */
diff --git a/extra/curl/curl-8.12.1/lib/idn.c b/extra/curl/curl-8.14.1/lib/idn.c
similarity index 98%
rename from extra/curl/curl-8.12.1/lib/idn.c
rename to extra/curl/curl-8.14.1/lib/idn.c
index ed20cdc16b75..798c9aaef1ab 100644
--- a/extra/curl/curl-8.12.1/lib/idn.c
+++ b/extra/curl/curl-8.14.1/lib/idn.c
@@ -30,8 +30,8 @@
 #include "urldata.h"
 #include "idn.h"
 #include "sendf.h"
-#include "curl_multibyte.h"
-#include "warnless.h"
+#include "curlx/multibyte.h"
+#include "curlx/warnless.h"
 
 #ifdef USE_LIBIDN2
 #include <idn2.h>
@@ -64,13 +64,14 @@ static CURLcode iconv_to_utf8(const char *in, size_t inlen,
   iconv_t cd = iconv_open("UTF-8", nl_langinfo(CODESET));
   if(cd != (iconv_t)-1) {
     size_t iconv_outlen = *outlen;
-    char *iconv_in = (char *)in;
+    char *iconv_in = (char *)CURL_UNCONST(in);
     size_t iconv_inlen = inlen;
     size_t iconv_result = iconv(cd, &iconv_in, &iconv_inlen,
                                 out, &iconv_outlen);
     *outlen -= iconv_outlen;
     iconv_close(cd);
     if(iconv_result == (size_t)-1) {
+      /* !checksrc! disable ERRNOVAR 1 */
       if(errno == ENOMEM)
         return CURLE_OUT_OF_MEMORY;
       else
@@ -80,6 +81,7 @@ static CURLcode iconv_to_utf8(const char *in, size_t inlen,
     return CURLE_OK;
   }
   else {
+    /* !checksrc! disable ERRNOVAR 1 */
     if(errno == ENOMEM)
       return CURLE_OUT_OF_MEMORY;
     else
diff --git a/extra/curl/curl-8.12.1/lib/idn.h b/extra/curl/curl-8.14.1/lib/idn.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/idn.h
rename to extra/curl/curl-8.14.1/lib/idn.h
diff --git a/extra/curl/curl-8.12.1/lib/if2ip.c b/extra/curl/curl-8.14.1/lib/if2ip.c
similarity index 97%
rename from extra/curl/curl-8.12.1/lib/if2ip.c
rename to extra/curl/curl-8.14.1/lib/if2ip.c
index 55afd553d6a1..6da68efd500d 100644
--- a/extra/curl/curl-8.12.1/lib/if2ip.c
+++ b/extra/curl/curl-8.14.1/lib/if2ip.c
@@ -67,7 +67,8 @@
 unsigned int Curl_ipv6_scope(const struct sockaddr *sa)
 {
   if(sa->sa_family == AF_INET6) {
-    const struct sockaddr_in6 * sa6 = (const struct sockaddr_in6 *)(void *) sa;
+    const struct sockaddr_in6 * sa6 =
+      (const struct sockaddr_in6 *)(const void *) sa;
     const unsigned char *b = sa6->sin6_addr.s6_addr;
     unsigned short w = (unsigned short) ((b[0] << 8) | b[1]);
 
@@ -92,7 +93,7 @@ unsigned int Curl_ipv6_scope(const struct sockaddr *sa)
 }
 #endif
 
-#ifndef CURL_DISABLE_BINDLOCAL
+#if !defined(CURL_DISABLE_BINDLOCAL) || !defined(CURL_DISABLE_FTP)
 
 #if defined(HAVE_GETIFADDRS)
 
@@ -265,4 +266,4 @@ if2ip_result_t Curl_if2ip(int af,
 
 #endif
 
-#endif /* CURL_DISABLE_BINDLOCAL */
+#endif /* CURL_DISABLE_BINDLOCAL && CURL_DISABLE_FTP */
diff --git a/extra/curl/curl-8.12.1/lib/if2ip.h b/extra/curl/curl-8.14.1/lib/if2ip.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/if2ip.h
rename to extra/curl/curl-8.14.1/lib/if2ip.h
diff --git a/extra/curl/curl-8.12.1/lib/imap.c b/extra/curl/curl-8.14.1/lib/imap.c
similarity index 73%
rename from extra/curl/curl-8.12.1/lib/imap.c
rename to extra/curl/curl-8.14.1/lib/imap.c
index 49abaf4277d8..fc38a5b61e0d 100644
--- a/extra/curl/curl-8.12.1/lib/imap.c
+++ b/extra/curl/curl-8.14.1/lib/imap.c
@@ -36,6 +36,7 @@
  ***************************************************************************/
 
 #include "curl_setup.h"
+#include "curlx/dynbuf.h"
 
 #ifndef CURL_DISABLE_IMAP
 
@@ -64,7 +65,7 @@
 #include "socks.h"
 #include "imap.h"
 #include "mime.h"
-#include "strtoofft.h"
+#include "curlx/strparse.h"
 #include "strcase.h"
 #include "vtls/vtls.h"
 #include "cfilters.h"
@@ -74,7 +75,7 @@
 #include "url.h"
 #include "bufref.h"
 #include "curl_sasl.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
 #include "curl_ctype.h"
 
 /* The last 3 #include files should be in this order */
@@ -82,8 +83,73 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
+
+/* meta key for storing protocol meta at easy handle */
+#define CURL_META_IMAP_EASY   "meta:proto:imap:easy"
+/* meta key for storing protocol meta at connection */
+#define CURL_META_IMAP_CONN   "meta:proto:imap:conn"
+
+typedef enum {
+  IMAP_STOP,         /* do nothing state, stops the state machine */
+  IMAP_SERVERGREET,  /* waiting for the initial greeting immediately after
+                        a connect */
+  IMAP_CAPABILITY,
+  IMAP_STARTTLS,
+  IMAP_UPGRADETLS,   /* asynchronously upgrade the connection to SSL/TLS
+                       (multi mode only) */
+  IMAP_AUTHENTICATE,
+  IMAP_LOGIN,
+  IMAP_LIST,
+  IMAP_SELECT,
+  IMAP_FETCH,
+  IMAP_FETCH_FINAL,
+  IMAP_APPEND,
+  IMAP_APPEND_FINAL,
+  IMAP_SEARCH,
+  IMAP_LOGOUT,
+  IMAP_LAST          /* never used */
+} imapstate;
+
+/* imap_conn is used for struct connection-oriented data */
+struct imap_conn {
+  struct pingpong pp;
+  struct SASL sasl;           /* SASL-related parameters */
+  struct dynbuf dyn;          /* for the IMAP commands */
+  char *mailbox;              /* The last selected mailbox */
+  char *mailbox_uidvalidity;  /* UIDVALIDITY parsed from select response */
+  imapstate state;            /* Always use imap.c:state() to change state! */
+  char resptag[5];            /* Response tag to wait for */
+  unsigned char preftype;     /* Preferred authentication type */
+  unsigned char cmdid;        /* Last used command ID */
+  BIT(ssldone);               /* Is connect() over SSL done? */
+  BIT(preauth);               /* Is this connection PREAUTH? */
+  BIT(tls_supported);         /* StartTLS capability supported by server */
+  BIT(login_disabled);        /* LOGIN command disabled by server */
+  BIT(ir_supported);          /* Initial response supported by server */
+};
+
+/* This IMAP struct is used in the Curl_easy. All IMAP data that is
+   connection-oriented must be in imap_conn to properly deal with the fact that
+   perhaps the Curl_easy is changed between the times the connection is
+   used. */
+struct IMAP {
+  curl_pp_transfer transfer;
+  char *mailbox;          /* Mailbox to select */
+  char *uidvalidity;      /* UIDVALIDITY to check in select */
+  char *uid;              /* Message UID to fetch */
+  char *mindex;           /* Index in mail box of mail to fetch */
+  char *section;          /* Message SECTION to fetch */
+  char *partial;          /* Message PARTIAL to fetch */
+  char *query;            /* Query to search for */
+  char *custom;           /* Custom request */
+  char *custom_params;    /* Parameters for the custom request */
+};
+
+
 /* Local API functions */
-static CURLcode imap_regular_transfer(struct Curl_easy *data, bool *done);
+static CURLcode imap_regular_transfer(struct Curl_easy *data,
+                                      struct IMAP *imap,
+                                      bool *done);
 static CURLcode imap_do(struct Curl_easy *data, bool *done);
 static CURLcode imap_done(struct Curl_easy *data, CURLcode status,
                           bool premature);
@@ -97,11 +163,15 @@ 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, const char *fmt, ...)
-  CURL_PRINTF(2, 3);
-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_sendf(struct Curl_easy *data,
+                           struct imap_conn *imapc,
+                           const char *fmt, ...) CURL_PRINTF(3, 4);
+static CURLcode imap_parse_url_options(struct connectdata *conn,
+                                       struct imap_conn *imapc);
+static CURLcode imap_parse_url_path(struct Curl_easy *data,
+                                    struct IMAP *imap);
+static CURLcode imap_parse_custom_request(struct Curl_easy *data,
+                                          struct IMAP *imap);
 static CURLcode imap_perform_authenticate(struct Curl_easy *data,
                                           const char *mech,
                                           const struct bufref *initresp);
@@ -111,6 +181,7 @@ static CURLcode imap_continue_authenticate(struct Curl_easy *data,
 static CURLcode imap_cancel_authenticate(struct Curl_easy *data,
                                          const char *mech);
 static CURLcode imap_get_message(struct Curl_easy *data, struct bufref *out);
+static void imap_easy_reset(struct IMAP *imap);
 
 /*
  * IMAP protocol handler.
@@ -192,6 +263,10 @@ static const struct SASLproto saslimap = {
   SASL_FLAG_BASE64            /* Configuration flags */
 };
 
+struct ulbits {
+  int bit;
+  const char *flag;
+};
 
 /***********************************************************************
  *
@@ -242,14 +317,21 @@ static bool imap_matchresp(const char *line, size_t len, const char *cmd)
  * response which can be processed by the response handler.
  */
 static bool imap_endofresp(struct Curl_easy *data, struct connectdata *conn,
-                           char *line, size_t len, int *resp)
+                           const char *line, size_t len, int *resp)
 {
-  struct IMAP *imap = data->req.p.imap;
-  struct imap_conn *imapc = &conn->proto.imapc;
-  const char *id = imapc->resptag;
-  size_t id_len = strlen(id);
+  struct imap_conn *imapc = Curl_conn_meta_get(conn, CURL_META_IMAP_CONN);
+  struct IMAP *imap = Curl_meta_get(data, CURL_META_IMAP_EASY);
+  const char *id;
+  size_t id_len;
+
+  DEBUGASSERT(imapc);
+  DEBUGASSERT(imap);
+  if(!imapc || !imap)
+    return FALSE;
 
   /* Do we have a tagged command response? */
+  id = imapc->resptag;
+  id_len = strlen(id);
   if(len >= id_len + 1 && !memcmp(id, line, id_len) && line[id_len] == ' ') {
     line += id_len + 1;
     len -= id_len + 1;
@@ -275,17 +357,17 @@ static bool imap_endofresp(struct Curl_easy *data, struct connectdata *conn,
 
       case IMAP_LIST:
         if((!imap->custom && !imap_matchresp(line, len, "LIST")) ||
-          (imap->custom && !imap_matchresp(line, len, imap->custom) &&
-           (!strcasecompare(imap->custom, "STORE") ||
-            !imap_matchresp(line, len, "FETCH")) &&
-           !strcasecompare(imap->custom, "SELECT") &&
-           !strcasecompare(imap->custom, "EXAMINE") &&
-           !strcasecompare(imap->custom, "SEARCH") &&
-           !strcasecompare(imap->custom, "EXPUNGE") &&
-           !strcasecompare(imap->custom, "LSUB") &&
-           !strcasecompare(imap->custom, "UID") &&
-           !strcasecompare(imap->custom, "GETQUOTAROOT") &&
-           !strcasecompare(imap->custom, "NOOP")))
+           (imap->custom && !imap_matchresp(line, len, imap->custom) &&
+            (!strcasecompare(imap->custom, "STORE") ||
+             !imap_matchresp(line, len, "FETCH")) &&
+            !strcasecompare(imap->custom, "SELECT") &&
+            !strcasecompare(imap->custom, "EXAMINE") &&
+            !strcasecompare(imap->custom, "SEARCH") &&
+            !strcasecompare(imap->custom, "EXPUNGE") &&
+            !strcasecompare(imap->custom, "LSUB") &&
+            !strcasecompare(imap->custom, "UID") &&
+            !strcasecompare(imap->custom, "GETQUOTAROOT") &&
+            !strcasecompare(imap->custom, "NOOP")))
           return FALSE;
         break;
 
@@ -317,8 +399,8 @@ static bool imap_endofresp(struct Curl_easy *data, struct connectdata *conn,
      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 email servers ignore this and only send a single + instead. */
-  if(imap && !imap->custom && ((len == 3 && line[0] == '+') ||
-     (len >= 2 && !memcmp("+ ", line, 2)))) {
+  if(!imap->custom && ((len == 3 && line[0] == '+') ||
+                       (len >= 2 && !memcmp("+ ", line, 2)))) {
     switch(imapc->state) {
       /* States which are interested in continuation responses */
       case IMAP_AUTHENTICATE:
@@ -346,9 +428,16 @@ static bool imap_endofresp(struct Curl_easy *data, struct connectdata *conn,
  */
 static CURLcode imap_get_message(struct Curl_easy *data, struct bufref *out)
 {
-  char *message = Curl_dyn_ptr(&data->conn->proto.imapc.pp.recvbuf);
-  size_t len = data->conn->proto.imapc.pp.nfinal;
+  struct imap_conn *imapc =
+    Curl_conn_meta_get(data->conn, CURL_META_IMAP_CONN);
+  char *message;
+  size_t len;
 
+  if(!imapc)
+    return CURLE_FAILED_INIT;
+
+  message = curlx_dyn_ptr(&imapc->pp.recvbuf);
+  len = imapc->pp.nfinal;
   if(len > 2) {
     /* Find the start of the message */
     len -= 2;
@@ -378,9 +467,10 @@ static CURLcode imap_get_message(struct Curl_easy *data, struct bufref *out)
  *
  * This is the ONLY way to change IMAP state!
  */
-static void imap_state(struct Curl_easy *data, imapstate newstate)
+static void imap_state(struct Curl_easy *data,
+                       struct imap_conn *imapc,
+                       imapstate newstate)
 {
-  struct imap_conn *imapc = &data->conn->proto.imapc;
 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
   /* for debug purposes */
   static const char * const names[]={
@@ -406,7 +496,7 @@ static void imap_state(struct Curl_easy *data, imapstate newstate)
     infof(data, "IMAP %p state change from %s to %s",
           (void *)imapc, names[imapc->state], names[newstate]);
 #endif
-
+  (void)data;
   imapc->state = newstate;
 }
 
@@ -418,19 +508,19 @@ static void imap_state(struct Curl_easy *data, imapstate newstate)
  * supported capabilities.
  */
 static CURLcode imap_perform_capability(struct Curl_easy *data,
-                                        struct connectdata *conn)
+                                        struct imap_conn *imapc)
 {
   CURLcode result = CURLE_OK;
-  struct imap_conn *imapc = &conn->proto.imapc;
+
   imapc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanisms yet */
   imapc->sasl.authused = SASL_AUTH_NONE;  /* Clear the auth. mechanism used */
   imapc->tls_supported = FALSE;           /* Clear the TLS capability */
 
   /* Send the CAPABILITY command */
-  result = imap_sendf(data, "CAPABILITY");
+  result = imap_sendf(data, imapc, "CAPABILITY");
 
   if(!result)
-    imap_state(data, IMAP_CAPABILITY);
+    imap_state(data, imapc, IMAP_CAPABILITY);
 
   return result;
 }
@@ -441,13 +531,14 @@ 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)
+static CURLcode imap_perform_starttls(struct Curl_easy *data,
+                                      struct imap_conn *imapc)
 {
   /* Send the STARTTLS command */
-  CURLcode result = imap_sendf(data, "STARTTLS");
+  CURLcode result = imap_sendf(data, imapc, "STARTTLS");
 
   if(!result)
-    imap_state(data, IMAP_STARTTLS);
+    imap_state(data, imapc, IMAP_STARTTLS);
 
   return result;
 }
@@ -459,11 +550,11 @@ static CURLcode imap_perform_starttls(struct Curl_easy *data)
  * Performs the upgrade to TLS.
  */
 static CURLcode imap_perform_upgrade_tls(struct Curl_easy *data,
+                                         struct imap_conn *imapc,
                                          struct connectdata *conn)
 {
 #ifdef USE_SSL
   /* Start the SSL connection */
-  struct imap_conn *imapc = &conn->proto.imapc;
   CURLcode result;
   bool ssldone = FALSE;
 
@@ -473,7 +564,6 @@ static CURLcode imap_perform_upgrade_tls(struct Curl_easy *data,
       goto out;
     /* Change the connection handler */
     conn->handler = &Curl_handler_imaps;
-    conn->bits.tls_upgraded = TRUE;
   }
 
   DEBUGASSERT(!imapc->ssldone);
@@ -483,12 +573,13 @@ static CURLcode imap_perform_upgrade_tls(struct Curl_easy *data,
   if(!result && ssldone) {
     imapc->ssldone = ssldone;
      /* perform CAPA now, changes imapc->state out of IMAP_UPGRADETLS */
-     result = imap_perform_capability(data, conn);
+     result = imap_perform_capability(data, imapc);
   }
 out:
   return result;
 #else
   (void)data;
+  (void)imapc;
   (void)conn;
   return CURLE_NOT_BUILT_IN;
 #endif
@@ -501,6 +592,7 @@ static CURLcode imap_perform_upgrade_tls(struct Curl_easy *data,
  * Sends a clear text LOGIN command to authenticate with.
  */
 static CURLcode imap_perform_login(struct Curl_easy *data,
+                                   struct imap_conn *imapc,
                                    struct connectdata *conn)
 {
   CURLcode result = CURLE_OK;
@@ -510,7 +602,7 @@ static CURLcode imap_perform_login(struct Curl_easy *data,
   /* Check we have a username and password to authenticate with and end the
      connect phase if we do not */
   if(!data->state.aptr.user) {
-    imap_state(data, IMAP_STOP);
+    imap_state(data, imapc, IMAP_STOP);
 
     return result;
   }
@@ -520,14 +612,14 @@ static CURLcode imap_perform_login(struct Curl_easy *data,
   passwd = imap_atom(conn->passwd, FALSE);
 
   /* Send the LOGIN command */
-  result = imap_sendf(data, "LOGIN %s %s", user ? user : "",
+  result = imap_sendf(data, imapc, "LOGIN %s %s", user ? user : "",
                       passwd ? passwd : "");
 
   free(user);
   free(passwd);
 
   if(!result)
-    imap_state(data, IMAP_LOGIN);
+    imap_state(data, imapc, IMAP_LOGIN);
 
   return result;
 }
@@ -543,16 +635,20 @@ static CURLcode imap_perform_authenticate(struct Curl_easy *data,
                                           const char *mech,
                                           const struct bufref *initresp)
 {
+  struct imap_conn *imapc =
+    Curl_conn_meta_get(data->conn, CURL_META_IMAP_CONN);
   CURLcode result = CURLE_OK;
   const char *ir = (const char *) Curl_bufref_ptr(initresp);
 
+  if(!imapc)
+    return CURLE_FAILED_INIT;
   if(ir) {
     /* Send the AUTHENTICATE command with the initial response */
-    result = imap_sendf(data, "AUTHENTICATE %s %s", mech, ir);
+    result = imap_sendf(data, imapc, "AUTHENTICATE %s %s", mech, ir);
   }
   else {
     /* Send the AUTHENTICATE command */
-    result = imap_sendf(data, "AUTHENTICATE %s", mech);
+    result = imap_sendf(data, imapc, "AUTHENTICATE %s", mech);
   }
 
   return result;
@@ -568,10 +664,12 @@ static CURLcode imap_continue_authenticate(struct Curl_easy *data,
                                            const char *mech,
                                            const struct bufref *resp)
 {
-  struct imap_conn *imapc = &data->conn->proto.imapc;
+  struct imap_conn *imapc =
+    Curl_conn_meta_get(data->conn, CURL_META_IMAP_CONN);
 
   (void)mech;
-
+  if(!imapc)
+    return CURLE_FAILED_INIT;
   return Curl_pp_sendf(data, &imapc->pp,
                        "%s", (const char *) Curl_bufref_ptr(resp));
 }
@@ -585,10 +683,12 @@ static CURLcode imap_continue_authenticate(struct Curl_easy *data,
 static CURLcode imap_cancel_authenticate(struct Curl_easy *data,
                                          const char *mech)
 {
-  struct imap_conn *imapc = &data->conn->proto.imapc;
+  struct imap_conn *imapc =
+    Curl_conn_meta_get(data->conn, CURL_META_IMAP_CONN);
 
   (void)mech;
-
+  if(!imapc)
+    return CURLE_FAILED_INIT;
   return Curl_pp_sendf(data, &imapc->pp, "*");
 }
 
@@ -601,17 +701,16 @@ static CURLcode imap_cancel_authenticate(struct Curl_easy *data,
  * mechanism not be available between the client and server.
  */
 static CURLcode imap_perform_authentication(struct Curl_easy *data,
-                                            struct connectdata *conn)
+                                            struct imap_conn *imapc)
 {
   CURLcode result = CURLE_OK;
-  struct imap_conn *imapc = &conn->proto.imapc;
   saslprogress progress;
 
   /* Check if already authenticated OR if there is enough data to authenticate
      with and end the connect phase if we do not */
   if(imapc->preauth ||
      !Curl_sasl_can_authenticate(&imapc->sasl, data)) {
-    imap_state(data, IMAP_STOP);
+    imap_state(data, imapc, IMAP_STOP);
     return result;
   }
 
@@ -620,15 +719,12 @@ static CURLcode imap_perform_authentication(struct Curl_easy *data,
 
   if(!result) {
     if(progress == SASL_INPROGRESS)
-      imap_state(data, IMAP_AUTHENTICATE);
+      imap_state(data, imapc, IMAP_AUTHENTICATE);
     else if(!imapc->login_disabled && (imapc->preftype & IMAP_TYPE_CLEARTEXT))
       /* Perform clear text authentication */
-      result = imap_perform_login(data, conn);
-    else {
-      /* Other mechanisms not supported */
-      infof(data, "No known authentication mechanisms supported");
-      result = CURLE_LOGIN_DENIED;
-    }
+      result = imap_perform_login(data, imapc, data->conn);
+    else
+      result = Curl_sasl_is_blocked(&imapc->sasl, data);
   }
 
   return result;
@@ -640,14 +736,15 @@ static CURLcode imap_perform_authentication(struct Curl_easy *data,
  *
  * Sends a LIST command or an alternative custom request.
  */
-static CURLcode imap_perform_list(struct Curl_easy *data)
+static CURLcode imap_perform_list(struct Curl_easy *data,
+                                  struct imap_conn *imapc,
+                                  struct IMAP *imap)
 {
   CURLcode result = CURLE_OK;
-  struct IMAP *imap = data->req.p.imap;
 
   if(imap->custom)
     /* Send the custom request */
-    result = imap_sendf(data, "%s%s", imap->custom,
+    result = imap_sendf(data, imapc, "%s%s", imap->custom,
                         imap->custom_params ? imap->custom_params : "");
   else {
     /* Make sure the mailbox is in the correct atom format if necessary */
@@ -657,13 +754,13 @@ static CURLcode imap_perform_list(struct Curl_easy *data)
       return CURLE_OUT_OF_MEMORY;
 
     /* Send the LIST command */
-    result = imap_sendf(data, "LIST \"%s\" *", mailbox);
+    result = imap_sendf(data, imapc, "LIST \"%s\" *", mailbox);
 
     free(mailbox);
   }
 
   if(!result)
-    imap_state(data, IMAP_LIST);
+    imap_state(data, imapc, IMAP_LIST);
 
   return result;
 }
@@ -674,12 +771,11 @@ static CURLcode imap_perform_list(struct Curl_easy *data)
  *
  * Sends a SELECT command to ask the server to change the selected mailbox.
  */
-static CURLcode imap_perform_select(struct Curl_easy *data)
+static CURLcode imap_perform_select(struct Curl_easy *data,
+                                    struct imap_conn *imapc,
+                                    struct IMAP *imap)
 {
   CURLcode result = CURLE_OK;
-  struct connectdata *conn = data->conn;
-  struct IMAP *imap = data->req.p.imap;
-  struct imap_conn *imapc = &conn->proto.imapc;
   char *mailbox;
 
   /* Invalidate old information as we are switching mailboxes */
@@ -698,12 +794,12 @@ static CURLcode imap_perform_select(struct Curl_easy *data)
     return CURLE_OUT_OF_MEMORY;
 
   /* Send the SELECT command */
-  result = imap_sendf(data, "SELECT %s", mailbox);
+  result = imap_sendf(data, imapc, "SELECT %s", mailbox);
 
   free(mailbox);
 
   if(!result)
-    imap_state(data, IMAP_SELECT);
+    imap_state(data, imapc, IMAP_SELECT);
 
   return result;
 }
@@ -714,30 +810,31 @@ 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)
+static CURLcode imap_perform_fetch(struct Curl_easy *data,
+                                   struct imap_conn *imapc,
+                                   struct IMAP *imap)
 {
   CURLcode result = CURLE_OK;
-  struct IMAP *imap = data->req.p.imap;
   /* Check we have a UID */
   if(imap->uid) {
 
     /* Send the FETCH command */
     if(imap->partial)
-      result = imap_sendf(data, "UID FETCH %s BODY[%s]<%s>",
+      result = imap_sendf(data, imapc, "UID FETCH %s BODY[%s]<%s>",
                           imap->uid, imap->section ? imap->section : "",
                           imap->partial);
     else
-      result = imap_sendf(data, "UID FETCH %s BODY[%s]",
+      result = imap_sendf(data, imapc, "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, "FETCH %s BODY[%s]<%s>",
+      result = imap_sendf(data, imapc, "FETCH %s BODY[%s]<%s>",
                           imap->mindex, imap->section ? imap->section : "",
                           imap->partial);
     else
-      result = imap_sendf(data, "FETCH %s BODY[%s]",
+      result = imap_sendf(data, imapc, "FETCH %s BODY[%s]",
                           imap->mindex, imap->section ? imap->section : "");
   }
   else {
@@ -745,7 +842,7 @@ static CURLcode imap_perform_fetch(struct Curl_easy *data)
     return CURLE_URL_MALFORMAT;
   }
   if(!result)
-    imap_state(data, IMAP_FETCH);
+    imap_state(data, imapc, IMAP_FETCH);
 
   return result;
 }
@@ -756,11 +853,13 @@ static CURLcode imap_perform_fetch(struct Curl_easy *data)
  *
  * Sends an APPEND command to initiate the upload of a message.
  */
-static CURLcode imap_perform_append(struct Curl_easy *data)
+static CURLcode imap_perform_append(struct Curl_easy *data,
+                                    struct imap_conn *imapc,
+                                    struct IMAP *imap)
 {
   CURLcode result = CURLE_OK;
-  struct IMAP *imap = data->req.p.imap;
   char *mailbox;
+  struct dynbuf flags;
 
   /* Check we have a mailbox */
   if(!imap->mailbox) {
@@ -809,14 +908,48 @@ static CURLcode imap_perform_append(struct Curl_easy *data)
   if(!mailbox)
     return CURLE_OUT_OF_MEMORY;
 
-  /* Send the APPEND command */
-  result = imap_sendf(data, "APPEND %s (\\Seen) {%" FMT_OFF_T "}",
-                      mailbox, data->state.infilesize);
+  /* Generate flags string and send the APPEND command */
+  curlx_dyn_init(&flags, 100);
+  if(data->set.upload_flags) {
+    int i;
+    struct ulbits ulflag[] = {
+      {CURLULFLAG_ANSWERED, "Answered"},
+      {CURLULFLAG_DELETED, "Deleted"},
+      {CURLULFLAG_DRAFT, "Draft"},
+      {CURLULFLAG_FLAGGED, "Flagged"},
+      {CURLULFLAG_SEEN, "Seen"},
+      {0, NULL}
+    };
 
+    result = CURLE_OUT_OF_MEMORY;
+    if(curlx_dyn_add(&flags, " (")) {
+      goto cleanup;
+    }
+
+    for(i = 0; ulflag[i].bit; i++) {
+      if(data->set.upload_flags & ulflag[i].bit) {
+        if((curlx_dyn_len(&flags) > 2 && curlx_dyn_add(&flags, " ")) ||
+           curlx_dyn_add(&flags, "\\") ||
+           curlx_dyn_add(&flags, ulflag[i].flag))
+          goto cleanup;
+      }
+    }
+
+    if(curlx_dyn_add(&flags, ")"))
+      goto cleanup;
+  }
+  else if(curlx_dyn_add(&flags, ""))
+    goto cleanup;
+
+  result = imap_sendf(data, imapc, "APPEND %s%s {%" FMT_OFF_T "}",
+                      mailbox, curlx_dyn_ptr(&flags), data->state.infilesize);
+
+cleanup:
+  curlx_dyn_free(&flags);
   free(mailbox);
 
   if(!result)
-    imap_state(data, IMAP_APPEND);
+    imap_state(data, imapc, IMAP_APPEND);
 
   return result;
 }
@@ -827,10 +960,11 @@ static CURLcode imap_perform_append(struct Curl_easy *data)
  *
  * Sends a SEARCH command.
  */
-static CURLcode imap_perform_search(struct Curl_easy *data)
+static CURLcode imap_perform_search(struct Curl_easy *data,
+                                    struct imap_conn *imapc,
+                                    struct IMAP *imap)
 {
   CURLcode result = CURLE_OK;
-  struct IMAP *imap = data->req.p.imap;
 
   /* Check we have a query string */
   if(!imap->query) {
@@ -839,10 +973,10 @@ static CURLcode imap_perform_search(struct Curl_easy *data)
   }
 
   /* Send the SEARCH command */
-  result = imap_sendf(data, "SEARCH %s", imap->query);
+  result = imap_sendf(data, imapc, "SEARCH %s", imap->query);
 
   if(!result)
-    imap_state(data, IMAP_SEARCH);
+    imap_state(data, imapc, IMAP_SEARCH);
 
   return result;
 }
@@ -853,28 +987,28 @@ 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)
+static CURLcode imap_perform_logout(struct Curl_easy *data,
+                                    struct imap_conn *imapc)
 {
   /* Send the LOGOUT command */
-  CURLcode result = imap_sendf(data, "LOGOUT");
+  CURLcode result = imap_sendf(data, imapc, "LOGOUT");
 
   if(!result)
-    imap_state(data, IMAP_LOGOUT);
+    imap_state(data, imapc, IMAP_LOGOUT);
 
   return result;
 }
 
 /* For the initial server greeting */
 static CURLcode imap_state_servergreet_resp(struct Curl_easy *data,
+                                            struct imap_conn *imapc,
                                             int imapcode,
                                             imapstate instate)
 {
-  struct connectdata *conn = data->conn;
   (void)instate; /* no use for this yet */
 
   if(imapcode == IMAP_RESP_PREAUTH) {
     /* PREAUTH */
-    struct imap_conn *imapc = &conn->proto.imapc;
     imapc->preauth = TRUE;
     infof(data, "PREAUTH connection, already authenticated");
   }
@@ -883,22 +1017,22 @@ static CURLcode imap_state_servergreet_resp(struct Curl_easy *data,
     return CURLE_WEIRD_SERVER_REPLY;
   }
 
-  return imap_perform_capability(data, conn);
+  return imap_perform_capability(data, imapc);
 }
 
 /* For CAPABILITY responses */
 static CURLcode imap_state_capability_resp(struct Curl_easy *data,
+                                           struct imap_conn *imapc,
                                            int imapcode,
                                            imapstate instate)
 {
   CURLcode result = CURLE_OK;
   struct connectdata *conn = data->conn;
-  struct imap_conn *imapc = &conn->proto.imapc;
-  const char *line = Curl_dyn_ptr(&imapc->pp.recvbuf);
+  const char *line = curlx_dyn_ptr(&imapc->pp.recvbuf);
 
   (void)instate; /* no use for this yet */
 
-  /* Do we have a untagged response? */
+  /* Do we have an untagged response? */
   if(imapcode == '*') {
     line += 2;
 
@@ -954,33 +1088,33 @@ 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);
+      result = imap_perform_starttls(data, imapc);
     }
     else if(data->set.use_ssl <= CURLUSESSL_TRY)
-      result = imap_perform_authentication(data, conn);
+      result = imap_perform_authentication(data, imapc);
     else {
       failf(data, "STARTTLS not available.");
       result = CURLE_USE_SSL_FAILED;
     }
   }
   else
-    result = imap_perform_authentication(data, conn);
+    result = imap_perform_authentication(data, imapc);
 
   return result;
 }
 
 /* For STARTTLS responses */
 static CURLcode imap_state_starttls_resp(struct Curl_easy *data,
+                                         struct imap_conn *imapc,
                                          int imapcode,
                                          imapstate instate)
 {
   CURLcode result = CURLE_OK;
-  struct connectdata *conn = data->conn;
 
   (void)instate; /* no use for this yet */
 
   /* Pipelining in response is forbidden. */
-  if(data->conn->proto.imapc.pp.overflow)
+  if(imapc->pp.overflow)
     return CURLE_WEIRD_SERVER_REPLY;
 
   if(imapcode != IMAP_RESP_OK) {
@@ -989,22 +1123,21 @@ static CURLcode imap_state_starttls_resp(struct Curl_easy *data,
       result = CURLE_USE_SSL_FAILED;
     }
     else
-      result = imap_perform_authentication(data, conn);
+      result = imap_perform_authentication(data, imapc);
   }
   else
-    imap_state(data, IMAP_UPGRADETLS);
+    imap_state(data, imapc, IMAP_UPGRADETLS);
 
   return result;
 }
 
 /* For SASL authentication responses */
 static CURLcode imap_state_auth_resp(struct Curl_easy *data,
-                                     struct connectdata *conn,
+                                     struct imap_conn *imapc,
                                      int imapcode,
                                      imapstate instate)
 {
   CURLcode result = CURLE_OK;
-  struct imap_conn *imapc = &conn->proto.imapc;
   saslprogress progress;
 
   (void)instate; /* no use for this yet */
@@ -1013,12 +1146,12 @@ static CURLcode imap_state_auth_resp(struct Curl_easy *data,
   if(!result)
     switch(progress) {
     case SASL_DONE:
-      imap_state(data, IMAP_STOP);  /* Authenticated */
+      imap_state(data, imapc, IMAP_STOP);  /* Authenticated */
       break;
     case SASL_IDLE:            /* No mechanism left after cancellation */
       if((!imapc->login_disabled) && (imapc->preftype & IMAP_TYPE_CLEARTEXT))
         /* Perform clear text authentication */
-        result = imap_perform_login(data, conn);
+        result = imap_perform_login(data, imapc, data->conn);
       else {
         failf(data, "Authentication cancelled");
         result = CURLE_LOGIN_DENIED;
@@ -1033,6 +1166,7 @@ static CURLcode imap_state_auth_resp(struct Curl_easy *data,
 
 /* For LOGIN responses */
 static CURLcode imap_state_login_resp(struct Curl_easy *data,
+                                      struct imap_conn *imapc,
                                       int imapcode,
                                       imapstate instate)
 {
@@ -1045,19 +1179,20 @@ static CURLcode imap_state_login_resp(struct Curl_easy *data,
   }
   else
     /* End of connect phase */
-    imap_state(data, IMAP_STOP);
+    imap_state(data, imapc, IMAP_STOP);
 
   return result;
 }
 
 /* For LIST and SEARCH responses */
 static CURLcode imap_state_listsearch_resp(struct Curl_easy *data,
+                                           struct imap_conn *imapc,
                                            int imapcode,
                                            imapstate instate)
 {
   CURLcode result = CURLE_OK;
-  char *line = Curl_dyn_ptr(&data->conn->proto.imapc.pp.recvbuf);
-  size_t len = data->conn->proto.imapc.pp.nfinal;
+  char *line = curlx_dyn_ptr(&imapc->pp.recvbuf);
+  size_t len = imapc->pp.nfinal;
 
   (void)instate; /* No use for this yet */
 
@@ -1067,20 +1202,20 @@ static CURLcode imap_state_listsearch_resp(struct Curl_easy *data,
     result = CURLE_QUOTE_ERROR;
   else
     /* End of DO phase */
-    imap_state(data, IMAP_STOP);
+    imap_state(data, imapc, IMAP_STOP);
 
   return result;
 }
 
 /* For SELECT responses */
-static CURLcode imap_state_select_resp(struct Curl_easy *data, int imapcode,
+static CURLcode imap_state_select_resp(struct Curl_easy *data,
+                                       struct imap_conn *imapc,
+                                       struct IMAP *imap,
+                                       int imapcode,
                                        imapstate instate)
 {
   CURLcode result = CURLE_OK;
-  struct connectdata *conn = data->conn;
-  struct IMAP *imap = data->req.p.imap;
-  struct imap_conn *imapc = &conn->proto.imapc;
-  const char *line = Curl_dyn_ptr(&data->conn->proto.imapc.pp.recvbuf);
+  const char *line = curlx_dyn_ptr(&imapc->pp.recvbuf);
 
   (void)instate; /* no use for this yet */
 
@@ -1093,11 +1228,11 @@ static CURLcode imap_state_select_resp(struct Curl_easy *data, int imapcode,
         len++;
       if(len && (p[len] == ']')) {
         struct dynbuf uid;
-        Curl_dyn_init(&uid, 20);
-        if(Curl_dyn_addn(&uid, p, len))
+        curlx_dyn_init(&uid, 20);
+        if(curlx_dyn_addn(&uid, p, len))
           return CURLE_OUT_OF_MEMORY;
-        Curl_safefree(imapc->mailbox_uidvalidity);
-        imapc->mailbox_uidvalidity = Curl_dyn_ptr(&uid);
+        free(imapc->mailbox_uidvalidity);
+        imapc->mailbox_uidvalidity = curlx_dyn_ptr(&uid);
       }
     }
   }
@@ -1116,11 +1251,11 @@ static CURLcode imap_state_select_resp(struct Curl_easy *data, int imapcode,
         return CURLE_OUT_OF_MEMORY;
 
       if(imap->custom)
-        result = imap_perform_list(data);
+        result = imap_perform_list(data, imapc, imap);
       else if(imap->query)
-        result = imap_perform_search(data);
+        result = imap_perform_search(data, imapc, imap);
       else
-        result = imap_perform_fetch(data);
+        result = imap_perform_fetch(data, imapc, imap);
     }
   }
   else {
@@ -1133,14 +1268,14 @@ static CURLcode imap_state_select_resp(struct Curl_easy *data, int imapcode,
 
 /* For the (first line of the) FETCH responses */
 static CURLcode imap_state_fetch_resp(struct Curl_easy *data,
-                                      struct connectdata *conn, int imapcode,
+                                      struct imap_conn *imapc,
+                                      int imapcode,
                                       imapstate instate)
 {
   CURLcode result = CURLE_OK;
-  struct imap_conn *imapc = &conn->proto.imapc;
   struct pingpong *pp = &imapc->pp;
-  const char *ptr = Curl_dyn_ptr(&data->conn->proto.imapc.pp.recvbuf);
-  size_t len = data->conn->proto.imapc.pp.nfinal;
+  const char *ptr = curlx_dyn_ptr(&imapc->pp.recvbuf);
+  size_t len = imapc->pp.nfinal;
   bool parsed = FALSE;
   curl_off_t size = 0;
 
@@ -1148,7 +1283,7 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data,
 
   if(imapcode != '*') {
     Curl_pgrsSetDownloadSize(data, -1);
-    imap_state(data, IMAP_STOP);
+    imap_state(data, imapc, IMAP_STOP);
     return CURLE_REMOTE_FILE_NOT_FOUND;
   }
 
@@ -1156,9 +1291,9 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data,
      the continuation data contained within the curly brackets */
   ptr = memchr(ptr, '{', len);
   if(ptr) {
-    char *endptr;
-    if(!curlx_strtoofft(ptr + 1, &endptr, 10, &size) &&
-       (endptr - ptr > 1 && *endptr == '}'))
+    ptr++;
+    if(!curlx_str_number(&ptr, &size, CURL_OFF_T_MAX) &&
+       !curlx_str_single(&ptr, '}'))
       parsed = TRUE;
   }
 
@@ -1173,7 +1308,7 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data,
       size_t chunk = pp->overflow;
 
       /* keep only the overflow */
-      Curl_dyn_tail(&pp->recvbuf, chunk);
+      curlx_dyn_tail(&pp->recvbuf, chunk);
       pp->nfinal = 0; /* done */
 
       if(chunk > (size_t)size)
@@ -1182,11 +1317,11 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data,
 
       if(!chunk) {
         /* no size, we are done with the data */
-        imap_state(data, IMAP_STOP);
+        imap_state(data, imapc, IMAP_STOP);
         return CURLE_OK;
       }
       result = Curl_client_write(data, CLIENTWRITE_BODY,
-                                 Curl_dyn_ptr(&pp->recvbuf), chunk);
+                                 curlx_dyn_ptr(&pp->recvbuf), chunk);
       if(result)
         return result;
 
@@ -1197,12 +1332,12 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data,
       if(pp->overflow > chunk) {
         /* remember the remaining trailing overflow data */
         pp->overflow -= chunk;
-        Curl_dyn_tail(&pp->recvbuf, pp->overflow);
+        curlx_dyn_tail(&pp->recvbuf, pp->overflow);
       }
       else {
         pp->overflow = 0; /* handled */
         /* Free the cache */
-        Curl_dyn_reset(&pp->recvbuf);
+        curlx_dyn_reset(&pp->recvbuf);
       }
     }
 
@@ -1225,13 +1360,14 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data,
   }
 
   /* End of DO phase */
-  imap_state(data, IMAP_STOP);
+  imap_state(data, imapc, IMAP_STOP);
 
   return result;
 }
 
 /* For final FETCH responses performed after the download */
 static CURLcode imap_state_fetch_final_resp(struct Curl_easy *data,
+                                            struct imap_conn *imapc,
                                             int imapcode,
                                             imapstate instate)
 {
@@ -1243,13 +1379,15 @@ static CURLcode imap_state_fetch_final_resp(struct Curl_easy *data,
     result = CURLE_WEIRD_SERVER_REPLY;
   else
     /* End of DONE phase */
-    imap_state(data, IMAP_STOP);
+    imap_state(data, imapc, IMAP_STOP);
 
   return result;
 }
 
 /* For APPEND responses */
-static CURLcode imap_state_append_resp(struct Curl_easy *data, int imapcode,
+static CURLcode imap_state_append_resp(struct Curl_easy *data,
+                                       struct imap_conn *imapc,
+                                       int imapcode,
                                        imapstate instate)
 {
   CURLcode result = CURLE_OK;
@@ -1266,7 +1404,7 @@ static CURLcode imap_state_append_resp(struct Curl_easy *data, int imapcode,
     Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE);
 
     /* End of DO phase */
-    imap_state(data, IMAP_STOP);
+    imap_state(data, imapc, IMAP_STOP);
   }
 
   return result;
@@ -1274,6 +1412,7 @@ static CURLcode imap_state_append_resp(struct Curl_easy *data, int imapcode,
 
 /* For final APPEND responses performed after the upload */
 static CURLcode imap_state_append_final_resp(struct Curl_easy *data,
+                                             struct imap_conn *imapc,
                                              int imapcode,
                                              imapstate instate)
 {
@@ -1285,25 +1424,29 @@ static CURLcode imap_state_append_final_resp(struct Curl_easy *data,
     result = CURLE_UPLOAD_FAILED;
   else
     /* End of DONE phase */
-    imap_state(data, IMAP_STOP);
+    imap_state(data, imapc, IMAP_STOP);
 
   return result;
 }
 
-static CURLcode imap_statemachine(struct Curl_easy *data,
-                                  struct connectdata *conn)
+static CURLcode imap_pp_statemachine(struct Curl_easy *data,
+                                     struct connectdata *conn)
 {
   CURLcode result = CURLE_OK;
   int imapcode;
-  struct imap_conn *imapc = &conn->proto.imapc;
-  struct pingpong *pp = &imapc->pp;
+  struct imap_conn *imapc = Curl_conn_meta_get(conn, CURL_META_IMAP_CONN);
+  struct IMAP *imap = Curl_meta_get(data, CURL_META_IMAP_EASY);
+  struct pingpong *pp;
   size_t nread = 0;
-  (void)data;
 
+  (void)data;
+  if(!imapc || !imap)
+    return CURLE_FAILED_INIT;
+  pp = &imapc->pp;
   /* Busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */
 upgrade_tls:
   if(imapc->state == IMAP_UPGRADETLS) {
-    result = imap_perform_upgrade_tls(data, conn);
+    result = imap_perform_upgrade_tls(data, imapc, conn);
     if(result || (imapc->state == IMAP_UPGRADETLS))
       return result;
   }
@@ -1328,15 +1471,16 @@ static CURLcode imap_statemachine(struct Curl_easy *data,
     /* We have now received a full IMAP server response */
     switch(imapc->state) {
     case IMAP_SERVERGREET:
-      result = imap_state_servergreet_resp(data, imapcode, imapc->state);
+      result = imap_state_servergreet_resp(data, imapc,
+                                           imapcode, imapc->state);
       break;
 
     case IMAP_CAPABILITY:
-      result = imap_state_capability_resp(data, imapcode, imapc->state);
+      result = imap_state_capability_resp(data, imapc, imapcode, imapc->state);
       break;
 
     case IMAP_STARTTLS:
-      result = imap_state_starttls_resp(data, imapcode, imapc->state);
+      result = imap_state_starttls_resp(data, imapc, imapcode, imapc->state);
       /* During UPGRADETLS, leave the read loop as we need to connect
        * (e.g. TLS handshake) before we continue sending/receiving. */
       if(!result && (imapc->state == IMAP_UPGRADETLS))
@@ -1344,42 +1488,45 @@ static CURLcode imap_statemachine(struct Curl_easy *data,
       break;
 
     case IMAP_AUTHENTICATE:
-      result = imap_state_auth_resp(data, conn, imapcode, imapc->state);
+      result = imap_state_auth_resp(data, imapc, imapcode, imapc->state);
       break;
 
     case IMAP_LOGIN:
-      result = imap_state_login_resp(data, imapcode, imapc->state);
+      result = imap_state_login_resp(data, imapc, imapcode, imapc->state);
       break;
 
     case IMAP_LIST:
     case IMAP_SEARCH:
-      result = imap_state_listsearch_resp(data, imapcode, imapc->state);
+      result = imap_state_listsearch_resp(data, imapc, imapcode, imapc->state);
       break;
 
     case IMAP_SELECT:
-      result = imap_state_select_resp(data, imapcode, imapc->state);
+      result = imap_state_select_resp(data, imapc, imap,
+                                      imapcode, imapc->state);
       break;
 
     case IMAP_FETCH:
-      result = imap_state_fetch_resp(data, conn, imapcode, imapc->state);
+      result = imap_state_fetch_resp(data, imapc, imapcode, imapc->state);
       break;
 
     case IMAP_FETCH_FINAL:
-      result = imap_state_fetch_final_resp(data, imapcode, imapc->state);
+      result = imap_state_fetch_final_resp(data, imapc,
+                                           imapcode, imapc->state);
       break;
 
     case IMAP_APPEND:
-      result = imap_state_append_resp(data, imapcode, imapc->state);
+      result = imap_state_append_resp(data, imapc, imapcode, imapc->state);
       break;
 
     case IMAP_APPEND_FINAL:
-      result = imap_state_append_final_resp(data, imapcode, imapc->state);
+      result = imap_state_append_final_resp(data, imapc,
+                                            imapcode, imapc->state);
       break;
 
     case IMAP_LOGOUT:
     default:
       /* internal error */
-      imap_state(data, IMAP_STOP);
+      imap_state(data, imapc, IMAP_STOP);
       break;
     }
   } while(!result && imapc->state != IMAP_STOP && Curl_pp_moredata(pp));
@@ -1391,9 +1538,12 @@ static CURLcode imap_statemachine(struct Curl_easy *data,
 static CURLcode imap_multi_statemach(struct Curl_easy *data, bool *done)
 {
   CURLcode result = CURLE_OK;
-  struct connectdata *conn = data->conn;
-  struct imap_conn *imapc = &conn->proto.imapc;
+  struct imap_conn *imapc =
+    Curl_conn_meta_get(data->conn, CURL_META_IMAP_CONN);
 
+  *done = FALSE;
+  if(!imapc)
+    return CURLE_FAILED_INIT;
   result = Curl_pp_statemach(data, &imapc->pp, FALSE, FALSE);
   *done = (imapc->state == IMAP_STOP);
 
@@ -1401,11 +1551,10 @@ static CURLcode imap_multi_statemach(struct Curl_easy *data, bool *done)
 }
 
 static CURLcode imap_block_statemach(struct Curl_easy *data,
-                                     struct connectdata *conn,
+                                     struct imap_conn *imapc,
                                      bool disconnecting)
 {
   CURLcode result = CURLE_OK;
-  struct imap_conn *imapc = &conn->proto.imapc;
 
   while(imapc->state != IMAP_STOP && !result)
     result = Curl_pp_statemach(data, &imapc->pp, TRUE, disconnecting);
@@ -1413,26 +1562,14 @@ static CURLcode imap_block_statemach(struct Curl_easy *data,
   return result;
 }
 
-/* Allocate and initialize the struct IMAP for the current Curl_easy if
-   required */
-static CURLcode imap_init(struct Curl_easy *data)
-{
-  CURLcode result = CURLE_OK;
-  struct IMAP *imap;
-
-  imap = data->req.p.imap = calloc(1, sizeof(struct IMAP));
-  if(!imap)
-    result = CURLE_OUT_OF_MEMORY;
-
-  return result;
-}
-
 /* For the IMAP "protocol connect" and "doing" phases only */
 static int imap_getsock(struct Curl_easy *data,
                         struct connectdata *conn,
                         curl_socket_t *socks)
 {
-  return Curl_pp_getsock(data, &conn->proto.imapc.pp, socks);
+  struct imap_conn *imapc = Curl_conn_meta_get(conn, CURL_META_IMAP_CONN);
+  return imapc ?
+         Curl_pp_getsock(data, &imapc->pp, socks) : GETSOCK_BLANK;
 }
 
 /***********************************************************************
@@ -1447,32 +1584,24 @@ static int imap_getsock(struct Curl_easy *data,
  */
 static CURLcode imap_connect(struct Curl_easy *data, bool *done)
 {
+  struct imap_conn *imapc =
+    Curl_conn_meta_get(data->conn, CURL_META_IMAP_CONN);
   CURLcode result = CURLE_OK;
-  struct connectdata *conn = data->conn;
-  struct imap_conn *imapc = &conn->proto.imapc;
-  struct pingpong *pp = &imapc->pp;
 
   *done = FALSE; /* default to not done yet */
+  if(!imapc)
+    return CURLE_FAILED_INIT;
 
   /* We always support persistent connections in IMAP */
-  connkeep(conn, "IMAP default");
-
-  PINGPONG_SETUP(pp, imap_statemachine, imap_endofresp);
-
-  /* Set the default preferred authentication type and mechanism */
-  imapc->preftype = IMAP_TYPE_ANY;
-  Curl_sasl_init(&imapc->sasl, data, &saslimap);
-
-  Curl_dyn_init(&imapc->dyn, DYN_IMAP_CMD);
-  Curl_pp_init(pp);
+  connkeep(data->conn, "IMAP default");
 
   /* Parse the URL options */
-  result = imap_parse_url_options(conn);
+  result = imap_parse_url_options(data->conn, imapc);
   if(result)
     return result;
 
   /* Start off waiting for the server greeting response */
-  imap_state(data, IMAP_SERVERGREET);
+  imap_state(data, imapc, IMAP_SERVERGREET);
 
   /* Start off with an response id of '*' */
   strcpy(imapc->resptag, "*");
@@ -1496,10 +1625,13 @@ static CURLcode imap_done(struct Curl_easy *data, CURLcode status,
 {
   CURLcode result = CURLE_OK;
   struct connectdata *conn = data->conn;
-  struct IMAP *imap = data->req.p.imap;
+  struct imap_conn *imapc = Curl_conn_meta_get(conn, CURL_META_IMAP_CONN);
+  struct IMAP *imap = Curl_meta_get(data, CURL_META_IMAP_EASY);
 
   (void)premature;
 
+  if(!imapc)
+    return CURLE_FAILED_INIT;
   if(!imap)
     return CURLE_OK;
 
@@ -1513,33 +1645,20 @@ static CURLcode imap_done(struct Curl_easy *data, CURLcode status,
     /* Handle responses after FETCH or APPEND transfer has finished */
 
     if(!data->state.upload && !IS_MIME_POST(data))
-      imap_state(data, IMAP_FETCH_FINAL);
+      imap_state(data, imapc, IMAP_FETCH_FINAL);
     else {
       /* End the APPEND command first by sending an empty line */
-      result = Curl_pp_sendf(data, &conn->proto.imapc.pp, "%s", "");
+      result = Curl_pp_sendf(data, &imapc->pp, "%s", "");
       if(!result)
-        imap_state(data, IMAP_APPEND_FINAL);
+        imap_state(data, imapc, IMAP_APPEND_FINAL);
     }
 
     /* Run the state-machine */
     if(!result)
-      result = imap_block_statemach(data, conn, FALSE);
+      result = imap_block_statemach(data, imapc, FALSE);
   }
 
-  /* Cleanup our per-request based variables */
-  Curl_safefree(imap->mailbox);
-  Curl_safefree(imap->uidvalidity);
-  Curl_safefree(imap->uid);
-  Curl_safefree(imap->mindex);
-  Curl_safefree(imap->section);
-  Curl_safefree(imap->partial);
-  Curl_safefree(imap->query);
-  Curl_safefree(imap->custom);
-  Curl_safefree(imap->custom_params);
-
-  /* Clear the transfer mode for the next request */
-  imap->transfer = PPTRANSFER_BODY;
-
+  imap_easy_reset(imap);
   return result;
 }
 
@@ -1556,11 +1675,13 @@ static CURLcode imap_perform(struct Curl_easy *data, bool *connected,
   /* This is IMAP and no proxy */
   CURLcode result = CURLE_OK;
   struct connectdata *conn = data->conn;
-  struct IMAP *imap = data->req.p.imap;
-  struct imap_conn *imapc = &conn->proto.imapc;
+  struct imap_conn *imapc = Curl_conn_meta_get(conn, CURL_META_IMAP_CONN);
+  struct IMAP *imap = Curl_meta_get(data, CURL_META_IMAP_EASY);
   bool selected = FALSE;
 
   DEBUGF(infof(data, "DO phase starts"));
+  if(!imapc || !imap)
+    return CURLE_FAILED_INIT;
 
   if(data->req.no_body) {
     /* Requested no body means no transfer */
@@ -1580,23 +1701,23 @@ static CURLcode imap_perform(struct Curl_easy *data, bool *connected,
   /* Start the first command in the DO phase */
   if(data->state.upload || IS_MIME_POST(data))
     /* APPEND can be executed directly */
-    result = imap_perform_append(data);
+    result = imap_perform_append(data, imapc, imap);
   else if(imap->custom && (selected || !imap->mailbox))
     /* Custom command using the same mailbox or no mailbox */
-    result = imap_perform_list(data);
+    result = imap_perform_list(data, imapc, imap);
   else if(!imap->custom && selected && (imap->uid || imap->mindex))
     /* FETCH from the same mailbox */
-    result = imap_perform_fetch(data);
+    result = imap_perform_fetch(data, imapc, imap);
   else if(!imap->custom && selected && imap->query)
     /* SEARCH the current mailbox */
-    result = imap_perform_search(data);
+    result = imap_perform_search(data, imapc, imap);
   else if(imap->mailbox && !selected &&
          (imap->custom || imap->uid || imap->mindex || imap->query))
     /* SELECT the mailbox */
-    result = imap_perform_select(data);
+    result = imap_perform_select(data, imapc, imap);
   else
     /* LIST */
-    result = imap_perform_list(data);
+    result = imap_perform_list(data, imapc, imap);
 
   if(result)
     return result;
@@ -1623,20 +1744,23 @@ static CURLcode imap_perform(struct Curl_easy *data, bool *connected,
  */
 static CURLcode imap_do(struct Curl_easy *data, bool *done)
 {
+  struct IMAP *imap = Curl_meta_get(data, CURL_META_IMAP_EASY);
   CURLcode result = CURLE_OK;
   *done = FALSE; /* default to false */
 
+  if(!imap)
+    return CURLE_FAILED_INIT;
   /* Parse the URL path */
-  result = imap_parse_url_path(data);
+  result = imap_parse_url_path(data, imap);
   if(result)
     return result;
 
   /* Parse the custom request */
-  result = imap_parse_custom_request(data);
+  result = imap_parse_custom_request(data, imap);
   if(result)
     return result;
 
-  result = imap_regular_transfer(data, done);
+  result = imap_regular_transfer(data, imap, done);
 
   return result;
 }
@@ -1651,39 +1775,32 @@ static CURLcode imap_do(struct Curl_easy *data, bool *done)
 static CURLcode imap_disconnect(struct Curl_easy *data,
                                 struct connectdata *conn, bool dead_connection)
 {
-  struct imap_conn *imapc = &conn->proto.imapc;
-  (void)data;
+  struct imap_conn *imapc = Curl_conn_meta_get(conn, CURL_META_IMAP_CONN);
 
-  /* We cannot send quit unconditionally. If this connection is stale or
-     bad in any way, sending quit and waiting around here will make the
-     disconnect wait in vain and cause more problems than we need to. */
+  (void)data;
+  if(imapc) {
+    /* We cannot send quit unconditionally. If this connection is stale or
+       bad in any way, sending quit and waiting around here will make the
+       disconnect wait in vain and cause more problems than we need to. */
+
+    /* 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, imapc))
+        (void)imap_block_statemach(data, imapc, TRUE); /* ignore errors */
+    }
 
-  /* 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))
-      (void)imap_block_statemach(data, conn, TRUE); /* ignore errors */
+    /* Cleanup the SASL module */
+    Curl_sasl_cleanup(conn, imapc->sasl.authused);
   }
-
-  /* Disconnect from the server */
-  Curl_pp_disconnect(&imapc->pp);
-  Curl_dyn_free(&imapc->dyn);
-
-  /* Cleanup the SASL module */
-  Curl_sasl_cleanup(conn, imapc->sasl.authused);
-
-  /* Cleanup our connection based variables */
-  Curl_safefree(imapc->mailbox);
-  Curl_safefree(imapc->mailbox_uidvalidity);
-
   return CURLE_OK;
 }
 
 /* Call this when the DO phase has completed */
-static CURLcode imap_dophase_done(struct Curl_easy *data, bool connected)
+static CURLcode imap_dophase_done(struct Curl_easy *data,
+                                  struct IMAP *imap,
+                                  bool connected)
 {
-  struct IMAP *imap = data->req.p.imap;
-
   (void)connected;
 
   if(imap->transfer != PPTRANSFER_BODY)
@@ -1696,12 +1813,17 @@ static CURLcode imap_dophase_done(struct Curl_easy *data, bool connected)
 /* Called from multi.c while DOing */
 static CURLcode imap_doing(struct Curl_easy *data, bool *dophase_done)
 {
-  CURLcode result = imap_multi_statemach(data, dophase_done);
+  struct IMAP *imap = Curl_meta_get(data, CURL_META_IMAP_EASY);
+  CURLcode result;
+
+  if(!imap)
+    return CURLE_FAILED_INIT;
 
+  result = imap_multi_statemach(data, dophase_done);
   if(result)
     DEBUGF(infof(data, "DO phase failed"));
   else if(*dophase_done) {
-    result = imap_dophase_done(data, FALSE /* not connected */);
+    result = imap_dophase_done(data, imap, FALSE /* not connected */);
 
     DEBUGF(infof(data, "DO phase is complete"));
   }
@@ -1719,6 +1841,7 @@ static CURLcode imap_doing(struct Curl_easy *data, bool *dophase_done)
  * remote host.
  */
 static CURLcode imap_regular_transfer(struct Curl_easy *data,
+                                      struct IMAP *imap,
                                       bool *dophase_done)
 {
   CURLcode result = CURLE_OK;
@@ -1738,21 +1861,75 @@ static CURLcode imap_regular_transfer(struct Curl_easy *data,
 
   /* Perform post DO phase operations if necessary */
   if(!result && *dophase_done)
-    result = imap_dophase_done(data, connected);
+    result = imap_dophase_done(data, imap, connected);
 
   return result;
 }
 
+static void imap_easy_reset(struct IMAP *imap)
+{
+  Curl_safefree(imap->mailbox);
+  Curl_safefree(imap->uidvalidity);
+  Curl_safefree(imap->uid);
+  Curl_safefree(imap->mindex);
+  Curl_safefree(imap->section);
+  Curl_safefree(imap->partial);
+  Curl_safefree(imap->query);
+  Curl_safefree(imap->custom);
+  Curl_safefree(imap->custom_params);
+  /* Clear the transfer mode for the next request */
+  imap->transfer = PPTRANSFER_BODY;
+}
+
+static void imap_easy_dtor(void *key, size_t klen, void *entry)
+{
+  struct IMAP *imap = entry;
+  (void)key;
+  (void)klen;
+  imap_easy_reset(imap);
+  free(imap);
+}
+
+static void imap_conn_dtor(void *key, size_t klen, void *entry)
+{
+  struct imap_conn *imapc = entry;
+  (void)key;
+  (void)klen;
+  Curl_pp_disconnect(&imapc->pp);
+  curlx_dyn_free(&imapc->dyn);
+  Curl_safefree(imapc->mailbox);
+  Curl_safefree(imapc->mailbox_uidvalidity);
+  free(imapc);
+}
+
 static CURLcode imap_setup_connection(struct Curl_easy *data,
                                       struct connectdata *conn)
 {
-  /* Initialise the IMAP layer */
-  CURLcode result = imap_init(data);
-  if(result)
-    return result;
+  struct imap_conn *imapc;
+  struct pingpong *pp;
+  struct IMAP *imap;
+
+  imapc = calloc(1, sizeof(*imapc));
+  if(!imapc)
+    return CURLE_OUT_OF_MEMORY;
+
+  pp = &imapc->pp;
+  PINGPONG_SETUP(pp, imap_pp_statemachine, imap_endofresp);
 
-  /* Clear the TLS upgraded flag */
-  conn->bits.tls_upgraded = FALSE;
+  /* Set the default preferred authentication type and mechanism */
+  imapc->preftype = IMAP_TYPE_ANY;
+  Curl_sasl_init(&imapc->sasl, data, &saslimap);
+
+  curlx_dyn_init(&imapc->dyn, DYN_IMAP_CMD);
+  Curl_pp_init(pp);
+
+  if(Curl_conn_meta_set(conn, CURL_META_IMAP_CONN, imapc, imap_conn_dtor))
+    return CURLE_OUT_OF_MEMORY;
+
+  imap = calloc(1, sizeof(struct IMAP));
+  if(!imap ||
+     Curl_meta_set(data, CURL_META_IMAP_EASY, imap, imap_easy_dtor))
+    return CURLE_OUT_OF_MEMORY;
 
   return CURLE_OK;
 }
@@ -1765,10 +1942,11 @@ static CURLcode imap_setup_connection(struct Curl_easy *data,
  *
  * Designed to never block.
  */
-static CURLcode imap_sendf(struct Curl_easy *data, const char *fmt, ...)
+static CURLcode imap_sendf(struct Curl_easy *data,
+                           struct imap_conn *imapc,
+                           const char *fmt, ...)
 {
   CURLcode result = CURLE_OK;
-  struct imap_conn *imapc = &data->conn->proto.imapc;
 
   DEBUGASSERT(fmt);
 
@@ -1778,10 +1956,10 @@ static CURLcode imap_sendf(struct Curl_easy *data, const char *fmt, ...)
             ++imapc->cmdid);
 
   /* start with a blank buffer */
-  Curl_dyn_reset(&imapc->dyn);
+  curlx_dyn_reset(&imapc->dyn);
 
   /* append tag + space + fmt */
-  result = Curl_dyn_addf(&imapc->dyn, "%s %s", imapc->resptag, fmt);
+  result = curlx_dyn_addf(&imapc->dyn, "%s %s", imapc->resptag, fmt);
   if(!result) {
     va_list ap;
     va_start(ap, fmt);
@@ -1789,7 +1967,7 @@ static CURLcode imap_sendf(struct Curl_easy *data, const char *fmt, ...)
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wformat-nonliteral"
 #endif
-    result = Curl_pp_vsendf(data, &imapc->pp, Curl_dyn_ptr(&imapc->dyn), ap);
+    result = Curl_pp_vsendf(data, &imapc->pp, curlx_dyn_ptr(&imapc->dyn), ap);
 #ifdef __clang__
 #pragma clang diagnostic pop
 #endif
@@ -1823,24 +2001,24 @@ static char *imap_atom(const char *str, bool escape_only)
     /* nothing to escape, return a strdup */
     return strdup(str);
 
-  Curl_dyn_init(&line, 2000);
+  curlx_dyn_init(&line, 2000);
 
-  if(!escape_only && Curl_dyn_addn(&line, "\"", 1))
+  if(!escape_only && curlx_dyn_addn(&line, "\"", 1))
     return NULL;
 
   while(*str) {
     if((*str == '\\' || *str == '"') &&
-       Curl_dyn_addn(&line, "\\", 1))
+       curlx_dyn_addn(&line, "\\", 1))
       return NULL;
-    if(Curl_dyn_addn(&line, str, 1))
+    if(curlx_dyn_addn(&line, str, 1))
       return NULL;
     str++;
   }
 
-  if(!escape_only && Curl_dyn_addn(&line, "\"", 1))
+  if(!escape_only && curlx_dyn_addn(&line, "\"", 1))
     return NULL;
 
-  return Curl_dyn_ptr(&line);
+  return curlx_dyn_ptr(&line);
 }
 
 /***********************************************************************
@@ -1882,10 +2060,10 @@ static bool imap_is_bchar(char ch)
  *
  * Parse the URL login options.
  */
-static CURLcode imap_parse_url_options(struct connectdata *conn)
+static CURLcode imap_parse_url_options(struct connectdata *conn,
+                                       struct imap_conn *imapc)
 {
   CURLcode result = CURLE_OK;
-  struct imap_conn *imapc = &conn->proto.imapc;
   const char *ptr = conn->options;
   bool prefer_login = FALSE;
 
@@ -1946,11 +2124,11 @@ static CURLcode imap_parse_url_options(struct connectdata *conn)
  * Parse the URL path into separate path components.
  *
  */
-static CURLcode imap_parse_url_path(struct Curl_easy *data)
+static CURLcode imap_parse_url_path(struct Curl_easy *data,
+                                    struct IMAP *imap)
 {
   /* The imap struct is already initialised in imap_connect() */
   CURLcode result = CURLE_OK;
-  struct IMAP *imap = data->req.p.imap;
   const char *begin = &data->state.up.path[1]; /* skip leading slash */
   const char *ptr = begin;
 
@@ -2078,10 +2256,10 @@ static CURLcode imap_parse_url_path(struct Curl_easy *data)
  *
  * Parse the custom request.
  */
-static CURLcode imap_parse_custom_request(struct Curl_easy *data)
+static CURLcode imap_parse_custom_request(struct Curl_easy *data,
+                                          struct IMAP *imap)
 {
   CURLcode result = CURLE_OK;
-  struct IMAP *imap = data->req.p.imap;
   const char *custom = data->set.str[STRING_CUSTOMREQUEST];
 
   if(custom) {
diff --git a/extra/curl/curl-8.14.1/lib/imap.h b/extra/curl/curl-8.14.1/lib/imap.h
new file mode 100644
index 000000000000..f802ed5c0c72
--- /dev/null
+++ b/extra/curl/curl-8.14.1/lib/imap.h
@@ -0,0 +1,42 @@
+#ifndef HEADER_CURL_IMAP_H
+#define HEADER_CURL_IMAP_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "pingpong.h"
+#include "curl_sasl.h"
+
+
+extern const struct Curl_handler Curl_handler_imap;
+extern const struct Curl_handler Curl_handler_imaps;
+
+/* Authentication type flags */
+#define IMAP_TYPE_CLEARTEXT (1 << 0)
+#define IMAP_TYPE_SASL      (1 << 1)
+
+/* Authentication type values */
+#define IMAP_TYPE_NONE      0
+#define IMAP_TYPE_ANY       (IMAP_TYPE_CLEARTEXT|IMAP_TYPE_SASL)
+
+#endif /* HEADER_CURL_IMAP_H */
diff --git a/extra/curl/curl-8.12.1/lib/inet_ntop.c b/extra/curl/curl-8.14.1/lib/inet_ntop.c
similarity index 94%
rename from extra/curl/curl-8.12.1/lib/inet_ntop.c
rename to extra/curl/curl-8.14.1/lib/inet_ntop.c
index bce0ed7c569d..bb2ec5787257 100644
--- a/extra/curl/curl-8.12.1/lib/inet_ntop.c
+++ b/extra/curl/curl-8.14.1/lib/inet_ntop.c
@@ -38,7 +38,7 @@
 #include "curl_printf.h"
 
 #define IN6ADDRSZ       16
-#define INADDRSZ         4
+/* #define INADDRSZ         4 */
 #define INT16SZ          2
 
 /*
@@ -56,7 +56,7 @@
  * Returns `dst' (as a const)
  * Note:
  *  - uses no statics
- *  - takes a unsigned char* not an in_addr as input
+ *  - takes an unsigned char* not an in_addr as input
  */
 static char *inet_ntop4(const unsigned char *src, char *dst, size_t size)
 {
@@ -74,7 +74,11 @@ static char *inet_ntop4(const unsigned char *src, char *dst, size_t size)
 
   len = strlen(tmp);
   if(len == 0 || len >= size) {
-    errno = ENOSPC;
+#ifdef USE_WINSOCK
+    CURL_SETERRNO(WSAEINVAL);
+#else
+    CURL_SETERRNO(ENOSPC);
+#endif
     return NULL;
   }
   strcpy(dst, tmp);
@@ -153,7 +157,6 @@ static char *inet_ntop6(const unsigned char *src, char *dst, size_t size)
     if(i == 6 && best.base == 0 &&
         (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) {
       if(!inet_ntop4(src + 12, tp, sizeof(tmp) - (tp - tmp))) {
-        errno = ENOSPC;
         return NULL;
       }
       tp += strlen(tp);
@@ -171,7 +174,11 @@ static char *inet_ntop6(const unsigned char *src, char *dst, size_t size)
   /* Check for overflow, copy, and we are done.
    */
   if((size_t)(tp - tmp) > size) {
-    errno = ENOSPC;
+#ifdef USE_WINSOCK
+    CURL_SETERRNO(WSAEINVAL);
+#else
+    CURL_SETERRNO(ENOSPC);
+#endif
     return NULL;
   }
   strcpy(dst, tmp);
@@ -197,7 +204,7 @@ char *Curl_inet_ntop(int af, const void *src, char *buf, size_t size)
   case AF_INET6:
     return inet_ntop6((const unsigned char *)src, buf, size);
   default:
-    errno = EAFNOSUPPORT;
+    CURL_SETERRNO(SOCKEAFNOSUPPORT);
     return NULL;
   }
 }
diff --git a/extra/curl/curl-8.12.1/lib/inet_ntop.h b/extra/curl/curl-8.14.1/lib/inet_ntop.h
similarity index 74%
rename from extra/curl/curl-8.12.1/lib/inet_ntop.h
rename to extra/curl/curl-8.14.1/lib/inet_ntop.h
index 6bc7e27a79f2..9923daaed1b9 100644
--- a/extra/curl/curl-8.12.1/lib/inet_ntop.h
+++ b/extra/curl/curl-8.14.1/lib/inet_ntop.h
@@ -38,19 +38,13 @@ char *Curl_inet_ntop(int af, const void *addr, char *buf, size_t size);
 #ifdef HAVE_ARPA_INET_H
 #include <arpa/inet.h>
 #endif
-#ifdef _WIN32
-#if defined(_MSC_VER) && (_MSC_VER <= 1900)
-#define Curl_inet_ntop(af,addr,buf,size) inet_ntop(af, (void *)addr, buf, size)
+#ifdef __AMIGA__
+#define Curl_inet_ntop(af,addr,buf,size)                                \
+  (char *)inet_ntop(af, CURL_UNCONST(addr), (unsigned char *)buf,       \
+                    (curl_socklen_t)(size))
 #else
-#define Curl_inet_ntop(af,addr,buf,size) inet_ntop(af, addr, buf, size)
-#endif
-#elif defined(__AMIGA__)
-#define Curl_inet_ntop(af,addr,buf,size) \
-        (char *)inet_ntop(af, (void *)addr, (unsigned char *)buf, \
-                          (curl_socklen_t)(size))
-#else
-#define Curl_inet_ntop(af,addr,buf,size) \
-        inet_ntop(af, addr, buf, (curl_socklen_t)(size))
+#define Curl_inet_ntop(af,addr,buf,size)                \
+  inet_ntop(af, addr, buf, (curl_socklen_t)(size))
 #endif
 #endif
 
diff --git a/extra/curl/curl-8.12.1/lib/krb5.c b/extra/curl/curl-8.14.1/lib/krb5.c
similarity index 94%
rename from extra/curl/curl-8.12.1/lib/krb5.c
rename to extra/curl/curl-8.14.1/lib/krb5.c
index 4faa26349964..9ebba4e3d1d0 100644
--- a/extra/curl/curl-8.12.1/lib/krb5.c
+++ b/extra/curl/curl-8.14.1/lib/krb5.c
@@ -46,15 +46,16 @@
 #endif
 
 #include "urldata.h"
+#include "url.h"
 #include "cfilters.h"
 #include "cf-socket.h"
-#include "curl_base64.h"
+#include "curlx/base64.h"
 #include "ftp.h"
 #include "curl_gssapi.h"
 #include "sendf.h"
 #include "transfer.h"
 #include "curl_krb5.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
 #include "strcase.h"
 #include "strdup.h"
 
@@ -173,7 +174,7 @@ krb5_encode(void *app_data, const void *from, int length, int level, void **to)
   /* NOTE that the cast is safe, neither of the krb5, gnu gss and heimdal
    * libraries modify the input buffer in gss_wrap()
    */
-  dec.value = (void *)from;
+  dec.value = CURL_UNCONST(from);
   dec.length = (size_t)length;
   maj = gss_wrap(&min, *context,
                  level == PROT_PRIVATE,
@@ -215,8 +216,12 @@ krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn)
   struct gss_channel_bindings_struct chan;
   size_t base64_sz = 0;
   struct sockaddr_in *remote_addr =
-    (struct sockaddr_in *)(void *)&conn->remote_addr->curl_sa_addr;
+    (struct sockaddr_in *)CURL_UNCONST(&conn->remote_addr->curl_sa_addr);
   char *stringp;
+  struct ftp_conn *ftpc = Curl_conn_meta_get(conn, CURL_META_FTP_CONN);
+
+  if(!ftpc)
+    return -2;
 
   if(getsockname(conn->sock[FIRSTSOCKET],
                  (struct sockaddr *)&conn->local_addr, &l) < 0)
@@ -242,8 +247,7 @@ krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn)
       if(Curl_GetFTPResponse(data, &nread, NULL))
         return -1;
       else {
-        struct pingpong *pp = &conn->proto.ftpc.pp;
-        char *line = Curl_dyn_ptr(&pp->recvbuf);
+        char *line = curlx_dyn_ptr(&ftpc->pp.recvbuf);
         if(line[0] != '3')
           return -1;
       }
@@ -304,7 +308,7 @@ krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn)
       if(output_buffer.length) {
         char *cmd;
 
-        result = Curl_base64_encode((char *)output_buffer.value,
+        result = curlx_base64_encode((char *)output_buffer.value,
                                     output_buffer.length, &p, &base64_sz);
         if(result) {
           infof(data, "base64-encoding: %s", curl_easy_strerror(result));
@@ -331,9 +335,8 @@ krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn)
           break;
         }
         else {
-          struct pingpong *pp = &conn->proto.ftpc.pp;
-          size_t len = Curl_dyn_len(&pp->recvbuf);
-          p = Curl_dyn_ptr(&pp->recvbuf);
+          size_t len = curlx_dyn_len(&ftpc->pp.recvbuf);
+          p = curlx_dyn_ptr(&ftpc->pp.recvbuf);
           if((len < 4) || (p[0] != '2' && p[0] != '3')) {
             infof(data, "Server did not accept auth data");
             ret = AUTH_ERROR;
@@ -348,7 +351,7 @@ krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn)
         if(p) {
           unsigned char *outptr;
           size_t outlen;
-          result = Curl_base64_decode(p + 5, &outptr, &outlen);
+          result = curlx_base64_decode(p + 5, &outptr, &outlen);
           if(result) {
             failf(data, "base64-decoding: %s", curl_easy_strerror(result));
             ret = AUTH_CONTINUE;
@@ -537,7 +540,7 @@ static CURLcode read_data(struct Curl_easy *data, int sockindex,
     if(len > CURL_MAX_INPUT_LENGTH)
       return CURLE_TOO_LARGE;
 
-    Curl_dyn_reset(&buf->buf);
+    curlx_dyn_reset(&buf->buf);
   }
   else
     return CURLE_RECV_ERROR;
@@ -548,18 +551,18 @@ static CURLcode read_data(struct Curl_easy *data, int sockindex,
     result = socket_read(data, sockindex, buffer, (size_t)nread);
     if(result)
       return result;
-    result = Curl_dyn_addn(&buf->buf, buffer, nread);
+    result = curlx_dyn_addn(&buf->buf, buffer, nread);
     if(result)
       return result;
     len -= nread;
   } while(len);
   /* this decodes the dynbuf *in place* */
   nread = conn->mech->decode(conn->app_data,
-                             Curl_dyn_ptr(&buf->buf),
+                             curlx_dyn_ptr(&buf->buf),
                              len, conn->data_prot, conn);
   if(nread < 0)
     return CURLE_RECV_ERROR;
-  Curl_dyn_setlen(&buf->buf, nread);
+  curlx_dyn_setlen(&buf->buf, nread);
   buf->index = 0;
   return CURLE_OK;
 }
@@ -567,10 +570,10 @@ static CURLcode read_data(struct Curl_easy *data, int sockindex,
 static size_t
 buffer_read(struct krb5buffer *buf, void *data, size_t len)
 {
-  size_t size = Curl_dyn_len(&buf->buf);
+  size_t size = curlx_dyn_len(&buf->buf);
   if(size - buf->index < len)
     len = size - buf->index;
-  memcpy(data, Curl_dyn_ptr(&buf->buf) + buf->index, len);
+  memcpy(data, curlx_dyn_ptr(&buf->buf) + buf->index, len);
   buf->index += len;
   return len;
 }
@@ -605,7 +608,7 @@ static ssize_t sec_recv(struct Curl_easy *data, int sockindex,
   while(len > 0) {
     if(read_data(data, sockindex, &conn->in_buffer))
       return -1;
-    if(Curl_dyn_len(&conn->in_buffer.buf) == 0) {
+    if(curlx_dyn_len(&conn->in_buffer.buf) == 0) {
       if(bytes_read > 0)
         conn->in_buffer.eof_flag = 1;
       return bytes_read;
@@ -645,7 +648,7 @@ static void do_sec_send(struct Curl_easy *data, struct connectdata *conn,
     return; /* error */
 
   if(iscmd) {
-    error = Curl_base64_encode(buffer, curlx_sitouz(bytes),
+    error = curlx_base64_encode(buffer, curlx_sitouz(bytes),
                                &cmd_buffer, &cmd_size);
     if(error) {
       free(buffer);
@@ -723,7 +726,7 @@ int Curl_sec_read_msg(struct Curl_easy *data, struct connectdata *conn,
 
   DEBUGASSERT(level > PROT_NONE && level < PROT_LAST);
 
-  error = Curl_base64_decode(buffer + 4, (unsigned char **)&buf, &decoded_sz);
+  error = curlx_base64_decode(buffer + 4, (unsigned char **)&buf, &decoded_sz);
   if(error || decoded_sz == 0)
     return -1;
 
@@ -781,9 +784,12 @@ static int sec_set_protection_level(struct Curl_easy *data)
   if(level) {
     char *pbsz;
     unsigned int buffer_size = 1 << 20; /* 1048576 */
-    struct pingpong *pp = &conn->proto.ftpc.pp;
+    struct ftp_conn *ftpc = Curl_conn_meta_get(conn, CURL_META_FTP_CONN);
     char *line;
 
+    if(!ftpc)
+      return -2;
+
     code = ftp_send_command(data, "PBSZ %u", buffer_size);
     if(code < 0)
       return -1;
@@ -794,7 +800,7 @@ static int sec_set_protection_level(struct Curl_easy *data)
     }
     conn->buffer_size = buffer_size;
 
-    line = Curl_dyn_ptr(&pp->recvbuf);
+    line = curlx_dyn_ptr(&ftpc->pp.recvbuf);
     pbsz = strstr(line, "PBSZ=");
     if(pbsz) {
       /* stick to default value if the check fails */
@@ -855,7 +861,6 @@ static CURLcode choose_mech(struct Curl_easy *data, struct connectdata *conn)
             mech->name);
       return CURLE_FAILED_INIT;
     }
-    Curl_dyn_init(&conn->in_buffer.buf, CURL_MAX_INPUT_LENGTH);
   }
 
   infof(data, "Trying mechanism %s...", mech->name);
@@ -914,14 +919,21 @@ Curl_sec_login(struct Curl_easy *data, struct connectdata *conn)
   return choose_mech(data, conn);
 }
 
+void
+Curl_sec_conn_init(struct connectdata *conn)
+{
+  curlx_dyn_init(&conn->in_buffer.buf, CURL_MAX_INPUT_LENGTH);
+  conn->in_buffer.index = 0;
+  conn->in_buffer.eof_flag = 0;
+}
 
 void
-Curl_sec_end(struct connectdata *conn)
+Curl_sec_conn_destroy(struct connectdata *conn)
 {
   if(conn->mech && conn->mech->end)
     conn->mech->end(conn->app_data);
   Curl_safefree(conn->app_data);
-  Curl_dyn_free(&conn->in_buffer.buf);
+  curlx_dyn_free(&conn->in_buffer.buf);
   conn->in_buffer.index = 0;
   conn->in_buffer.eof_flag = 0;
   conn->sec_complete = 0;
diff --git a/extra/curl/curl-8.12.1/lib/ldap.c b/extra/curl/curl-8.14.1/lib/ldap.c
similarity index 93%
rename from extra/curl/curl-8.12.1/lib/ldap.c
rename to extra/curl/curl-8.14.1/lib/ldap.c
index 77bd9fba67af..c1be2f4bda93 100644
--- a/extra/curl/curl-8.12.1/lib/ldap.c
+++ b/extra/curl/curl-8.14.1/lib/ldap.c
@@ -89,10 +89,10 @@
 #include "progress.h"
 #include "transfer.h"
 #include "strcase.h"
-#include "strtok.h"
+#include "curlx/strparse.h"
 #include "curl_ldap.h"
-#include "curl_multibyte.h"
-#include "curl_base64.h"
+#include "curlx/multibyte.h"
+#include "curlx/base64.h"
 #include "connect.h"
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -285,8 +285,8 @@ static int ldap_win_bind(struct Curl_easy *data, LDAP *server,
   PTCHAR inpass = NULL;
 
   if(user && passwd && (data->set.httpauth & CURLAUTH_BASIC)) {
-    inuser = curlx_convert_UTF8_to_tchar((char *) user);
-    inpass = curlx_convert_UTF8_to_tchar((char *) passwd);
+    inuser = curlx_convert_UTF8_to_tchar(user);
+    inpass = curlx_convert_UTF8_to_tchar(passwd);
 
     rc = (int)ldap_simple_bind_s(server, inuser, inpass);
 
@@ -543,7 +543,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
 #endif
       name_len = strlen(name);
 
-      result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"DN: ", 4);
+      result = Curl_client_write(data, CLIENTWRITE_BODY, "DN: ", 4);
       if(result) {
         FREE_ON_WINLDAP(name);
         ldap_memfree(dn);
@@ -557,7 +557,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
         goto quit;
       }
 
-      result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 1);
+      result = Curl_client_write(data, CLIENTWRITE_BODY, "\n", 1);
       if(result) {
         FREE_ON_WINLDAP(name);
         ldap_memfree(dn);
@@ -593,7 +593,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
       vals = ldap_get_values_len(server, entryIterator, attribute);
       if(vals) {
         for(i = 0; (vals[i] != NULL); i++) {
-          result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\t", 1);
+          result = Curl_client_write(data, CLIENTWRITE_BODY, "\t", 1);
           if(result) {
             ldap_value_free_len(vals);
             FREE_ON_WINLDAP(attr);
@@ -615,7 +615,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
             goto quit;
           }
 
-          result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)": ", 2);
+          result = Curl_client_write(data, CLIENTWRITE_BODY, ": ", 2);
           if(result) {
             ldap_value_free_len(vals);
             FREE_ON_WINLDAP(attr);
@@ -629,8 +629,8 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
           if((attr_len > 7) &&
              (strcmp(";binary", attr + (attr_len - 7)) == 0)) {
             /* Binary attribute, encode to base64. */
-            result = Curl_base64_encode(vals[i]->bv_val, vals[i]->bv_len,
-                                        &val_b64, &val_b64_sz);
+            result = curlx_base64_encode(vals[i]->bv_val, vals[i]->bv_len,
+                                         &val_b64, &val_b64_sz);
             if(result) {
               ldap_value_free_len(vals);
               FREE_ON_WINLDAP(attr);
@@ -670,7 +670,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
             }
           }
 
-          result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 1);
+          result = Curl_client_write(data, CLIENTWRITE_BODY, "\n", 1);
           if(result) {
             ldap_value_free_len(vals);
             FREE_ON_WINLDAP(attr);
@@ -690,7 +690,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
       FREE_ON_WINLDAP(attr);
       ldap_memfree(attribute);
 
-      result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 1);
+      result = Curl_client_write(data, CLIENTWRITE_BODY, "\n", 1);
       if(result)
         goto quit;
     }
@@ -728,7 +728,9 @@ static void _ldap_trace(const char *fmt, ...)
 
   if(do_trace == -1) {
     const char *env = getenv("CURL_TRACE");
-    do_trace = (env && strtol(env, NULL, 10) > 0);
+    curl_off_t e = 0;
+    if(!curlx_str_number(&env, &e, INT_MAX))
+      do_trace = e > 0;
   }
   if(!do_trace)
     return;
@@ -759,36 +761,17 @@ static int str2scope(const char *p)
   return -1;
 }
 
-/*
- * Split 'str' into strings separated by commas.
- * Note: out[] points into 'str'.
- */
-static bool split_str(char *str, char ***out, size_t *count)
+/* number of entries in the attributes list */
+static size_t num_entries(const char *s)
 {
-  char **res;
-  char *lasts;
-  char *s;
-  size_t  i;
   size_t items = 1;
 
-  s = strchr(str, ',');
+  s = strchr(s, ',');
   while(s) {
     items++;
-    s = strchr(++s, ',');
+    s = strchr(s + 1, ',');
   }
-
-  res = calloc(items, sizeof(char *));
-  if(!res)
-    return FALSE;
-
-  for(i = 0, s = Curl_strtok_r(str, ",", &lasts); s && i < items;
-      s = Curl_strtok_r(NULL, ",", &lasts), i++)
-    res[i] = s;
-
-  *out = res;
-  *count = items;
-
-  return TRUE;
+  return items;
 }
 
 /*
@@ -882,15 +865,8 @@ static int _ldap_url_parse2(struct Curl_easy *data,
     *q++ = '\0';
 
   if(*p) {
-    char **attributes;
-    size_t count = 0;
-
-    /* Split the string into an array of attributes */
-    if(!split_str(p, &attributes, &count)) {
-      rc = LDAP_NO_MEMORY;
-
-      goto quit;
-    }
+    size_t count = num_entries(p); /* at least one */
+    const char *atp = p;
 
     /* Allocate our array (+1 for the NULL entry) */
 #if defined(USE_WIN32_LDAP)
@@ -899,27 +875,25 @@ static int _ldap_url_parse2(struct Curl_easy *data,
     ludp->lud_attrs = calloc(count + 1, sizeof(char *));
 #endif
     if(!ludp->lud_attrs) {
-      free(attributes);
-
       rc = LDAP_NO_MEMORY;
-
       goto quit;
     }
 
     for(i = 0; i < count; i++) {
       char *unescaped;
       CURLcode result;
+      struct Curl_str out;
 
-      LDAP_TRACE(("attr[%zu] '%s'\n", i, attributes[i]));
+      if(curlx_str_until(&atp, &out, 1024, ','))
+        break;
+
+      LDAP_TRACE(("attr[%zu] '%.*s'\n", i, (int)out.len, out.str));
 
       /* Unescape the attribute */
-      result = Curl_urldecode(attributes[i], 0, &unescaped, NULL,
+      result = Curl_urldecode(out.str, out.len, &unescaped, NULL,
                               REJECT_ZERO);
       if(result) {
-        free(attributes);
-
         rc = LDAP_NO_MEMORY;
-
         goto quit;
       }
 
@@ -931,10 +905,7 @@ static int _ldap_url_parse2(struct Curl_easy *data,
       free(unescaped);
 
       if(!ludp->lud_attrs[i]) {
-        free(attributes);
-
         rc = LDAP_NO_MEMORY;
-
         goto quit;
       }
 #else
@@ -942,9 +913,9 @@ static int _ldap_url_parse2(struct Curl_easy *data,
 #endif
 
       ludp->lud_attrs_dups++;
+      if(curlx_str_single(&atp, ','))
+        break;
     }
-
-    free(attributes);
   }
 
   p = q;
diff --git a/extra/curl/curl-8.12.1/lib/libcurl.def b/extra/curl/curl-8.14.1/lib/libcurl.def
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/libcurl.def
rename to extra/curl/curl-8.14.1/lib/libcurl.def
diff --git a/extra/curl/curl-8.12.1/lib/libcurl.rc b/extra/curl/curl-8.14.1/lib/libcurl.rc
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/libcurl.rc
rename to extra/curl/curl-8.14.1/lib/libcurl.rc
diff --git a/extra/curl/curl-8.12.1/lib/libcurl.vers.in b/extra/curl/curl-8.14.1/lib/libcurl.vers.in
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/libcurl.vers.in
rename to extra/curl/curl-8.14.1/lib/libcurl.vers.in
diff --git a/extra/curl/curl-8.12.1/lib/llist.c b/extra/curl/curl-8.14.1/lib/llist.c
similarity index 99%
rename from extra/curl/curl-8.12.1/lib/llist.c
rename to extra/curl/curl-8.14.1/lib/llist.c
index a2c199bc626b..82934425cfc9 100644
--- a/extra/curl/curl-8.12.1/lib/llist.c
+++ b/extra/curl/curl-8.14.1/lib/llist.c
@@ -32,12 +32,11 @@
 /* this must be the last include file */
 #include "memdebug.h"
 
+#ifdef DEBUGBUILD
 #define LLISTINIT 0x100cc001 /* random pattern */
 #define NODEINIT  0x12344321 /* random pattern */
 #define NODEREM   0x54321012 /* random pattern */
 
-
-#ifdef DEBUGBUILD
 #define VERIFYNODE(x) verifynode(x)
 static struct Curl_llist_node *verifynode(struct Curl_llist_node *n)
 {
@@ -86,7 +85,7 @@ Curl_llist_insert_next(struct Curl_llist *list,
 #ifdef DEBUGBUILD
   ne->_init = NODEINIT;
 #endif
-  ne->_ptr = (void *) p;
+  ne->_ptr = CURL_UNCONST(p);
   ne->_list = list;
   if(list->_size == 0) {
     list->_head = ne;
diff --git a/extra/curl/curl-8.12.1/lib/llist.h b/extra/curl/curl-8.14.1/lib/llist.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/llist.h
rename to extra/curl/curl-8.14.1/lib/llist.h
diff --git a/extra/curl/curl-8.12.1/lib/macos.c b/extra/curl/curl-8.14.1/lib/macos.c
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/macos.c
rename to extra/curl/curl-8.14.1/lib/macos.c
diff --git a/extra/curl/curl-8.12.1/lib/macos.h b/extra/curl/curl-8.14.1/lib/macos.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/macos.h
rename to extra/curl/curl-8.14.1/lib/macos.h
diff --git a/extra/curl/curl-8.12.1/lib/md4.c b/extra/curl/curl-8.14.1/lib/md4.c
similarity index 96%
rename from extra/curl/curl-8.12.1/lib/md4.c
rename to extra/curl/curl-8.14.1/lib/md4.c
index 8a3c88441548..a77085a6b28c 100644
--- a/extra/curl/curl-8.12.1/lib/md4.c
+++ b/extra/curl/curl-8.14.1/lib/md4.c
@@ -30,7 +30,7 @@
 
 #include "strdup.h"
 #include "curl_md4.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
 
 #ifdef USE_OPENSSL
 #include <openssl/opensslv.h>
@@ -170,7 +170,12 @@ static int MD4_Init(MD4_CTX *ctx)
 
 static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size)
 {
-  CryptHashData(ctx->hHash, (BYTE *)data, (unsigned int) size, 0);
+#ifdef __MINGW32CE__
+  CryptHashData(ctx->hHash, (BYTE *)CURL_UNCONST(data),
+                (unsigned int) size, 0);
+#else
+  CryptHashData(ctx->hHash, (const BYTE *)data, (unsigned int) size, 0);
+#endif
 }
 
 static void MD4_Final(unsigned char *result, MD4_CTX *ctx)
@@ -308,16 +313,16 @@ static void MD4_Final(unsigned char *result, MD4_CTX *ctx);
  */
 #if defined(__i386__) || defined(__x86_64__) || defined(__vax__)
 #define MD4_SET(n) \
-        (*(MD4_u32plus *)(void *)&ptr[(n) * 4])
+        (*(const MD4_u32plus *)(const void *)&ptr[(n) * 4])
 #define MD4_GET(n) \
         MD4_SET(n)
 #else
 #define MD4_SET(n) \
         (ctx->block[(n)] = \
-        (MD4_u32plus)ptr[(n) * 4] | \
-        ((MD4_u32plus)ptr[(n) * 4 + 1] << 8) | \
-        ((MD4_u32plus)ptr[(n) * 4 + 2] << 16) | \
-        ((MD4_u32plus)ptr[(n) * 4 + 3] << 24))
+          (MD4_u32plus)ptr[(n) * 4] | \
+          ((MD4_u32plus)ptr[(n) * 4 + 1] << 8) | \
+          ((MD4_u32plus)ptr[(n) * 4 + 2] << 16) | \
+          ((MD4_u32plus)ptr[(n) * 4 + 3] << 24))
 #define MD4_GET(n) \
         (ctx->block[(n)])
 #endif
diff --git a/extra/curl/curl-8.12.1/lib/md5.c b/extra/curl/curl-8.14.1/lib/md5.c
similarity index 98%
rename from extra/curl/curl-8.12.1/lib/md5.c
rename to extra/curl/curl-8.14.1/lib/md5.c
index 1cf123181076..6a273c56c0e3 100644
--- a/extra/curl/curl-8.12.1/lib/md5.c
+++ b/extra/curl/curl-8.14.1/lib/md5.c
@@ -32,7 +32,7 @@
 
 #include "curl_md5.h"
 #include "curl_hmac.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
 
 #ifdef USE_MBEDTLS
 #include <mbedtls/version.h>
@@ -252,7 +252,11 @@ static void my_md5_update(void *in,
                           unsigned int inputLen)
 {
   my_md5_ctx *ctx = in;
-  CryptHashData(ctx->hHash, (unsigned char *)input, inputLen, 0);
+#ifdef __MINGW32CE__
+  CryptHashData(ctx->hHash, (BYTE *)CURL_UNCONST(input), inputLen, 0);
+#else
+  CryptHashData(ctx->hHash, (const BYTE *)input, inputLen, 0);
+#endif
 }
 
 static void my_md5_final(unsigned char *digest, void *in)
@@ -356,7 +360,7 @@ static void my_md5_final(unsigned char *result, void *ctx);
  */
 #if defined(__i386__) || defined(__x86_64__) || defined(__vax__)
 #define MD5_SET(n) \
-        (*(MD5_u32plus *)(void *)&ptr[(n) * 4])
+        (*(const MD5_u32plus *)(const void *)&ptr[(n) * 4])
 #define MD5_GET(n) \
         MD5_SET(n)
 #else
diff --git a/extra/curl/curl-8.12.1/lib/memdebug.c b/extra/curl/curl-8.14.1/lib/memdebug.c
similarity index 88%
rename from extra/curl/curl-8.12.1/lib/memdebug.c
rename to extra/curl/curl-8.14.1/lib/memdebug.c
index 9c284ede518f..b351726b320d 100644
--- a/extra/curl/curl-8.12.1/lib/memdebug.c
+++ b/extra/curl/curl-8.14.1/lib/memdebug.c
@@ -117,20 +117,19 @@ static bool countcheck(const char *func, int line, const char *source)
       fprintf(stderr, "LIMIT %s:%d %s reached memlimit\n",
               source, line, func);
       fflush(curl_dbg_logfile); /* because it might crash now */
-      errno = ENOMEM;
+      /* !checksrc! disable ERRNOVAR 1 */
+      CURL_SETERRNO(ENOMEM);
       return TRUE; /* RETURN ERROR! */
     }
     else
       memsize--; /* countdown */
-
-
   }
 
   return FALSE; /* allow this */
 }
 
-ALLOC_FUNC void *curl_dbg_malloc(size_t wantedsize,
-                                 int line, const char *source)
+ALLOC_FUNC
+void *curl_dbg_malloc(size_t wantedsize, int line, const char *source)
 {
   struct memdebug *mem;
   size_t size;
@@ -156,8 +155,9 @@ ALLOC_FUNC void *curl_dbg_malloc(size_t wantedsize,
   return mem ? mem->mem : NULL;
 }
 
-ALLOC_FUNC void *curl_dbg_calloc(size_t wanted_elements, size_t wanted_size,
-                                 int line, const char *source)
+ALLOC_FUNC
+void *curl_dbg_calloc(size_t wanted_elements, size_t wanted_size,
+                      int line, const char *source)
 {
   struct memdebug *mem;
   size_t size, user_size;
@@ -184,8 +184,8 @@ ALLOC_FUNC void *curl_dbg_calloc(size_t wanted_elements, size_t wanted_size,
   return mem ? mem->mem : NULL;
 }
 
-ALLOC_FUNC char *curl_dbg_strdup(const char *str,
-                                 int line, const char *source)
+ALLOC_FUNC
+char *curl_dbg_strdup(const char *str, int line, const char *source)
 {
   char *mem;
   size_t len;
@@ -209,8 +209,8 @@ ALLOC_FUNC char *curl_dbg_strdup(const char *str,
 }
 
 #if defined(_WIN32) && defined(UNICODE)
-ALLOC_FUNC wchar_t *curl_dbg_wcsdup(const wchar_t *str,
-                                    int line, const char *source)
+ALLOC_FUNC
+wchar_t *curl_dbg_wcsdup(const wchar_t *str, int line, const char *source)
 {
   wchar_t *mem;
   size_t wsiz, bsiz;
@@ -229,7 +229,7 @@ ALLOC_FUNC wchar_t *curl_dbg_wcsdup(const wchar_t *str,
 
   if(source)
     curl_dbg_log("MEM %s:%d wcsdup(%p) (%zu) = %p\n",
-                source, line, (void *)str, bsiz, (void *)mem);
+                source, line, (const void *)str, bsiz, (void *)mem);
 
   return mem;
 }
@@ -238,7 +238,7 @@ ALLOC_FUNC wchar_t *curl_dbg_wcsdup(const wchar_t *str,
 /* We provide a realloc() that accepts a NULL as pointer, which then
    performs a malloc(). In order to work with ares. */
 void *curl_dbg_realloc(void *ptr, size_t wantedsize,
-                      int line, const char *source)
+                       int line, const char *source)
 {
   struct memdebug *mem = NULL;
 
@@ -378,6 +378,24 @@ curl_socket_t curl_dbg_accept(curl_socket_t s, void *saddr, void *saddrlen,
   return sockfd;
 }
 
+#ifdef HAVE_ACCEPT4
+curl_socket_t curl_dbg_accept4(curl_socket_t s, void *saddr, void *saddrlen,
+                               int flags,
+                               int line, const char *source)
+{
+  struct sockaddr *addr = (struct sockaddr *)saddr;
+  curl_socklen_t *addrlen = (curl_socklen_t *)saddrlen;
+
+  curl_socket_t sockfd = accept4(s, addr, addrlen, flags);
+
+  if(source && (sockfd != CURL_SOCKET_BAD))
+    curl_dbg_log("FD %s:%d accept() = %" FMT_SOCKET_T "\n",
+                 source, line, sockfd);
+
+  return sockfd;
+}
+#endif
+
 /* separate function to allow libcurl to mark a "faked" close */
 void curl_dbg_mark_sclose(curl_socket_t sockfd, int line, const char *source)
 {
@@ -394,8 +412,9 @@ int curl_dbg_sclose(curl_socket_t sockfd, int line, const char *source)
   return res;
 }
 
-ALLOC_FUNC FILE *curl_dbg_fopen(const char *file, const char *mode,
-                                int line, const char *source)
+ALLOC_FUNC
+FILE *curl_dbg_fopen(const char *file, const char *mode,
+                     int line, const char *source)
 {
   FILE *res = fopen(file, mode);
 
@@ -406,8 +425,9 @@ ALLOC_FUNC FILE *curl_dbg_fopen(const char *file, const char *mode,
   return res;
 }
 
-ALLOC_FUNC FILE *curl_dbg_fdopen(int filedes, const char *mode,
-                                 int line, const char *source)
+ALLOC_FUNC
+FILE *curl_dbg_fdopen(int filedes, const char *mode,
+                      int line, const char *source)
 {
   FILE *res = fdopen(filedes, mode);
   if(source)
@@ -431,33 +451,25 @@ int curl_dbg_fclose(FILE *file, int line, const char *source)
   return res;
 }
 
-#define LOGLINE_BUFSIZE  1024
-
 /* this does the writing to the memory tracking log file */
 void curl_dbg_log(const char *format, ...)
 {
-  char *buf;
+  char buf[1024];
   int nchars;
   va_list ap;
 
   if(!curl_dbg_logfile)
     return;
 
-  buf = (Curl_cmalloc)(LOGLINE_BUFSIZE);
-  if(!buf)
-    return;
-
   va_start(ap, format);
-  nchars = mvsnprintf(buf, LOGLINE_BUFSIZE, format, ap);
+  nchars = mvsnprintf(buf, sizeof(buf), format, ap);
   va_end(ap);
 
-  if(nchars > LOGLINE_BUFSIZE - 1)
-    nchars = LOGLINE_BUFSIZE - 1;
+  if(nchars > (int)sizeof(buf) - 1)
+    nchars = (int)sizeof(buf) - 1;
 
   if(nchars > 0)
     fwrite(buf, 1, (size_t)nchars, curl_dbg_logfile);
-
-  (Curl_cfree)(buf);
 }
 
 #endif /* CURLDEBUG */
diff --git a/extra/curl/curl-8.12.1/lib/memdebug.h b/extra/curl/curl-8.14.1/lib/memdebug.h
similarity index 72%
rename from extra/curl/curl-8.12.1/lib/memdebug.h
rename to extra/curl/curl-8.14.1/lib/memdebug.h
index 80f3374e52af..11b250dea233 100644
--- a/extra/curl/curl-8.12.1/lib/memdebug.h
+++ b/extra/curl/curl-8.14.1/lib/memdebug.h
@@ -33,12 +33,21 @@
 #include <curl/curl.h>
 #include "functypes.h"
 
-#if defined(__GNUC__) && __GNUC__ >= 3
-#  define ALLOC_FUNC __attribute__((__malloc__))
-#  define ALLOC_SIZE(s) __attribute__((__alloc_size__(s)))
-#  define ALLOC_SIZE2(n, s) __attribute__((__alloc_size__(n, s)))
+#ifdef __clang__
+#  define ALLOC_FUNC         __attribute__((__malloc__))
+#  if __clang_major__ >= 4
+#  define ALLOC_SIZE(s)      __attribute__((__alloc_size__(s)))
+#  define ALLOC_SIZE2(n, s)  __attribute__((__alloc_size__(n, s)))
+#  else
+#  define ALLOC_SIZE(s)
+#  define ALLOC_SIZE2(n, s)
+#  endif
+#elif defined(__GNUC__) && __GNUC__ >= 3
+#  define ALLOC_FUNC         __attribute__((__malloc__))
+#  define ALLOC_SIZE(s)      __attribute__((__alloc_size__(s)))
+#  define ALLOC_SIZE2(n, s)  __attribute__((__alloc_size__(n, s)))
 #elif defined(_MSC_VER)
-#  define ALLOC_FUNC __declspec(restrict)
+#  define ALLOC_FUNC         __declspec(restrict)
 #  define ALLOC_SIZE(s)
 #  define ALLOC_SIZE2(n, s)
 #else
@@ -49,25 +58,25 @@
 
 #define CURL_MT_LOGFNAME_BUFSIZE 512
 
+/* Avoid redundant redeclaration warnings with modern compilers, when including
+   this header multiple times. */
+#ifndef HEADER_CURL_MEMDEBUG_H_EXTERNS
+#define HEADER_CURL_MEMDEBUG_H_EXTERNS
 extern FILE *curl_dbg_logfile;
 
 /* memory functions */
-CURL_EXTERN ALLOC_FUNC ALLOC_SIZE(1) void *curl_dbg_malloc(size_t size,
-                                                           int line,
-                                                           const char *source);
-CURL_EXTERN ALLOC_FUNC ALLOC_SIZE2(1, 2) void *curl_dbg_calloc(size_t elements,
-                                   size_t size, int line, const char *source);
-CURL_EXTERN ALLOC_SIZE(2) void *curl_dbg_realloc(void *ptr,
-                                                 size_t size,
-                                                 int line,
-                                                 const char *source);
 CURL_EXTERN void curl_dbg_free(void *ptr, int line, const char *source);
-CURL_EXTERN ALLOC_FUNC char *curl_dbg_strdup(const char *str, int line,
-                                             const char *src);
+CURL_EXTERN ALLOC_FUNC ALLOC_SIZE(1)
+  void *curl_dbg_malloc(size_t size, int line, const char *source);
+CURL_EXTERN ALLOC_FUNC ALLOC_SIZE2(1, 2)
+  void *curl_dbg_calloc(size_t n, size_t size, int line, const char *source);
+CURL_EXTERN ALLOC_SIZE(2)
+  void *curl_dbg_realloc(void *ptr, size_t size, int line, const char *source);
+CURL_EXTERN ALLOC_FUNC
+  char *curl_dbg_strdup(const char *str, int line, const char *src);
 #if defined(_WIN32) && defined(UNICODE)
-CURL_EXTERN ALLOC_FUNC wchar_t *curl_dbg_wcsdup(const wchar_t *str,
-                                                int line,
-                                                const char *source);
+CURL_EXTERN ALLOC_FUNC
+  wchar_t *curl_dbg_wcsdup(const wchar_t *str, int line, const char *source);
 #endif
 
 CURL_EXTERN void curl_dbg_memdebug(const char *logname);
@@ -83,6 +92,11 @@ CURL_EXTERN int curl_dbg_sclose(curl_socket_t sockfd,
                                 int line, const char *source);
 CURL_EXTERN curl_socket_t curl_dbg_accept(curl_socket_t s, void *a, void *alen,
                                           int line, const char *source);
+#ifdef HAVE_ACCEPT4
+CURL_EXTERN curl_socket_t curl_dbg_accept4(curl_socket_t s, void *saddr,
+                                           void *saddrlen, int flags,
+                                           int line, const char *source);
+#endif
 #ifdef HAVE_SOCKETPAIR
 CURL_EXTERN int curl_dbg_socketpair(int domain, int type, int protocol,
                                     curl_socket_t socket_vector[2],
@@ -102,12 +116,15 @@ CURL_EXTERN RECV_TYPE_RETV curl_dbg_recv(RECV_TYPE_ARG1 sockfd,
                                          const char *source);
 
 /* FILE functions */
-CURL_EXTERN ALLOC_FUNC FILE *curl_dbg_fopen(const char *file, const char *mode,
-                                  int line, const char *source);
-CURL_EXTERN ALLOC_FUNC FILE *curl_dbg_fdopen(int filedes, const char *mode,
-                                             int line, const char *source);
-
 CURL_EXTERN int curl_dbg_fclose(FILE *file, int line, const char *source);
+CURL_EXTERN ALLOC_FUNC
+  FILE *curl_dbg_fopen(const char *file, const char *mode,
+                       int line, const char *source);
+CURL_EXTERN ALLOC_FUNC
+  FILE *curl_dbg_fdopen(int filedes, const char *mode,
+                        int line, const char *source);
+
+#endif /* HEADER_CURL_MEMDEBUG_H_EXTERNS */
 
 #ifndef MEMDEBUG_NODEFINES
 
@@ -147,34 +164,17 @@ CURL_EXTERN int curl_dbg_fclose(FILE *file, int line, const char *source);
 #undef accept /* for those with accept as a macro */
 #define accept(sock,addr,len)\
  curl_dbg_accept(sock, addr, len, __LINE__, __FILE__)
+#ifdef HAVE_ACCEPT4
+#undef accept4 /* for those with accept4 as a macro */
+#define accept4(sock,addr,len,flags)\
+ curl_dbg_accept4(sock, addr, len, flags, __LINE__, __FILE__)
+#endif
 #ifdef HAVE_SOCKETPAIR
 #define socketpair(domain,type,protocol,socket_vector)\
  curl_dbg_socketpair((int)domain, type, protocol, socket_vector, \
                      __LINE__, __FILE__)
 #endif
 
-#ifndef CURL_NO_GETADDRINFO_OVERRIDE
-#ifdef HAVE_GETADDRINFO
-#if defined(getaddrinfo) && defined(__osf__)
-/* OSF/1 and Tru64 have getaddrinfo as a define already, so we cannot define
-   our macro as for other platforms. Instead, we redefine the new name they
-   define getaddrinfo to become! */
-#define ogetaddrinfo(host,serv,hint,res) \
-  curl_dbg_getaddrinfo(host, serv, hint, res, __LINE__, __FILE__)
-#else
-#undef getaddrinfo
-#define getaddrinfo(host,serv,hint,res) \
-  curl_dbg_getaddrinfo(host, serv, hint, res, __LINE__, __FILE__)
-#endif
-#endif /* HAVE_GETADDRINFO */
-
-#ifdef HAVE_FREEADDRINFO
-#undef freeaddrinfo
-#define freeaddrinfo(data) \
-  curl_dbg_freeaddrinfo(data, __LINE__, __FILE__)
-#endif /* HAVE_FREEADDRINFO */
-#endif /* !CURL_NO_GETADDRINFO_OVERRIDE */
-
 /* sclose is probably already defined, redefine it! */
 #undef sclose
 #define sclose(sockfd) curl_dbg_sclose(sockfd,__LINE__,__FILE__)
diff --git a/extra/curl/curl-8.12.1/lib/mime.c b/extra/curl/curl-8.14.1/lib/mime.c
similarity index 98%
rename from extra/curl/curl-8.12.1/lib/mime.c
rename to extra/curl/curl-8.14.1/lib/mime.c
index a90d170579d8..c90c34898d59 100644
--- a/extra/curl/curl-8.12.1/lib/mime.c
+++ b/extra/curl/curl-8.14.1/lib/mime.c
@@ -29,10 +29,11 @@
 struct Curl_easy;
 
 #include "mime.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
 #include "urldata.h"
 #include "sendf.h"
 #include "strdup.h"
+#include "curlx/base64.h"
 
 #if !defined(CURL_DISABLE_MIME) && (!defined(CURL_DISABLE_HTTP) ||      \
                                     !defined(CURL_DISABLE_SMTP) ||      \
@@ -45,7 +46,7 @@ struct Curl_easy;
 #include "rand.h"
 #include "slist.h"
 #include "strcase.h"
-#include "dynbuf.h"
+#include "curlx/dynbuf.h"
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
@@ -87,10 +88,6 @@ static const struct mime_encoder encoders[] = {
   {ZERO_NULL, ZERO_NULL, ZERO_NULL}
 };
 
-/* Base64 encoding table */
-static const char base64enc[] =
-  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
 /* Quoted-printable character class table.
  *
  * We cannot rely on ctype functions since quoted-printable input data
@@ -318,19 +315,19 @@ static char *escape_string(struct Curl_easy *data,
   if(strategy == MIMESTRATEGY_MAIL || (data && (data->set.mime_formescape)))
     table = mimetable;
 
-  Curl_dyn_init(&db, CURL_MAX_INPUT_LENGTH);
+  curlx_dyn_init(&db, CURL_MAX_INPUT_LENGTH);
 
-  for(result = Curl_dyn_addn(&db, STRCONST("")); !result && *src; src++) {
+  for(result = curlx_dyn_addn(&db, STRCONST("")); !result && *src; src++) {
     for(p = table; *p && **p != *src; p++)
       ;
 
     if(*p)
-      result = Curl_dyn_add(&db, *p + 1);
+      result = curlx_dyn_add(&db, *p + 1);
     else
-      result = Curl_dyn_addn(&db, src, 1);
+      result = curlx_dyn_addn(&db, src, 1);
   }
 
-  return Curl_dyn_ptr(&db);
+  return curlx_dyn_ptr(&db);
 }
 
 /* Check if header matches. */
@@ -472,10 +469,10 @@ static size_t encoder_base64_read(char *buffer, size_t size, bool ateof,
     i = st->buf[st->bufbeg++] & 0xFF;
     i = (i << 8) | (st->buf[st->bufbeg++] & 0xFF);
     i = (i << 8) | (st->buf[st->bufbeg++] & 0xFF);
-    *ptr++ = base64enc[(i >> 18) & 0x3F];
-    *ptr++ = base64enc[(i >> 12) & 0x3F];
-    *ptr++ = base64enc[(i >> 6) & 0x3F];
-    *ptr++ = base64enc[i & 0x3F];
+    *ptr++ = Curl_base64encdec[(i >> 18) & 0x3F];
+    *ptr++ = Curl_base64encdec[(i >> 12) & 0x3F];
+    *ptr++ = Curl_base64encdec[(i >> 6) & 0x3F];
+    *ptr++ = Curl_base64encdec[i & 0x3F];
     cursize += 4;
     st->pos += 4;
     size -= 4;
@@ -499,10 +496,10 @@ static size_t encoder_base64_read(char *buffer, size_t size, bool ateof,
           i = (st->buf[st->bufbeg + 1] & 0xFF) << 8;
 
         i |= (st->buf[st->bufbeg] & 0xFF) << 16;
-        ptr[0] = base64enc[(i >> 18) & 0x3F];
-        ptr[1] = base64enc[(i >> 12) & 0x3F];
+        ptr[0] = Curl_base64encdec[(i >> 18) & 0x3F];
+        ptr[1] = Curl_base64encdec[(i >> 12) & 0x3F];
         if(++st->bufbeg != st->bufend) {
-          ptr[2] = base64enc[(i >> 6) & 0x3F];
+          ptr[2] = Curl_base64encdec[(i >> 6) & 0x3F];
           st->bufbeg++;
         }
         cursize += 4;
@@ -1742,7 +1739,7 @@ const char *Curl_mime_contenttype(const char *filename)
     const char *nameend = filename + len1;
     unsigned int i;
 
-    for(i = 0; i < sizeof(ctts) / sizeof(ctts[0]); i++) {
+    for(i = 0; i < CURL_ARRAYSIZE(ctts); i++) {
       size_t len2 = strlen(ctts[i].extension);
 
       if(len1 >= len2 && strcasecompare(nameend - len2, ctts[i].extension))
diff --git a/extra/curl/curl-8.12.1/lib/mime.h b/extra/curl/curl-8.14.1/lib/mime.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/mime.h
rename to extra/curl/curl-8.14.1/lib/mime.h
diff --git a/extra/curl/curl-8.12.1/lib/mprintf.c b/extra/curl/curl-8.14.1/lib/mprintf.c
similarity index 66%
rename from extra/curl/curl-8.12.1/lib/mprintf.c
rename to extra/curl/curl-8.14.1/lib/mprintf.c
index 8bc9054407c0..176f8a3e4b78 100644
--- a/extra/curl/curl-8.12.1/lib/mprintf.c
+++ b/extra/curl/curl-8.14.1/lib/mprintf.c
@@ -23,17 +23,14 @@
  */
 
 #include "curl_setup.h"
-#include "dynbuf.h"
+#include "curlx/dynbuf.h"
 #include "curl_printf.h"
+#include "curlx/strparse.h"
 
 #include "curl_memory.h"
 /* The last #include file should be: */
 #include "memdebug.h"
 
-/*
- * If SIZEOF_SIZE_T has not been defined, default to the size of long.
- */
-
 #ifdef HAVE_LONGLONG
 #  define LONG_LONG_TYPE long long
 #  define HAVE_LONG_LONG_TYPE
@@ -67,17 +64,16 @@
 #endif
 
 /* Lower-case digits.  */
-static const char lower_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+const unsigned char Curl_ldigits[] = "0123456789abcdef";
 
 /* Upper-case digits.  */
-static const char upper_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
-
-#define OUTCHAR(x)                                      \
-  do {                                                  \
-    if(!stream((unsigned char)x, userp))                \
-      done++;                                           \
-    else                                                \
-      return done; /* return on failure */              \
+const unsigned char Curl_udigits[] = "0123456789ABCDEF";
+
+#define OUTCHAR(x)                                       \
+  do {                                                   \
+    if(stream((unsigned char)x, userp))                  \
+      return TRUE;                                       \
+    (*donep)++;                                          \
   } while(0)
 
 /* Data type to read from the arglist */
@@ -134,7 +130,7 @@ enum {
 struct va_input {
   FormatType type; /* FormatType */
   union {
-    char *str;
+    const char *str;
     void *ptr;
     mp_intmax_t nums; /* signed */
     mp_uintmax_t numu; /* unsigned */
@@ -150,7 +146,7 @@ struct outsegment {
   int precision; /* precision OR precision parameter number */
   unsigned int flags;
   unsigned int input; /* input argument array index */
-  char *start;      /* format string start to output */
+  const char *start; /* format string start to output */
   size_t outlen;     /* number of bytes from the format string to output */
 };
 
@@ -169,26 +165,19 @@ struct asprintf {
 
    returns -1 if no valid number was provided.
 */
-static int dollarstring(char *input, char **end)
+static int dollarstring(const char *p, const char **end)
 {
-  if(ISDIGIT(*input)) {
-    int number = 0;
-    do {
-      if(number < MAX_PARAMETERS) {
-        number *= 10;
-        number += *input - '0';
-      }
-      input++;
-    } while(ISDIGIT(*input));
-
-    if(number && (number <= MAX_PARAMETERS) && ('$' == *input)) {
-      *end = ++input;
-      return number - 1;
-    }
-  }
-  return -1;
+  curl_off_t num;
+  if(curlx_str_number(&p, &num, MAX_PARAMETERS) ||
+     curlx_str_single(&p, '$') || !num)
+    return -1;
+  *end = p;
+  return (int)num - 1;
 }
 
+#define is_arg_used(x,y) ((x)[(y)/8] & (1 << ((y)&7)))
+#define mark_arg_used(x,y) ((x)[y/8] |= (unsigned char)(1 << ((y)&7)))
+
 /*
  * Parse the format string.
  *
@@ -216,13 +205,8 @@ static int parsefmt(const char *format,
                     int *opieces,
                     int *ipieces, va_list arglist)
 {
-  char *fmt = (char *)format;
+  const char *fmt = format;
   int param_num = 0;
-  int param;
-  int width;
-  int precision;
-  unsigned int flags;
-  FormatType type;
   int max_param = -1;
   int i;
   int ocount = 0;
@@ -230,7 +214,7 @@ static int parsefmt(const char *format,
   size_t outlen = 0;
   struct outsegment *optr;
   int use_dollar = DOLLAR_UNKNOWN;
-  char *start = fmt;
+  const char *start = fmt;
 
   /* clear, set a bit for each used input */
   memset(usedinput, 0, sizeof(usedinput));
@@ -239,6 +223,11 @@ static int parsefmt(const char *format,
     if(*fmt == '%') {
       struct va_input *iptr;
       bool loopit = TRUE;
+      FormatType type;
+      unsigned int flags = 0;
+      int width = 0;
+      int precision = 0;
+      int param = -1;
       fmt++;
       outlen = (size_t)(fmt - start - 1);
       if(*fmt == '%') {
@@ -258,9 +247,6 @@ static int parsefmt(const char *format,
         continue; /* while */
       }
 
-      flags = 0;
-      width = precision = 0;
-
       if(use_dollar != DOLLAR_NOPE) {
         param = dollarstring(fmt, &fmt);
         if(param < 0) {
@@ -275,8 +261,6 @@ static int parsefmt(const char *format,
         else
           use_dollar = DOLLAR_USE;
       }
-      else
-        param = -1;
 
       /* Handle the flags */
       while(loopit) {
@@ -311,20 +295,15 @@ static int parsefmt(const char *format,
               precision = -1;
           }
           else {
-            bool is_neg = FALSE;
+            bool is_neg;
+            curl_off_t num;
             flags |= FLAGS_PREC;
-            precision = 0;
-            if('-' == *fmt) {
-              is_neg = TRUE;
-              fmt++;
-            }
-            while(ISDIGIT(*fmt)) {
-              int n = *fmt - '0';
-              if(precision > (INT_MAX - n) / 10)
-                return PFMT_PREC;
-              precision = precision * 10 + n;
+            is_neg = ('-' == *fmt);
+            if(is_neg)
               fmt++;
-            }
+            if(curlx_str_number(&fmt, &num, INT_MAX))
+              return PFMT_PREC;
+            precision = (int)num;
             if(is_neg)
               precision = -precision;
           }
@@ -337,7 +316,7 @@ static int parsefmt(const char *format,
         case 'h':
           flags |= FLAGS_SHORT;
           break;
-#if defined(_WIN32) || defined(_WIN32_WCE)
+#ifdef _WIN32
         case 'I':
           /* Non-ANSI integer extensions I32 I64 */
           if((fmt[0] == '3') && (fmt[1] == '2')) {
@@ -356,7 +335,7 @@ static int parsefmt(const char *format,
 #endif
           }
           break;
-#endif /* _WIN32 || _WIN32_WCE */
+#endif /* _WIN32 */
         case 'l':
           if(flags & FLAGS_LONG)
             flags |= FLAGS_LONGLONG;
@@ -390,18 +369,15 @@ static int parsefmt(const char *format,
             flags |= FLAGS_PAD_NIL;
           FALLTHROUGH();
         case '1': case '2': case '3': case '4':
-        case '5': case '6': case '7': case '8': case '9':
+        case '5': case '6': case '7': case '8': case '9': {
+          curl_off_t num;
           flags |= FLAGS_WIDTH;
-          width = 0;
           fmt--;
-          do {
-            int n = *fmt - '0';
-            if(width > (INT_MAX - n) / 10)
-              return PFMT_WIDTH;
-            width = width * 10 + n;
-            fmt++;
-          } while(ISDIGIT(*fmt));
+          if(curlx_str_number(&fmt, &num, INT_MAX))
+            return PFMT_WIDTH;
+          width = (int)num;
           break;
+        }
         case '*':  /* read width from argument list */
           flags |= FLAGS_WIDTHPARAM;
           if(use_dollar == DOLLAR_USE) {
@@ -511,9 +487,8 @@ static int parsefmt(const char *format,
         if(width < 0)
           width = param_num++;
         else {
-          /* if this identifies a parameter already used, this
-             is illegal */
-          if(usedinput[width/8] & (1 << (width&7)))
+          /* if this identifies a parameter already used, this is illegal */
+          if(is_arg_used(usedinput, width))
             return PFMT_WIDTHARG;
         }
         if(width >= MAX_PARAMETERS)
@@ -523,16 +498,15 @@ static int parsefmt(const char *format,
 
         in[width].type = FORMAT_WIDTH;
         /* mark as used */
-        usedinput[width/8] |= (unsigned char)(1 << (width&7));
+        mark_arg_used(usedinput, width);
       }
 
       if(flags & FLAGS_PRECPARAM) {
         if(precision < 0)
           precision = param_num++;
         else {
-          /* if this identifies a parameter already used, this
-             is illegal */
-          if(usedinput[precision/8] & (1 << (precision&7)))
+          /* if this identifies a parameter already used, this is illegal */
+          if(is_arg_used(usedinput, precision))
             return PFMT_PRECARG;
         }
         if(precision >= MAX_PARAMETERS)
@@ -541,7 +515,7 @@ static int parsefmt(const char *format,
           max_param = precision;
 
         in[precision].type = FORMAT_PRECISION;
-        usedinput[precision/8] |= (unsigned char)(1 << (precision&7));
+        mark_arg_used(usedinput, precision);
       }
 
       /* Handle the specifier */
@@ -556,7 +530,7 @@ static int parsefmt(const char *format,
       iptr->type = type;
 
       /* mark this input as used */
-      usedinput[param/8] |= (unsigned char)(1 << (param&7));
+      mark_arg_used(usedinput, param);
 
       fmt++;
       optr = &out[ocount++];
@@ -589,14 +563,14 @@ static int parsefmt(const char *format,
   /* Read the arg list parameters into our data list */
   for(i = 0; i < max_param + 1; i++) {
     struct va_input *iptr = &in[i];
-    if(!(usedinput[i/8] & (1 << (i&7))))
+    if(!is_arg_used(usedinput, i))
       /* bad input */
       return PFMT_INPUTGAP;
 
     /* based on the type, read the correct argument */
     switch(iptr->type) {
     case FORMAT_STRING:
-      iptr->val.str = va_arg(arglist, char *);
+      iptr->val.str = va_arg(arglist, const char *);
       break;
 
     case FORMAT_INTPTR:
@@ -645,6 +619,333 @@ static int parsefmt(const char *format,
   return PFMT_OK;
 }
 
+struct mproperty {
+  int width;            /* Width of a field.  */
+  int prec;             /* Precision of a field.  */
+  unsigned int flags;
+};
+
+static bool out_double(void *userp,
+                       int (*stream)(unsigned char, void *),
+                       struct mproperty *p,
+                       double dnum,
+                       char *work, int *donep)
+{
+  char formatbuf[32]="%";
+  char *fptr = &formatbuf[1];
+  size_t left = sizeof(formatbuf)-strlen(formatbuf);
+  int flags = p->flags;
+  int width = p->width;
+  int prec = p->prec;
+
+  if(flags & FLAGS_LEFT)
+    *fptr++ = '-';
+  if(flags & FLAGS_SHOWSIGN)
+    *fptr++ = '+';
+  if(flags & FLAGS_SPACE)
+    *fptr++ = ' ';
+  if(flags & FLAGS_ALT)
+    *fptr++ = '#';
+
+  *fptr = 0;
+
+  if(width >= 0) {
+    size_t dlen;
+    if(width >= BUFFSIZE)
+      width = BUFFSIZE - 1;
+    /* RECURSIVE USAGE */
+    dlen = (size_t)curl_msnprintf(fptr, left, "%d", width);
+    fptr += dlen;
+    left -= dlen;
+  }
+  if(prec >= 0) {
+    /* for each digit in the integer part, we can have one less
+       precision */
+    int maxprec = BUFFSIZE - 1;
+    double val = dnum;
+    int len;
+    if(prec > maxprec)
+      prec = maxprec - 1;
+    if(width > 0 && prec <= width)
+      maxprec -= width;
+    while(val >= 10.0) {
+      val /= 10;
+      maxprec--;
+    }
+
+    if(prec > maxprec)
+      prec = maxprec - 1;
+    if(prec < 0)
+      prec = 0;
+    /* RECURSIVE USAGE */
+    len = curl_msnprintf(fptr, left, ".%d", prec);
+    fptr += len;
+  }
+  if(flags & FLAGS_LONG)
+    *fptr++ = 'l';
+
+  if(flags & FLAGS_FLOATE)
+    *fptr++ = (char)((flags & FLAGS_UPPER) ? 'E' : 'e');
+  else if(flags & FLAGS_FLOATG)
+    *fptr++ = (char)((flags & FLAGS_UPPER) ? 'G' : 'g');
+  else
+    *fptr++ = 'f';
+
+  *fptr = 0; /* and a final null-termination */
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-nonliteral"
+#endif
+  /* NOTE NOTE NOTE!! Not all sprintf implementations return number of
+     output characters */
+#ifdef HAVE_SNPRINTF
+  /* !checksrc! disable LONGLINE */
+  /* NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) */
+  (snprintf)(work, BUFFSIZE, formatbuf, dnum);
+#ifdef _WIN32
+  /* Old versions of the Windows CRT do not terminate the snprintf output
+     buffer if it reaches the max size so we do that here. */
+  work[BUFFSIZE - 1] = 0;
+#endif
+#else
+  (sprintf)(work, formatbuf, dnum);
+#endif
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+  DEBUGASSERT(strlen(work) < BUFFSIZE);
+  while(*work) {
+    if(stream(*work++, userp))
+      return TRUE;
+    (*donep)++;
+  }
+  return 0;
+}
+
+static bool out_number(void *userp,
+                       int (*stream)(unsigned char, void *),
+                       struct mproperty *p,
+                       mp_uintmax_t num,
+                       mp_intmax_t nums,
+                       char *work, int *donep)
+{
+  const unsigned char *digits = Curl_ldigits;
+  int flags = p->flags;
+  int width = p->width;
+  int prec = p->prec;
+  bool is_alt = flags & FLAGS_ALT;
+  bool is_neg = FALSE;
+  int base = 10;
+
+  /* 'workend' points to the final buffer byte position, but with an extra
+     byte as margin to avoid the (FALSE?) warning Coverity gives us
+     otherwise */
+  char *workend = &work[BUFFSIZE - 2];
+  char *w;
+
+  if(flags & FLAGS_CHAR) {
+    /* Character.  */
+    if(!(flags & FLAGS_LEFT))
+      while(--width > 0)
+        OUTCHAR(' ');
+    OUTCHAR((char) num);
+    if(flags & FLAGS_LEFT)
+      while(--width > 0)
+        OUTCHAR(' ');
+    return FALSE;
+  }
+  if(flags & FLAGS_OCTAL)
+    /* Octal unsigned integer */
+    base = 8;
+
+  else if(flags & FLAGS_HEX) {
+    /* Hexadecimal unsigned integer */
+    digits = (flags & FLAGS_UPPER) ? Curl_udigits : Curl_ldigits;
+    base = 16;
+  }
+  else if(flags & FLAGS_UNSIGNED)
+    /* Decimal unsigned integer */
+    ;
+
+  else {
+    /* Decimal integer.  */
+    is_neg = (nums < 0);
+    if(is_neg) {
+      /* signed_num might fail to hold absolute negative minimum by 1 */
+      mp_intmax_t signed_num; /* Used to convert negative in positive.  */
+      signed_num = nums + (mp_intmax_t)1;
+      signed_num = -signed_num;
+      num = (mp_uintmax_t)signed_num;
+      num += (mp_uintmax_t)1;
+    }
+  }
+
+  /* Supply a default precision if none was given.  */
+  if(prec == -1)
+    prec = 1;
+
+  /* Put the number in WORK.  */
+  w = workend;
+  DEBUGASSERT(base <= 16);
+  switch(base) {
+  case 10:
+    while(num > 0) {
+      *w-- = (char)('0' + (num % 10));
+      num /= 10;
+    }
+    break;
+  default:
+    while(num > 0) {
+      *w-- = digits[num % base];
+      num /= base;
+    }
+    break;
+  }
+  width -= (int)(workend - w);
+  prec -= (int)(workend - w);
+
+  if(is_alt && base == 8 && prec <= 0) {
+    *w-- = '0';
+    --width;
+  }
+
+  if(prec > 0) {
+    width -= prec;
+    while(prec-- > 0 && w >= work)
+      *w-- = '0';
+  }
+
+  if(is_alt && base == 16)
+    width -= 2;
+
+  if(is_neg || (flags & FLAGS_SHOWSIGN) || (flags & FLAGS_SPACE))
+    --width;
+
+  if(!(flags & FLAGS_LEFT) && !(flags & FLAGS_PAD_NIL))
+    while(width-- > 0)
+      OUTCHAR(' ');
+
+  if(is_neg)
+    OUTCHAR('-');
+  else if(flags & FLAGS_SHOWSIGN)
+    OUTCHAR('+');
+  else if(flags & FLAGS_SPACE)
+    OUTCHAR(' ');
+
+  if(is_alt && base == 16) {
+    OUTCHAR('0');
+    if(flags & FLAGS_UPPER)
+      OUTCHAR('X');
+    else
+      OUTCHAR('x');
+  }
+
+  if(!(flags & FLAGS_LEFT) && (flags & FLAGS_PAD_NIL))
+    while(width-- > 0)
+      OUTCHAR('0');
+
+  /* Write the number.  */
+  while(++w <= workend) {
+    OUTCHAR(*w);
+  }
+
+  if(flags & FLAGS_LEFT)
+    while(width-- > 0)
+      OUTCHAR(' ');
+
+  return FALSE;
+}
+
+static const char nilstr[] = "(nil)";
+
+static bool out_string(void *userp,
+                       int (*stream)(unsigned char, void *),
+                       struct mproperty *p,
+                       const char *str,
+                       int *donep)
+{
+  int flags = p->flags;
+  int width = p->width;
+  int prec = p->prec;
+  size_t len;
+
+  if(!str) {
+    /* Write null string if there is space.  */
+    if(prec == -1 || prec >= (int) sizeof(nilstr) - 1) {
+      str = nilstr;
+      len = sizeof(nilstr) - 1;
+      /* Disable quotes around (nil) */
+      flags &= ~(unsigned int)FLAGS_ALT;
+    }
+    else {
+      str = "";
+      len = 0;
+    }
+  }
+  else if(prec != -1)
+    len = (size_t)prec;
+  else if(*str == '\0')
+    len = 0;
+  else
+    len = strlen(str);
+
+  width -= (len > INT_MAX) ? INT_MAX : (int)len;
+
+  if(flags & FLAGS_ALT)
+    OUTCHAR('"');
+
+  if(!(flags & FLAGS_LEFT))
+    while(width-- > 0)
+      OUTCHAR(' ');
+
+  for(; len && *str; len--)
+    OUTCHAR(*str++);
+  if(flags & FLAGS_LEFT)
+    while(width-- > 0)
+      OUTCHAR(' ');
+
+  if(flags & FLAGS_ALT)
+    OUTCHAR('"');
+
+  return FALSE;
+}
+
+static bool out_pointer(void *userp,
+                        int (*stream)(unsigned char, void *),
+                        struct mproperty *p,
+                        const char *ptr,
+                        char *work,
+                        int *donep)
+{
+  /* Generic pointer.  */
+  if(ptr) {
+    size_t num = (size_t) ptr;
+
+    /* If the pointer is not NULL, write it as a %#x spec.  */
+    p->flags |= FLAGS_HEX|FLAGS_ALT;
+    if(out_number(userp, stream, p, num, 0, work, donep))
+      return TRUE;
+  }
+  else {
+    /* Write "(nil)" for a nil pointer.  */
+    const char *point;
+    int width = p->width;
+    int flags = p->flags;
+
+    width -= (int)(sizeof(nilstr) - 1);
+    if(flags & FLAGS_LEFT)
+      while(width-- > 0)
+        OUTCHAR(' ');
+    for(point = nilstr; *point; ++point)
+      OUTCHAR(*point);
+    if(!(flags & FLAGS_LEFT))
+      while(width-- > 0)
+        OUTCHAR(' ');
+  }
+  return FALSE;
+}
+
 /*
  * formatf() - the general printf function.
  *
@@ -669,8 +970,6 @@ static int formatf(
   const char *format,    /* %-formatted string */
   va_list ap_save) /* list of parameters */
 {
-  static const char nilstr[] = "(nil)";
-  const char *digits = lower_digits;   /* Base-36 digits for numbers.  */
   int done = 0;   /* number of characters written  */
   int i;
   int ocount = 0; /* number of output segments */
@@ -680,368 +979,102 @@ static int formatf(
   struct va_input input[MAX_PARAMETERS];
   char work[BUFFSIZE + 2];
 
-  /* 'workend' points to the final buffer byte position, but with an extra
-     byte as margin to avoid the (FALSE?) warning Coverity gives us
-     otherwise */
-  char *workend = &work[BUFFSIZE - 2];
-
   /* Parse the format string */
   if(parsefmt(format, output, input, &ocount, &icount, ap_save))
     return 0;
 
   for(i = 0; i < ocount; i++) {
     struct outsegment *optr = &output[i];
-    struct va_input *iptr;
-    bool is_alt;            /* Format spec modifiers.  */
-    int width;              /* Width of a field.  */
-    int prec;               /* Precision of a field.  */
-    bool is_neg;            /* Decimal integer is negative.  */
-    unsigned long base;     /* Base of a number to be written.  */
-    mp_uintmax_t num;       /* Integral values to be written.  */
-    mp_intmax_t signed_num; /* Used to convert negative in positive.  */
-    char *w;
+    struct va_input *iptr = &input[optr->input];
+    struct mproperty p;
     size_t outlen = optr->outlen;
-    unsigned int flags = optr->flags;
 
     if(outlen) {
-      char *str = optr->start;
-      for(; outlen && *str; outlen--)
-        OUTCHAR(*str++);
+      const char *str = optr->start;
+      for(; outlen && *str; outlen--) {
+        if(stream(*str++, userp))
+          return done;
+        done++;
+      }
       if(optr->flags & FLAGS_SUBSTR)
         /* this is just a substring */
         continue;
     }
 
+    p.flags = optr->flags;
+
     /* pick up the specified width */
-    if(flags & FLAGS_WIDTHPARAM) {
-      width = (int)input[optr->width].val.nums;
-      if(width < 0) {
+    if(p.flags & FLAGS_WIDTHPARAM) {
+      p.width = (int)input[optr->width].val.nums;
+      if(p.width < 0) {
         /* "A negative field width is taken as a '-' flag followed by a
            positive field width." */
-        if(width == INT_MIN)
-          width = INT_MAX;
+        if(p.width == INT_MIN)
+          p.width = INT_MAX;
         else
-          width = -width;
-        flags |= FLAGS_LEFT;
-        flags &= ~(unsigned int)FLAGS_PAD_NIL;
+          p.width = -p.width;
+        p.flags |= FLAGS_LEFT;
+        p.flags &= ~(unsigned int)FLAGS_PAD_NIL;
       }
     }
     else
-      width = optr->width;
+      p.width = optr->width;
 
     /* pick up the specified precision */
-    if(flags & FLAGS_PRECPARAM) {
-      prec = (int)input[optr->precision].val.nums;
-      if(prec < 0)
+    if(p.flags & FLAGS_PRECPARAM) {
+      p.prec = (int)input[optr->precision].val.nums;
+      if(p.prec < 0)
         /* "A negative precision is taken as if the precision were
            omitted." */
-        prec = -1;
+        p.prec = -1;
     }
-    else if(flags & FLAGS_PREC)
-      prec = optr->precision;
+    else if(p.flags & FLAGS_PREC)
+      p.prec = optr->precision;
     else
-      prec = -1;
-
-    is_alt = (flags & FLAGS_ALT) ? 1 : 0;
-    iptr = &input[optr->input];
+      p.prec = -1;
 
     switch(iptr->type) {
     case FORMAT_INTU:
     case FORMAT_LONGU:
     case FORMAT_LONGLONGU:
-      flags |= FLAGS_UNSIGNED;
-      FALLTHROUGH();
+      p.flags |= FLAGS_UNSIGNED;
+      if(out_number(userp, stream, &p, iptr->val.numu, 0, work, &done))
+        return done;
+      break;
+
     case FORMAT_INT:
     case FORMAT_LONG:
     case FORMAT_LONGLONG:
-      num = iptr->val.numu;
-      if(flags & FLAGS_CHAR) {
-        /* Character.  */
-        if(!(flags & FLAGS_LEFT))
-          while(--width > 0)
-            OUTCHAR(' ');
-        OUTCHAR((char) num);
-        if(flags & FLAGS_LEFT)
-          while(--width > 0)
-            OUTCHAR(' ');
-        break;
-      }
-      if(flags & FLAGS_OCTAL) {
-        /* Octal unsigned integer */
-        base = 8;
-        is_neg = FALSE;
-      }
-      else if(flags & FLAGS_HEX) {
-        /* Hexadecimal unsigned integer */
-        digits = (flags & FLAGS_UPPER) ? upper_digits : lower_digits;
-        base = 16;
-        is_neg = FALSE;
-      }
-      else if(flags & FLAGS_UNSIGNED) {
-        /* Decimal unsigned integer */
-        base = 10;
-        is_neg = FALSE;
-      }
-      else {
-        /* Decimal integer.  */
-        base = 10;
-
-        is_neg = (iptr->val.nums < (mp_intmax_t)0);
-        if(is_neg) {
-          /* signed_num might fail to hold absolute negative minimum by 1 */
-          signed_num = iptr->val.nums + (mp_intmax_t)1;
-          signed_num = -signed_num;
-          num = (mp_uintmax_t)signed_num;
-          num += (mp_uintmax_t)1;
-        }
-      }
-number:
-      /* Supply a default precision if none was given.  */
-      if(prec == -1)
-        prec = 1;
-
-      /* Put the number in WORK.  */
-      w = workend;
-      switch(base) {
-      case 10:
-        while(num > 0) {
-          *w-- = (char)('0' + (num % 10));
-          num /= 10;
-        }
-        break;
-      default:
-        while(num > 0) {
-          *w-- = digits[num % base];
-          num /= base;
-        }
-        break;
-      }
-      width -= (int)(workend - w);
-      prec -= (int)(workend - w);
-
-      if(is_alt && base == 8 && prec <= 0) {
-        *w-- = '0';
-        --width;
-      }
-
-      if(prec > 0) {
-        width -= prec;
-        while(prec-- > 0 && w >= work)
-          *w-- = '0';
-      }
-
-      if(is_alt && base == 16)
-        width -= 2;
-
-      if(is_neg || (flags & FLAGS_SHOWSIGN) || (flags & FLAGS_SPACE))
-        --width;
-
-      if(!(flags & FLAGS_LEFT) && !(flags & FLAGS_PAD_NIL))
-        while(width-- > 0)
-          OUTCHAR(' ');
-
-      if(is_neg)
-        OUTCHAR('-');
-      else if(flags & FLAGS_SHOWSIGN)
-        OUTCHAR('+');
-      else if(flags & FLAGS_SPACE)
-        OUTCHAR(' ');
-
-      if(is_alt && base == 16) {
-        OUTCHAR('0');
-        if(flags & FLAGS_UPPER)
-          OUTCHAR('X');
-        else
-          OUTCHAR('x');
-      }
-
-      if(!(flags & FLAGS_LEFT) && (flags & FLAGS_PAD_NIL))
-        while(width-- > 0)
-          OUTCHAR('0');
-
-      /* Write the number.  */
-      while(++w <= workend) {
-        OUTCHAR(*w);
-      }
-
-      if(flags & FLAGS_LEFT)
-        while(width-- > 0)
-          OUTCHAR(' ');
+      if(out_number(userp, stream, &p, iptr->val.numu,
+                    iptr->val.nums, work, &done))
+        return done;
       break;
 
-    case FORMAT_STRING: {
-      const char *str;
-      size_t len;
-
-      str = (char *)iptr->val.str;
-      if(!str) {
-        /* Write null string if there is space.  */
-        if(prec == -1 || prec >= (int) sizeof(nilstr) - 1) {
-          str = nilstr;
-          len = sizeof(nilstr) - 1;
-          /* Disable quotes around (nil) */
-          flags &= ~(unsigned int)FLAGS_ALT;
-        }
-        else {
-          str = "";
-          len = 0;
-        }
-      }
-      else if(prec != -1)
-        len = (size_t)prec;
-      else if(*str == '\0')
-        len = 0;
-      else
-        len = strlen(str);
-
-      width -= (len > INT_MAX) ? INT_MAX : (int)len;
-
-      if(flags & FLAGS_ALT)
-        OUTCHAR('"');
-
-      if(!(flags & FLAGS_LEFT))
-        while(width-- > 0)
-          OUTCHAR(' ');
-
-      for(; len && *str; len--)
-        OUTCHAR(*str++);
-      if(flags & FLAGS_LEFT)
-        while(width-- > 0)
-          OUTCHAR(' ');
-
-      if(flags & FLAGS_ALT)
-        OUTCHAR('"');
+    case FORMAT_STRING:
+      if(out_string(userp, stream, &p, iptr->val.str, &done))
+        return done;
       break;
-    }
 
     case FORMAT_PTR:
-      /* Generic pointer.  */
-      if(iptr->val.ptr) {
-        /* If the pointer is not NULL, write it as a %#x spec.  */
-        base = 16;
-        digits = (flags & FLAGS_UPPER) ? upper_digits : lower_digits;
-        is_alt = TRUE;
-        num = (size_t) iptr->val.ptr;
-        is_neg = FALSE;
-        goto number;
-      }
-      else {
-        /* Write "(nil)" for a nil pointer.  */
-        const char *point;
-
-        width -= (int)(sizeof(nilstr) - 1);
-        if(flags & FLAGS_LEFT)
-          while(width-- > 0)
-            OUTCHAR(' ');
-        for(point = nilstr; *point != '\0'; ++point)
-          OUTCHAR(*point);
-        if(!(flags & FLAGS_LEFT))
-          while(width-- > 0)
-            OUTCHAR(' ');
-      }
+      if(out_pointer(userp, stream, &p, iptr->val.ptr, work, &done))
+        return done;
       break;
 
-    case FORMAT_DOUBLE: {
-      char formatbuf[32]="%";
-      char *fptr = &formatbuf[1];
-      size_t left = sizeof(formatbuf)-strlen(formatbuf);
-      int len;
-
-      if(flags & FLAGS_WIDTH)
-        width = optr->width;
-
-      if(flags & FLAGS_PREC)
-        prec = optr->precision;
-
-      if(flags & FLAGS_LEFT)
-        *fptr++ = '-';
-      if(flags & FLAGS_SHOWSIGN)
-        *fptr++ = '+';
-      if(flags & FLAGS_SPACE)
-        *fptr++ = ' ';
-      if(flags & FLAGS_ALT)
-        *fptr++ = '#';
-
-      *fptr = 0;
-
-      if(width >= 0) {
-        size_t dlen;
-        if(width >= BUFFSIZE)
-          width = BUFFSIZE - 1;
-        /* RECURSIVE USAGE */
-        dlen = (size_t)curl_msnprintf(fptr, left, "%d", width);
-        fptr += dlen;
-        left -= dlen;
-      }
-      if(prec >= 0) {
-        /* for each digit in the integer part, we can have one less
-           precision */
-        int maxprec = BUFFSIZE - 1;
-        double val = iptr->val.dnum;
-        if(prec > maxprec)
-          prec = maxprec - 1;
-        if(width > 0 && prec <= width)
-          maxprec -= width;
-        while(val >= 10.0) {
-          val /= 10;
-          maxprec--;
-        }
-
-        if(prec > maxprec)
-          prec = maxprec - 1;
-        if(prec < 0)
-          prec = 0;
-        /* RECURSIVE USAGE */
-        len = curl_msnprintf(fptr, left, ".%d", prec);
-        fptr += len;
-      }
-      if(flags & FLAGS_LONG)
-        *fptr++ = 'l';
-
-      if(flags & FLAGS_FLOATE)
-        *fptr++ = (char)((flags & FLAGS_UPPER) ? 'E' : 'e');
-      else if(flags & FLAGS_FLOATG)
-        *fptr++ = (char)((flags & FLAGS_UPPER) ? 'G' : 'g');
-      else
-        *fptr++ = 'f';
-
-      *fptr = 0; /* and a final null-termination */
-
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wformat-nonliteral"
-#endif
-      /* NOTE NOTE NOTE!! Not all sprintf implementations return number of
-         output characters */
-#ifdef HAVE_SNPRINTF
-      (snprintf)(work, BUFFSIZE, formatbuf, iptr->val.dnum); /* NOLINT */
-#ifdef _WIN32
-      /* Old versions of the Windows CRT do not terminate the snprintf output
-         buffer if it reaches the max size so we do that here. */
-      work[BUFFSIZE - 1] = 0;
-#endif
-#else
-      (sprintf)(work, formatbuf, iptr->val.dnum);
-#endif
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
-      DEBUGASSERT(strlen(work) < BUFFSIZE);
-      for(fptr = work; *fptr; fptr++)
-        OUTCHAR(*fptr);
+    case FORMAT_DOUBLE:
+      if(out_double(userp, stream, &p, iptr->val.dnum, work, &done))
+        return done;
       break;
-    }
 
     case FORMAT_INTPTR:
       /* Answer the count of characters written.  */
 #ifdef HAVE_LONG_LONG_TYPE
-      if(flags & FLAGS_LONGLONG)
+      if(p.flags & FLAGS_LONGLONG)
         *(LONG_LONG_TYPE *) iptr->val.ptr = (LONG_LONG_TYPE)done;
       else
 #endif
-        if(flags & FLAGS_LONG)
+        if(p.flags & FLAGS_LONG)
           *(long *) iptr->val.ptr = (long)done;
-      else if(!(flags & FLAGS_SHORT))
+      else if(!(p.flags & FLAGS_SHORT))
         *(int *) iptr->val.ptr = (int)done;
       else
         *(short *) iptr->val.ptr = (short)done;
@@ -1106,7 +1139,7 @@ int curl_msnprintf(char *buffer, size_t maxlength, const char *format, ...)
 static int alloc_addbyter(unsigned char outc, void *f)
 {
   struct asprintf *infop = f;
-  CURLcode result = Curl_dyn_addn(infop->b, &outc, 1);
+  CURLcode result = curlx_dyn_addn(infop->b, &outc, 1);
   if(result) {
     infop->merr = result == CURLE_TOO_LARGE ? MERR_TOO_LARGE : MERR_MEM;
     return 1 ; /* fail */
@@ -1115,7 +1148,7 @@ static int alloc_addbyter(unsigned char outc, void *f)
 }
 
 /* appends the formatted string, returns MERR error code */
-int Curl_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save)
+int curlx_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save)
 {
   struct asprintf info;
   info.b = dyn;
@@ -1123,7 +1156,7 @@ int Curl_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save)
 
   (void)formatf(&info, alloc_addbyter, format, ap_save);
   if(info.merr) {
-    Curl_dyn_free(info.b);
+    curlx_dyn_free(info.b);
     return info.merr;
   }
   return 0;
@@ -1134,16 +1167,16 @@ char *curl_mvaprintf(const char *format, va_list ap_save)
   struct asprintf info;
   struct dynbuf dyn;
   info.b = &dyn;
-  Curl_dyn_init(info.b, DYN_APRINTF);
+  curlx_dyn_init(info.b, DYN_APRINTF);
   info.merr = MERR_OK;
 
   (void)formatf(&info, alloc_addbyter, format, ap_save);
   if(info.merr) {
-    Curl_dyn_free(info.b);
+    curlx_dyn_free(info.b);
     return NULL;
   }
-  if(Curl_dyn_len(info.b))
-    return Curl_dyn_ptr(info.b);
+  if(curlx_dyn_len(info.b))
+    return curlx_dyn_ptr(info.b);
   return strdup("");
 }
 
@@ -1189,7 +1222,6 @@ int curl_mprintf(const char *format, ...)
   int retcode;
   va_list ap_save; /* argument pointer */
   va_start(ap_save, format);
-
   retcode = formatf(stdout, fputc_wrapper, format, ap_save);
   va_end(ap_save);
   return retcode;
diff --git a/extra/curl/curl-8.12.1/lib/mqtt.c b/extra/curl/curl-8.14.1/lib/mqtt.c
similarity index 73%
rename from extra/curl/curl-8.12.1/lib/mqtt.c
rename to extra/curl/curl-8.14.1/lib/mqtt.c
index fe242c778471..77e733461312 100644
--- a/extra/curl/curl-8.12.1/lib/mqtt.c
+++ b/extra/curl/curl-8.14.1/lib/mqtt.c
@@ -37,7 +37,7 @@
 #include "strdup.h"
 #include "url.h"
 #include "escape.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
 #include "curl_printf.h"
 #include "curl_memory.h"
 #include "multiif.h"
@@ -46,17 +46,59 @@
 /* The last #include file should be: */
 #include "memdebug.h"
 
-#define MQTT_MSG_CONNECT   0x10
-#define MQTT_MSG_CONNACK   0x20
-#define MQTT_MSG_PUBLISH   0x30
-#define MQTT_MSG_SUBSCRIBE 0x82
-#define MQTT_MSG_SUBACK    0x90
+/* first byte is command.
+   second byte is for flags. */
+#define MQTT_MSG_CONNECT    0x10
+/* #define MQTT_MSG_CONNACK    0x20 */
+#define MQTT_MSG_PUBLISH    0x30
+#define MQTT_MSG_SUBSCRIBE  0x82
+#define MQTT_MSG_SUBACK     0x90
 #define MQTT_MSG_DISCONNECT 0xe0
+#define MQTT_MSG_PINGREQ    0xC0
+#define MQTT_MSG_PINGRESP   0xD0
 
 #define MQTT_CONNACK_LEN 2
 #define MQTT_SUBACK_LEN 3
 #define MQTT_CLIENTID_LEN 12 /* "curl0123abcd" */
 
+/* meta key for storing protocol meta at easy handle */
+#define CURL_META_MQTT_EASY   "meta:proto:mqtt:easy"
+/* meta key for storing protocol meta at connection */
+#define CURL_META_MQTT_CONN   "meta:proto:mqtt:conn"
+
+enum mqttstate {
+  MQTT_FIRST,             /* 0 */
+  MQTT_REMAINING_LENGTH,  /* 1 */
+  MQTT_CONNACK,           /* 2 */
+  MQTT_SUBACK,            /* 3 */
+  MQTT_SUBACK_COMING,     /* 4 - the SUBACK remainder */
+  MQTT_PUBWAIT,    /* 5 - wait for publish */
+  MQTT_PUB_REMAIN,  /* 6 - wait for the remainder of the publish */
+
+  MQTT_NOSTATE /* 7 - never used an actual state */
+};
+
+struct mqtt_conn {
+  enum mqttstate state;
+  enum mqttstate nextstate; /* switch to this after remaining length is
+                               done */
+  unsigned int packetid;
+};
+
+/* protocol-specific transfer-related data */
+struct MQTT {
+  struct dynbuf sendbuf;
+  /* when receiving */
+  struct dynbuf recvbuf;
+  size_t npacket; /* byte counter */
+  size_t remaining_length;
+  unsigned char pkt_hd[4]; /* for decoding the arriving packet length */
+  struct curltime lastTime; /* last time we sent or received data */
+  unsigned char firstbyte;
+  BIT(pingsent); /* 1 while we wait for ping response */
+};
+
+
 /*
  * Forward declarations.
  */
@@ -99,45 +141,72 @@ const struct Curl_handler Curl_handler_mqtt = {
   PROTOPT_NONE                        /* flags */
 };
 
+static void mqtt_easy_dtor(void *key, size_t klen, void *entry)
+{
+  struct MQTT *mq = entry;
+  (void)key;
+  (void)klen;
+  curlx_dyn_free(&mq->sendbuf);
+  curlx_dyn_free(&mq->recvbuf);
+  free(mq);
+}
+
+static void mqtt_conn_dtor(void *key, size_t klen, void *entry)
+{
+  (void)key;
+  (void)klen;
+  free(entry);
+}
+
 static CURLcode mqtt_setup_conn(struct Curl_easy *data,
                                 struct connectdata *conn)
 {
-  /* allocate the HTTP-specific struct for the Curl_easy, only to survive
-     during this request */
+  /* setup MQTT specific meta data at easy handle and connection */
+  struct mqtt_conn *mqtt;
   struct MQTT *mq;
-  (void)conn;
-  DEBUGASSERT(data->req.p.mqtt == NULL);
+
+  mqtt = calloc(1, sizeof(*mqtt));
+  if(!mqtt ||
+     Curl_conn_meta_set(conn, CURL_META_MQTT_CONN, mqtt, mqtt_conn_dtor))
+    return CURLE_OUT_OF_MEMORY;
 
   mq = calloc(1, sizeof(struct MQTT));
   if(!mq)
     return CURLE_OUT_OF_MEMORY;
-  Curl_dyn_init(&mq->recvbuf, DYN_MQTT_RECV);
-  data->req.p.mqtt = mq;
+  curlx_dyn_init(&mq->recvbuf, DYN_MQTT_RECV);
+  curlx_dyn_init(&mq->sendbuf, DYN_MQTT_SEND);
+  if(Curl_meta_set(data, CURL_META_MQTT_EASY, mq, mqtt_easy_dtor))
+    return CURLE_OUT_OF_MEMORY;
   return CURLE_OK;
 }
 
 static CURLcode mqtt_send(struct Curl_easy *data,
-                          char *buf, size_t len)
+                          const char *buf, size_t len)
 {
-  CURLcode result = CURLE_OK;
-  struct MQTT *mq = data->req.p.mqtt;
   size_t n;
+  CURLcode result;
+  struct MQTT *mq = Curl_meta_get(data, CURL_META_MQTT_EASY);
+
+  if(!mq)
+    return CURLE_FAILED_INIT;
+
   result = Curl_xfer_send(data, buf, len, FALSE, &n);
   if(result)
     return result;
+  mq->lastTime = curlx_now();
   Curl_debug(data, CURLINFO_HEADER_OUT, buf, (size_t)n);
   if(len != n) {
     size_t nsend = len - n;
-    char *sendleftovers = Curl_memdup(&buf[n], nsend);
-    if(!sendleftovers)
-      return CURLE_OUT_OF_MEMORY;
-    mq->sendleftovers = sendleftovers;
-    mq->nsend = nsend;
-  }
-  else {
-    mq->sendleftovers = NULL;
-    mq->nsend = 0;
+    if(curlx_dyn_len(&mq->sendbuf)) {
+      DEBUGASSERT(curlx_dyn_len(&mq->sendbuf) >= nsend);
+      result = curlx_dyn_tail(&mq->sendbuf, nsend); /* keep this much */
+    }
+    else {
+      result = curlx_dyn_addn(&mq->sendbuf, &buf[n], nsend);
+    }
   }
+  else
+    curlx_dyn_reset(&mq->sendbuf);
   return result;
 }
 
@@ -257,7 +326,6 @@ static CURLcode mqtt_connect(struct Curl_easy *data)
   int remain_pos = 0;
   char remain[4] = {0};
   size_t packetlen = 0;
-  size_t payloadlen = 0;
   size_t start_user = 0;
   size_t start_pwd = 0;
   char client_id[MQTT_CLIENTID_LEN + 1] = "curl";
@@ -272,14 +340,11 @@ static CURLcode mqtt_connect(struct Curl_easy *data)
   const char *passwd = data->state.aptr.passwd ?
     data->state.aptr.passwd : "";
   const size_t plen = strlen(passwd);
-
-  payloadlen = ulen + plen + MQTT_CLIENTID_LEN + 2;
-  /* The plus 2 are for the MSB and LSB describing the length of the string to
-   * be added on the payload. Refer to spec 1.5.2 and 1.5.4 */
-  if(ulen)
-    payloadlen += 2;
-  if(plen)
-    payloadlen += 2;
+  const size_t payloadlen = ulen + plen + MQTT_CLIENTID_LEN + 2 +
+  /* The plus 2s below are for the MSB and LSB describing the length of the
+     string to be added on the payload. Refer to spec 1.5.2 and 1.5.4 */
+    (ulen ? 2 : 0) +
+    (plen ? 2 : 0);
 
   /* getting how much occupy the remain length */
   remain_pos = mqtt_encode_len(remain, payloadlen + 10);
@@ -288,12 +353,11 @@ static CURLcode mqtt_connect(struct Curl_easy *data)
   packetlen = payloadlen + 10 + remain_pos + 1;
 
   /* allocating packet */
-  if(packetlen > 268435455)
+  if(packetlen > 0xFFFFFFF)
     return CURLE_WEIRD_SERVER_REPLY;
-  packet = malloc(packetlen);
+  packet = calloc(1, packetlen);
   if(!packet)
     return CURLE_OUT_OF_MEMORY;
-  memset(packet, 0, packetlen);
 
   /* set initial values for the CONNECT packet */
   pos = init_connpack(packet, remain, remain_pos);
@@ -309,9 +373,9 @@ static CURLcode mqtt_connect(struct Curl_easy *data)
   }
   infof(data, "Using client id '%s'", client_id);
 
-  /* position where starts the user payload */
+  /* position where the user payload starts */
   start_user = pos + 3 + MQTT_CLIENTID_LEN;
-  /* position where starts the password payload */
+  /* position where the password payload starts */
   start_pwd = start_user + ulen;
   /* if username was provided, add it to the packet */
   if(ulen) {
@@ -320,7 +384,7 @@ static CURLcode mqtt_connect(struct Curl_easy *data)
     rc = add_user(username, ulen,
                   (unsigned char *)packet, start_user, remain_pos);
     if(rc) {
-      failf(data, "Username is too large: [%zu]", ulen);
+      failf(data, "Username too long: [%zu]", ulen);
       result = CURLE_WEIRD_SERVER_REPLY;
       goto end;
     }
@@ -330,7 +394,7 @@ static CURLcode mqtt_connect(struct Curl_easy *data)
   if(plen) {
     rc = add_passwd(passwd, plen, packet, start_pwd, remain_pos);
     if(rc) {
-      failf(data, "Password is too large: [%zu]", plen);
+      failf(data, "Password too long: [%zu]", plen);
       result = CURLE_WEIRD_SERVER_REPLY;
       goto end;
     }
@@ -349,20 +413,19 @@ static CURLcode mqtt_connect(struct Curl_easy *data)
 
 static CURLcode mqtt_disconnect(struct Curl_easy *data)
 {
-  CURLcode result = CURLE_OK;
-  struct MQTT *mq = data->req.p.mqtt;
-  result = mqtt_send(data, (char *)"\xe0\x00", 2);
-  Curl_safefree(mq->sendleftovers);
-  Curl_dyn_free(&mq->recvbuf);
-  return result;
+  return mqtt_send(data, "\xe0\x00", 2);
 }
 
 static CURLcode mqtt_recv_atleast(struct Curl_easy *data, size_t nbytes)
 {
-  struct MQTT *mq = data->req.p.mqtt;
-  size_t rlen = Curl_dyn_len(&mq->recvbuf);
+  struct MQTT *mq = Curl_meta_get(data, CURL_META_MQTT_EASY);
+  size_t rlen;
   CURLcode result;
 
+  if(!mq)
+    return CURLE_FAILED_INIT;
+  rlen = curlx_dyn_len(&mq->recvbuf);
+
   if(rlen < nbytes) {
     unsigned char readbuf[1024];
     ssize_t nread;
@@ -372,42 +435,49 @@ static CURLcode mqtt_recv_atleast(struct Curl_easy *data, size_t nbytes)
     if(result)
       return result;
     DEBUGASSERT(nread >= 0);
-    if(Curl_dyn_addn(&mq->recvbuf, readbuf, (size_t)nread))
+    if(curlx_dyn_addn(&mq->recvbuf, readbuf, (size_t)nread))
       return CURLE_OUT_OF_MEMORY;
-    rlen = Curl_dyn_len(&mq->recvbuf);
+    rlen = curlx_dyn_len(&mq->recvbuf);
   }
   return (rlen >= nbytes) ? CURLE_OK : CURLE_AGAIN;
 }
 
 static void mqtt_recv_consume(struct Curl_easy *data, size_t nbytes)
 {
-  struct MQTT *mq = data->req.p.mqtt;
-  size_t rlen = Curl_dyn_len(&mq->recvbuf);
-  if(rlen <= nbytes)
-    Curl_dyn_reset(&mq->recvbuf);
-  else
-    Curl_dyn_tail(&mq->recvbuf, rlen - nbytes);
+  struct MQTT *mq = Curl_meta_get(data, CURL_META_MQTT_EASY);
+  DEBUGASSERT(mq);
+  if(mq) {
+    size_t rlen = curlx_dyn_len(&mq->recvbuf);
+    if(rlen <= nbytes)
+      curlx_dyn_reset(&mq->recvbuf);
+    else
+      curlx_dyn_tail(&mq->recvbuf, rlen - nbytes);
+  }
 }
 
 static CURLcode mqtt_verify_connack(struct Curl_easy *data)
 {
-  struct MQTT *mq = data->req.p.mqtt;
+  struct MQTT *mq = Curl_meta_get(data, CURL_META_MQTT_EASY);
   CURLcode result;
   char *ptr;
 
+  DEBUGASSERT(mq);
+  if(!mq)
+    return CURLE_FAILED_INIT;
+
   result = mqtt_recv_atleast(data, MQTT_CONNACK_LEN);
   if(result)
     goto fail;
 
   /* verify CONNACK */
-  DEBUGASSERT(Curl_dyn_len(&mq->recvbuf) >= MQTT_CONNACK_LEN);
-  ptr = Curl_dyn_ptr(&mq->recvbuf);
+  DEBUGASSERT(curlx_dyn_len(&mq->recvbuf) >= MQTT_CONNACK_LEN);
+  ptr = curlx_dyn_ptr(&mq->recvbuf);
   Curl_debug(data, CURLINFO_HEADER_IN, ptr, MQTT_CONNACK_LEN);
 
   if(ptr[0] != 0x00 || ptr[1] != 0x00) {
     failf(data, "Expected %02x%02x but got %02x%02x",
           0x00, 0x00, ptr[0], ptr[1]);
-    Curl_dyn_reset(&mq->recvbuf);
+    curlx_dyn_reset(&mq->recvbuf);
     result = CURLE_WEIRD_SERVER_REPLY;
     goto fail;
   }
@@ -444,12 +514,16 @@ static CURLcode mqtt_subscribe(struct Curl_easy *data)
   char encodedsize[4];
   size_t n;
   struct connectdata *conn = data->conn;
+  struct mqtt_conn *mqtt = Curl_conn_meta_get(conn, CURL_META_MQTT_CONN);
+
+  if(!mqtt)
+    return CURLE_FAILED_INIT;
 
   result = mqtt_get_topic(data, &topic, &topiclen);
   if(result)
     goto fail;
 
-  conn->proto.mqtt.packetid++;
+  mqtt->packetid++;
 
   packetlen = topiclen + 5; /* packetid + topic (has a two byte length field)
                                + 2 bytes topic length + QoS byte */
@@ -464,14 +538,14 @@ static CURLcode mqtt_subscribe(struct Curl_easy *data)
 
   packet[0] = MQTT_MSG_SUBSCRIBE;
   memcpy(&packet[1], encodedsize, n);
-  packet[1 + n] = (conn->proto.mqtt.packetid >> 8) & 0xff;
-  packet[2 + n] = conn->proto.mqtt.packetid & 0xff;
+  packet[1 + n] = (mqtt->packetid >> 8) & 0xff;
+  packet[2 + n] = mqtt->packetid & 0xff;
   packet[3 + n] = (topiclen >> 8) & 0xff;
   packet[4 + n ] = topiclen & 0xff;
   memcpy(&packet[5 + n], topic, topiclen);
   packet[5 + n + topiclen] = 0; /* QoS zero */
 
-  result = mqtt_send(data, (char *)packet, packetlen);
+  result = mqtt_send(data, (const char *)packet, packetlen);
 
 fail:
   free(topic);
@@ -484,25 +558,28 @@ static CURLcode mqtt_subscribe(struct Curl_easy *data)
  */
 static CURLcode mqtt_verify_suback(struct Curl_easy *data)
 {
-  struct MQTT *mq = data->req.p.mqtt;
+  struct MQTT *mq = Curl_meta_get(data, CURL_META_MQTT_EASY);
   struct connectdata *conn = data->conn;
-  struct mqtt_conn *mqtt = &conn->proto.mqtt;
+  struct mqtt_conn *mqtt = Curl_conn_meta_get(conn, CURL_META_MQTT_CONN);
   CURLcode result;
   char *ptr;
 
+  if(!mqtt || !mq)
+    return CURLE_FAILED_INIT;
+
   result = mqtt_recv_atleast(data, MQTT_SUBACK_LEN);
   if(result)
     goto fail;
 
   /* verify SUBACK */
-  DEBUGASSERT(Curl_dyn_len(&mq->recvbuf) >= MQTT_SUBACK_LEN);
-  ptr = Curl_dyn_ptr(&mq->recvbuf);
+  DEBUGASSERT(curlx_dyn_len(&mq->recvbuf) >= MQTT_SUBACK_LEN);
+  ptr = curlx_dyn_ptr(&mq->recvbuf);
   Curl_debug(data, CURLINFO_HEADER_IN, ptr, MQTT_SUBACK_LEN);
 
   if(((unsigned char)ptr[0]) != ((mqtt->packetid >> 8) & 0xff) ||
      ((unsigned char)ptr[1]) != (mqtt->packetid & 0xff) ||
      ptr[2] != 0x00) {
-    Curl_dyn_reset(&mq->recvbuf);
+    curlx_dyn_reset(&mq->recvbuf);
     result = CURLE_WEIRD_SERVER_REPLY;
     goto fail;
   }
@@ -558,7 +635,7 @@ static CURLcode mqtt_publish(struct Curl_easy *data)
   i += topiclen;
   memcpy(&pkt[i], payload, payloadlen);
   i += payloadlen;
-  result = mqtt_send(data, (char *)pkt, i);
+  result = mqtt_send(data, (const char *)pkt, i);
 
 fail:
   free(pkt);
@@ -606,7 +683,10 @@ static void mqstate(struct Curl_easy *data,
                     enum mqttstate nextstate) /* used if state == FIRST */
 {
   struct connectdata *conn = data->conn;
-  struct mqtt_conn *mqtt = &conn->proto.mqtt;
+  struct mqtt_conn *mqtt = Curl_conn_meta_get(conn, CURL_META_MQTT_CONN);
+  DEBUGASSERT(mqtt);
+  if(!mqtt)
+    return;
 #ifdef DEBUGBUILD
   infof(data, "%s (from %s) (next is %s)",
         statenames[state],
@@ -625,10 +705,14 @@ static CURLcode mqtt_read_publish(struct Curl_easy *data, bool *done)
   struct connectdata *conn = data->conn;
   ssize_t nread;
   size_t remlen;
-  struct mqtt_conn *mqtt = &conn->proto.mqtt;
-  struct MQTT *mq = data->req.p.mqtt;
+  struct mqtt_conn *mqtt = Curl_conn_meta_get(conn, CURL_META_MQTT_CONN);
+  struct MQTT *mq = Curl_meta_get(data, CURL_META_MQTT_EASY);
   unsigned char packet;
 
+  DEBUGASSERT(mqtt);
+  if(!mqtt || !mq)
+    return CURLE_FAILED_INIT;
+
   switch(mqtt->state) {
 MQTT_SUBACK_COMING:
   case MQTT_SUBACK_COMING:
@@ -692,6 +776,9 @@ static CURLcode mqtt_read_publish(struct Curl_easy *data, bool *done)
       goto end;
     }
 
+    /* we received something */
+    mq->lastTime = curlx_now();
+
     /* if QoS is set, message contains packet id */
     result = Curl_client_write(data, CLIENTWRITE_BODY, buffer, nread);
     if(result)
@@ -714,9 +801,15 @@ static CURLcode mqtt_read_publish(struct Curl_easy *data, bool *done)
 
 static CURLcode mqtt_do(struct Curl_easy *data, bool *done)
 {
+  struct MQTT *mq = Curl_meta_get(data, CURL_META_MQTT_EASY);
   CURLcode result = CURLE_OK;
   *done = FALSE; /* unconditionally */
 
+  if(!mq)
+    return CURLE_FAILED_INIT;
+  mq->lastTime = curlx_now();
+  mq->pingsent = FALSE;
+
   result = mqtt_connect(data);
   if(result) {
     failf(data, "Error %d sending MQTT CONNECT request", result);
@@ -729,34 +822,73 @@ static CURLcode mqtt_do(struct Curl_easy *data, bool *done)
 static CURLcode mqtt_done(struct Curl_easy *data,
                           CURLcode status, bool premature)
 {
-  struct MQTT *mq = data->req.p.mqtt;
+  struct MQTT *mq = Curl_meta_get(data, CURL_META_MQTT_EASY);
   (void)status;
   (void)premature;
-  Curl_safefree(mq->sendleftovers);
-  Curl_dyn_free(&mq->recvbuf);
+  if(mq) {
+    curlx_dyn_free(&mq->sendbuf);
+    curlx_dyn_free(&mq->recvbuf);
+  }
   return CURLE_OK;
 }
 
-static CURLcode mqtt_doing(struct Curl_easy *data, bool *done)
+/* we ping regularly to avoid being disconnected by the server */
+static CURLcode mqtt_ping(struct Curl_easy *data)
 {
+  struct MQTT *mq = Curl_meta_get(data, CURL_META_MQTT_EASY);
   CURLcode result = CURLE_OK;
   struct connectdata *conn = data->conn;
-  struct mqtt_conn *mqtt = &conn->proto.mqtt;
-  struct MQTT *mq = data->req.p.mqtt;
+  struct mqtt_conn *mqtt = Curl_conn_meta_get(conn, CURL_META_MQTT_CONN);
+
+  if(!mqtt || !mq)
+    return CURLE_FAILED_INIT;
+
+  if(mqtt->state == MQTT_FIRST &&
+     !mq->pingsent &&
+     data->set.upkeep_interval_ms > 0) {
+    struct curltime t = curlx_now();
+    timediff_t diff = curlx_timediff(t, mq->lastTime);
+
+    if(diff > data->set.upkeep_interval_ms) {
+      /* 0xC0 is PINGREQ, and 0x00 is remaining length */
+      unsigned char packet[2] = { 0xC0, 0x00 };
+      size_t packetlen = sizeof(packet);
+
+      result = mqtt_send(data, (char *)packet, packetlen);
+      if(!result) {
+        mq->pingsent = TRUE;
+      }
+      infof(data, "mqtt_ping: sent ping request.");
+    }
+  }
+  return result;
+}
+
+static CURLcode mqtt_doing(struct Curl_easy *data, bool *done)
+{
+  struct MQTT *mq = Curl_meta_get(data, CURL_META_MQTT_EASY);
+  CURLcode result = CURLE_OK;
   ssize_t nread;
   unsigned char recvbyte;
+  struct mqtt_conn *mqtt = Curl_conn_meta_get(data->conn, CURL_META_MQTT_CONN);
+
+  if(!mqtt || !mq)
+    return CURLE_FAILED_INIT;
 
   *done = FALSE;
 
-  if(mq->nsend) {
+  if(curlx_dyn_len(&mq->sendbuf)) {
     /* send the remainder of an outgoing packet */
-    char *ptr = mq->sendleftovers;
-    result = mqtt_send(data, mq->sendleftovers, mq->nsend);
-    free(ptr);
+    result = mqtt_send(data, curlx_dyn_ptr(&mq->sendbuf),
+                       curlx_dyn_len(&mq->sendbuf));
     if(result)
       return result;
   }
 
+  result = mqtt_ping(data);
+  if(result)
+    return result;
+
   infof(data, "mqtt_doing: state [%d]", (int) mqtt->state);
   switch(mqtt->state) {
   case MQTT_FIRST:
@@ -770,7 +902,11 @@ static CURLcode mqtt_doing(struct Curl_easy *data, bool *done)
       result = CURLE_RECV_ERROR;
       break;
     }
-    Curl_debug(data, CURLINFO_HEADER_IN, (char *)&mq->firstbyte, 1);
+    Curl_debug(data, CURLINFO_HEADER_IN, (const char *)&mq->firstbyte, 1);
+
+    /* we received something */
+    mq->lastTime = curlx_now();
+
     /* remember the first byte */
     mq->npacket = 0;
     mqstate(data, MQTT_REMAINING_LENGTH, MQTT_NOSTATE);
@@ -780,7 +916,7 @@ static CURLcode mqtt_doing(struct Curl_easy *data, bool *done)
       result = Curl_xfer_recv(data, (char *)&recvbyte, 1, &nread);
       if(result || !nread)
         break;
-      Curl_debug(data, CURLINFO_HEADER_IN, (char *)&recvbyte, 1);
+      Curl_debug(data, CURLINFO_HEADER_IN, (const char *)&recvbyte, 1);
       mq->pkt_hd[mq->npacket++] = recvbyte;
     } while((recvbyte & 0x80) && (mq->npacket < 4));
     if(!result && nread && (recvbyte & 0x80))
@@ -801,6 +937,13 @@ static CURLcode mqtt_doing(struct Curl_easy *data, bool *done)
       infof(data, "Got DISCONNECT");
       *done = TRUE;
     }
+
+    /* ping response */
+    if(mq->firstbyte == MQTT_MSG_PINGRESP) {
+      infof(data, "Received ping response.");
+      mq->pingsent = FALSE;
+      mqstate(data, MQTT_FIRST, MQTT_PUBWAIT);
+    }
     break;
   case MQTT_CONNACK:
     result = mqtt_verify_connack(data);
diff --git a/extra/curl/curl-8.12.1/lib/mqtt.h b/extra/curl/curl-8.14.1/lib/mqtt.h
similarity index 57%
rename from extra/curl/curl-8.12.1/lib/mqtt.h
rename to extra/curl/curl-8.14.1/lib/mqtt.h
index 99ab12a98a40..8fb8a33c022a 100644
--- a/extra/curl/curl-8.12.1/lib/mqtt.h
+++ b/extra/curl/curl-8.14.1/lib/mqtt.h
@@ -28,36 +28,4 @@
 extern const struct Curl_handler Curl_handler_mqtt;
 #endif
 
-enum mqttstate {
-  MQTT_FIRST,             /* 0 */
-  MQTT_REMAINING_LENGTH,  /* 1 */
-  MQTT_CONNACK,           /* 2 */
-  MQTT_SUBACK,            /* 3 */
-  MQTT_SUBACK_COMING,     /* 4 - the SUBACK remainder */
-  MQTT_PUBWAIT,    /* 5 - wait for publish */
-  MQTT_PUB_REMAIN,  /* 6 - wait for the remainder of the publish */
-
-  MQTT_NOSTATE /* 7 - never used an actual state */
-};
-
-struct mqtt_conn {
-  enum mqttstate state;
-  enum mqttstate nextstate; /* switch to this after remaining length is
-                               done */
-  unsigned int packetid;
-};
-
-/* protocol-specific transfer-related data */
-struct MQTT {
-  char *sendleftovers;
-  size_t nsend; /* size of sendleftovers */
-
-  /* when receiving */
-  size_t npacket; /* byte counter */
-  unsigned char firstbyte;
-  size_t remaining_length;
-  struct dynbuf recvbuf;
-  unsigned char pkt_hd[4]; /* for decoding the arriving packet length */
-};
-
 #endif /* HEADER_CURL_MQTT_H */
diff --git a/extra/curl/curl-8.12.1/lib/multi.c b/extra/curl/curl-8.14.1/lib/multi.c
similarity index 77%
rename from extra/curl/curl-8.12.1/lib/multi.c
rename to extra/curl/curl-8.14.1/lib/multi.c
index 2b05e94a0468..64eb1ae8540e 100644
--- a/extra/curl/curl-8.12.1/lib/multi.c
+++ b/extra/curl/curl-8.14.1/lib/multi.c
@@ -36,11 +36,12 @@
 #include "share.h"
 #include "psl.h"
 #include "multiif.h"
+#include "multi_ev.h"
 #include "sendf.h"
-#include "timeval.h"
+#include "curlx/timeval.h"
 #include "http.h"
 #include "select.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
 #include "speedcheck.h"
 #include "conncache.h"
 #include "multihandle.h"
@@ -57,6 +58,9 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
+/* initial multi->xfers table size for a full multi */
+#define CURL_XFER_TABLE_SIZE    512
+
 /*
   CURL_SOCKET_HASH_TABLE_SIZE should be a prime number. Increasing it from 97
   to 911 takes on a 32-bit machine 4 x 804 = 3211 more bytes. Still, every
@@ -94,8 +98,6 @@
 
 static void move_pending_to_connect(struct Curl_multi *multi,
                                     struct Curl_easy *data);
-static CURLMcode singlesocket(struct Curl_multi *multi,
-                              struct Curl_easy *data);
 static CURLMcode add_next_timeout(struct curltime now,
                                   struct Curl_multi *multi,
                                   struct Curl_easy *d);
@@ -104,30 +106,8 @@ static CURLMcode multi_timeout(struct Curl_multi *multi,
                                long *timeout_ms);
 static void process_pending_handles(struct Curl_multi *multi);
 static void multi_xfer_bufs_free(struct Curl_multi *multi);
-static void expire_ex(struct Curl_easy *data, const struct curltime *nowp,
-                      timediff_t milli, expire_id id);
-
-#if defined( DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
-static const char * const multi_statename[]={
-  "INIT",
-  "PENDING",
-  "SETUP",
-  "CONNECT",
-  "RESOLVING",
-  "CONNECTING",
-  "TUNNELING",
-  "PROTOCONNECT",
-  "PROTOCONNECTING",
-  "DO",
-  "DOING",
-  "DOING_MORE",
-  "DID",
-  "PERFORMING",
-  "RATELIMITING",
-  "DONE",
-  "COMPLETED",
-  "MSGSENT",
-};
+#ifdef DEBUGBUILD
+static void multi_xfer_tbl_dump(struct Curl_multi *multi);
 #endif
 
 /* function pointer called once when switching TO a state */
@@ -179,31 +159,25 @@ static void mstate(struct Curl_easy *data, CURLMstate state
     NULL               /* MSGSENT */
   };
 
-#if defined(DEBUGBUILD) && defined(CURL_DISABLE_VERBOSE_STRINGS)
-  (void) lineno;
-#endif
-
   if(oldstate == state)
     /* do not bother when the new state is the same as the old state */
     return;
 
-  data->mstate = state;
-
-#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
-  if(data->mstate >= MSTATE_PENDING &&
-     data->mstate < MSTATE_COMPLETED) {
-    infof(data,
-          "STATE: %s => %s handle %p; line %d",
-          multi_statename[oldstate], multi_statename[data->mstate],
-          (void *)data, lineno);
-  }
+#ifdef DEBUGBUILD
+  CURL_TRC_M(data, "-> [%s] (line %d)", CURL_MSTATE_NAME(state), lineno);
+#else
+  CURL_TRC_M(data, "-> [%s]", CURL_MSTATE_NAME(state));
 #endif
 
+  data->mstate = state;
+
   if(state == MSTATE_COMPLETED) {
-    /* changing to COMPLETED means there is one less easy handle 'alive' */
-    DEBUGASSERT(data->multi->num_alive > 0);
-    data->multi->num_alive--;
-    if(!data->multi->num_alive) {
+    /* changing to COMPLETED means it is in process and needs to go */
+    DEBUGASSERT(Curl_uint_bset_contains(&data->multi->process, data->mid));
+    Curl_uint_bset_remove(&data->multi->process, data->mid);
+    Curl_uint_bset_remove(&data->multi->pending, data->mid); /* to be sure */
+
+    if(Curl_uint_bset_empty(&data->multi->process)) {
       /* free the transfer buffer when we have no more active transfers */
       multi_xfer_bufs_free(data->multi);
     }
@@ -220,163 +194,6 @@ static void mstate(struct Curl_easy *data, CURLMstate state
 #define multistate(x,y) mstate(x,y, __LINE__)
 #endif
 
-/*
- * We add one of these structs to the sockhash for each socket
- */
-
-struct Curl_sh_entry {
-  struct Curl_hash transfers; /* hash of transfers using this socket */
-  unsigned int action;  /* what combined action READ/WRITE this socket waits
-                           for */
-  unsigned int users; /* number of transfers using this */
-  void *socketp; /* settable by users with curl_multi_assign() */
-  unsigned int readers; /* this many transfers want to read */
-  unsigned int writers; /* this many transfers want to write */
-};
-
-/* look up a given socket in the socket hash, skip invalid sockets */
-static struct Curl_sh_entry *sh_getentry(struct Curl_hash *sh,
-                                         curl_socket_t s)
-{
-  if(s != CURL_SOCKET_BAD) {
-    /* only look for proper sockets */
-    return Curl_hash_pick(sh, (char *)&s, sizeof(curl_socket_t));
-  }
-  return NULL;
-}
-
-#define TRHASH_SIZE 13
-
-/* the given key here is a struct Curl_easy pointer */
-static size_t trhash(void *key, size_t key_length, size_t slots_num)
-{
-  unsigned char bytes = ((unsigned char *)key)[key_length - 1] ^
-    ((unsigned char *)key)[0];
-  return (bytes % slots_num);
-}
-
-static size_t trhash_compare(void *k1, size_t k1_len, void *k2, size_t k2_len)
-{
-  (void)k2_len;
-  return !memcmp(k1, k2, k1_len);
-}
-
-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,
-                                         curl_socket_t s)
-{
-  struct Curl_sh_entry *there = sh_getentry(sh, s);
-  struct Curl_sh_entry *check;
-
-  if(there) {
-    /* it is present, return fine */
-    return there;
-  }
-
-  /* not present, add it */
-  check = calloc(1, sizeof(struct Curl_sh_entry));
-  if(!check)
-    return NULL; /* major failure */
-
-  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)) {
-    Curl_hash_destroy(&check->transfers);
-    free(check);
-    return NULL; /* major failure */
-  }
-
-  return check; /* things are good in sockhash land */
-}
-
-
-/* delete the given socket + handle from the hash */
-static void sh_delentry(struct Curl_sh_entry *entry,
-                        struct Curl_hash *sh, curl_socket_t s)
-{
-  Curl_hash_destroy(&entry->transfers);
-
-  /* We remove the hash entry. This will end up in a call to
-     sh_freeentry(). */
-  Curl_hash_delete(sh, (char *)&s, sizeof(curl_socket_t));
-}
-
-/*
- * free a sockhash entry
- */
-static void sh_freeentry(void *freethis)
-{
-  struct Curl_sh_entry *p = (struct Curl_sh_entry *) freethis;
-
-  free(p);
-}
-
-static size_t fd_key_compare(void *k1, size_t k1_len, void *k2, size_t k2_len)
-{
-  (void) k1_len; (void) k2_len;
-
-  return (*((curl_socket_t *) k1)) == (*((curl_socket_t *) k2));
-}
-
-static size_t hash_fd(void *key, size_t key_length, size_t slots_num)
-{
-  curl_socket_t fd = *((curl_socket_t *) key);
-  (void) key_length;
-
-  return (fd % (curl_socket_t)slots_num);
-}
-
-/*
- * sh_init() creates a new socket hash and returns the handle for it.
- *
- * Quote from README.multi_socket:
- *
- * "Some tests at 7000 and 9000 connections showed that the socket hash lookup
- * is somewhat of a bottle neck. Its current implementation may be a bit too
- * limiting. It simply has a fixed-size array, and on each entry in the array
- * it has a linked list with entries. The hash only checks which list to scan
- * through. The code I had used so for used a list with merely 7 slots (as
- * that is what the DNS hash uses) but with 7000 connections that would make
- * an average of 1000 nodes in each list to run through. I upped that to 97
- * slots (I believe a prime is suitable) and noticed a significant speed
- * increase. I need to reconsider the hash implementation or use a rather
- * large default value like this. At 9000 connections I was still below 10us
- * per call."
- *
- */
-static void sh_init(struct Curl_hash *hash, size_t hashsize)
-{
-  Curl_hash_init(hash, hashsize, hash_fd, fd_key_compare,
-                 sh_freeentry);
-}
 
 /* multi->proto_hash destructor. Should never be called as elements
  * MUST be added with their own destructor */
@@ -400,7 +217,8 @@ static void multi_addmsg(struct Curl_multi *multi, struct Curl_message *msg)
   Curl_llist_append(&multi->msglist, msg, &msg->list);
 }
 
-struct Curl_multi *Curl_multi_handle(size_t hashsize,  /* socket hash */
+struct Curl_multi *Curl_multi_handle(unsigned int xfer_table_size,
+                                     size_t ev_hashsize,  /* event hash */
                                      size_t chashsize, /* connection hash */
                                      size_t dnssize,   /* dns hash */
                                      size_t sesssize)  /* TLS session cache */
@@ -412,28 +230,46 @@ struct Curl_multi *Curl_multi_handle(size_t hashsize,  /* socket hash */
 
   multi->magic = CURL_MULTI_HANDLE;
 
-  Curl_init_dnscache(&multi->hostcache, dnssize);
+  Curl_dnscache_init(&multi->dnscache, dnssize);
+  Curl_multi_ev_init(multi, ev_hashsize);
+  Curl_uint_tbl_init(&multi->xfers, NULL);
+  Curl_uint_bset_init(&multi->process);
+  Curl_uint_bset_init(&multi->pending);
+  Curl_uint_bset_init(&multi->msgsent);
+  Curl_hash_init(&multi->proto_hash, 23,
+                 Curl_hash_str, curlx_str_key_compare, ph_freeentry);
+  Curl_llist_init(&multi->msglist, NULL);
 
-  sh_init(&multi->sockhash, hashsize);
+  multi->multiplexing = TRUE;
+  multi->max_concurrent_streams = 100;
+  multi->last_timeout_ms = -1;
 
-  Curl_hash_init(&multi->proto_hash, 23,
-                 Curl_hash_str, Curl_str_key_compare, ph_freeentry);
+  if(Curl_uint_bset_resize(&multi->process, xfer_table_size) ||
+     Curl_uint_bset_resize(&multi->pending, xfer_table_size) ||
+     Curl_uint_bset_resize(&multi->msgsent, xfer_table_size) ||
+     Curl_uint_tbl_resize(&multi->xfers, xfer_table_size))
+    goto error;
 
-  if(Curl_cpool_init(&multi->cpool, Curl_on_disconnect,
-                     multi, NULL, chashsize))
+  multi->admin = curl_easy_init();
+  if(!multi->admin)
     goto error;
+  /* Initialize admin handle to operate inside this multi */
+  multi->admin->multi = multi;
+  multi->admin->state.internal = TRUE;
+  Curl_llist_init(&multi->admin->state.timeoutlist, NULL);
+#ifdef DEBUGBUILD
+  if(getenv("CURL_DEBUG"))
+    multi->admin->set.verbose = TRUE;
+#endif
+  Curl_uint_tbl_add(&multi->xfers, multi->admin, &multi->admin->mid);
 
-  if(Curl_ssl_scache_create(sesssize, 2, &multi->ssl_scache))
+  if(Curl_cshutdn_init(&multi->cshutdn, multi))
     goto error;
 
-  Curl_llist_init(&multi->msglist, NULL);
-  Curl_llist_init(&multi->process, NULL);
-  Curl_llist_init(&multi->pending, NULL);
-  Curl_llist_init(&multi->msgsent, NULL);
+  Curl_cpool_init(&multi->cpool, multi->admin, NULL, chashsize);
 
-  multi->multiplexing = TRUE;
-  multi->max_concurrent_streams = 100;
-  multi->last_timeout_ms = -1;
+  if(Curl_ssl_scache_create(sesssize, 2, &multi->ssl_scache))
+    goto error;
 
 #ifdef USE_WINSOCK
   multi->wsa_event = WSACreateEvent();
@@ -452,18 +288,30 @@ struct Curl_multi *Curl_multi_handle(size_t hashsize,  /* socket hash */
 
 error:
 
-  sockhash_destroy(&multi->sockhash);
+  Curl_multi_ev_cleanup(multi);
   Curl_hash_destroy(&multi->proto_hash);
-  Curl_hash_destroy(&multi->hostcache);
+  Curl_dnscache_destroy(&multi->dnscache);
   Curl_cpool_destroy(&multi->cpool);
+  Curl_cshutdn_destroy(&multi->cshutdn, multi->admin);
   Curl_ssl_scache_destroy(multi->ssl_scache);
+  if(multi->admin) {
+    multi->admin->multi = NULL;
+    Curl_close(&multi->admin);
+  }
+
+  Curl_uint_bset_destroy(&multi->process);
+  Curl_uint_bset_destroy(&multi->pending);
+  Curl_uint_bset_destroy(&multi->msgsent);
+  Curl_uint_tbl_destroy(&multi->xfers);
+
   free(multi);
   return NULL;
 }
 
 CURLM *curl_multi_init(void)
 {
-  return Curl_multi_handle(CURL_SOCKET_HASH_TABLE_SIZE,
+  return Curl_multi_handle(CURL_XFER_TABLE_SIZE,
+                           CURL_SOCKET_HASH_TABLE_SIZE,
                            CURL_CONNECTION_HASH_SIZE,
                            CURL_DNS_HASH_SIZE,
                            CURL_TLS_SESSION_SIZE);
@@ -483,6 +331,44 @@ static void multi_warn_debug(struct Curl_multi *multi, struct Curl_easy *data)
 #define multi_warn_debug(x,y) Curl_nop_stmt
 #endif
 
+
+static CURLMcode multi_xfers_add(struct Curl_multi *multi,
+                                 struct Curl_easy *data)
+{
+  /* We want `multi->xfers` to have "sufficient" free rows, so that we do
+   * have to reuse the `mid` from a just removed easy right away.
+   * Since uint_tbl and uint_bset is quite memory efficient,
+   * regard less than 25% free as insufficient.
+   * (for low capacities, e.g. multi_easy, 4 or less). */
+  unsigned int capacity = Curl_uint_tbl_capacity(&multi->xfers);
+  unsigned int unused = capacity - Curl_uint_tbl_count(&multi->xfers);
+  unsigned int min_unused = CURLMAX(capacity >> 2, 4);
+
+  if(unused <= min_unused) {
+     /* make it a 64 multiple, since our bitsets frow by that and
+      * small (easy_multi) grows to at least 64 on first resize. */
+    unsigned int newsize = (((capacity + min_unused) + 63) / 64) * 64;
+    DEBUGASSERT(newsize > capacity);
+    /* Grow the bitsets first. Should one fail, we do not need
+     * to downsize the already resized ones. The sets continue
+     * to work properly when larger than the table, but not
+     * the other way around. */
+    if(Curl_uint_bset_resize(&multi->process, newsize) ||
+       Curl_uint_bset_resize(&multi->pending, newsize) ||
+       Curl_uint_bset_resize(&multi->msgsent, newsize) ||
+       Curl_uint_tbl_resize(&multi->xfers, newsize))
+      return CURLM_OUT_OF_MEMORY;
+    CURL_TRC_M(data, "increased xfer table size to %u", newsize);
+  }
+  /* Insert the easy into the table now that MUST have room for it */
+  if(!Curl_uint_tbl_add(&multi->xfers, data, &data->mid)) {
+    DEBUGASSERT(0);
+    return CURLM_OUT_OF_MEMORY;
+  }
+  return CURLM_OK;
+}
+
+
 CURLMcode curl_multi_add_handle(CURLM *m, CURL *d)
 {
   CURLMcode rc;
@@ -507,10 +393,15 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *d)
   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)
+       fine to start over and unmark the "deadness" of this handle.
+       This means only the admin handle MUST be present. */
+    if((Curl_uint_tbl_count(&multi->xfers) != 1) ||
+       !Curl_uint_tbl_contains(&multi->xfers, 0))
       return CURLM_ABORTED_BY_CALLBACK;
     multi->dead = FALSE;
+    Curl_uint_bset_clear(&multi->process);
+    Curl_uint_bset_clear(&multi->pending);
+    Curl_uint_bset_clear(&multi->msgsent);
   }
 
   if(data->multi_easy) {
@@ -520,6 +411,10 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *d)
     data->multi_easy = NULL;
   }
 
+  /* Insert the easy into the multi->xfers table, assigning it a `mid`. */
+  if(multi_xfers_add(multi, data))
+    return CURLM_OUT_OF_MEMORY;
+
   /* Initialize timeout list for this handle */
   Curl_llist_init(&data->state.timeoutlist, NULL);
 
@@ -549,20 +444,14 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *d)
   rc = Curl_update_timer(multi);
   if(rc) {
     data->multi = NULL; /* not anymore */
+    Curl_uint_tbl_remove(&multi->xfers, data->mid);
+    data->mid = UINT_MAX;
     return rc;
   }
 
   /* set the easy handle */
   multistate(data, MSTATE_INIT);
 
-  /* for multi interface connections, we share DNS cache automatically if the
-     easy handle's one is currently not set. */
-  if(!data->dns.hostcache ||
-     (data->dns.hostcachetype == HCACHE_NONE)) {
-    data->dns.hostcache = &multi->hostcache;
-    data->dns.hostcachetype = HCACHE_MULTI;
-  }
-
 #ifdef USE_LIBPSL
   /* Do the same for PSL. */
   if(data->share && (data->share->specifier & (1 << CURL_LOCK_DATA_PSL)))
@@ -571,23 +460,25 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *d)
     data->psl = &multi->psl;
 #endif
 
-  /* add the easy handle to the process list */
-  Curl_llist_append(&multi->process, data, &data->multi_queue);
-
-  /* increase the node-counter */
-  multi->num_easy++;
-
-  /* increase the alive-counter */
-  multi->num_alive++;
-
-  /* the identifier inside the multi instance */
-  data->mid = multi->next_easy_mid++;
-  if(multi->next_easy_mid <= 0)
-    multi->next_easy_mid = 0;
+  /* add the easy handle to the process set */
+  Curl_uint_bset_add(&multi->process, data->mid);
+  ++multi->xfers_alive;
 
   Curl_cpool_xfer_init(data);
   multi_warn_debug(multi, data);
 
+  /* The admin handle only ever has default timeouts set. To improve the
+     state somewhat we clone the timeouts from each added handle so that the
+     admin handle always has the same timeouts as the most recently added
+     easy handle. */
+  multi->admin->set.timeout = data->set.timeout;
+  multi->admin->set.server_response_timeout =
+    data->set.server_response_timeout;
+  multi->admin->set.no_signal = data->set.no_signal;
+
+  CURL_TRC_M(data, "added to multi, mid=%u, running=%u, total=%u",
+             data->mid, Curl_multi_xfers_running(multi),
+             Curl_uint_tbl_count(&multi->xfers));
   return CURLM_OK;
 }
 
@@ -619,20 +510,21 @@ static void multi_done_locked(struct connectdata *conn,
 
   Curl_detach_connection(data);
 
+  CURL_TRC_M(data, "multi_done_locked, in use=%u",
+             Curl_uint_spbset_count(&conn->xfers_attached));
   if(CONN_INUSE(conn)) {
     /* Stop if still used. */
-    DEBUGF(infof(data, "Connection still in use %zu, "
-                 "no more multi_done now!",
-                 Curl_llist_count(&conn->easyq)));
+    CURL_TRC_M(data, "Connection still in use %u, no more multi_done now!",
+               Curl_uint_spbset_count(&conn->xfers_attached));
     return;
   }
 
   data->state.done = TRUE; /* called just now! */
   data->state.recent_conn_id = conn->connection_id;
 
-  if(conn->dns_entry)
-    Curl_resolv_unlink(data, &conn->dns_entry); /* done with this */
-  Curl_hostcache_prune(data);
+  Curl_resolv_unlink(data, &data->state.dns[0]); /* done with this */
+  Curl_resolv_unlink(data, &data->state.dns[1]);
+  Curl_dnscache_prune(data);
 
   /* if data->set.reuse_forbid is TRUE, it means the libcurl client has
      forced us to close this connection. This is ignored for requests taking
@@ -660,14 +552,14 @@ static void multi_done_locked(struct connectdata *conn,
 #endif
      ) || conn->bits.close
        || (mdctx->premature && !Curl_conn_is_multiplex(conn, FIRSTSOCKET))) {
-    DEBUGF(infof(data, "multi_done, not reusing connection=%"
-                 FMT_OFF_T ", forbid=%d"
-                 ", close=%d, premature=%d, conn_multiplex=%d",
-                 conn->connection_id, data->set.reuse_forbid,
-                 conn->bits.close, mdctx->premature,
-                 Curl_conn_is_multiplex(conn, FIRSTSOCKET)));
+    CURL_TRC_M(data, "multi_done, not reusing connection=%"
+                     FMT_OFF_T ", forbid=%d"
+                     ", close=%d, premature=%d, conn_multiplex=%d",
+                     conn->connection_id, data->set.reuse_forbid,
+                     conn->bits.close, mdctx->premature,
+                     Curl_conn_is_multiplex(conn, FIRSTSOCKET));
     connclose(conn, "disconnecting");
-    Curl_cpool_disconnect(data, conn, mdctx->premature);
+    Curl_conn_terminate(data, conn, mdctx->premature);
   }
   else {
     /* the connection is no longer in use by any transfer */
@@ -703,21 +595,15 @@ static CURLcode multi_done(struct Curl_easy *data,
 
   memset(&mdctx, 0, sizeof(mdctx));
 
-#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
-  DEBUGF(infof(data, "multi_done[%s]: status: %d prem: %d done: %d",
-               multi_statename[data->mstate],
-               (int)status, (int)premature, data->state.done));
-#else
-  DEBUGF(infof(data, "multi_done: status: %d prem: %d done: %d",
-               (int)status, (int)premature, data->state.done));
-#endif
+  CURL_TRC_M(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 */
     return CURLE_OK;
 
-  /* Stop the resolver and free its own resources (but not dns_entry yet). */
-  Curl_resolver_kill(data);
+  /* Shut down any ongoing async resolver operation. */
+  Curl_async_shutdown(data);
 
   /* Cleanup possible redirect junk */
   Curl_safefree(data->req.newurl);
@@ -738,7 +624,7 @@ static CURLcode multi_done(struct Curl_easy *data,
   }
 
   /* this calls the protocol-specific function pointer previously set */
-  if(conn->handler->done)
+  if(conn->handler->done && (data->mstate >= MSTATE_PROTOCONNECT))
     result = conn->handler->done(data, status, premature);
   else
     result = status;
@@ -792,6 +678,7 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *d)
   struct Curl_llist_node *e;
   CURLMcode rc;
   bool removed_timer = FALSE;
+  unsigned int mid;
 
   /* First, make some basic checks that the CURLM handle is a good handle */
   if(!GOOD_MULTI_HANDLE(multi))
@@ -809,7 +696,11 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *d)
   if(data->multi != multi)
     return CURLM_BAD_EASY_HANDLE;
 
-  if(!multi->num_easy) {
+  if(data->mid == UINT_MAX) {
+    DEBUGASSERT(0);
+    return CURLM_INTERNAL_ERROR;
+  }
+  if(Curl_uint_tbl_get(&multi->xfers, data->mid) != data) {
     DEBUGASSERT(0);
     return CURLM_INTERNAL_ERROR;
   }
@@ -821,12 +712,6 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *d)
 
   /* If the 'state' is not INIT or COMPLETED, we might need to do something
      nice to put the easy_handle in a good known state when this returns. */
-  if(premature) {
-    /* this handle is "alive" so we need to count down the total number of
-       alive connections when this is removed */
-    multi->num_alive--;
-  }
-
   if(data->conn &&
      data->mstate > MSTATE_DO &&
      data->mstate < MSTATE_COMPLETED) {
@@ -849,30 +734,20 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *d)
      called. Do it after multi_done() in case that sets another time! */
   removed_timer = Curl_expire_clear(data);
 
-  /* the handle is in a list, remove it from whichever it is */
-  Curl_node_remove(&data->multi_queue);
-
-  if(data->dns.hostcachetype == HCACHE_MULTI) {
-    /* stop using the multi handle's DNS cache, *after* the possible
-       multi_done() call above */
-    data->dns.hostcache = NULL;
-    data->dns.hostcachetype = HCACHE_NONE;
-  }
+  /* If in `msgsent`, it was deducted from `multi->xfers_alive` already. */
+  if(!Curl_uint_bset_contains(&multi->msgsent, data->mid))
+    --multi->xfers_alive;
 
   Curl_wildcard_dtor(&data->wildcard);
 
-  /* change state without using multistate(), only to make singlesocket() do
-     what we want */
   data->mstate = MSTATE_COMPLETED;
 
-  /* This ignores the return code even in case of problems because there is
-     nothing more to do about that, here */
-  (void)singlesocket(multi, data); /* to let the application know what sockets
-                                      that vanish with this handle */
-
   /* Remove the association between the connection and the handle */
   Curl_detach_connection(data);
 
+  /* Tell event handling that this transfer is definitely going away */
+  Curl_multi_ev_xfer_done(multi, data);
+
   if(data->set.connect_only && !data->multi_easy) {
     /* This removes a handle that was part the multi interface that used
        CONNECT_ONLY, that connection is now left alive but since this handle
@@ -886,7 +761,7 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *d)
     curl_socket_t s;
     s = Curl_getconnectinfo(data, &c);
     if((s != CURL_SOCKET_BAD) && c) {
-      Curl_cpool_disconnect(data, c, TRUE);
+      Curl_conn_terminate(data, c, TRUE);
     }
   }
 
@@ -914,12 +789,19 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *d)
     }
   }
 
-  data->multi = NULL; /* clear the association to this multi handle */
-  data->mid = -1;
+  /* clear the association to this multi handle */
+  mid = data->mid;
+  DEBUGASSERT(Curl_uint_tbl_contains(&multi->xfers, mid));
+  Curl_uint_tbl_remove(&multi->xfers, mid);
+  Curl_uint_bset_remove(&multi->process, mid);
+  Curl_uint_bset_remove(&multi->pending, mid);
+  Curl_uint_bset_remove(&multi->msgsent, mid);
+  data->multi = NULL;
+  data->mid = UINT_MAX;
+  data->master_mid = UINT_MAX;
 
   /* NOTE NOTE NOTE
      We do not touch the easy handle here! */
-  multi->num_easy--; /* one less to care about now */
   process_pending_handles(multi);
 
   if(removed_timer) {
@@ -927,6 +809,10 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *d)
     if(rc)
       return rc;
   }
+
+  CURL_TRC_M(data, "removed from multi, mid=%u, running=%u, total=%u",
+             mid, Curl_multi_xfers_running(multi),
+             Curl_uint_tbl_count(&multi->xfers));
   return CURLM_OK;
 }
 
@@ -946,7 +832,9 @@ void Curl_detach_connection(struct Curl_easy *data)
 {
   struct connectdata *conn = data->conn;
   if(conn) {
-    Curl_node_remove(&data->conn_queue);
+    Curl_uint_spbset_remove(&conn->xfers_attached, data->mid);
+    if(Curl_uint_spbset_empty(&conn->xfers_attached))
+      conn->attached_multi = NULL;
   }
   data->conn = NULL;
 }
@@ -963,7 +851,12 @@ void Curl_attach_connection(struct Curl_easy *data,
   DEBUGASSERT(!data->conn);
   DEBUGASSERT(conn);
   data->conn = conn;
-  Curl_llist_append(&conn->easyq, data, &data->conn_queue);
+  Curl_uint_spbset_add(&conn->xfers_attached, data->mid);
+  /* all attached transfers must be from the same multi */
+  if(!conn->attached_multi)
+    conn->attached_multi = data->multi;
+  DEBUGASSERT(conn->attached_multi == data->multi);
+
   if(conn->handler && conn->handler->attach)
     conn->handler->attach(data, conn);
 }
@@ -1068,13 +961,15 @@ static int perform_getsock(struct Curl_easy *data, curl_socket_t *sock)
 
 /* Initializes `poll_set` with the current socket poll actions needed
  * for transfer `data`. */
-static void multi_getsock(struct Curl_easy *data,
-                          struct easy_pollset *ps)
+void Curl_multi_getsock(struct Curl_easy *data,
+                        struct easy_pollset *ps,
+                        const char *caller)
 {
   bool expect_sockets = TRUE;
-  /* The no connection case can happen when this is called from
-     curl_multi_remove_handle() => singlesocket() => multi_getsock().
-  */
+
+  /* If the transfer has no connection, this is fine. Happens when
+     called via curl_multi_remove_handle() => Curl_multi_ev_assess() =>
+     Curl_multi_getsock(). */
   Curl_pollset_reset(data, ps);
   if(!data->conn)
     return;
@@ -1098,30 +993,30 @@ static void multi_getsock(struct Curl_easy *data,
   case MSTATE_CONNECTING:
   case MSTATE_TUNNELING:
     Curl_pollset_add_socks(data, ps, connecting_getsock);
-    Curl_conn_adjust_pollset(data, ps);
+    Curl_conn_adjust_pollset(data, data->conn, ps);
     break;
 
   case MSTATE_PROTOCONNECT:
   case MSTATE_PROTOCONNECTING:
     Curl_pollset_add_socks(data, ps, protocol_getsock);
-    Curl_conn_adjust_pollset(data, ps);
+    Curl_conn_adjust_pollset(data, data->conn, ps);
     break;
 
   case MSTATE_DO:
   case MSTATE_DOING:
     Curl_pollset_add_socks(data, ps, doing_getsock);
-    Curl_conn_adjust_pollset(data, ps);
+    Curl_conn_adjust_pollset(data, data->conn, ps);
     break;
 
   case MSTATE_DOING_MORE:
     Curl_pollset_add_socks(data, ps, domore_getsock);
-    Curl_conn_adjust_pollset(data, ps);
+    Curl_conn_adjust_pollset(data, data->conn, ps);
     break;
 
   case MSTATE_DID: /* same as PERFORMING in regard to polling */
   case MSTATE_PERFORMING:
     Curl_pollset_add_socks(data, ps, perform_getsock);
-    Curl_conn_adjust_pollset(data, ps);
+    Curl_conn_adjust_pollset(data, data->conn, ps);
     break;
 
   case MSTATE_RATELIMITING:
@@ -1143,6 +1038,35 @@ static void multi_getsock(struct Curl_easy *data,
     break;
   }
 
+  switch(ps->num) {
+    case 0:
+      CURL_TRC_M(data, "%s pollset[], timeouts=%zu, paused %d/%d (r/w)",
+                 caller, Curl_llist_count(&data->state.timeoutlist),
+                 Curl_creader_is_paused(data), Curl_cwriter_is_paused(data));
+      break;
+    case 1:
+      CURL_TRC_M(data, "%s pollset[fd=%" FMT_SOCKET_T " %s%s], timeouts=%zu",
+                 caller, ps->sockets[0],
+                 (ps->actions[0] & CURL_POLL_IN) ? "IN" : "",
+                 (ps->actions[0] & CURL_POLL_OUT) ? "OUT" : "",
+                 Curl_llist_count(&data->state.timeoutlist));
+      break;
+    case 2:
+      CURL_TRC_M(data, "%s pollset[fd=%" FMT_SOCKET_T " %s%s, "
+                 "fd=%" FMT_SOCKET_T " %s%s], timeouts=%zu",
+                 caller, ps->sockets[0],
+                 (ps->actions[0] & CURL_POLL_IN) ? "IN" : "",
+                 (ps->actions[0] & CURL_POLL_OUT) ? "OUT" : "",
+                 ps->sockets[1],
+                 (ps->actions[1] & CURL_POLL_IN) ? "IN" : "",
+                 (ps->actions[1] & CURL_POLL_OUT) ? "OUT" : "",
+                 Curl_llist_count(&data->state.timeoutlist));
+      break;
+    default:
+      CURL_TRC_M(data, "%s pollset[fds=%u], timeouts=%zu",
+                 caller, ps->num, Curl_llist_count(&data->state.timeoutlist));
+      break;
+  }
   if(expect_sockets && !ps->num &&
      !Curl_llist_count(&data->state.timeoutlist) &&
      !Curl_cwriter_is_paused(data) && !Curl_creader_is_paused(data) &&
@@ -1164,9 +1088,8 @@ CURLMcode curl_multi_fdset(CURLM *m,
      Some easy handles may not have connected to the remote host yet,
      and then we must make sure that is done. */
   int this_max_fd = -1;
-  struct Curl_llist_node *e;
   struct Curl_multi *multi = m;
-  unsigned int i;
+  unsigned int i, mid;
   (void)exc_fd_set; /* not used */
 
   if(!GOOD_MULTI_HANDLE(multi))
@@ -1175,32 +1098,41 @@ CURLMcode curl_multi_fdset(CURLM *m,
   if(multi->in_callback)
     return CURLM_RECURSIVE_API_CALL;
 
-  for(e = Curl_llist_head(&multi->process); e; e = Curl_node_next(e)) {
-    struct Curl_easy *data = Curl_node_elem(e);
-
-    multi_getsock(data, &data->last_poll);
+  if(Curl_uint_bset_first(&multi->process, &mid)) {
+    do {
+      struct Curl_easy *data = Curl_multi_get_easy(multi, mid);
+      struct easy_pollset ps;
 
-    for(i = 0; i < data->last_poll.num; i++) {
-      if(!FDSET_SOCK(data->last_poll.sockets[i]))
-        /* pretend it does not exist */
+      if(!data) {
+        DEBUGASSERT(0);
         continue;
+      }
+
+      Curl_multi_getsock(data, &ps, "curl_multi_fdset");
+      for(i = 0; i < ps.num; i++) {
+        if(!FDSET_SOCK(ps.sockets[i]))
+          /* pretend it does not exist */
+          continue;
 #if defined(__DJGPP__)
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Warith-conversion"
 #endif
-      if(data->last_poll.actions[i] & CURL_POLL_IN)
-        FD_SET(data->last_poll.sockets[i], read_fd_set);
-      if(data->last_poll.actions[i] & CURL_POLL_OUT)
-        FD_SET(data->last_poll.sockets[i], write_fd_set);
+        if(ps.actions[i] & CURL_POLL_IN)
+          FD_SET(ps.sockets[i], read_fd_set);
+        if(ps.actions[i] & CURL_POLL_OUT)
+          FD_SET(ps.sockets[i], write_fd_set);
 #if defined(__DJGPP__)
 #pragma GCC diagnostic pop
 #endif
-      if((int)data->last_poll.sockets[i] > this_max_fd)
-        this_max_fd = (int)data->last_poll.sockets[i];
+        if((int)ps.sockets[i] > this_max_fd)
+          this_max_fd = (int)ps.sockets[i];
+      }
     }
+    while(Curl_uint_bset_next(&multi->process, mid, &mid));
   }
 
-  Curl_cpool_setfds(&multi->cpool, read_fd_set, write_fd_set, &this_max_fd);
+  Curl_cshutdn_setfds(&multi->cshutdn, multi->admin,
+                      read_fd_set, write_fd_set, &this_max_fd);
 
   *max_fd = this_max_fd;
 
@@ -1214,9 +1146,8 @@ CURLMcode curl_multi_waitfds(CURLM *m,
 {
   struct Curl_waitfds cwfds;
   CURLMcode result = CURLM_OK;
-  struct Curl_llist_node *e;
   struct Curl_multi *multi = m;
-  unsigned int need = 0;
+  unsigned int need = 0, mid;
 
   if(!ufds && (size || !fd_count))
     return CURLM_BAD_FUNCTION_ARGUMENT;
@@ -1228,13 +1159,22 @@ CURLMcode curl_multi_waitfds(CURLM *m,
     return CURLM_RECURSIVE_API_CALL;
 
   Curl_waitfds_init(&cwfds, ufds, size);
-  for(e = Curl_llist_head(&multi->process); e; e = Curl_node_next(e)) {
-    struct Curl_easy *data = Curl_node_elem(e);
-    multi_getsock(data, &data->last_poll);
-    need += Curl_waitfds_add_ps(&cwfds, &data->last_poll);
+  if(Curl_uint_bset_first(&multi->process, &mid)) {
+    do {
+      struct Curl_easy *data = Curl_multi_get_easy(multi, mid);
+      struct easy_pollset ps;
+      if(!data) {
+        DEBUGASSERT(0);
+        Curl_uint_bset_remove(&multi->process, mid);
+        continue;
+      }
+      Curl_multi_getsock(data, &ps, "curl_multi_waitfds");
+      need += Curl_waitfds_add_ps(&cwfds, &ps);
+    }
+    while(Curl_uint_bset_next(&multi->process, mid, &mid));
   }
 
-  need += Curl_cpool_add_waitfds(&multi->cpool, &cwfds);
+  need += Curl_cshutdn_add_waitfds(&multi->cshutdn, multi->admin, &cwfds);
 
   if(need != cwfds.n && ufds) {
     result = CURLM_OUT_OF_MEMORY;
@@ -1279,7 +1219,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
   struct curl_pollfds cpfds;
   unsigned int curl_nfds = 0; /* how many pfds are for curl transfers */
   CURLMcode result = CURLM_OK;
-  struct Curl_llist_node *e;
+  unsigned int mid;
 
 #ifdef USE_WINSOCK
   WSANETWORKEVENTS wsa_events;
@@ -1301,17 +1241,25 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
   Curl_pollfds_init(&cpfds, a_few_on_stack, NUM_POLLS_ON_STACK);
 
   /* Add the curl handles to our pollfds first */
-  for(e = Curl_llist_head(&multi->process); e; e = Curl_node_next(e)) {
-    struct Curl_easy *data = Curl_node_elem(e);
-
-    multi_getsock(data, &data->last_poll);
-    if(Curl_pollfds_add_ps(&cpfds, &data->last_poll)) {
-      result = CURLM_OUT_OF_MEMORY;
-      goto out;
+  if(Curl_uint_bset_first(&multi->process, &mid)) {
+    do {
+      struct Curl_easy *data = Curl_multi_get_easy(multi, mid);
+      struct easy_pollset ps;
+      if(!data) {
+        DEBUGASSERT(0);
+        Curl_uint_bset_remove(&multi->process, mid);
+        continue;
+      }
+      Curl_multi_getsock(data, &ps, "multi_wait");
+      if(Curl_pollfds_add_ps(&cpfds, &ps)) {
+        result = CURLM_OUT_OF_MEMORY;
+        goto out;
+      }
     }
+    while(Curl_uint_bset_next(&multi->process, mid, &mid));
   }
 
-  if(Curl_cpool_add_pollfds(&multi->cpool, &cpfds)) {
+  if(Curl_cshutdn_add_pollfds(&multi->cshutdn, multi->admin, &cpfds)) {
     result = CURLM_OUT_OF_MEMORY;
     goto out;
   }
@@ -1440,23 +1388,14 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
 #ifdef USE_WINSOCK
       /* Count up all our own sockets that had activity,
          and remove them from the event. */
-      if(curl_nfds) {
-        for(e = Curl_llist_head(&multi->process); e && !result;
-            e = Curl_node_next(e)) {
-          struct Curl_easy *data = Curl_node_elem(e);
-
-          for(i = 0; i < data->last_poll.num; i++) {
-            wsa_events.lNetworkEvents = 0;
-            if(WSAEnumNetworkEvents(data->last_poll.sockets[i], NULL,
-                                    &wsa_events) == 0) {
-              if(ret && !pollrc && wsa_events.lNetworkEvents)
-                retcode++;
-            }
-            WSAEventSelect(data->last_poll.sockets[i], multi->wsa_event, 0);
-          }
+      for(i = 0; i < curl_nfds; ++i) {
+        wsa_events.lNetworkEvents = 0;
+        if(WSAEnumNetworkEvents(cpfds.pfds[i].fd, NULL, &wsa_events) == 0) {
+          if(ret && !pollrc && wsa_events.lNetworkEvents)
+            retcode++;
         }
+        WSAEventSelect(cpfds.pfds[i].fd, multi->wsa_event, 0);
       }
-
       WSAResetEvent(multi->wsa_event);
 #else
 #ifdef ENABLE_WAKEUP
@@ -1471,7 +1410,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
                when there is no more data, breaking the loop. */
             nread = wakeup_read(multi->wakeup_pair[0], buf, sizeof(buf));
             if(nread <= 0) {
-              if(nread < 0 && EINTR == SOCKERRNO)
+              if(nread < 0 && SOCKEINTR == SOCKERRNO)
                 continue;
               break;
             }
@@ -1572,11 +1511,11 @@ CURLMcode curl_multi_wakeup(CURLM *m)
         int err = SOCKERRNO;
         int return_success;
 #ifdef USE_WINSOCK
-        return_success = WSAEWOULDBLOCK == err;
+        return_success = SOCKEWOULDBLOCK == err;
 #else
-        if(EINTR == err)
+        if(SOCKEINTR == err)
           continue;
-        return_success = EWOULDBLOCK == err || EAGAIN == err;
+        return_success = SOCKEWOULDBLOCK == err || EAGAIN == err;
 #endif
         if(!return_success)
           return CURLM_WAKEUP_FAILURE;
@@ -1629,10 +1568,15 @@ CURLMcode Curl_multi_add_perform(struct Curl_multi *multi,
   rc = curl_multi_add_handle(multi, data);
   if(!rc) {
     struct SingleRequest *k = &data->req;
+    CURLcode result;
 
     /* pass in NULL for 'conn' here since we do not want to init the
        connection, only this transfer */
-    Curl_init_do(data, NULL);
+    result = Curl_init_do(data, NULL);
+    if(result) {
+      curl_multi_remove_handle(multi, data);
+      return CURLM_INTERNAL_ERROR;
+    }
 
     /* take this handle to the perform state right away */
     multistate(data, MSTATE_PERFORMING);
@@ -1697,22 +1641,22 @@ static bool multi_handle_timeout(struct Curl_easy *data,
       since = data->progress.t_startop;
     if(data->mstate == MSTATE_RESOLVING)
       failf(data, "Resolving timed out after %" FMT_TIMEDIFF_T
-            " milliseconds", Curl_timediff(*now, since));
+            " milliseconds", curlx_timediff(*now, since));
     else if(data->mstate == MSTATE_CONNECTING)
       failf(data, "Connection timed out after %" FMT_TIMEDIFF_T
-            " milliseconds", Curl_timediff(*now, since));
+            " milliseconds", curlx_timediff(*now, since));
     else {
       struct SingleRequest *k = &data->req;
       if(k->size != -1) {
         failf(data, "Operation timed out after %" FMT_TIMEDIFF_T
               " milliseconds with %" FMT_OFF_T " out of %"
               FMT_OFF_T " bytes received",
-              Curl_timediff(*now, since), k->bytecount, k->size);
+              curlx_timediff(*now, since), k->bytecount, k->size);
       }
       else {
         failf(data, "Operation timed out after %" FMT_TIMEDIFF_T
               " milliseconds with %" FMT_OFF_T " bytes received",
-              Curl_timediff(*now, since), k->bytecount);
+              curlx_timediff(*now, since), k->bytecount);
       }
     }
     *result = CURLE_OPERATION_TIMEDOUT;
@@ -1910,6 +1854,7 @@ static CURLMcode state_performing(struct Curl_easy *data,
       data->req.done = TRUE;
     }
   }
+#ifndef CURL_DISABLE_HTTP
   else if((CURLE_HTTP2_STREAM == result) &&
           Curl_h2_http_1_1_error(data)) {
     CURLcode ret = Curl_retry_request(data, &newurl);
@@ -1917,7 +1862,8 @@ static CURLMcode state_performing(struct Curl_easy *data,
     if(!ret) {
       infof(data, "Downgrades to HTTP/1.1");
       streamclose(data->conn, "Disconnect HTTP/2 for HTTP/1");
-      data->state.httpwant = CURL_HTTP_VERSION_1_1;
+      data->state.http_neg.wanted = CURL_HTTP_V1x;
+      data->state.http_neg.allowed = CURL_HTTP_V1x;
       /* clear the error message bit too as we ignore the one we got */
       data->state.errorbuf = FALSE;
       if(!newurl)
@@ -1932,6 +1878,7 @@ static CURLMcode state_performing(struct Curl_easy *data,
     else
       result = ret;
   }
+#endif
 
   if(result) {
     /*
@@ -2207,43 +2154,18 @@ static CURLMcode state_resolving(struct Curl_multi *multi,
                                  CURLcode *resultp)
 {
   struct Curl_dns_entry *dns = NULL;
-  struct connectdata *conn = data->conn;
-  const char *hostname;
-  CURLcode result = CURLE_OK;
+  CURLcode result;
   CURLMcode rc = CURLM_OK;
 
-  DEBUGASSERT(conn);
-#ifndef CURL_DISABLE_PROXY
-  if(conn->bits.httpproxy)
-    hostname = conn->http_proxy.host.name;
-  else
-#endif
-    if(conn->bits.conn_to_host)
-      hostname = conn->conn_to_host.name;
-    else
-      hostname = conn->host.name;
-
-  /* check if we have the name resolved by now */
-  dns = Curl_fetch_addr(data, hostname, conn->primary.remote_port);
-
-  if(dns) {
-#ifdef CURLRES_ASYNCH
-    data->state.async.dns = dns;
-    data->state.async.done = TRUE;
-#endif
-    result = CURLE_OK;
-    infof(data, "Hostname '%s' was found in DNS cache", hostname);
-  }
-
-  if(!dns)
-    result = Curl_resolv_check(data, &dns);
-
+  result = Curl_resolv_check(data, &dns);
+  CURL_TRC_DNS(data, "Curl_resolv_check() -> %d, %s",
+               result, dns ? "found" : "missing");
   /* Update sockets here, because the socket(s) may have been closed and the
      application thus needs to be told, even if it is likely that the same
      socket(s) will again be used further 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. */
-  rc = singlesocket(multi, data);
+  rc = Curl_multi_ev_assess_xfer(multi, data);
   if(rc)
     return rc;
 
@@ -2251,7 +2173,7 @@ static CURLMcode state_resolving(struct Curl_multi *multi,
     bool connected;
     /* Perform the next step in the connection phase, and then move on to the
        WAITCONNECT state */
-    result = Curl_once_resolved(data, &connected);
+    result = Curl_once_resolved(data, dns, &connected);
 
     if(result)
       /* if Curl_once_resolved() returns failure, the connection struct is
@@ -2291,10 +2213,9 @@ static CURLMcode state_connect(struct Curl_multi *multi,
     /* There was no connection available. We will go to the pending state and
        wait for an available connection. */
     multistate(data, MSTATE_PENDING);
-    /* unlink from process list */
-    Curl_node_remove(&data->multi_queue);
-    /* add handle to pending list */
-    Curl_llist_append(&multi->pending, data, &data->multi_queue);
+    /* move from process to pending set */
+    Curl_uint_bset_remove(&multi->process, data->mid);
+    Curl_uint_bset_add(&multi->pending, data->mid);
     *resultp = CURLE_OK;
     return rc;
   }
@@ -2362,7 +2283,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
     rc = CURLM_OK;
 
     if(multi_ischanged(multi, TRUE)) {
-      DEBUGF(infof(data, "multi changed, check CONNECT_PEND queue"));
+      CURL_TRC_M(data, "multi changed, check CONNECT_PEND queue");
       process_pending_handles(multi); /* multiplexed */
     }
 
@@ -2618,7 +2539,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
     case MSTATE_PENDING:
     case MSTATE_MSGSENT:
       /* handles in these states should NOT be in this list */
-      DEBUGASSERT(0);
       break;
 
     default:
@@ -2663,7 +2583,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
                We do not have to do this in every case block above where a
                failure is detected */
             Curl_detach_connection(data);
-            Curl_cpool_disconnect(data, conn, dead_connection);
+            Curl_conn_terminate(data, conn, dead_connection);
           }
         }
         else if(data->mstate == MSTATE_CONNECT) {
@@ -2690,9 +2610,22 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
     }
 
     if(MSTATE_COMPLETED == data->mstate) {
-      if(data->set.fmultidone) {
-        /* signal via callback instead */
-        data->set.fmultidone(data, result);
+      if(data->master_mid != UINT_MAX) {
+        /* A sub transfer, not for msgsent to application */
+        struct Curl_easy *mdata;
+
+        CURL_TRC_M(data, "sub xfer done for master %u", data->master_mid);
+        mdata = Curl_multi_get_easy(multi, data->master_mid);
+        if(mdata) {
+          if(mdata->sub_xfer_done)
+            mdata->sub_xfer_done(mdata, data, result);
+          else
+            CURL_TRC_M(data, "master easy %u without sub_xfer_done callback.",
+                       data->master_mid);
+        }
+        else {
+          CURL_TRC_M(data, "master easy %u already gone.", data->master_mid);
+        }
       }
       else {
         /* now fill in the Curl_message with this info */
@@ -2707,10 +2640,11 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
       }
       multistate(data, MSTATE_MSGSENT);
 
-      /* unlink from the process list */
-      Curl_node_remove(&data->multi_queue);
-      /* add this handle msgsent list */
-      Curl_llist_append(&multi->msgsent, data, &data->multi_queue);
+      /* remove from the other sets, add to msgsent */
+      Curl_uint_bset_remove(&multi->process, data->mid);
+      Curl_uint_bset_remove(&multi->pending, data->mid);
+      Curl_uint_bset_add(&multi->msgsent, data->mid);
+      --multi->xfers_alive;
       return CURLM_OK;
     }
   } while((rc == CURLM_CALL_MULTI_PERFORM) || multi_ischanged(multi, FALSE));
@@ -2724,10 +2658,9 @@ CURLMcode curl_multi_perform(CURLM *m, int *running_handles)
 {
   CURLMcode returncode = CURLM_OK;
   struct Curl_tree *t = NULL;
-  struct curltime now = Curl_now();
-  struct Curl_llist_node *e;
-  struct Curl_llist_node *n = NULL;
+  struct curltime now = curlx_now();
   struct Curl_multi *multi = m;
+  unsigned int mid;
   SIGPIPE_VARIABLE(pipe_st);
 
   if(!GOOD_MULTI_HANDLE(multi))
@@ -2737,27 +2670,30 @@ CURLMcode curl_multi_perform(CURLM *m, int *running_handles)
     return CURLM_RECURSIVE_API_CALL;
 
   sigpipe_init(&pipe_st);
-  for(e = Curl_llist_head(&multi->process); e; e = n) {
-    struct Curl_easy *data = Curl_node_elem(e);
-    CURLMcode result;
-    /* Do the loop and only alter the signal ignore state if the next handle
-       has a different NO_SIGNAL state than the previous */
-
-    /* the current node might be unlinked in multi_runsingle(), get the next
-       pointer now */
-    n = Curl_node_next(e);
-
-    if(data != multi->cpool.idata) {
-      /* connection pool handle is processed below */
-      sigpipe_apply(data, &pipe_st);
-      result = multi_runsingle(multi, &now, data);
-      if(result)
-        returncode = result;
+  if(Curl_uint_bset_first(&multi->process, &mid)) {
+    CURL_TRC_M(multi->admin, "multi_perform(running=%u)",
+               Curl_multi_xfers_running(multi));
+    do {
+      struct Curl_easy *data = Curl_multi_get_easy(multi, mid);
+      CURLMcode result;
+      if(!data) {
+        DEBUGASSERT(0);
+        Curl_uint_bset_remove(&multi->process, mid);
+        continue;
+      }
+      if(data != multi->admin) {
+        /* admin handle is processed below */
+        sigpipe_apply(data, &pipe_st);
+        result = multi_runsingle(multi, &now, data);
+        if(result)
+          returncode = result;
+      }
     }
+    while(Curl_uint_bset_next(&multi->process, mid, &mid));
   }
 
-  sigpipe_apply(multi->cpool.idata, &pipe_st);
-  Curl_cpool_multi_perform(multi);
+  sigpipe_apply(multi->admin, &pipe_st);
+  Curl_cshutdn_perform(&multi->cshutdn, multi->admin, CURL_SOCKET_TIMEOUT);
   sigpipe_restore(&pipe_st);
 
   if(multi_ischanged(m, TRUE))
@@ -2790,8 +2726,10 @@ CURLMcode curl_multi_perform(CURLM *m, int *running_handles)
     }
   } while(t);
 
-  if(running_handles)
-    *running_handles = (int)multi->num_alive;
+  if(running_handles) {
+    unsigned int running = Curl_multi_xfers_running(multi);
+    *running_handles = (running < INT_MAX) ? (int)running : INT_MAX;
+  }
 
   if(CURLM_OK >= returncode)
     returncode = Curl_update_timer(multi);
@@ -2799,69 +2737,66 @@ CURLMcode curl_multi_perform(CURLM *m, int *running_handles)
   return returncode;
 }
 
-/* unlink_all_msgsent_handles() moves all nodes back from the msgsent list to
-   the process list */
-static void unlink_all_msgsent_handles(struct Curl_multi *multi)
-{
-  struct Curl_llist_node *e;
-  for(e = Curl_llist_head(&multi->msgsent); e; e = Curl_node_next(e)) {
-    struct Curl_easy *data = Curl_node_elem(e);
-    if(data) {
-      DEBUGASSERT(data->mstate == MSTATE_MSGSENT);
-      Curl_node_remove(&data->multi_queue);
-      /* put it into the process list */
-      Curl_llist_append(&multi->process, data, &data->multi_queue);
-    }
-  }
-}
-
 CURLMcode curl_multi_cleanup(CURLM *m)
 {
   struct Curl_multi *multi = m;
   if(GOOD_MULTI_HANDLE(multi)) {
-    struct Curl_llist_node *e;
-    struct Curl_llist_node *n;
+    void *entry;
+    unsigned int mid;
     if(multi->in_callback)
       return CURLM_RECURSIVE_API_CALL;
 
-    /* move the pending and msgsent entries back to process
-       so that there is just one list to iterate over */
-    unlink_all_msgsent_handles(multi);
-    process_pending_handles(multi);
+    /* First remove all remaining easy handles,
+     * close internal ones. admin handle is special */
+    if(Curl_uint_tbl_first(&multi->xfers, &mid, &entry)) {
+      do {
+        struct Curl_easy *data = entry;
+        if(!GOOD_EASY_HANDLE(data))
+          return CURLM_BAD_HANDLE;
 
-    /* First remove all remaining easy handles */
-    for(e = Curl_llist_head(&multi->process); e; e = n) {
-      struct Curl_easy *data = Curl_node_elem(e);
-
-      if(!GOOD_EASY_HANDLE(data))
-        return CURLM_BAD_HANDLE;
-
-      n = Curl_node_next(e);
-      if(!data->state.done && data->conn)
-        /* if DONE was never called for this handle */
-        (void)multi_done(data, CURLE_OK, TRUE);
-      if(data->dns.hostcachetype == HCACHE_MULTI) {
-        /* clear out the usage of the shared DNS cache */
-        Curl_hostcache_clean(data, data->dns.hostcache);
-        data->dns.hostcache = NULL;
-        data->dns.hostcachetype = HCACHE_NONE;
-      }
+#ifdef DEBUGBUILD
+        if(mid != data->mid) {
+          CURL_TRC_M(data, "multi_cleanup: still present with mid=%u, "
+                  "but unexpected data->mid=%u\n", mid, data->mid);
+          DEBUGASSERT(0);
+        }
+#endif
 
-      data->multi = NULL; /* clear the association */
+        if(data == multi->admin)
+          continue;
+
+        if(!data->state.done && data->conn)
+          /* if DONE was never called for this handle */
+          (void)multi_done(data, CURLE_OK, TRUE);
+
+        data->multi = NULL; /* clear the association */
+        Curl_uint_tbl_remove(&multi->xfers, mid);
+        data->mid = UINT_MAX;
 
 #ifdef USE_LIBPSL
-      if(data->psl == &multi->psl)
-        data->psl = NULL;
+        if(data->psl == &multi->psl)
+          data->psl = NULL;
 #endif
+        if(data->state.internal)
+          Curl_close(&data);
+      }
+      while(Curl_uint_tbl_next(&multi->xfers, mid, &mid, &entry));
     }
 
     Curl_cpool_destroy(&multi->cpool);
+    Curl_cshutdn_destroy(&multi->cshutdn, multi->admin);
+    if(multi->admin) {
+      CURL_TRC_M(multi->admin, "multi_cleanup, closing admin handle, done");
+      multi->admin->multi = NULL;
+      Curl_uint_tbl_remove(&multi->xfers, multi->admin->mid);
+      Curl_close(&multi->admin);
+    }
 
     multi->magic = 0; /* not good anymore */
 
-    sockhash_destroy(&multi->sockhash);
+    Curl_multi_ev_cleanup(multi);
     Curl_hash_destroy(&multi->proto_hash);
-    Curl_hash_destroy(&multi->hostcache);
+    Curl_dnscache_destroy(&multi->dnscache);
     Curl_psl_destroy(&multi->psl);
     Curl_ssl_scache_destroy(multi->ssl_scache);
 
@@ -2877,6 +2812,16 @@ CURLMcode curl_multi_cleanup(CURLM *m)
 #endif
 
     multi_xfer_bufs_free(multi);
+#ifdef DEBUGBUILD
+    if(Curl_uint_tbl_count(&multi->xfers)) {
+      multi_xfer_tbl_dump(multi);
+      DEBUGASSERT(0);
+    }
+#endif
+    Curl_uint_bset_destroy(&multi->process);
+    Curl_uint_bset_destroy(&multi->pending);
+    Curl_uint_bset_destroy(&multi->msgsent);
+    Curl_uint_tbl_destroy(&multi->xfers);
     free(multi);
 
     return CURLM_OK;
@@ -2922,233 +2867,14 @@ CURLMsg *curl_multi_info_read(CURLM *m, int *msgs_in_queue)
   return NULL;
 }
 
-/*
- * singlesocket() checks what sockets we deal with and their "action state"
- * and if we have a different state in any of those sockets from last time we
- * call the callback accordingly.
- */
-static CURLMcode singlesocket(struct Curl_multi *multi,
-                              struct Curl_easy *data)
-{
-  struct easy_pollset cur_poll;
-  CURLMcode mresult;
-
-  /* Fill in the 'current' struct with the state as it is now: what sockets to
-     supervise and for what actions */
-  multi_getsock(data, &cur_poll);
-  mresult = Curl_multi_pollset_ev(multi, data, &cur_poll, &data->last_poll);
-
-  if(!mresult) /* Remember for next time */
-    memcpy(&data->last_poll, &cur_poll, sizeof(cur_poll));
-  return mresult;
-}
-
-CURLMcode Curl_multi_pollset_ev(struct Curl_multi *multi,
-                                struct Curl_easy *data,
-                                struct easy_pollset *ps,
-                                struct easy_pollset *last_ps)
-{
-  unsigned int i;
-  struct Curl_sh_entry *entry;
-  curl_socket_t s;
-  int rc;
-
-  /* We have 0 .. N sockets already and we get to know about the 0 .. M
-     sockets we should have from now on. Detect the differences, remove no
-     longer supervised ones and add new ones */
-
-  /* walk over the sockets we got right now */
-  for(i = 0; i < ps->num; i++) {
-    unsigned char cur_action = ps->actions[i];
-    unsigned char last_action = 0;
-    int comboaction;
-
-    s = ps->sockets[i];
-
-    /* get it from the hash */
-    entry = sh_getentry(&multi->sockhash, s);
-    if(entry) {
-      /* check if new for this transfer */
-      unsigned int j;
-      for(j = 0; j < last_ps->num; j++) {
-        if(s == last_ps->sockets[j]) {
-          last_action = last_ps->actions[j];
-          break;
-        }
-      }
-    }
-    else {
-      /* this is a socket we did not have before, add it to the hash! */
-      entry = sh_addentry(&multi->sockhash, s);
-      if(!entry)
-        /* fatal */
-        return CURLM_OUT_OF_MEMORY;
-    }
-    if(last_action && (last_action != cur_action)) {
-      /* Socket was used already, but different action now */
-      if(last_action & CURL_POLL_IN) {
-        DEBUGASSERT(entry->readers);
-        entry->readers--;
-      }
-      if(last_action & CURL_POLL_OUT) {
-        DEBUGASSERT(entry->writers);
-        entry->writers--;
-      }
-      if(cur_action & CURL_POLL_IN) {
-        entry->readers++;
-      }
-      if(cur_action & CURL_POLL_OUT)
-        entry->writers++;
-    }
-    else if(!last_action &&
-            !Curl_hash_pick(&entry->transfers, (char *)&data, /* hash key */
-                            sizeof(struct Curl_easy *))) {
-      DEBUGASSERT(entry->users < 100000); /* detect weird values */
-      /* a new transfer using this socket */
-      entry->users++;
-      if(cur_action & CURL_POLL_IN)
-        entry->readers++;
-      if(cur_action & CURL_POLL_OUT)
-        entry->writers++;
-      /* add 'data' to the transfer hash on this socket! */
-      if(!Curl_hash_add(&entry->transfers, (char *)&data, /* hash key */
-                        sizeof(struct Curl_easy *), data)) {
-        Curl_hash_destroy(&entry->transfers);
-        return CURLM_OUT_OF_MEMORY;
-      }
-    }
-
-    comboaction = (entry->writers ? CURL_POLL_OUT : 0) |
-                   (entry->readers ? CURL_POLL_IN : 0);
-
-    /* socket existed before and has the same action set as before */
-    if(last_action && ((int)entry->action == comboaction))
-      /* same, continue */
-      continue;
-
-    if(multi->socket_cb) {
-      set_in_callback(multi, TRUE);
-      rc = multi->socket_cb(data, s, comboaction, multi->socket_userp,
-                            entry->socketp);
-
-      set_in_callback(multi, FALSE);
-      if(rc == -1) {
-        multi->dead = TRUE;
-        return CURLM_ABORTED_BY_CALLBACK;
-      }
-    }
-
-    /* store the current action state */
-    entry->action = (unsigned int)comboaction;
-  }
-
-  /* Check for last_poll.sockets that no longer appear in ps->sockets.
-   * Need to remove the easy handle from the multi->sockhash->transfers and
-   * remove multi->sockhash entry when this was the last transfer */
-  for(i = 0; i < last_ps->num; i++) {
-    unsigned int j;
-    bool stillused = FALSE;
-    s = last_ps->sockets[i];
-    for(j = 0; j < ps->num; j++) {
-      if(s == ps->sockets[j]) {
-        /* this is still supervised */
-        stillused = TRUE;
-        break;
-      }
-    }
-    if(stillused)
-      continue;
-
-    entry = sh_getentry(&multi->sockhash, s);
-    /* if this is NULL here, the socket has been closed and notified so
-       already by Curl_multi_closed() */
-    if(entry) {
-      unsigned char oldactions = last_ps->actions[i];
-      /* this socket has been removed. Decrease user count */
-      DEBUGASSERT(entry->users);
-      entry->users--;
-      if(oldactions & CURL_POLL_OUT)
-        entry->writers--;
-      if(oldactions & CURL_POLL_IN)
-        entry->readers--;
-      if(!entry->users) {
-        bool dead = FALSE;
-        if(multi->socket_cb) {
-          set_in_callback(multi, TRUE);
-          rc = multi->socket_cb(data, s, CURL_POLL_REMOVE,
-                                multi->socket_userp, entry->socketp);
-          set_in_callback(multi, FALSE);
-          if(rc == -1)
-            dead = TRUE;
-        }
-        sh_delentry(entry, &multi->sockhash, s);
-        if(dead) {
-          multi->dead = TRUE;
-          return CURLM_ABORTED_BY_CALLBACK;
-        }
-      }
-      else {
-        /* still users, but remove this handle as a user of this socket */
-        if(Curl_hash_delete(&entry->transfers, (char *)&data,
-                            sizeof(struct Curl_easy *))) {
-          DEBUGASSERT(NULL);
-        }
-      }
-    }
-  } /* for loop over num */
-
-  return CURLM_OK;
-}
-
-CURLcode Curl_updatesocket(struct Curl_easy *data)
-{
-  if(singlesocket(data->multi, data))
-    return CURLE_ABORTED_BY_CALLBACK;
-  return CURLE_OK;
-}
 
-
-/*
- * Curl_multi_closed()
- *
- * Used by the connect code to tell the multi_socket code that one of the
- * sockets we were using is about to be closed. This function will then
- * remove it from the sockethash for this handle to make the multi_socket API
- * behave properly, especially for the case when libcurl will create another
- * socket again and it gets the same file descriptor number.
- */
-
-void Curl_multi_closed(struct Curl_easy *data, curl_socket_t s)
+void Curl_multi_will_close(struct Curl_easy *data, curl_socket_t s)
 {
   if(data) {
-    /* if there is still an easy handle associated with this connection */
     struct Curl_multi *multi = data->multi;
-    DEBUGF(infof(data, "Curl_multi_closed, fd=%" FMT_SOCKET_T
-                 " multi is %p", s, (void *)multi));
     if(multi) {
-      /* this is set if this connection is part of a handle that is added to
-         a multi handle, and only then this is necessary */
-      struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s);
-
-      DEBUGF(infof(data, "Curl_multi_closed, fd=%" FMT_SOCKET_T
-                   " entry is %p", s, (void *)entry));
-      if(entry) {
-        int rc = 0;
-        if(multi->socket_cb) {
-          set_in_callback(multi, TRUE);
-          rc = multi->socket_cb(data, s, CURL_POLL_REMOVE,
-                                multi->socket_userp, entry->socketp);
-          set_in_callback(multi, FALSE);
-        }
-
-        /* 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;
-      }
+      CURL_TRC_M(data, "Curl_multi_will_close fd=%" FMT_SOCKET_T, s);
+      Curl_multi_ev_socket_done(multi, data, s);
     }
   }
 }
@@ -3179,7 +2905,7 @@ static CURLMcode add_next_timeout(struct curltime now,
   for(e = Curl_llist_head(list); e;) {
     struct Curl_llist_node *n = Curl_node_next(e);
     struct time_node *node = Curl_node_elem(e);
-    timediff_t diff = Curl_timediff_us(node->time, now);
+    timediff_t diff = curlx_timediff_us(node->time, now);
     if(diff <= 0)
       /* remove outdated entry */
       Curl_node_remove(e);
@@ -3240,7 +2966,7 @@ static CURLMcode multi_run_expired(struct multi_run_ctx *mrc)
       continue;
 
     (void)add_next_timeout(mrc->now, multi, data);
-    if(data == multi->cpool.idata) {
+    if(data == multi->admin) {
       mrc->run_cpool = TRUE;
       continue;
     }
@@ -3250,9 +2976,8 @@ static CURLMcode multi_run_expired(struct multi_run_ctx *mrc)
     result = multi_runsingle(multi, &mrc->now, data);
 
     if(CURLM_OK >= result) {
-      /* get the socket(s) and check if the state has been changed since
-         last */
-      result = singlesocket(multi, data);
+      /* reassess event handling of data */
+      result = Curl_multi_ev_assess_xfer(multi, data);
       if(result)
         goto out;
     }
@@ -3268,66 +2993,28 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
                               int *running_handles)
 {
   CURLMcode result = CURLM_OK;
-  struct Curl_easy *data = NULL;
   struct multi_run_ctx mrc;
 
   (void)ev_bitmask;
   memset(&mrc, 0, sizeof(mrc));
   mrc.multi = multi;
-  mrc.now = Curl_now();
+  mrc.now = curlx_now();
   sigpipe_init(&mrc.pipe_st);
 
   if(checkall) {
-    struct Curl_llist_node *e;
     /* *perform() deals with running_handles on its own */
     result = curl_multi_perform(multi, running_handles);
 
-    /* walk through each easy handle and do the socket state change magic
-       and callbacks */
     if(result != CURLM_BAD_HANDLE) {
-      for(e = Curl_llist_head(&multi->process); e && !result;
-          e = Curl_node_next(e)) {
-        result = singlesocket(multi, Curl_node_elem(e));
-      }
+      /* Reassess event status of all active transfers */
+      result = Curl_multi_ev_assess_xfer_bset(multi, &multi->process);
     }
     mrc.run_cpool = TRUE;
     goto out;
   }
 
   if(s != CURL_SOCKET_TIMEOUT) {
-    struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s);
-
-    if(!entry) {
-      /* Unmatched socket, we cannot act on it but we ignore this fact. In
-         real-world tests it has been proved that libevent can in fact give
-         the application actions even though the socket was just previously
-         asked to get removed, so thus we better survive stray socket actions
-         and just move on. */
-      /* The socket might come from a connection that is being shut down
-       * by the multi's connection pool. */
-      Curl_cpool_multi_socket(multi, s, ev_bitmask);
-    }
-    else {
-      struct Curl_hash_iterator iter;
-      struct Curl_hash_element *he;
-
-      /* the socket can be shared by many transfers, iterate */
-      Curl_hash_start_iterate(&entry->transfers, &iter);
-      for(he = Curl_hash_next_element(&iter); he;
-          he = Curl_hash_next_element(&iter)) {
-        data = (struct Curl_easy *)he->ptr;
-        DEBUGASSERT(data);
-        DEBUGASSERT(data->magic == CURLEASY_MAGIC_NUMBER);
-
-        if(data == multi->cpool.idata)
-          mrc.run_cpool = TRUE;
-        else {
-          /* Expire with out current now, so we will get it below when
-           * asking the splaytree for expired transfers. */
-          expire_ex(data, &mrc.now, 0, EXPIRE_RUN_NOW);
-        }
-      }
-    }
+    Curl_multi_ev_expire_xfers(multi, s, &mrc.now, &mrc.run_cpool);
   }
   else {
     /* Asked to run due to time-out. Clear the 'last_expire_ts' variable to
@@ -3336,6 +3023,7 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
        handles the case when the application asks libcurl to run the timeout
        prematurely. */
     memset(&multi->last_expire_ts, 0, sizeof(multi->last_expire_ts));
+    mrc.run_cpool = TRUE;
   }
 
   result = multi_run_expired(&mrc);
@@ -3348,22 +3036,24 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
      * to set a 0 timeout and call us again, we run them here.
      * Do that only once or it might be unfair to transfers on other
      * sockets. */
-    mrc.now = Curl_now();
+    mrc.now = curlx_now();
     result = multi_run_expired(&mrc);
   }
 
 out:
   if(mrc.run_cpool) {
-    sigpipe_apply(multi->cpool.idata, &mrc.pipe_st);
-    Curl_cpool_multi_perform(multi);
+    sigpipe_apply(multi->admin, &mrc.pipe_st);
+    Curl_cshutdn_perform(&multi->cshutdn, multi->admin, s);
   }
   sigpipe_restore(&mrc.pipe_st);
 
   if(multi_ischanged(multi, TRUE))
     process_pending_handles(multi);
 
-  if(running_handles)
-    *running_handles = (int)multi->num_alive;
+  if(running_handles) {
+    unsigned int running = Curl_multi_xfers_running(multi);
+    *running_handles = (running < INT_MAX) ? (int)running : INT_MAX;
+  }
 
   if(CURLM_OK >= result)
     result = Curl_update_timer(multi);
@@ -3488,20 +3178,20 @@ static CURLMcode multi_timeout(struct Curl_multi *multi,
 
   if(multi->timetree) {
     /* we have a tree of expire times */
-    struct curltime now = Curl_now();
+    struct curltime now = curlx_now();
 
     /* splay the lowest to the bottom */
     multi->timetree = Curl_splay(tv_zero, multi->timetree);
-    /* this will not return NULL from a non-emtpy tree, but some compilers
+    /* this will not return NULL from a non-empty tree, but some compilers
      * are not convinced of that. Analyzers are hard. */
     *expire_time = multi->timetree ? multi->timetree->key : tv_zero;
 
     /* 'multi->timetree' will be non-NULL here but the compilers sometimes
        yell at us if we assume so */
     if(multi->timetree &&
-       Curl_timediff_us(multi->timetree->key, now) > 0) {
+       curlx_timediff_us(multi->timetree->key, now) > 0) {
       /* some time left before expiration */
-      timediff_t diff = Curl_timediff_ceil(multi->timetree->key, now);
+      timediff_t diff = curlx_timediff_ceil(multi->timetree->key, now);
       /* this should be safe even on 32-bit archs, as we do not use that
          overly long timeouts */
       *timeout_ms = (long)diff;
@@ -3574,7 +3264,7 @@ CURLMcode Curl_update_timer(struct Curl_multi *multi)
 #endif
     set_value = TRUE;
   }
-  else if(Curl_timediff_us(multi->last_expire_ts, expire_ts)) {
+  else if(curlx_timediff_us(multi->last_expire_ts, expire_ts)) {
     /* We had a timeout before and have one now, the absolute timestamp
      * differs. The relative timeout_ms may be the same, but the starting
      * point differs. Let the application restart its timer. */
@@ -3658,7 +3348,7 @@ multi_addtimeout(struct Curl_easy *data,
     /* find the correct spot in the list */
     for(e = Curl_llist_head(timeoutlist); e; e = Curl_node_next(e)) {
       struct time_node *check = Curl_node_elem(e);
-      timediff_t diff = Curl_timediff(check->time, node->time);
+      timediff_t diff = curlx_timediff(check->time, node->time);
       if(diff > 0)
         break;
       prev = e;
@@ -3672,9 +3362,9 @@ multi_addtimeout(struct Curl_easy *data,
   return CURLM_OK;
 }
 
-static void expire_ex(struct Curl_easy *data,
-                      const struct curltime *nowp,
-                      timediff_t milli, expire_id id)
+void Curl_expire_ex(struct Curl_easy *data,
+                    const struct curltime *nowp,
+                    timediff_t milli, expire_id id)
 {
   struct Curl_multi *multi = data->multi;
   struct curltime *curr_expire = &data->state.expiretime;
@@ -3707,7 +3397,7 @@ static void expire_ex(struct Curl_easy *data,
     /* This means that the struct is added as a node in the splay tree.
        Compare if the new time is earlier, and only remove-old/add-new if it
        is. */
-    timediff_t diff = Curl_timediff(set, *curr_expire);
+    timediff_t diff = curlx_timediff(set, *curr_expire);
     int rc;
 
     if(diff > 0) {
@@ -3745,8 +3435,8 @@ static void expire_ex(struct Curl_easy *data,
  */
 void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id)
 {
-  struct curltime now = Curl_now();
-  expire_ex(data, &now, milli, id);
+  struct curltime now = curlx_now();
+  Curl_expire_ex(data, &now, milli, id);
 }
 
 /*
@@ -3790,9 +3480,7 @@ bool Curl_expire_clear(struct Curl_easy *data)
     /* clear the timeout list too */
     Curl_llist_destroy(list, NULL);
 
-#ifdef DEBUGBUILD
-    infof(data, "Expire cleared");
-#endif
+    CURL_TRC_M(data, "Expire cleared");
     nowp->tv_sec = 0;
     nowp->tv_usec = 0;
     return TRUE;
@@ -3803,19 +3491,11 @@ bool Curl_expire_clear(struct Curl_easy *data)
 CURLMcode curl_multi_assign(CURLM *m, curl_socket_t s,
                             void *hashp)
 {
-  struct Curl_sh_entry *there = NULL;
   struct Curl_multi *multi = m;
   if(!GOOD_MULTI_HANDLE(multi))
     return CURLM_BAD_HANDLE;
 
-  there = sh_getentry(&multi->sockhash, s);
-
-  if(!there)
-    return CURLM_BAD_SOCKET;
-
-  there->socketp = hashp;
-
-  return CURLM_OK;
+  return Curl_multi_ev_assign(multi, s, hashp);
 }
 
 static void move_pending_to_connect(struct Curl_multi *multi,
@@ -3823,11 +3503,9 @@ static void move_pending_to_connect(struct Curl_multi *multi,
 {
   DEBUGASSERT(data->mstate == MSTATE_PENDING);
 
-  /* Remove this node from the pending list */
-  Curl_node_remove(&data->multi_queue);
-
-  /* put it into the process list */
-  Curl_llist_append(&multi->process, data, &data->multi_queue);
+  /* Remove this node from the pending set, add into process set */
+  Curl_uint_bset_remove(&multi->pending, data->mid);
+  Curl_uint_bset_add(&multi->process, data->mid);
 
   multistate(data, MSTATE_CONNECT);
 
@@ -3851,10 +3529,15 @@ static void move_pending_to_connect(struct Curl_multi *multi,
 */
 static void process_pending_handles(struct Curl_multi *multi)
 {
-  struct Curl_llist_node *e = Curl_llist_head(&multi->pending);
-  if(e) {
-    struct Curl_easy *data = Curl_node_elem(e);
-    move_pending_to_connect(multi, data);
+  unsigned int mid;
+  if(Curl_uint_bset_first(&multi->pending, &mid)) {
+    do {
+      struct Curl_easy *data = Curl_multi_get_easy(multi, mid);
+      DEBUGASSERT(data);
+      if(data)
+        move_pending_to_connect(multi, data);
+    }
+    while(Curl_uint_bset_next(&multi->pending, mid, &mid));
   }
 }
 
@@ -3878,15 +3561,20 @@ unsigned int Curl_multi_max_concurrent_streams(struct Curl_multi *multi)
 CURL **curl_multi_get_handles(CURLM *m)
 {
   struct Curl_multi *multi = m;
-  CURL **a = malloc(sizeof(struct Curl_easy *) * (multi->num_easy + 1));
+  void *entry;
+  unsigned int count = Curl_uint_tbl_count(&multi->xfers);
+  CURL **a = malloc(sizeof(struct Curl_easy *) * (count + 1));
   if(a) {
-    unsigned int i = 0;
-    struct Curl_llist_node *e;
-    for(e = Curl_llist_head(&multi->process); e; e = Curl_node_next(e)) {
-      struct Curl_easy *data = Curl_node_elem(e);
-      DEBUGASSERT(i < multi->num_easy);
-      if(!data->state.internal)
-        a[i++] = data;
+    unsigned int i = 0, mid;
+
+    if(Curl_uint_tbl_first(&multi->xfers, &mid, &entry)) {
+      do {
+        struct Curl_easy *data = entry;
+        DEBUGASSERT(i < count);
+        if(!data->state.internal)
+          a[i++] = data;
+      }
+      while(Curl_uint_tbl_next(&multi->xfers, mid, &mid, &entry));
     }
     a[i] = NULL; /* last entry is a NULL */
   }
@@ -4058,31 +3746,53 @@ static void multi_xfer_bufs_free(struct Curl_multi *multi)
   multi->xfer_sockbuf_borrowed = FALSE;
 }
 
-struct Curl_easy *Curl_multi_get_handle(struct Curl_multi *multi,
-                                        curl_off_t mid)
+struct Curl_easy *Curl_multi_get_easy(struct Curl_multi *multi,
+                                      unsigned int mid)
 {
+  struct Curl_easy *data = mid ? Curl_uint_tbl_get(&multi->xfers, mid) : NULL;
+  if(data && GOOD_EASY_HANDLE(data))
+    return data;
+  CURL_TRC_M(multi->admin, "invalid easy handle in xfer table for mid=%u",
+             mid);
+  Curl_uint_tbl_remove(&multi->xfers, mid);
+  return NULL;
+}
 
-  if(mid >= 0) {
-    struct Curl_easy *data;
-    struct Curl_llist_node *e;
+unsigned int Curl_multi_xfers_running(struct Curl_multi *multi)
+{
+  return multi->xfers_alive;
+}
 
-    for(e = Curl_llist_head(&multi->process); e; e = Curl_node_next(e)) {
-      data = Curl_node_elem(e);
-      if(data->mid == mid)
-        return data;
-    }
-    /* may be in msgsent queue */
-    for(e = Curl_llist_head(&multi->msgsent); e; e = Curl_node_next(e)) {
-      data = Curl_node_elem(e);
-      if(data->mid == mid)
-        return data;
-    }
-    /* may be in pending queue */
-    for(e = Curl_llist_head(&multi->pending); e; e = Curl_node_next(e)) {
-      data = Curl_node_elem(e);
-      if(data->mid == mid)
-        return data;
-    }
+#ifdef DEBUGBUILD
+static void multi_xfer_dump(struct Curl_multi *multi, unsigned int mid,
+                             void *entry)
+{
+  struct Curl_easy *data = entry;
+
+  (void)multi;
+  if(!data) {
+    fprintf(stderr, "mid=%u, entry=NULL, bug in xfer table?\n", mid);
   }
-  return NULL;
+  else {
+    fprintf(stderr, "mid=%u, magic=%s, p=%p, id=%" FMT_OFF_T ", url=%s\n",
+            mid, (data->magic == CURLEASY_MAGIC_NUMBER) ? "GOOD" : "BAD!",
+            (void *)data, data->id, data->state.url);
+  }
+}
+
+static void multi_xfer_tbl_dump(struct Curl_multi *multi)
+{
+  unsigned int mid;
+  void *entry;
+  fprintf(stderr, "=== multi xfer table (count=%u, capacity=%u\n",
+          Curl_uint_tbl_count(&multi->xfers),
+          Curl_uint_tbl_capacity(&multi->xfers));
+  if(Curl_uint_tbl_first(&multi->xfers, &mid, &entry)) {
+    multi_xfer_dump(multi, mid, entry);
+    while(Curl_uint_tbl_next(&multi->xfers, mid, &mid, &entry))
+      multi_xfer_dump(multi, mid, entry);
+  }
+  fprintf(stderr, "===\n");
+  fflush(stderr);
 }
+#endif /* DEBUGBUILD */
diff --git a/extra/curl/curl-8.14.1/lib/multi_ev.c b/extra/curl/curl-8.14.1/lib/multi_ev.c
new file mode 100644
index 000000000000..21d28676195f
--- /dev/null
+++ b/extra/curl/curl-8.14.1/lib/multi_ev.c
@@ -0,0 +1,637 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include <curl/curl.h>
+
+#include "urldata.h"
+#include "url.h"
+#include "cfilters.h"
+#include "curl_trc.h"
+#include "multiif.h"
+#include "curlx/timeval.h"
+#include "multi_ev.h"
+#include "select.h"
+#include "uint-bset.h"
+#include "uint-spbset.h"
+#include "uint-table.h"
+#include "curlx/warnless.h"
+#include "multihandle.h"
+#include "socks.h"
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+
+static void mev_in_callback(struct Curl_multi *multi, bool value)
+{
+  multi->in_callback = value;
+}
+
+#define CURL_MEV_CONN_HASH_SIZE 3
+
+/* Information about a socket for which we inform the libcurl application
+ * what to supervise (CURL_POLL_IN/CURL_POLL_OUT/CURL_POLL_REMOVE)
+ */
+struct mev_sh_entry {
+  struct uint_spbset xfers; /* bitset of transfers `mid`s on this socket */
+  struct connectdata *conn; /* connection using this socket or NULL */
+  void *user_data;      /* libcurl app data via curl_multi_assign() */
+  unsigned int action;  /* CURL_POLL_IN/CURL_POLL_OUT we last told the
+                         * libcurl application to watch out for */
+  unsigned int readers; /* this many transfers want to read */
+  unsigned int writers; /* this many transfers want to write */
+};
+
+static size_t mev_sh_entry_hash(void *key, size_t key_length, size_t slots_num)
+{
+  curl_socket_t fd = *((curl_socket_t *) key);
+  (void) key_length;
+  return (fd % (curl_socket_t)slots_num);
+}
+
+static size_t mev_sh_entry_compare(void *k1, size_t k1_len,
+                                   void *k2, size_t k2_len)
+{
+  (void) k1_len; (void) k2_len;
+  return (*((curl_socket_t *) k1)) == (*((curl_socket_t *) k2));
+}
+
+/* sockhash entry destructor callback */
+static void mev_sh_entry_dtor(void *freethis)
+{
+  struct mev_sh_entry *entry = (struct mev_sh_entry *)freethis;
+  Curl_uint_spbset_destroy(&entry->xfers);
+  free(entry);
+}
+
+/* look up a given socket in the socket hash, skip invalid sockets */
+static struct mev_sh_entry *
+mev_sh_entry_get(struct Curl_hash *sh, curl_socket_t s)
+{
+  if(s != CURL_SOCKET_BAD) {
+    /* only look for proper sockets */
+    return Curl_hash_pick(sh, (char *)&s, sizeof(curl_socket_t));
+  }
+  return NULL;
+}
+
+/* make sure this socket is present in the hash for this handle */
+static struct mev_sh_entry *
+mev_sh_entry_add(struct Curl_hash *sh, curl_socket_t s)
+{
+  struct mev_sh_entry *there = mev_sh_entry_get(sh, s);
+  struct mev_sh_entry *check;
+
+  if(there) {
+    /* it is present, return fine */
+    return there;
+  }
+
+  /* not present, add it */
+  check = calloc(1, sizeof(struct mev_sh_entry));
+  if(!check)
+    return NULL; /* major failure */
+
+  Curl_uint_spbset_init(&check->xfers);
+
+  /* make/add new hash entry */
+  if(!Curl_hash_add(sh, (char *)&s, sizeof(curl_socket_t), check)) {
+    mev_sh_entry_dtor(check);
+    return NULL; /* major failure */
+  }
+
+  return check; /* things are good in sockhash land */
+}
+
+/* delete the given socket entry from the hash */
+static void mev_sh_entry_kill(struct Curl_multi *multi, curl_socket_t s)
+{
+  Curl_hash_delete(&multi->ev.sh_entries, (char *)&s, sizeof(curl_socket_t));
+}
+
+static size_t mev_sh_entry_user_count(struct mev_sh_entry *e)
+{
+  return Curl_uint_spbset_count(&e->xfers) + (e->conn ? 1 : 0);
+}
+
+static bool mev_sh_entry_xfer_known(struct mev_sh_entry *e,
+                                    struct Curl_easy *data)
+{
+  return Curl_uint_spbset_contains(&e->xfers, data->mid);
+}
+
+static bool mev_sh_entry_conn_known(struct mev_sh_entry *e,
+                                    struct connectdata *conn)
+{
+  return (e->conn == conn);
+}
+
+static bool mev_sh_entry_xfer_add(struct mev_sh_entry *e,
+                                  struct Curl_easy *data)
+{
+   /* detect weird values */
+  DEBUGASSERT(mev_sh_entry_user_count(e) < 100000);
+  return Curl_uint_spbset_add(&e->xfers, data->mid);
+}
+
+static bool mev_sh_entry_conn_add(struct mev_sh_entry *e,
+                                  struct connectdata *conn)
+{
+   /* detect weird values */
+  DEBUGASSERT(mev_sh_entry_user_count(e) < 100000);
+  DEBUGASSERT(!e->conn);
+  if(e->conn)
+    return FALSE;
+  e->conn = conn;
+  return TRUE;
+}
+
+
+static bool mev_sh_entry_xfer_remove(struct mev_sh_entry *e,
+                                     struct Curl_easy *data)
+{
+  bool present = Curl_uint_spbset_contains(&e->xfers, data->mid);
+  if(present)
+    Curl_uint_spbset_remove(&e->xfers, data->mid);
+  return present;
+}
+
+static bool mev_sh_entry_conn_remove(struct mev_sh_entry *e,
+                                     struct connectdata *conn)
+{
+  DEBUGASSERT(e->conn == conn);
+  if(e->conn == conn) {
+    e->conn = NULL;
+    return TRUE;
+  }
+  return FALSE;
+}
+
+/* Purge any information about socket `s`.
+ * Let the socket callback know as well when necessary */
+static CURLMcode mev_forget_socket(struct Curl_multi *multi,
+                                   struct Curl_easy *data,
+                                   curl_socket_t s,
+                                   const char *cause)
+{
+  struct mev_sh_entry *entry = mev_sh_entry_get(&multi->ev.sh_entries, s);
+  int rc = 0;
+
+  if(!entry) /* we never knew or already forgot about this socket */
+    return CURLM_OK;
+
+  /* We managed this socket before, tell the socket callback to forget it. */
+  if(multi->socket_cb) {
+    CURL_TRC_M(data, "ev %s, call(fd=%" FMT_SOCKET_T ", ev=REMOVE)",
+               cause, s);
+    mev_in_callback(multi, TRUE);
+    rc = multi->socket_cb(data, s, CURL_POLL_REMOVE,
+                          multi->socket_userp, entry->user_data);
+    mev_in_callback(multi, FALSE);
+  }
+
+  mev_sh_entry_kill(multi, s);
+  if(rc == -1) {
+    multi->dead = TRUE;
+    return CURLM_ABORTED_BY_CALLBACK;
+  }
+  return CURLM_OK;
+}
+
+static CURLMcode mev_sh_entry_update(struct Curl_multi *multi,
+                                     struct Curl_easy *data,
+                                     struct mev_sh_entry *entry,
+                                     curl_socket_t s,
+                                     unsigned char last_action,
+                                     unsigned char cur_action)
+{
+  int rc, comboaction;
+
+  /* we should only be called when the callback exists */
+  DEBUGASSERT(multi->socket_cb);
+  if(!multi->socket_cb)
+    return CURLM_OK;
+
+  /* Transfer `data` goes from `last_action` to `cur_action` on socket `s`
+   * with `multi->ev.sh_entries` entry `entry`. Update `entry` and trigger
+   * `multi->socket_cb` on change, if the callback is set. */
+  if(last_action == cur_action)  /* nothing from `data` changed */
+    return CURLM_OK;
+
+  if(last_action & CURL_POLL_IN) {
+    DEBUGASSERT(entry->readers);
+    if(!(cur_action & CURL_POLL_IN))
+      entry->readers--;
+  }
+  else if(cur_action & CURL_POLL_IN)
+    entry->readers++;
+
+  if(last_action & CURL_POLL_OUT) {
+    DEBUGASSERT(entry->writers);
+    if(!(cur_action & CURL_POLL_OUT))
+      entry->writers--;
+  }
+  else if(cur_action & CURL_POLL_OUT)
+    entry->writers++;
+
+  DEBUGASSERT(entry->readers <= mev_sh_entry_user_count(entry));
+  DEBUGASSERT(entry->writers <= mev_sh_entry_user_count(entry));
+  DEBUGASSERT(entry->writers + entry->readers);
+
+  CURL_TRC_M(data, "ev update fd=%" FMT_SOCKET_T ", action '%s%s' -> '%s%s'"
+             " (%d/%d r/w)", s,
+             (last_action & CURL_POLL_IN) ? "IN" : "",
+             (last_action & CURL_POLL_OUT) ? "OUT" : "",
+             (cur_action & CURL_POLL_IN) ? "IN" : "",
+             (cur_action & CURL_POLL_OUT) ? "OUT" : "",
+             entry->readers, entry->writers);
+
+  comboaction = (entry->writers ? CURL_POLL_OUT : 0) |
+                (entry->readers ? CURL_POLL_IN : 0);
+  if(((int)entry->action == comboaction)) /* nothing for socket changed */
+    return CURLM_OK;
+
+  CURL_TRC_M(data, "ev update call(fd=%" FMT_SOCKET_T ", ev=%s%s)",
+             s, (comboaction & CURL_POLL_IN) ? "IN" : "",
+             (comboaction & CURL_POLL_OUT) ? "OUT" : "");
+  mev_in_callback(multi, TRUE);
+  rc = multi->socket_cb(data, s, comboaction, multi->socket_userp,
+                        entry->user_data);
+
+  mev_in_callback(multi, FALSE);
+  if(rc == -1) {
+    multi->dead = TRUE;
+    return CURLM_ABORTED_BY_CALLBACK;
+  }
+  entry->action = (unsigned int)comboaction;
+  return CURLM_OK;
+}
+
+static CURLMcode mev_pollset_diff(struct Curl_multi *multi,
+                                  struct Curl_easy *data,
+                                  struct connectdata *conn,
+                                  struct easy_pollset *ps,
+                                  struct easy_pollset *prev_ps)
+{
+  struct mev_sh_entry *entry;
+  curl_socket_t s;
+  unsigned int i, j;
+  CURLMcode mresult;
+
+  /* The transfer `data` reports in `ps` the sockets it is interested
+   * in and which combination of CURL_POLL_IN/CURL_POLL_OUT it wants
+   * to have monitored for events.
+   * There can be more than 1 transfer interested in the same socket
+   * and 1 transfer might be interested in more than 1 socket.
+   * `prev_ps` is the pollset copy from the previous call here. On
+   * the 1st call it will be empty.
+   */
+  DEBUGASSERT(ps);
+  DEBUGASSERT(prev_ps);
+
+  /* Handle changes to sockets the transfer is interested in. */
+  for(i = 0; i < ps->num; i++) {
+    unsigned char last_action;
+    bool first_time = FALSE; /* data/conn appears first time on socket */
+
+    s = ps->sockets[i];
+    /* Have we handled this socket before? */
+    entry = mev_sh_entry_get(&multi->ev.sh_entries, s);
+    if(!entry) {
+      /* new socket, add new entry */
+      first_time = TRUE;
+      entry = mev_sh_entry_add(&multi->ev.sh_entries, s);
+      if(!entry) /* fatal */
+        return CURLM_OUT_OF_MEMORY;
+      CURL_TRC_M(data, "ev new entry fd=%" FMT_SOCKET_T, s);
+    }
+    else if(conn) {
+      first_time = !mev_sh_entry_conn_known(entry, conn);
+    }
+    else {
+      first_time = !mev_sh_entry_xfer_known(entry, data);
+    }
+
+    /* What was the previous action the transfer had regarding this socket?
+     * If the transfer is new to the socket, disregard the information
+     * in `last_poll`, because the socket might have been destroyed and
+     * reopened. We'd have cleared the sh_entry for that, but the socket
+     * might still be mentioned in the hashed pollsets. */
+    last_action = 0;
+    if(first_time) {
+      if(conn) {
+        if(!mev_sh_entry_conn_add(entry, conn))
+          return CURLM_OUT_OF_MEMORY;
+      }
+      else {
+        if(!mev_sh_entry_xfer_add(entry, data))
+          return CURLM_OUT_OF_MEMORY;
+      }
+      CURL_TRC_M(data, "ev entry fd=%" FMT_SOCKET_T ", added %s #%" FMT_OFF_T
+                 ", total=%u/%d (xfer/conn)", s,
+                 conn ? "connection" : "transfer",
+                 conn ? conn->connection_id : data->mid,
+                 Curl_uint_spbset_count(&entry->xfers),
+                 entry->conn ? 1 : 0);
+    }
+    else {
+      for(j = 0; j < prev_ps->num; j++) {
+        if(s == prev_ps->sockets[j]) {
+          last_action = prev_ps->actions[j];
+          break;
+        }
+      }
+    }
+    /* track readers/writers changes and report to socket callback */
+    mresult = mev_sh_entry_update(multi, data, entry, s,
+                                  last_action, ps->actions[i]);
+    if(mresult)
+      return mresult;
+  }
+
+  /* Handle changes to sockets the transfer is NO LONGER interested in. */
+  for(i = 0; i < prev_ps->num; i++) {
+    bool stillused = FALSE;
+
+    s = prev_ps->sockets[i];
+    for(j = 0; j < ps->num; j++) {
+      if(s == ps->sockets[j]) {
+        /* socket is still supervised */
+        stillused = TRUE;
+        break;
+      }
+    }
+    if(stillused)
+      continue;
+
+    entry = mev_sh_entry_get(&multi->ev.sh_entries, s);
+    /* if entry does not exist, we were either never told about it or
+     * have already cleaned up this socket via Curl_multi_ev_socket_done().
+     * In other words: this is perfectly normal */
+    if(!entry)
+      continue;
+
+    if(conn && !mev_sh_entry_conn_remove(entry, conn)) {
+      /* `conn` says in `prev_ps` that it had been using a socket,
+       * but `conn` has not been registered for it.
+       * This should not happen if our book-keeping is correct? */
+      CURL_TRC_M(data, "ev entry fd=%" FMT_SOCKET_T ", conn lost "
+                 "interest but is not registered", s);
+      DEBUGASSERT(NULL);
+      continue;
+    }
+
+    if(!conn && !mev_sh_entry_xfer_remove(entry, data)) {
+      /* `data` says in `prev_ps` that it had been using a socket,
+       * but `data` has not been registered for it.
+       * This should not happen if our book-keeping is correct? */
+      CURL_TRC_M(data, "ev entry fd=%" FMT_SOCKET_T ", transfer lost "
+                 "interest but is not registered", s);
+      DEBUGASSERT(NULL);
+      continue;
+    }
+
+    if(mev_sh_entry_user_count(entry)) {
+      /* track readers/writers changes and report to socket callback */
+      mresult = mev_sh_entry_update(multi, data, entry, s,
+                                    prev_ps->actions[i], 0);
+      if(mresult)
+        return mresult;
+      CURL_TRC_M(data, "ev entry fd=%" FMT_SOCKET_T ", removed transfer, "
+                 "total=%u/%d (xfer/conn)", s,
+                 Curl_uint_spbset_count(&entry->xfers),
+                 entry->conn ? 1 : 0);
+    }
+    else {
+      mresult = mev_forget_socket(multi, data, s, "last user gone");
+      if(mresult)
+        return mresult;
+    }
+  } /* for loop over num */
+
+  /* Remember for next time */
+  memcpy(prev_ps, ps, sizeof(*prev_ps));
+  return CURLM_OK;
+}
+
+static void mev_pollset_dtor(void *key, size_t klen, void *entry)
+{
+  (void)key;
+  (void)klen;
+  free(entry);
+}
+
+static struct easy_pollset*
+mev_add_new_conn_pollset(struct connectdata *conn)
+{
+  struct easy_pollset *ps;
+
+  ps = calloc(1, sizeof(*ps));
+  if(!ps)
+    return NULL;
+  if(Curl_conn_meta_set(conn, CURL_META_MEV_POLLSET, ps, mev_pollset_dtor))
+    return NULL;
+  return ps;
+}
+
+static struct easy_pollset*
+mev_add_new_xfer_pollset(struct Curl_easy *data)
+{
+  struct easy_pollset *ps;
+
+  ps = calloc(1, sizeof(*ps));
+  if(!ps)
+    return NULL;
+  if(Curl_meta_set(data, CURL_META_MEV_POLLSET, ps, mev_pollset_dtor))
+    return NULL;
+  return ps;
+}
+
+static struct easy_pollset *
+mev_get_last_pollset(struct Curl_easy *data,
+                     struct connectdata *conn)
+{
+  if(data) {
+    if(conn)
+      return Curl_conn_meta_get(conn, CURL_META_MEV_POLLSET);
+    return Curl_meta_get(data, CURL_META_MEV_POLLSET);
+  }
+  return NULL;
+}
+
+static void mev_init_cur_pollset(struct easy_pollset *ps,
+                                 struct Curl_easy *data,
+                                 struct connectdata *conn)
+{
+  memset(ps, 0, sizeof(*ps));
+  if(conn)
+    Curl_conn_adjust_pollset(data, conn, ps);
+  else if(data)
+    Curl_multi_getsock(data, ps, "ev assess");
+}
+
+static CURLMcode mev_assess(struct Curl_multi *multi,
+                            struct Curl_easy *data,
+                            struct connectdata *conn)
+{
+  if(multi && multi->socket_cb) {
+    struct easy_pollset ps, *last_ps;
+
+    mev_init_cur_pollset(&ps, data, conn);
+    last_ps = mev_get_last_pollset(data, conn);
+
+    if(!last_ps && ps.num) {
+      if(conn)
+        last_ps = mev_add_new_conn_pollset(conn);
+      else
+        last_ps = mev_add_new_xfer_pollset(data);
+      if(!last_ps)
+        return CURLM_OUT_OF_MEMORY;
+    }
+
+    if(last_ps)
+      return mev_pollset_diff(multi, data, conn, &ps, last_ps);
+    else
+      DEBUGASSERT(!ps.num);
+  }
+  return CURLM_OK;
+}
+
+CURLMcode Curl_multi_ev_assess_xfer(struct Curl_multi *multi,
+                                    struct Curl_easy *data)
+{
+  return mev_assess(multi, data, NULL);
+}
+
+CURLMcode Curl_multi_ev_assess_conn(struct Curl_multi *multi,
+                                    struct Curl_easy *data,
+                                    struct connectdata *conn)
+{
+  return mev_assess(multi, data, conn);
+}
+
+CURLMcode Curl_multi_ev_assess_xfer_bset(struct Curl_multi *multi,
+                                         struct uint_bset *set)
+{
+  unsigned int mid;
+  CURLMcode result = CURLM_OK;
+
+  if(multi && multi->socket_cb && Curl_uint_bset_first(set, &mid)) {
+    do {
+      struct Curl_easy *data = Curl_multi_get_easy(multi, mid);
+      if(data)
+        result = Curl_multi_ev_assess_xfer(multi, data);
+    }
+    while(!result && Curl_uint_bset_next(set, mid, &mid));
+  }
+  return result;
+}
+
+
+CURLMcode Curl_multi_ev_assign(struct Curl_multi *multi,
+                               curl_socket_t s,
+                               void *user_data)
+{
+  struct mev_sh_entry *e = mev_sh_entry_get(&multi->ev.sh_entries, s);
+  if(!e)
+    return CURLM_BAD_SOCKET;
+  e->user_data = user_data;
+  return CURLM_OK;
+}
+
+void Curl_multi_ev_expire_xfers(struct Curl_multi *multi,
+                                curl_socket_t s,
+                                const struct curltime *nowp,
+                                bool *run_cpool)
+{
+  struct mev_sh_entry *entry;
+
+  DEBUGASSERT(s != CURL_SOCKET_TIMEOUT);
+  entry = mev_sh_entry_get(&multi->ev.sh_entries, s);
+
+  /* Unmatched socket, we cannot act on it but we ignore this fact. In
+     real-world tests it has been proved that libevent can in fact give
+     the application actions even though the socket was just previously
+     asked to get removed, so thus we better survive stray socket actions
+     and just move on. */
+  if(entry) {
+    struct Curl_easy *data;
+    unsigned int mid;
+
+    if(Curl_uint_spbset_first(&entry->xfers, &mid)) {
+      do {
+        data = Curl_multi_get_easy(multi, mid);
+        if(data) {
+          /* Expire with out current now, so we will get it below when
+           * asking the splaytree for expired transfers. */
+          Curl_expire_ex(data, nowp, 0, EXPIRE_RUN_NOW);
+        }
+      }
+      while(Curl_uint_spbset_next(&entry->xfers, mid, &mid));
+    }
+
+    if(entry->conn)
+      *run_cpool = TRUE;
+  }
+}
+
+void Curl_multi_ev_socket_done(struct Curl_multi *multi,
+                               struct Curl_easy *data, curl_socket_t s)
+{
+  mev_forget_socket(multi, data, s, "socket done");
+}
+
+void Curl_multi_ev_xfer_done(struct Curl_multi *multi,
+                             struct Curl_easy *data)
+{
+  DEBUGASSERT(!data->conn); /* transfer should have been detached */
+  if(data != multi->admin) {
+    (void)mev_assess(multi, data, NULL);
+    Curl_meta_remove(data, CURL_META_MEV_POLLSET);
+  }
+}
+
+void Curl_multi_ev_conn_done(struct Curl_multi *multi,
+                             struct Curl_easy *data,
+                             struct connectdata *conn)
+{
+  (void)mev_assess(multi, data, conn);
+  Curl_conn_meta_remove(conn, CURL_META_MEV_POLLSET);
+}
+
+#define CURL_MEV_PS_HASH_SLOTS   (991)  /* nice prime */
+
+void Curl_multi_ev_init(struct Curl_multi *multi, size_t hashsize)
+{
+  Curl_hash_init(&multi->ev.sh_entries, hashsize, mev_sh_entry_hash,
+                 mev_sh_entry_compare, mev_sh_entry_dtor);
+}
+
+void Curl_multi_ev_cleanup(struct Curl_multi *multi)
+{
+  Curl_hash_destroy(&multi->ev.sh_entries);
+}
diff --git a/extra/curl/curl-8.14.1/lib/multi_ev.h b/extra/curl/curl-8.14.1/lib/multi_ev.h
new file mode 100644
index 000000000000..06be842fb96e
--- /dev/null
+++ b/extra/curl/curl-8.14.1/lib/multi_ev.h
@@ -0,0 +1,83 @@
+#ifndef HEADER_CURL_MULTI_EV_H
+#define HEADER_CURL_MULTI_EV_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "hash.h"
+
+struct Curl_easy;
+struct Curl_multi;
+struct easy_pollset;
+struct uint_bset;
+
+/* meta key for event pollset at easy handle or connection */
+#define CURL_META_MEV_POLLSET   "meta:mev:ps"
+
+struct curl_multi_ev {
+  struct Curl_hash sh_entries;
+};
+
+/* Setup/teardown of multi event book-keeping. */
+void Curl_multi_ev_init(struct Curl_multi *multi, size_t hashsize);
+void Curl_multi_ev_cleanup(struct Curl_multi *multi);
+
+/* Assign a 'user_data' to be passed to the socket callback when
+ * invoked with the given socket. This will fail if this socket
+ * is not active, e.g. the application has not been told to monitor it. */
+CURLMcode Curl_multi_ev_assign(struct Curl_multi *multi, curl_socket_t s,
+                               void *user_data);
+
+/* Assess the transfer by getting its current pollset, compute
+ * any changes to the last one and inform the application's socket
+ * callback if things have changed. */
+CURLMcode Curl_multi_ev_assess_xfer(struct Curl_multi *multi,
+                                    struct Curl_easy *data);
+/* Assess all easy handles on the list */
+CURLMcode Curl_multi_ev_assess_xfer_bset(struct Curl_multi *multi,
+                                         struct uint_bset *set);
+/* Assess the connection by getting its current pollset */
+CURLMcode Curl_multi_ev_assess_conn(struct Curl_multi *multi,
+                                    struct Curl_easy *data,
+                                    struct connectdata *conn);
+
+/* Expire all transfers tied to the given socket */
+void Curl_multi_ev_expire_xfers(struct Curl_multi *multi,
+                                curl_socket_t s,
+                                const struct curltime *nowp,
+                                bool *run_cpool);
+
+/* Socket will be closed, forget anything we know about it. */
+void Curl_multi_ev_socket_done(struct Curl_multi *multi,
+                               struct Curl_easy *data, curl_socket_t s);
+
+/* Transfer is removed from the multi */
+void Curl_multi_ev_xfer_done(struct Curl_multi *multi,
+                             struct Curl_easy *data);
+
+/* Connection is being destroyed */
+void Curl_multi_ev_conn_done(struct Curl_multi *multi,
+                             struct Curl_easy *data,
+                             struct connectdata *conn);
+
+#endif /* HEADER_CURL_MULTI_EV_H */
diff --git a/extra/curl/curl-8.12.1/lib/multihandle.h b/extra/curl/curl-8.14.1/lib/multihandle.h
similarity index 86%
rename from extra/curl/curl-8.12.1/lib/multihandle.h
rename to extra/curl/curl-8.14.1/lib/multihandle.h
index c0a3d0960805..04db02f0b160 100644
--- a/extra/curl/curl-8.12.1/lib/multihandle.h
+++ b/extra/curl/curl-8.14.1/lib/multihandle.h
@@ -27,10 +27,17 @@
 #include "llist.h"
 #include "hash.h"
 #include "conncache.h"
+#include "cshutdn.h"
+#include "hostip.h"
+#include "multi_ev.h"
 #include "psl.h"
 #include "socketpair.h"
+#include "uint-bset.h"
+#include "uint-spbset.h"
+#include "uint-table.h"
 
 struct connectdata;
+struct Curl_easy;
 
 struct Curl_message {
   struct Curl_llist_node list;
@@ -38,9 +45,9 @@ struct Curl_message {
   struct CURLMsg extmsg;
 };
 
-/* NOTE: if you add a state here, add the name to the statename[] array as
-   well!
-*/
+/* NOTE: if you add a state here, add the name to the statenames[] array
+ * in curl_trc.c as well!
+ */
 typedef enum {
   MSTATE_INIT,         /* 0 - start in this state */
   MSTATE_PENDING,      /* 1 - no connections, waiting for one */
@@ -86,17 +93,18 @@ struct Curl_multi {
      this multi handle with an easy handle. Set this to CURL_MULTI_HANDLE. */
   unsigned int magic;
 
-  unsigned int num_easy; /* amount of entries in the linked list above. */
-  unsigned int num_alive; /* amount of easy handles that are added but have
-                             not yet reached COMPLETE state */
+  unsigned int xfers_alive; /* amount of added transfers that have
+                               not yet reached COMPLETE state */
+  struct uint_tbl xfers; /* transfers added to this multi */
+  /* Each transfer's mid may be present in at most one of these */
+  struct uint_bset process; /* transfer being processed */
+  struct uint_bset pending; /* transfers in waiting (conn limit etc.) */
+  struct uint_bset msgsent; /* transfers done with message for application */
 
   struct Curl_llist msglist; /* a list of messages from completed transfers */
 
-  /* Each added easy handle is added to ONE of these three lists */
-  struct Curl_llist process; /* not in PENDING or MSGSENT */
-  struct Curl_llist pending; /* in PENDING */
-  struct Curl_llist msgsent; /* in MSGSENT */
-  curl_off_t next_easy_mid; /* next multi-id for easy handle added */
+  struct Curl_easy *admin; /* internal easy handle for admin operations.
+                              gets assigned `mid` 0 on multi init */
 
   /* callback function and user data pointer for the *socket() API */
   curl_socket_callback socket_cb;
@@ -106,7 +114,7 @@ struct Curl_multi {
   curl_push_callback push_cb;
   void *push_userp;
 
-  struct Curl_hash hostcache; /* Hostname cache */
+  struct Curl_dnscache dnscache; /* DNS cache */
   struct Curl_ssl_scache *ssl_scache; /* TLS session pool */
 
 #ifdef USE_LIBPSL
@@ -128,10 +136,9 @@ struct Curl_multi {
   char *xfer_sockbuf; /* the actual buffer */
   size_t xfer_sockbuf_len; /* the allocated length */
 
-  /* 'sockhash' is the lookup hash for socket descriptor => easy handles (note
-     the pluralis form, there can be more than one easy handle waiting on the
-     same actual socket) */
-  struct Curl_hash sockhash;
+  /* multi event related things */
+  struct curl_multi_ev ev;
+
   /* `proto_hash` is a general key-value store for protocol implementations
    * with the lifetime of the multi handle. The number of elements kept here
    * should be in the order of supported protocols (and sub-protocols like
@@ -140,8 +147,8 @@ struct Curl_multi {
    * the multi handle is cleaned up (see Curl_hash_add2()).*/
   struct Curl_hash proto_hash;
 
-  /* Shared connection cache (bundles)*/
-  struct cpool cpool;
+  struct cshutdn cshutdn; /* connection shutdown handling */
+  struct cpool cpool;     /* connection pool (bundles) */
 
   long max_host_connections; /* if >0, a fixed limit of the maximum number
                                 of connections per host */
diff --git a/extra/curl/curl-8.12.1/lib/multiif.h b/extra/curl/curl-8.14.1/lib/multiif.h
similarity index 84%
rename from extra/curl/curl-8.12.1/lib/multiif.h
rename to extra/curl/curl-8.14.1/lib/multiif.h
index 89ede92c03ef..eae634ab4057 100644
--- a/extra/curl/curl-8.12.1/lib/multiif.h
+++ b/extra/curl/curl-8.14.1/lib/multiif.h
@@ -28,8 +28,10 @@
  * Prototypes for library-wide functions provided by multi.c
  */
 
-CURLcode Curl_updatesocket(struct Curl_easy *data);
 void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id);
+void Curl_expire_ex(struct Curl_easy *data,
+                    const struct curltime *nowp,
+                    timediff_t milli, expire_id id);
 bool Curl_expire_clear(struct Curl_easy *data);
 void Curl_expire_done(struct Curl_easy *data, expire_id id);
 CURLMcode Curl_update_timer(struct Curl_multi *multi) WARN_UNUSED_RESULT;
@@ -45,7 +47,8 @@ void Curl_multi_connchanged(struct Curl_multi *multi);
 
 /* Internal version of curl_multi_init() accepts size parameters for the
    socket, connection and dns hashes */
-struct Curl_multi *Curl_multi_handle(size_t hashsize,
+struct Curl_multi *Curl_multi_handle(unsigned int xfer_table_size,
+                                     size_t hashsize,
                                      size_t chashsize,
                                      size_t dnssize,
                                      size_t sesssize);
@@ -64,26 +67,13 @@ struct Curl_multi *Curl_multi_handle(size_t hashsize,
 /* mask for checking if read and/or write is set for index x */
 #define GETSOCK_MASK_RW(x) (GETSOCK_READSOCK(x)|GETSOCK_WRITESOCK(x))
 
-/*
- * Curl_multi_closed()
- *
- * Used by the connect code to tell the multi_socket code that one of the
- * sockets we were using is about to be closed. This function will then
- * remove it from the sockethash for this handle to make the multi_socket API
- * behave properly, especially for the case when libcurl will create another
- * socket again and it gets the same file descriptor number.
- */
-
-void Curl_multi_closed(struct Curl_easy *data, curl_socket_t s);
-
-/* Compare the two pollsets to notify the multi_socket API of changes
- * in socket polling, e.g calling multi->socket_cb() with the changes if
- * differences are seen.
+/**
+ * Let the multi handle know that the socket is about to be closed.
+ * The multi will then remove anything it knows about the socket, so
+ * when the OS is using this socket (number) again subsequently,
+ * the internal book keeping will not get confused.
  */
-CURLMcode Curl_multi_pollset_ev(struct Curl_multi *multi,
-                                struct Curl_easy *data,
-                                struct easy_pollset *ps,
-                                struct easy_pollset *last_ps);
+void Curl_multi_will_close(struct Curl_easy *data, curl_socket_t s);
 
 /*
  * Add a handle and move it into PERFORM state at once. For pushed streams.
@@ -96,6 +86,10 @@ CURLMcode Curl_multi_add_perform(struct Curl_multi *multi,
 /* Return the value of the CURLMOPT_MAX_CONCURRENT_STREAMS option */
 unsigned int Curl_multi_max_concurrent_streams(struct Curl_multi *multi);
 
+void Curl_multi_getsock(struct Curl_easy *data,
+                        struct easy_pollset *ps,
+                        const char *caller);
+
 /**
  * Borrow the transfer buffer from the multi, suitable
  * for the given transfer `data`. The buffer may only be used in one
@@ -170,9 +164,13 @@ CURLcode Curl_multi_xfer_sockbuf_borrow(struct Curl_easy *data,
 void Curl_multi_xfer_sockbuf_release(struct Curl_easy *data, char *buf);
 
 /**
- * Get the transfer handle for the given id. Returns NULL if not found.
+ * Get the easy handle for the given mid.
+ * Returns NULL if not found.
  */
-struct Curl_easy *Curl_multi_get_handle(struct Curl_multi *multi,
-                                        curl_off_t id);
+struct Curl_easy *Curl_multi_get_easy(struct Curl_multi *multi,
+                                      unsigned int mid);
+
+/* Get the # of transfers current in process/pending. */
+unsigned int Curl_multi_xfers_running(struct Curl_multi *multi);
 
 #endif /* HEADER_CURL_MULTIIF_H */
diff --git a/extra/curl/curl-8.12.1/lib/netrc.c b/extra/curl/curl-8.14.1/lib/netrc.c
similarity index 92%
rename from extra/curl/curl-8.12.1/lib/netrc.c
rename to extra/curl/curl-8.14.1/lib/netrc.c
index ba6708991dfd..7df3f17fc3ae 100644
--- a/extra/curl/curl-8.12.1/lib/netrc.c
+++ b/extra/curl/curl-8.14.1/lib/netrc.c
@@ -26,15 +26,20 @@
 #ifndef CURL_DISABLE_NETRC
 
 #ifdef HAVE_PWD_H
+#ifdef __AMIGA__
 #undef __NO_NET_API /* required for AmigaOS to declare getpwuid() */
+#endif
 #include <pwd.h>
+#ifdef __AMIGA__
 #define __NO_NET_API
 #endif
+#endif
 
 #include <curl/curl.h>
 #include "netrc.h"
 #include "strcase.h"
 #include "curl_get_line.h"
+#include "curlx/strparse.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -73,19 +78,18 @@ static NETRCcode file2memory(const char *filename, struct dynbuf *filebuf)
   NETRCcode ret = NETRC_FILE_MISSING; /* if it cannot open the file */
   FILE *file = fopen(filename, FOPEN_READTEXT);
   struct dynbuf linebuf;
-  Curl_dyn_init(&linebuf, MAX_NETRC_LINE);
+  curlx_dyn_init(&linebuf, MAX_NETRC_LINE);
 
   if(file) {
     ret = NETRC_OK;
     while(Curl_get_line(&linebuf, file)) {
       CURLcode result;
-      const char *line = Curl_dyn_ptr(&linebuf);
+      const char *line = curlx_dyn_ptr(&linebuf);
       /* skip comments on load */
-      while(ISBLANK(*line))
-        line++;
+      curlx_str_passblanks(&line);
       if(*line == '#')
         continue;
-      result = Curl_dyn_add(filebuf, line);
+      result = curlx_dyn_add(filebuf, line);
       if(result) {
         ret = curl2netrc(result);
         goto done;
@@ -93,7 +97,7 @@ static NETRCcode file2memory(const char *filename, struct dynbuf *filebuf)
     }
   }
 done:
-  Curl_dyn_free(&linebuf);
+  curlx_dyn_free(&linebuf);
   if(file)
     fclose(file);
   return ret;
@@ -122,7 +126,7 @@ static NETRCcode parsenetrc(struct store_netrc *store,
   struct dynbuf token;
   struct dynbuf *filebuf = &store->filebuf;
   DEBUGASSERT(!*passwordp);
-  Curl_dyn_init(&token, MAX_NETRC_TOKEN);
+  curlx_dyn_init(&token, MAX_NETRC_TOKEN);
 
   if(!store->loaded) {
     NETRCcode ret = file2memory(netrcfile, filebuf);
@@ -131,16 +135,15 @@ static NETRCcode parsenetrc(struct store_netrc *store,
     store->loaded = TRUE;
   }
 
-  netrcbuffer = Curl_dyn_ptr(filebuf);
+  netrcbuffer = curlx_dyn_ptr(filebuf);
 
   while(!done) {
-    char *tok = netrcbuffer;
+    const char *tok = netrcbuffer;
     while(tok && !done) {
-      char *tok_end;
+      const char *tok_end;
       bool quoted;
-      Curl_dyn_reset(&token);
-      while(ISBLANK(*tok))
-        tok++;
+      curlx_dyn_reset(&token);
+      curlx_str_passblanks(&tok);
       /* tok is first non-space letter */
       if(state == MACDEF) {
         if((*tok == '\n') || (*tok == '\r'))
@@ -158,7 +161,7 @@ static NETRCcode parsenetrc(struct store_netrc *store,
       if(!quoted) {
         size_t len = 0;
         CURLcode result;
-        while(!ISSPACE(*tok_end)) {
+        while(*tok_end > ' ') {
           tok_end++;
           len++;
         }
@@ -166,7 +169,7 @@ static NETRCcode parsenetrc(struct store_netrc *store,
           retcode = NETRC_SYNTAX_ERROR;
           goto out;
         }
-        result = Curl_dyn_addn(&token, tok, len);
+        result = curlx_dyn_addn(&token, tok, len);
         if(result) {
           retcode = curl2netrc(result);
           goto out;
@@ -203,7 +206,7 @@ static NETRCcode parsenetrc(struct store_netrc *store,
             endquote = TRUE;
             break;
           }
-          result = Curl_dyn_addn(&token, &s, 1);
+          result = curlx_dyn_addn(&token, &s, 1);
           if(result) {
             retcode = curl2netrc(result);
             goto out;
@@ -217,7 +220,12 @@ static NETRCcode parsenetrc(struct store_netrc *store,
         }
       }
 
-      tok = Curl_dyn_ptr(&token);
+      if(curlx_dyn_len(&token))
+        tok = curlx_dyn_ptr(&token);
+      else
+        /* since tok might actually be NULL for no content, set it to blank
+           to avoid having to deal with it being NULL */
+        tok = "";
 
       switch(state) {
       case NOTHING:
@@ -329,7 +337,7 @@ static NETRCcode parsenetrc(struct store_netrc *store,
   } /* while !done */
 
 out:
-  Curl_dyn_free(&token);
+  curlx_dyn_free(&token);
   if(!retcode) {
     if(!password && our_login) {
       /* success without a password, set a blank one */
@@ -348,7 +356,7 @@ static NETRCcode parsenetrc(struct store_netrc *store,
     *passwordp = password;
   }
   else {
-    Curl_dyn_free(filebuf);
+    curlx_dyn_free(filebuf);
     if(!specific_login)
       free(login);
     free(password);
@@ -453,12 +461,12 @@ NETRCcode Curl_parsenetrc(struct store_netrc *store, const char *host,
 
 void Curl_netrc_init(struct store_netrc *s)
 {
-  Curl_dyn_init(&s->filebuf, MAX_NETRC_FILE);
+  curlx_dyn_init(&s->filebuf, MAX_NETRC_FILE);
   s->loaded = FALSE;
 }
 void Curl_netrc_cleanup(struct store_netrc *s)
 {
-  Curl_dyn_free(&s->filebuf);
+  curlx_dyn_free(&s->filebuf);
   s->loaded = FALSE;
 }
 #endif
diff --git a/extra/curl/curl-8.12.1/lib/netrc.h b/extra/curl/curl-8.14.1/lib/netrc.h
similarity index 98%
rename from extra/curl/curl-8.12.1/lib/netrc.h
rename to extra/curl/curl-8.14.1/lib/netrc.h
index ac0f88622b79..ef3bde5212ad 100644
--- a/extra/curl/curl-8.12.1/lib/netrc.h
+++ b/extra/curl/curl-8.14.1/lib/netrc.h
@@ -26,7 +26,7 @@
 
 #include "curl_setup.h"
 #ifndef CURL_DISABLE_NETRC
-#include "dynbuf.h"
+#include "curlx/dynbuf.h"
 
 struct store_netrc {
   struct dynbuf filebuf;
diff --git a/extra/curl/curl-8.12.1/lib/noproxy.c b/extra/curl/curl-8.14.1/lib/noproxy.c
similarity index 94%
rename from extra/curl/curl-8.12.1/lib/noproxy.c
rename to extra/curl/curl-8.14.1/lib/noproxy.c
index 78cc06fa019f..f70f57eb5af1 100644
--- a/extra/curl/curl-8.12.1/lib/noproxy.c
+++ b/extra/curl/curl-8.14.1/lib/noproxy.c
@@ -26,9 +26,10 @@
 
 #ifndef CURL_DISABLE_PROXY
 
-#include "inet_pton.h"
+#include "curlx/inet_pton.h"
 #include "strcase.h"
 #include "noproxy.h"
+#include "curlx/strparse.h"
 
 #ifdef HAVE_NETINET_IN_H
 #include <netinet/in.h>
@@ -53,9 +54,9 @@ UNITTEST bool Curl_cidr4_match(const char *ipv4,    /* 1.2.3.4 address */
     /* strange input */
     return FALSE;
 
-  if(1 != Curl_inet_pton(AF_INET, ipv4, &address))
+  if(1 != curlx_inet_pton(AF_INET, ipv4, &address))
     return FALSE;
-  if(1 != Curl_inet_pton(AF_INET, network, &check))
+  if(1 != curlx_inet_pton(AF_INET, network, &check))
     return FALSE;
 
   if(bits && (bits != 32)) {
@@ -91,9 +92,9 @@ UNITTEST bool Curl_cidr6_match(const char *ipv6,
   rest = bits & 0x07;
   if((bytes > 16) || ((bytes == 16) && rest))
     return FALSE;
-  if(1 != Curl_inet_pton(AF_INET6, ipv6, address))
+  if(1 != curlx_inet_pton(AF_INET6, ipv6, address))
     return FALSE;
-  if(1 != Curl_inet_pton(AF_INET6, network, check))
+  if(1 != curlx_inet_pton(AF_INET6, network, check))
     return FALSE;
   if(bytes && memcmp(address, check, bytes))
     return FALSE;
@@ -162,7 +163,7 @@ bool Curl_check_noproxy(const char *name, const char *no_proxy)
     else {
       unsigned int address;
       namelen = strlen(name);
-      if(1 == Curl_inet_pton(AF_INET, name, &address))
+      if(1 == curlx_inet_pton(AF_INET, name, &address))
         type = TYPE_IPV4;
       else {
         /* ignore trailing dots in the hostname */
@@ -177,8 +178,7 @@ bool Curl_check_noproxy(const char *name, const char *no_proxy)
       bool match = FALSE;
 
       /* pass blanks */
-      while(*p && ISBLANK(*p))
-        p++;
+      curlx_str_passblanks(&p);
 
       token = p;
       /* pass over the pattern */
@@ -234,7 +234,7 @@ bool Curl_check_noproxy(const char *name, const char *no_proxy)
             /* if the bits variable gets a crazy value here, that is fine as
                the value will then be rejected in the cidr function */
             bits = (unsigned int)atoi(slash + 1);
-            *slash = 0; /* null terminate there */
+            *slash = 0; /* null-terminate there */
           }
           if(type == TYPE_IPV6)
             match = Curl_cidr6_match(name, check, bits);
@@ -247,8 +247,7 @@ bool Curl_check_noproxy(const char *name, const char *no_proxy)
           return TRUE;
       } /* if(tokenlen) */
       /* pass blanks after pattern */
-      while(ISBLANK(*p))
-        p++;
+      curlx_str_passblanks(&p);
       /* if not a comma, this ends the loop */
       if(*p != ',')
         break;
diff --git a/extra/curl/curl-8.12.1/lib/noproxy.h b/extra/curl/curl-8.14.1/lib/noproxy.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/noproxy.h
rename to extra/curl/curl-8.14.1/lib/noproxy.h
diff --git a/extra/curl/curl-8.12.1/lib/openldap.c b/extra/curl/curl-8.14.1/lib/openldap.c
similarity index 81%
rename from extra/curl/curl-8.12.1/lib/openldap.c
rename to extra/curl/curl-8.14.1/lib/openldap.c
index 22e5bdd27901..6343c40c094a 100644
--- a/extra/curl/curl-8.12.1/lib/openldap.c
+++ b/extra/curl/curl-8.14.1/lib/openldap.c
@@ -41,12 +41,13 @@
 #include <ldap.h>
 
 #include "urldata.h"
+#include "url.h"
 #include <curl/curl.h>
 #include "sendf.h"
 #include "vtls/vtls.h"
 #include "transfer.h"
 #include "curl_ldap.h"
-#include "curl_base64.h"
+#include "curlx/base64.h"
 #include "cfilters.h"
 #include "connect.h"
 #include "curl_sasl.h"
@@ -202,15 +203,20 @@ struct ldapreqinfo {
   int nument;
 };
 
+/* meta key for storing ldapconninfo at easy handle */
+#define CURL_META_LDAP_EASY   "meta:proto:ldap:easy"
+/* meta key for storing ldapconninfo at connection */
+#define CURL_META_LDAP_CONN   "meta:proto:ldap:conn"
+
+
 /*
  * oldap_state()
  *
  * This is the ONLY way to change LDAP state!
  */
-static void oldap_state(struct Curl_easy *data, ldapstate newstate)
+static void oldap_state(struct Curl_easy *data, struct ldapconninfo *li,
+                        ldapstate newstate)
 {
-  struct ldapconninfo *ldapc = data->conn->proto.ldapc;
-
 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
   /* for debug purposes */
   static const char * const names[] = {
@@ -225,12 +231,12 @@ static void oldap_state(struct Curl_easy *data, ldapstate newstate)
     /* LAST */
   };
 
-  if(ldapc->state != newstate)
+  if(li->state != newstate)
     infof(data, "LDAP %p state change from %s to %s",
-          (void *)ldapc, names[ldapc->state], names[newstate]);
+          (void *)li, names[li->state], names[newstate]);
 #endif
-
-  ldapc->state = newstate;
+  (void)data;
+  li->state = newstate;
 }
 
 /* Map some particular LDAP error codes to CURLcode values. */
@@ -277,7 +283,7 @@ static CURLcode oldap_url_parse(struct Curl_easy *data, LDAPURLDesc **ludp)
     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]))
+    if((size_t) rc < CURL_ARRAYSIZE(url_errs))
       msg = url_errs[rc];
     failf(data, "LDAP local: %s", msg);
   }
@@ -288,9 +294,13 @@ static CURLcode oldap_url_parse(struct Curl_easy *data, LDAPURLDesc **ludp)
 static CURLcode oldap_parse_login_options(struct connectdata *conn)
 {
   CURLcode result = CURLE_OK;
-  struct ldapconninfo *li = conn->proto.ldapc;
+  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
   const char *ptr = conn->options;
 
+  DEBUGASSERT(li);
+  if(!li)
+    return CURLE_FAILED_INIT;
+
   while(!result && ptr && *ptr) {
     const char *key = ptr;
     const char *value;
@@ -334,7 +344,12 @@ static CURLcode oldap_setup_connection(struct Curl_easy *data,
  */
 static CURLcode oldap_get_message(struct Curl_easy *data, struct bufref *out)
 {
-  struct berval *servercred = data->conn->proto.ldapc->servercred;
+  struct ldapconninfo *li =
+    Curl_conn_meta_get(data->conn, CURL_META_LDAP_CONN);
+  struct berval *servercred = li ? li->servercred : NULL;
+  DEBUGASSERT(li);
+  if(!li)
+    return CURLE_FAILED_INIT;
 
   if(!servercred || !servercred->bv_val)
     return CURLE_WEIRD_SERVER_REPLY;
@@ -349,12 +364,15 @@ static CURLcode oldap_perform_auth(struct Curl_easy *data, const char *mech,
                                    const struct bufref *initresp)
 {
   struct connectdata *conn = data->conn;
-  struct ldapconninfo *li = conn->proto.ldapc;
+  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
   struct berval cred;
   struct berval *pcred = &cred;
   int rc;
 
-  cred.bv_val = (char *) Curl_bufref_ptr(initresp);
+  DEBUGASSERT(li);
+  if(!li)
+    return CURLE_FAILED_INIT;
+  cred.bv_val = (char *)CURL_UNCONST(Curl_bufref_ptr(initresp));
   cred.bv_len = Curl_bufref_len(initresp);
   if(!cred.bv_val)
     pcred = NULL;
@@ -371,12 +389,14 @@ static CURLcode oldap_continue_auth(struct Curl_easy *data, const char *mech,
                                     const struct bufref *resp)
 {
   struct connectdata *conn = data->conn;
-  struct ldapconninfo *li = conn->proto.ldapc;
+  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
   struct berval cred;
   struct berval *pcred = &cred;
   int rc;
 
-  cred.bv_val = (char *) Curl_bufref_ptr(resp);
+  if(!li)
+    return CURLE_FAILED_INIT;
+  cred.bv_val = (char *)CURL_UNCONST(Curl_bufref_ptr(resp));
   cred.bv_len = Curl_bufref_len(resp);
   if(!cred.bv_val)
     pcred = NULL;
@@ -391,11 +411,15 @@ static CURLcode oldap_continue_auth(struct Curl_easy *data, const char *mech,
  */
 static CURLcode oldap_cancel_auth(struct Curl_easy *data, const char *mech)
 {
-  struct ldapconninfo *li = data->conn->proto.ldapc;
-  int rc = ldap_sasl_bind(li->ld, NULL, LDAP_SASL_NULL, NULL, NULL, NULL,
-                          &li->msgid);
+  struct ldapconninfo *li =
+    Curl_conn_meta_get(data->conn, CURL_META_LDAP_CONN);
+  int rc;
 
   (void)mech;
+  if(!li)
+    return CURLE_FAILED_INIT;
+  rc = ldap_sasl_bind(li->ld, NULL, LDAP_SASL_NULL, NULL, NULL, NULL,
+                      &li->msgid);
   if(rc != LDAP_SUCCESS)
     return oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND);
   return CURLE_OK;
@@ -405,11 +429,13 @@ static CURLcode oldap_cancel_auth(struct Curl_easy *data, const char *mech)
 static CURLcode oldap_perform_bind(struct Curl_easy *data, ldapstate newstate)
 {
   struct connectdata *conn = data->conn;
-  struct ldapconninfo *li = conn->proto.ldapc;
+  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
   char *binddn = NULL;
   struct berval passwd;
   int rc;
 
+  if(!li)
+    return CURLE_FAILED_INIT;
   passwd.bv_val = NULL;
   passwd.bv_len = 0;
 
@@ -425,39 +451,47 @@ static CURLcode oldap_perform_bind(struct Curl_easy *data, ldapstate newstate)
     return oldap_map_error(rc,
                            data->state.aptr.user ?
                            CURLE_LOGIN_DENIED : CURLE_LDAP_CANNOT_BIND);
-  oldap_state(data, newstate);
+  oldap_state(data, li, newstate);
   return CURLE_OK;
 }
 
 /* Query the supported SASL authentication mechanisms. */
 static CURLcode oldap_perform_mechs(struct Curl_easy *data)
 {
-  struct ldapconninfo *li = data->conn->proto.ldapc;
+  struct ldapconninfo *li =
+    Curl_conn_meta_get(data->conn, CURL_META_LDAP_CONN);
   int rc;
   static const char * const supportedSASLMechanisms[] = {
     "supportedSASLMechanisms",
     NULL
   };
 
+  if(!li)
+    return CURLE_FAILED_INIT;
   rc = ldap_search_ext(li->ld, "", LDAP_SCOPE_BASE, "(objectclass=*)",
-                       (char **) supportedSASLMechanisms, 0,
+                       (char **)CURL_UNCONST(supportedSASLMechanisms), 0,
                        NULL, NULL, NULL, 0, &li->msgid);
   if(rc != LDAP_SUCCESS)
     return oldap_map_error(rc, CURLE_LOGIN_DENIED);
-  oldap_state(data, OLDAP_MECHS);
+  oldap_state(data, li, OLDAP_MECHS);
   return CURLE_OK;
 }
 
 /* Starts SASL bind. */
 static CURLcode oldap_perform_sasl(struct Curl_easy *data)
 {
+  struct ldapconninfo *li =
+    Curl_conn_meta_get(data->conn, CURL_META_LDAP_CONN);
   saslprogress progress = SASL_IDLE;
-  struct ldapconninfo *li = data->conn->proto.ldapc;
-  CURLcode result = Curl_sasl_start(&li->sasl, data, TRUE, &progress);
+  CURLcode result;
+
+  if(!li)
+    return CURLE_FAILED_INIT;
+  result = Curl_sasl_start(&li->sasl, data, TRUE, &progress);
 
-  oldap_state(data, OLDAP_SASL);
+  oldap_state(data, li, OLDAP_SASL);
   if(!result && progress != SASL_INPROGRESS)
-    result = CURLE_LOGIN_DENIED;
+    result = Curl_sasl_is_blocked(&li->sasl, data);
   return result;
 }
 
@@ -466,17 +500,22 @@ static Sockbuf_IO ldapsb_tls;
 
 static bool ssl_installed(struct connectdata *conn)
 {
-  return conn->proto.ldapc->recv != NULL;
+  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
+  return li && li->recv != NULL;
 }
 
 static CURLcode oldap_ssl_connect(struct Curl_easy *data, ldapstate newstate)
 {
   struct connectdata *conn = data->conn;
-  struct ldapconninfo *li = conn->proto.ldapc;
+  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
   bool ssldone = FALSE;
-  CURLcode result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
+  CURLcode result;
+
+  if(!li)
+    return CURLE_FAILED_INIT;
+  result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
   if(!result) {
-    oldap_state(data, newstate);
+    oldap_state(data, li, newstate);
 
     if(ssldone) {
       Sockbuf *sb;
@@ -495,48 +534,72 @@ static CURLcode oldap_ssl_connect(struct Curl_easy *data, ldapstate newstate)
 /* Send the STARTTLS request */
 static CURLcode oldap_perform_starttls(struct Curl_easy *data)
 {
-  struct ldapconninfo *li = data->conn->proto.ldapc;
-  int rc = ldap_start_tls(li->ld, NULL, NULL, &li->msgid);
+  struct ldapconninfo *li =
+    Curl_conn_meta_get(data->conn, CURL_META_LDAP_CONN);
+  int rc;
 
+  if(!li)
+    return CURLE_FAILED_INIT;
+  rc = ldap_start_tls(li->ld, NULL, NULL, &li->msgid);
   if(rc != LDAP_SUCCESS)
     return oldap_map_error(rc, CURLE_USE_SSL_FAILED);
-  oldap_state(data, OLDAP_STARTTLS);
+  oldap_state(data, li, OLDAP_STARTTLS);
   return CURLE_OK;
 }
 #endif
 
+static void oldap_easy_dtor(void *key, size_t klen, void *entry)
+{
+  struct ldapreqinfo *lr = entry;
+  (void)key;
+  (void)klen;
+  free(lr);
+}
+
+static void oldap_conn_dtor(void *key, size_t klen, void *entry)
+{
+  struct ldapconninfo *li = entry;
+  (void)key;
+  (void)klen;
+  if(li->ld) {
+    ldap_unbind_ext(li->ld, NULL, NULL);
+    li->ld = NULL;
+  }
+  free(li);
+}
+
 static CURLcode oldap_connect(struct Curl_easy *data, bool *done)
 {
   struct connectdata *conn = data->conn;
   struct ldapconninfo *li;
   static const int version = LDAP_VERSION3;
+  char *hosturl = NULL;
+  CURLcode result;
   int rc;
-  char *hosturl;
 #ifdef CURL_OPENLDAP_DEBUG
   static int do_trace = -1;
 #endif
 
   (void)done;
 
-  DEBUGASSERT(!conn->proto.ldapc);
   li = calloc(1, sizeof(struct ldapconninfo));
-  if(!li)
-    return CURLE_OUT_OF_MEMORY;
-  else {
-    CURLcode result;
-    li->proto = ldap_pvt_url_scheme2proto(data->state.up.scheme);
-    conn->proto.ldapc = li;
+  if(!li) {
+    result = CURLE_OUT_OF_MEMORY;
+    goto out;
+  }
 
-    /* Initialize the SASL storage */
-    Curl_sasl_init(&li->sasl, data, &saslldap);
+  result = Curl_conn_meta_set(conn, CURL_META_LDAP_CONN, li, oldap_conn_dtor);
+  if(result)
+    goto out;
 
-    /* Clear the TLS upgraded flag */
-    conn->bits.tls_upgraded = FALSE;
+  li->proto = ldap_pvt_url_scheme2proto(data->state.up.scheme);
 
-    result = oldap_parse_login_options(conn);
-    if(result)
-      return result;
-  }
+  /* Initialize the SASL storage */
+  Curl_sasl_init(&li->sasl, data, &saslldap);
+
+  result = oldap_parse_login_options(conn);
+  if(result)
+    goto out;
 
   hosturl = aprintf("%s://%s%s%s:%d",
                     conn->handler->scheme,
@@ -544,23 +607,25 @@ static CURLcode oldap_connect(struct Curl_easy *data, bool *done)
                     conn->host.name,
                     conn->bits.ipv6_ip ? "]" : "",
                     conn->remote_port);
-  if(!hosturl)
-    return CURLE_OUT_OF_MEMORY;
+  if(!hosturl) {
+    result = CURLE_OUT_OF_MEMORY;
+    goto out;
+  }
 
   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;
+    result = CURLE_COULDNT_CONNECT;
+    goto out;
   }
 
-  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);
+    curl_off_t e = 0;
+    if(!curlx_str_number(&env, &e, INT_MAX))
+      do_trace = e > 0;
   }
   if(do_trace)
     ldap_set_option(li->ld, LDAP_OPT_DEBUG_LEVEL, &do_trace);
@@ -573,23 +638,30 @@ static CURLcode oldap_connect(struct Curl_easy *data, bool *done)
   ldap_set_option(li->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
 
 #ifdef USE_SSL
-  if(Curl_conn_is_ssl(conn, FIRSTSOCKET))
-    return oldap_ssl_connect(data, OLDAP_SSL);
+  if(Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
+    result = oldap_ssl_connect(data, OLDAP_SSL);
+    goto out;
+  }
 
   if(data->set.use_ssl) {
-    CURLcode result = oldap_perform_starttls(data);
-
+    result = oldap_perform_starttls(data);
     if(!result || data->set.use_ssl != CURLUSESSL_TRY)
-      return result;
+      goto out;
   }
 #endif
 
-  if(li->sasl.prefmech != SASL_AUTH_NONE)
-    return oldap_perform_mechs(data);
+  if(li->sasl.prefmech != SASL_AUTH_NONE) {
+    result = oldap_perform_mechs(data);
+    goto out;
+  }
 
   /* 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);
+  result = oldap_perform_bind(data, OLDAP_BIND);
+
+out:
+  free(hosturl);
+  return result;
 }
 
 /* Handle the supported SASL mechanisms query response */
@@ -597,12 +669,14 @@ static CURLcode oldap_state_mechs_resp(struct Curl_easy *data,
                                        LDAPMessage *msg, int code)
 {
   struct connectdata *conn = data->conn;
-  struct ldapconninfo *li = conn->proto.ldapc;
+  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
   int rc;
   BerElement *ber = NULL;
   CURLcode result = CURLE_OK;
   struct berval bv, *bvals;
 
+  if(!li)
+    return CURLE_FAILED_INIT;
   switch(ldap_msgtype(msg)) {
   case LDAP_RES_SEARCH_ENTRY:
     /* Got a list of supported SASL mechanisms. */
@@ -662,11 +736,13 @@ static CURLcode oldap_state_sasl_resp(struct Curl_easy *data,
                                       LDAPMessage *msg, int code)
 {
   struct connectdata *conn = data->conn;
-  struct ldapconninfo *li = conn->proto.ldapc;
+  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
   CURLcode result = CURLE_OK;
   saslprogress progress;
   int rc;
 
+  if(!li)
+    return CURLE_FAILED_INIT;
   li->servercred = NULL;
   rc = ldap_parse_sasl_bind_result(li->ld, msg, &li->servercred, 0);
   if(rc != LDAP_SUCCESS) {
@@ -676,7 +752,7 @@ static CURLcode oldap_state_sasl_resp(struct Curl_easy *data,
   else {
     result = Curl_sasl_continue(&li->sasl, data, code, &progress);
     if(!result && progress != SASL_INPROGRESS)
-      oldap_state(data, OLDAP_STOP);
+      oldap_state(data, li, OLDAP_STOP);
   }
 
   if(li->servercred)
@@ -689,11 +765,14 @@ 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;
+  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
   CURLcode result = CURLE_OK;
   struct berval *bv = NULL;
   int rc;
 
+  if(!li)
+    return CURLE_FAILED_INIT;
+
   if(code != LDAP_SUCCESS)
     return oldap_map_error(code, CURLE_LDAP_CANNOT_BIND);
 
@@ -704,7 +783,7 @@ static CURLcode oldap_state_bind_resp(struct Curl_easy *data, LDAPMessage *msg,
     result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND);
   }
   else
-    oldap_state(data, OLDAP_STOP);
+    oldap_state(data, li, OLDAP_STOP);
 
   if(bv)
     ber_bvfree(bv);
@@ -715,12 +794,15 @@ 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;
+  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
   LDAPMessage *msg = NULL;
   struct timeval tv = {0, 0};
   int code = LDAP_SUCCESS;
   int rc;
 
+  if(!li)
+    return CURLE_FAILED_INIT;
+
   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);
@@ -795,14 +877,13 @@ static CURLcode oldap_connecting(struct Curl_easy *data, bool *done)
     if(result)
       result = oldap_map_error(code, CURLE_USE_SSL_FAILED);
     else if(ssl_installed(conn)) {
-      conn->bits.tls_upgraded = TRUE;
       if(li->sasl.prefmech != SASL_AUTH_NONE)
         result = oldap_perform_mechs(data);
       else if(data->state.aptr.user)
         result = oldap_perform_bind(data, OLDAP_BIND);
       else {
         /* Version 3 supported: no bind required */
-        oldap_state(data, OLDAP_STOP);
+        oldap_state(data, li, OLDAP_STOP);
         result = CURLE_OK;
       }
     }
@@ -842,7 +923,7 @@ static CURLcode oldap_disconnect(struct Curl_easy *data,
                                  struct connectdata *conn,
                                  bool dead_connection)
 {
-  struct ldapconninfo *li = conn->proto.ldapc;
+  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
   (void) dead_connection;
 #ifndef USE_SSL
   (void)data;
@@ -861,8 +942,6 @@ static CURLcode oldap_disconnect(struct Curl_easy *data,
       li->ld = NULL;
     }
     Curl_sasl_cleanup(conn, li->sasl.authused);
-    conn->proto.ldapc = NULL;
-    free(li);
   }
   return CURLE_OK;
 }
@@ -870,50 +949,55 @@ static CURLcode oldap_disconnect(struct Curl_easy *data,
 static CURLcode oldap_do(struct Curl_easy *data, bool *done)
 {
   struct connectdata *conn = data->conn;
-  struct ldapconninfo *li = conn->proto.ldapc;
+  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
   struct ldapreqinfo *lr;
   CURLcode result;
   int rc;
   LDAPURLDesc *lud;
   int msgid;
 
+  if(!li)
+    return CURLE_FAILED_INIT;
   connkeep(conn, "OpenLDAP do");
 
   infof(data, "LDAP local: %s", data->state.url);
 
   result = oldap_url_parse(data, &lud);
-  if(!result) {
+  if(result)
+    goto out;
+
 #ifdef USE_SSL
-    if(ssl_installed(conn)) {
-      Sockbuf *sb;
-      /* re-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);
-    }
+  if(ssl_installed(conn)) {
+    Sockbuf *sb;
+    /* re-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);
+  }
 #endif
 
-    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_xfer_setup1(data, CURL_XFER_RECV, -1, FALSE);
-        *done = TRUE;
-      }
-    }
+  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;
+    goto out;
+  }
+
+  lr = calloc(1, sizeof(struct ldapreqinfo));
+  if(!lr ||
+     Curl_meta_set(data, CURL_META_LDAP_EASY, lr, oldap_easy_dtor)) {
+    ldap_abandon_ext(li->ld, msgid, NULL, NULL);
+    result = CURLE_OUT_OF_MEMORY;
+    goto out;
   }
+
+  lr->msgid = msgid;
+  Curl_xfer_setup1(data, CURL_XFER_RECV, -1, FALSE);
+  *done = TRUE;
+
+out:
   return result;
 }
 
@@ -921,7 +1005,7 @@ static CURLcode oldap_done(struct Curl_easy *data, CURLcode res,
                            bool premature)
 {
   struct connectdata *conn = data->conn;
-  struct ldapreqinfo *lr = data->req.p.ldap;
+  struct ldapreqinfo *lr = Curl_meta_get(data, CURL_META_LDAP_EASY);
 
   (void)res;
   (void)premature;
@@ -929,12 +1013,13 @@ static CURLcode oldap_done(struct Curl_easy *data, CURLcode res,
   if(lr) {
     /* if there was a search in progress, abandon it */
     if(lr->msgid) {
-      struct ldapconninfo *li = conn->proto.ldapc;
-      ldap_abandon_ext(li->ld, lr->msgid, NULL, NULL);
+      struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
+      if(li && li->ld) {
+        ldap_abandon_ext(li->ld, lr->msgid, NULL, NULL);
+      }
       lr->msgid = 0;
     }
-    data->req.p.ldap = NULL;
-    free(lr);
+    Curl_meta_remove(data, CURL_META_LDAP_EASY);
   }
 
   return CURLE_OK;
@@ -952,13 +1037,13 @@ static CURLcode client_write(struct Curl_easy *data,
        separator, drop the latter. */
     if(!len && plen && prefix[plen - 1] == ' ')
       plen--;
-    result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) prefix, plen);
+    result = Curl_client_write(data, CLIENTWRITE_BODY, prefix, plen);
   }
   if(!result && value) {
-    result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) value, len);
+    result = Curl_client_write(data, CLIENTWRITE_BODY, value, len);
   }
   if(!result && suffix) {
-    result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) suffix, slen);
+    result = Curl_client_write(data, CLIENTWRITE_BODY, suffix, slen);
   }
   return result;
 }
@@ -967,8 +1052,8 @@ 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;
+  struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
+  struct ldapreqinfo *lr = Curl_meta_get(data, CURL_META_LDAP_EASY);
   int rc;
   LDAPMessage *msg = NULL;
   BerElement *ber = NULL;
@@ -982,6 +1067,10 @@ static ssize_t oldap_recv(struct Curl_easy *data, int sockindex, char *buf,
   (void)len;
   (void)buf;
   (void)sockindex;
+  if(!li || !lr) {
+    *err = CURLE_FAILED_INIT;
+    return -1;
+  }
 
   rc = ldap_result(li->ld, lr->msgid, LDAP_MSG_ONE, &tv, &msg);
   if(rc < 0) {
@@ -1088,8 +1177,8 @@ static ssize_t oldap_recv(struct Curl_easy *data, int sockindex, char *buf,
 
           /* Binary value, encode to base64. */
           if(bvals[i].bv_len)
-            result = Curl_base64_encode(bvals[i].bv_val, bvals[i].bv_len,
-                                        &val_b64, &val_b64_sz);
+            result = curlx_base64_encode(bvals[i].bv_val, bvals[i].bv_len,
+                                         &val_b64, &val_b64_sz);
           if(!result)
             result = client_write(data, STRCONST(": "), val_b64, val_b64_sz,
                                   STRCONST("\n"));
@@ -1167,18 +1256,21 @@ ldapsb_tls_read(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
   if(data) {
     struct connectdata *conn = data->conn;
     if(conn) {
-      struct ldapconninfo *li = conn->proto.ldapc;
+      struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
       CURLcode err = CURLE_RECV_ERROR;
 
+      if(!li) {
+        SET_SOCKERRNO(SOCKEINVAL);
+        return -1;
+      }
       ret = (li->recv)(data, FIRSTSOCKET, buf, len, &err);
       if(ret < 0 && err == CURLE_AGAIN) {
-        SET_SOCKERRNO(EWOULDBLOCK);
+        SET_SOCKERRNO(SOCKEWOULDBLOCK);
       }
     }
   }
   return ret;
 }
-
 static ber_slen_t
 ldapsb_tls_write(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
 {
@@ -1187,11 +1279,16 @@ ldapsb_tls_write(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
   if(data) {
     struct connectdata *conn = data->conn;
     if(conn) {
-      struct ldapconninfo *li = conn->proto.ldapc;
+      struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
       CURLcode err = CURLE_SEND_ERROR;
+
+      if(!li) {
+        SET_SOCKERRNO(SOCKEINVAL);
+        return -1;
+      }
       ret = (li->send)(data, FIRSTSOCKET, buf, len, FALSE, &err);
       if(ret < 0 && err == CURLE_AGAIN) {
-        SET_SOCKERRNO(EWOULDBLOCK);
+        SET_SOCKERRNO(SOCKEWOULDBLOCK);
       }
     }
   }
diff --git a/extra/curl/curl-8.12.1/lib/optiontable.pl b/extra/curl/curl-8.14.1/lib/optiontable.pl
similarity index 97%
rename from extra/curl/curl-8.12.1/lib/optiontable.pl
rename to extra/curl/curl-8.14.1/lib/optiontable.pl
index 350262c70f1b..cc38a922cc96 100755
--- a/extra/curl/curl-8.12.1/lib/optiontable.pl
+++ b/extra/curl/curl-8.14.1/lib/optiontable.pl
@@ -31,7 +31,7 @@
 #include "easyoptions.h"
 
 /* all easy setopt options listed in alphabetical order */
-struct curl_easyoption Curl_easyopts[] = {
+const struct curl_easyoption Curl_easyopts[] = {
 HEAD
     ;
 
@@ -109,7 +109,7 @@ sub add {
             $o =~ s/^CURLOPT_//;
             $n =~ s/^CURLOPT_//;
             $alias{$o} = $n;
-            push @names, $o,
+            push @names, $o;
         }
     }
 }
diff --git a/extra/curl/curl-8.12.1/lib/parsedate.c b/extra/curl/curl-8.14.1/lib/parsedate.c
similarity index 95%
rename from extra/curl/curl-8.12.1/lib/parsedate.c
rename to extra/curl/curl-8.14.1/lib/parsedate.c
index 65b231ad5c69..7e0c69106c0a 100644
--- a/extra/curl/curl-8.12.1/lib/parsedate.c
+++ b/extra/curl/curl-8.14.1/lib/parsedate.c
@@ -81,8 +81,9 @@
 
 #include <curl/curl.h>
 #include "strcase.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
 #include "parsedate.h"
+#include "curlx/strparse.h"
 
 /*
  * parsedate()
@@ -100,10 +101,12 @@ static int parsedate(const char *date, time_t *output);
 #define PARSEDATE_OK     0
 #define PARSEDATE_FAIL   -1
 #define PARSEDATE_LATER  1
+#if defined(HAVE_TIME_T_UNSIGNED) || (SIZEOF_TIME_T < 5)
 #define PARSEDATE_SOONER 2
+#endif
 
 #if !defined(CURL_DISABLE_PARSEDATE) || !defined(CURL_DISABLE_FTP) || \
-  !defined(CURL_DISABLE_FILE)
+  !defined(CURL_DISABLE_FILE) || defined(USE_GNUTLS)
 /* These names are also used by FTP and FILE code */
 const char * const Curl_wkday[] =
 {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
@@ -253,7 +256,7 @@ static int checktz(const char *check, size_t len)
   if(len > 4) /* longer than any valid timezone */
     return -1;
 
-  for(i = 0; i < sizeof(tz)/sizeof(tz[0]); i++) {
+  for(i = 0; i < CURL_ARRAYSIZE(tz); i++) {
     size_t ilen = strlen(what->name);
     if((ilen == len) &&
        strncasecompare(check, what->name, len))
@@ -336,7 +339,7 @@ static bool match_time(const char *date,
   *h = hh;
   *m = mm;
   *s = ss;
-  *endp = (char *)p;
+  *endp = (char *)CURL_UNCONST(p);
   return TRUE;
 }
 
@@ -409,7 +412,7 @@ static int parsedate(const char *date, time_t *output)
     }
     else if(ISDIGIT(*date)) {
       /* a digit */
-      int val;
+      unsigned int val;
       char *end;
       if((secnum == -1) &&
          match_time(date, &hournum, &minnum, &secnum, &end)) {
@@ -417,29 +420,18 @@ static int parsedate(const char *date, time_t *output)
         date = end;
       }
       else {
-        long lval;
-        int error;
-        int old_errno;
-
-        old_errno = errno;
-        errno = 0;
-        lval = strtol(date, &end, 10);
-        error = errno;
-        if(errno != old_errno)
-          errno = old_errno;
-
-        if(error)
+        curl_off_t lval;
+        int num_digits = 0;
+        const char *p = date;
+        if(curlx_str_number(&p, &lval, 99999999))
           return PARSEDATE_FAIL;
 
-#if LONG_MAX != INT_MAX
-        if((lval > (long)INT_MAX) || (lval < (long)INT_MIN))
-          return PARSEDATE_FAIL;
-#endif
-
-        val = curlx_sltosi(lval);
+        /* we know num_digits cannot be larger than 8 */
+        num_digits = (int)(p - date);
+        val = (unsigned int)lval;
 
         if((tzoff == -1) &&
-           ((end - date) == 4) &&
+           (num_digits == 4) &&
            (val <= 1400) &&
            (indate < date) &&
            ((date[-1] == '+' || date[-1] == '-'))) {
@@ -459,10 +451,10 @@ static int parsedate(const char *date, time_t *output)
           tzoff = date[-1]=='+' ? -tzoff : tzoff;
         }
 
-        if(((end - date) == 8) &&
-           (yearnum == -1) &&
-           (monnum == -1) &&
-           (mdaynum == -1)) {
+        else if((num_digits == 8) &&
+                (yearnum == -1) &&
+                (monnum == -1) &&
+                (mdaynum == -1)) {
           /* 8 digits, no year, month or day yet. This is YYYYMMDD */
           found = TRUE;
           yearnum = val/10000;
@@ -494,7 +486,7 @@ static int parsedate(const char *date, time_t *output)
         if(!found)
           return PARSEDATE_FAIL;
 
-        date = end;
+        date = p;
       }
     }
 
diff --git a/extra/curl/curl-8.12.1/lib/parsedate.h b/extra/curl/curl-8.14.1/lib/parsedate.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/parsedate.h
rename to extra/curl/curl-8.14.1/lib/parsedate.h
diff --git a/extra/curl/curl-8.12.1/lib/pingpong.c b/extra/curl/curl-8.14.1/lib/pingpong.c
similarity index 86%
rename from extra/curl/curl-8.12.1/lib/pingpong.c
rename to extra/curl/curl-8.14.1/lib/pingpong.c
index bae6dd273fb4..c5513f605028 100644
--- a/extra/curl/curl-8.12.1/lib/pingpong.c
+++ b/extra/curl/curl-8.14.1/lib/pingpong.c
@@ -29,6 +29,7 @@
 
 #include "urldata.h"
 #include "cfilters.h"
+#include "connect.h"
 #include "sendf.h"
 #include "select.h"
 #include "progress.h"
@@ -50,10 +51,10 @@
 timediff_t Curl_pp_state_timeout(struct Curl_easy *data,
                                  struct pingpong *pp, bool disconnecting)
 {
-  struct connectdata *conn = data->conn;
   timediff_t timeout_ms; /* in milliseconds */
   timediff_t response_time = (data->set.server_response_timeout) ?
     data->set.server_response_timeout : pp->response_time;
+  struct curltime now = curlx_now();
 
   /* if CURLOPT_SERVER_RESPONSE_TIMEOUT is set, use that to determine
      remaining time, or use pp->response because SERVER_RESPONSE_TIMEOUT is
@@ -62,18 +63,20 @@ timediff_t Curl_pp_state_timeout(struct Curl_easy *data,
 
   /* Without a requested timeout, we only wait 'response_time' seconds for the
      full response to arrive before we bail out */
-  timeout_ms = response_time -
-    Curl_timediff(Curl_now(), pp->response); /* spent time */
+  timeout_ms = response_time - curlx_timediff(now, pp->response);
 
   if(data->set.timeout && !disconnecting) {
-    /* if timeout is requested, find out how much remaining time we have */
-    timediff_t timeout2_ms = data->set.timeout - /* timeout time */
-      Curl_timediff(Curl_now(), conn->now); /* spent time */
-
+    /* if timeout is requested, find out how much overall remains */
+    timediff_t timeout2_ms = Curl_timeleft(data, &now, FALSE);
     /* pick the lowest number */
     timeout_ms = CURLMIN(timeout_ms, timeout2_ms);
   }
 
+  if(disconnecting) {
+    timediff_t total_left_ms = Curl_timeleft(data, NULL, FALSE);
+    timeout_ms = CURLMIN(timeout_ms, CURLMAX(total_left_ms, 0));
+  }
+
   return timeout_ms;
 }
 
@@ -96,6 +99,7 @@ CURLcode Curl_pp_statemach(struct Curl_easy *data,
     return CURLE_OPERATION_TIMEDOUT; /* already too little time */
   }
 
+  DEBUGF(infof(data, "pp_statematch, timeout=%" FMT_TIMEDIFF_T, timeout_ms));
   if(block) {
     interval_ms = 1000;  /* use 1 second timeout intervals */
     if(timeout_ms < interval_ms)
@@ -123,7 +127,7 @@ CURLcode Curl_pp_statemach(struct Curl_easy *data,
     if(Curl_pgrsUpdate(data))
       result = CURLE_ABORTED_BY_CALLBACK;
     else
-      result = Curl_speedcheck(data, Curl_now());
+      result = Curl_speedcheck(data, curlx_now());
 
     if(result)
       return result;
@@ -135,6 +139,8 @@ CURLcode Curl_pp_statemach(struct Curl_easy *data,
   }
   else if(rc)
     result = pp->statemachine(data, data->conn);
+  else if(disconnecting)
+    return CURLE_OPERATION_TIMEDOUT;
 
   return result;
 }
@@ -142,11 +148,13 @@ CURLcode Curl_pp_statemach(struct Curl_easy *data,
 /* initialize stuff to prepare for reading a fresh new response */
 void Curl_pp_init(struct pingpong *pp)
 {
+  DEBUGASSERT(!pp->initialised);
   pp->nread_resp = 0;
-  pp->response = Curl_now(); /* start response time-out now! */
+  pp->response = curlx_now(); /* start response time-out now! */
   pp->pending_resp = TRUE;
-  Curl_dyn_init(&pp->sendbuf, DYN_PINGPPONG_CMD);
-  Curl_dyn_init(&pp->recvbuf, DYN_PINGPPONG_CMD);
+  curlx_dyn_init(&pp->sendbuf, DYN_PINGPPONG_CMD);
+  curlx_dyn_init(&pp->recvbuf, DYN_PINGPPONG_CMD);
+  pp->initialised = TRUE;
 }
 
 /***********************************************************************
@@ -182,19 +190,19 @@ CURLcode Curl_pp_vsendf(struct Curl_easy *data,
     /* cannot send without a connection! */
     return CURLE_SEND_ERROR;
 
-  Curl_dyn_reset(&pp->sendbuf);
-  result = Curl_dyn_vaddf(&pp->sendbuf, fmt, args);
+  curlx_dyn_reset(&pp->sendbuf);
+  result = curlx_dyn_vaddf(&pp->sendbuf, fmt, args);
   if(result)
     return result;
 
   /* append CRLF */
-  result = Curl_dyn_addn(&pp->sendbuf, "\r\n", 2);
+  result = curlx_dyn_addn(&pp->sendbuf, "\r\n", 2);
   if(result)
     return result;
 
   pp->pending_resp = TRUE;
-  write_len = Curl_dyn_len(&pp->sendbuf);
-  s = Curl_dyn_ptr(&pp->sendbuf);
+  write_len = curlx_dyn_len(&pp->sendbuf);
+  s = curlx_dyn_ptr(&pp->sendbuf);
 
 #ifdef HAVE_GSSAPI
   conn->data_prot = PROT_CMD;
@@ -223,7 +231,7 @@ CURLcode Curl_pp_vsendf(struct Curl_easy *data,
   else {
     pp->sendthis = NULL;
     pp->sendleft = pp->sendsize = 0;
-    pp->response = Curl_now();
+    pp->response = curlx_now();
   }
 
   return CURLE_OK;
@@ -297,10 +305,10 @@ CURLcode Curl_pp_readresp(struct Curl_easy *data,
     if(pp->nfinal) {
       /* a previous call left this many bytes in the beginning of the buffer as
          that was the final line; now ditch that */
-      size_t full = Curl_dyn_len(&pp->recvbuf);
+      size_t full = curlx_dyn_len(&pp->recvbuf);
 
       /* trim off the "final" leading part */
-      Curl_dyn_tail(&pp->recvbuf, full -  pp->nfinal);
+      curlx_dyn_tail(&pp->recvbuf, full -  pp->nfinal);
 
       pp->nfinal = 0; /* now gone */
     }
@@ -318,7 +326,7 @@ CURLcode Curl_pp_readresp(struct Curl_easy *data,
         return CURLE_RECV_ERROR;
       }
 
-      result = Curl_dyn_addn(&pp->recvbuf, buffer, gotbytes);
+      result = curlx_dyn_addn(&pp->recvbuf, buffer, gotbytes);
       if(result)
         return result;
 
@@ -328,8 +336,8 @@ CURLcode Curl_pp_readresp(struct Curl_easy *data,
     }
 
     do {
-      char *line = Curl_dyn_ptr(&pp->recvbuf);
-      char *nl = memchr(line, '\n', Curl_dyn_len(&pp->recvbuf));
+      char *line = curlx_dyn_ptr(&pp->recvbuf);
+      char *nl = memchr(line, '\n', curlx_dyn_len(&pp->recvbuf));
       if(nl) {
         /* a newline is CRLF in pp-talk, so the CR is ignored as
            the line is not really terminated until the LF comes */
@@ -355,8 +363,8 @@ CURLcode Curl_pp_readresp(struct Curl_easy *data,
              parsers). Store the overflow counter to inform about additional
              data in this buffer after the endofresp line. */
           pp->nfinal = length;
-          if(Curl_dyn_len(&pp->recvbuf) > length)
-            pp->overflow = Curl_dyn_len(&pp->recvbuf) - length;
+          if(curlx_dyn_len(&pp->recvbuf) > length)
+            pp->overflow = curlx_dyn_len(&pp->recvbuf) - length;
           else
             pp->overflow = 0;
           *size = pp->nread_resp; /* size of the response */
@@ -364,11 +372,11 @@ CURLcode Curl_pp_readresp(struct Curl_easy *data,
           gotbytes = 0; /* force break out of outer loop */
           break;
         }
-        if(Curl_dyn_len(&pp->recvbuf) > length)
+        if(curlx_dyn_len(&pp->recvbuf) > length)
           /* keep the remaining piece */
-          Curl_dyn_tail((&pp->recvbuf), Curl_dyn_len(&pp->recvbuf) - length);
+          curlx_dyn_tail((&pp->recvbuf), curlx_dyn_len(&pp->recvbuf) - length);
         else
-          Curl_dyn_reset(&pp->recvbuf);
+          curlx_dyn_reset(&pp->recvbuf);
       }
       else {
         /* without a newline, there is no overflow */
@@ -434,21 +442,24 @@ CURLcode Curl_pp_flushsend(struct Curl_easy *data,
   else {
     pp->sendthis = NULL;
     pp->sendleft = pp->sendsize = 0;
-    pp->response = Curl_now();
+    pp->response = curlx_now();
   }
   return CURLE_OK;
 }
 
 CURLcode Curl_pp_disconnect(struct pingpong *pp)
 {
-  Curl_dyn_free(&pp->sendbuf);
-  Curl_dyn_free(&pp->recvbuf);
+  if(pp->initialised) {
+    curlx_dyn_free(&pp->sendbuf);
+    curlx_dyn_free(&pp->recvbuf);
+    memset(pp, 0, sizeof(*pp));
+  }
   return CURLE_OK;
 }
 
 bool Curl_pp_moredata(struct pingpong *pp)
 {
-  return !pp->sendleft && Curl_dyn_len(&pp->recvbuf) > pp->nfinal;
+  return !pp->sendleft && curlx_dyn_len(&pp->recvbuf) > pp->nfinal;
 }
 
 #endif
diff --git a/extra/curl/curl-8.12.1/lib/pingpong.h b/extra/curl/curl-8.14.1/lib/pingpong.h
similarity index 95%
rename from extra/curl/curl-8.12.1/lib/pingpong.h
rename to extra/curl/curl-8.14.1/lib/pingpong.h
index 72239ff05913..0665b83659eb 100644
--- a/extra/curl/curl-8.12.1/lib/pingpong.h
+++ b/extra/curl/curl-8.14.1/lib/pingpong.h
@@ -48,9 +48,6 @@ typedef enum {
  */
 struct pingpong {
   size_t nread_resp;  /* number of bytes currently read of a server response */
-  bool pending_resp;  /* set TRUE when a server response is pending or in
-                         progress, and is cleared once the last response is
-                         read */
   char *sendthis; /* pointer to a buffer that is to be sent to the server */
   size_t sendleft; /* number of bytes left to send from the sendthis buffer */
   size_t sendsize; /* total size of the sendthis buffer */
@@ -69,14 +66,18 @@ struct pingpong {
 
   CURLcode (*statemachine)(struct Curl_easy *data, struct connectdata *conn);
   bool (*endofresp)(struct Curl_easy *data, struct connectdata *conn,
-                    char *ptr, size_t len, int *code);
+                    const char *ptr, size_t len, int *code);
+  BIT(initialised);
+  BIT(pending_resp);  /* set TRUE when a server response is pending or in
+                         progress, and is cleared once the last response is
+                         read */
 };
 
 #define PINGPONG_SETUP(pp,s,e)                   \
   do {                                           \
-    pp->response_time = RESP_TIMEOUT;            \
-    pp->statemachine = s;                        \
-    pp->endofresp = e;                           \
+    (pp)->response_time = RESP_TIMEOUT;          \
+    (pp)->statemachine = s;                      \
+    (pp)->endofresp = e;                         \
   } while(0)
 
 /*
diff --git a/extra/curl/curl-8.12.1/lib/pop3.c b/extra/curl/curl-8.14.1/lib/pop3.c
similarity index 83%
rename from extra/curl/curl-8.12.1/lib/pop3.c
rename to extra/curl/curl-8.14.1/lib/pop3.c
index 07c7dbac403d..aa8c2d834a90 100644
--- a/extra/curl/curl-8.12.1/lib/pop3.c
+++ b/extra/curl/curl-8.14.1/lib/pop3.c
@@ -64,8 +64,8 @@
 #include "escape.h"
 #include "http.h" /* for HTTP proxy tunnel stuff */
 #include "socks.h"
+#include "pingpong.h"
 #include "pop3.h"
-#include "strtoofft.h"
 #include "strcase.h"
 #include "vtls/vtls.h"
 #include "cfilters.h"
@@ -76,13 +76,75 @@
 #include "bufref.h"
 #include "curl_sasl.h"
 #include "curl_md5.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
 #include "strdup.h"
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
 #include "memdebug.h"
 
+/* Authentication type flags */
+#define POP3_TYPE_CLEARTEXT (1 << 0)
+#define POP3_TYPE_APOP      (1 << 1)
+#define POP3_TYPE_SASL      (1 << 2)
+
+/* Authentication type values */
+#define POP3_TYPE_NONE      0
+#define POP3_TYPE_ANY       (POP3_TYPE_CLEARTEXT|POP3_TYPE_APOP|POP3_TYPE_SASL)
+
+/* This is the 5-bytes End-Of-Body marker for POP3 */
+#define POP3_EOB "\x0d\x0a\x2e\x0d\x0a"
+#define POP3_EOB_LEN 5
+
+/* meta key for storing protocol meta at easy handle */
+#define CURL_META_POP3_EASY   "meta:proto:pop3:easy"
+/* meta key for storing protocol meta at connection */
+#define CURL_META_POP3_CONN   "meta:proto:pop3:conn"
+
+/*
+ * POP3 easy handle state
+ */
+struct POP3 {
+  curl_pp_transfer transfer;
+  char *id;               /* Message ID */
+  char *custom;           /* Custom Request */
+};
+
+/*
+ * POP3 connection state
+ */
+typedef enum {
+  POP3_STOP,         /* do nothing state, stops the state machine */
+  POP3_SERVERGREET,  /* waiting for the initial greeting immediately after
+                        a connect */
+  POP3_CAPA,
+  POP3_STARTTLS,
+  POP3_UPGRADETLS,   /* asynchronously upgrade the connection to SSL/TLS
+                       (multi mode only) */
+  POP3_AUTH,
+  POP3_APOP,
+  POP3_USER,
+  POP3_PASS,
+  POP3_COMMAND,
+  POP3_QUIT,
+  POP3_LAST          /* never used */
+} pop3state;
+
+struct pop3_conn {
+  struct pingpong pp;
+  pop3state state;        /* Always use pop3.c:state() to change state! */
+  size_t eob;             /* Number of bytes of the EOB (End Of Body) that
+                             have been received so far */
+  size_t strip;           /* Number of bytes from the start to ignore as
+                             non-body */
+  struct SASL sasl;       /* SASL-related storage */
+  char *apoptimestamp;    /* APOP timestamp from the server greeting */
+  unsigned char authtypes; /* Accepted authentication types */
+  unsigned char preftype;  /* Preferred authentication type */
+  BIT(ssldone);           /* Is connect() over SSL done? */
+  BIT(tls_supported);     /* StartTLS capability supported by server */
+};
+
 /* Local API functions */
 static CURLcode pop3_regular_transfer(struct Curl_easy *data, bool *done);
 static CURLcode pop3_do(struct Curl_easy *data, bool *done);
@@ -245,10 +307,13 @@ static bool pop3_is_multiline(const char *cmdline)
  * types and allowed SASL mechanisms.
  */
 static bool pop3_endofresp(struct Curl_easy *data, struct connectdata *conn,
-                           char *line, size_t len, int *resp)
+                           const char *line, size_t len, int *resp)
 {
-  struct pop3_conn *pop3c = &conn->proto.pop3c;
+  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
   (void)data;
+  DEBUGASSERT(pop3c);
+  if(!pop3c) /* internal error */
+    return TRUE;
 
   /* Do we have an error response? */
   if(len >= 4 && !memcmp("-ERR", line, 4)) {
@@ -295,9 +360,15 @@ static bool pop3_endofresp(struct Curl_easy *data, struct connectdata *conn,
  */
 static CURLcode pop3_get_message(struct Curl_easy *data, struct bufref *out)
 {
-  char *message = Curl_dyn_ptr(&data->conn->proto.pop3c.pp.recvbuf);
-  size_t len = data->conn->proto.pop3c.pp.nfinal;
-
+  struct pop3_conn *pop3c =
+    Curl_conn_meta_get(data->conn, CURL_META_POP3_CONN);
+  char *message;
+  size_t len;
+
+  if(!pop3c)
+    return CURLE_FAILED_INIT;
+  message = curlx_dyn_ptr(&pop3c->pp.recvbuf);
+  len = pop3c->pp.nfinal;
   if(len > 2) {
     /* Find the start of the message */
     len -= 2;
@@ -329,30 +400,33 @@ static CURLcode pop3_get_message(struct Curl_easy *data, struct bufref *out)
  */
 static void pop3_state(struct Curl_easy *data, pop3state newstate)
 {
-  struct pop3_conn *pop3c = &data->conn->proto.pop3c;
+  struct pop3_conn *pop3c =
+    Curl_conn_meta_get(data->conn, CURL_META_POP3_CONN);
+  if(pop3c) {
 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
-  /* for debug purposes */
-  static const char * const names[] = {
-    "STOP",
-    "SERVERGREET",
-    "CAPA",
-    "STARTTLS",
-    "UPGRADETLS",
-    "AUTH",
-    "APOP",
-    "USER",
-    "PASS",
-    "COMMAND",
-    "QUIT",
-    /* LAST */
-  };
-
-  if(pop3c->state != newstate)
-    infof(data, "POP3 %p state change from %s to %s",
-          (void *)pop3c, names[pop3c->state], names[newstate]);
+    /* for debug purposes */
+    static const char * const names[] = {
+      "STOP",
+      "SERVERGREET",
+      "CAPA",
+      "STARTTLS",
+      "UPGRADETLS",
+      "AUTH",
+      "APOP",
+      "USER",
+      "PASS",
+      "COMMAND",
+      "QUIT",
+      /* LAST */
+    };
+
+    if(pop3c->state != newstate)
+      infof(data, "POP3 %p state change from %s to %s",
+            (void *)pop3c, names[pop3c->state], names[newstate]);
 #endif
 
-  pop3c->state = newstate;
+    pop3c->state = newstate;
+  }
 }
 
 /***********************************************************************
@@ -365,8 +439,11 @@ static void pop3_state(struct Curl_easy *data, pop3state newstate)
 static CURLcode pop3_perform_capa(struct Curl_easy *data,
                                   struct connectdata *conn)
 {
+  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
   CURLcode result = CURLE_OK;
-  struct pop3_conn *pop3c = &conn->proto.pop3c;
+
+  if(!pop3c)
+    return CURLE_FAILED_INIT;
 
   pop3c->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanisms yet */
   pop3c->sasl.authused = SASL_AUTH_NONE;  /* Clear the auth. mechanism used */
@@ -390,9 +467,14 @@ static CURLcode pop3_perform_capa(struct Curl_easy *data,
 static CURLcode pop3_perform_starttls(struct Curl_easy *data,
                                       struct connectdata *conn)
 {
-  /* Send the STLS command */
-  CURLcode result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "%s", "STLS");
+  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
+  CURLcode result;
 
+  if(!pop3c)
+    return CURLE_FAILED_INIT;
+
+  /* Send the STLS command */
+  result = Curl_pp_sendf(data, &pop3c->pp, "%s", "STLS");
   if(!result)
     pop3_state(data, POP3_STARTTLS);
 
@@ -410,17 +492,19 @@ static CURLcode pop3_perform_upgrade_tls(struct Curl_easy *data,
 {
 #ifdef USE_SSL
   /* Start the SSL connection */
-  struct pop3_conn *pop3c = &conn->proto.pop3c;
+  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
   CURLcode result;
   bool ssldone = FALSE;
 
+  if(!pop3c)
+    return CURLE_FAILED_INIT;
+
   if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
     result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
     if(result)
       goto out;
     /* Change the connection handler */
     conn->handler = &Curl_handler_pop3s;
-    conn->bits.tls_upgraded = TRUE;
   }
 
   DEBUGASSERT(!pop3c->ssldone);
@@ -450,8 +534,12 @@ static CURLcode pop3_perform_upgrade_tls(struct Curl_easy *data,
 static CURLcode pop3_perform_user(struct Curl_easy *data,
                                   struct connectdata *conn)
 {
+  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
   CURLcode result = CURLE_OK;
 
+  if(!pop3c)
+    return CURLE_FAILED_INIT;
+
   /* Check we have a username and password to authenticate with and end the
      connect phase if we do not */
   if(!data->state.aptr.user) {
@@ -461,7 +549,7 @@ static CURLcode pop3_perform_user(struct Curl_easy *data,
   }
 
   /* Send the USER command */
-  result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "USER %s",
+  result = Curl_pp_sendf(data, &pop3c->pp, "USER %s",
                          conn->user ? conn->user : "");
   if(!result)
     pop3_state(data, POP3_USER);
@@ -479,13 +567,16 @@ static CURLcode pop3_perform_user(struct Curl_easy *data,
 static CURLcode pop3_perform_apop(struct Curl_easy *data,
                                   struct connectdata *conn)
 {
+  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
   CURLcode result = CURLE_OK;
-  struct pop3_conn *pop3c = &conn->proto.pop3c;
   size_t i;
   struct MD5_context *ctxt;
   unsigned char digest[MD5_DIGEST_LEN];
   char secret[2 * MD5_DIGEST_LEN + 1];
 
+  if(!pop3c)
+    return CURLE_FAILED_INIT;
+
   /* Check we have a username and password to authenticate with and end the
      connect phase if we do not */
   if(!data->state.aptr.user) {
@@ -532,10 +623,14 @@ static CURLcode pop3_perform_auth(struct Curl_easy *data,
                                   const char *mech,
                                   const struct bufref *initresp)
 {
+  struct pop3_conn *pop3c =
+    Curl_conn_meta_get(data->conn, CURL_META_POP3_CONN);
   CURLcode result = CURLE_OK;
-  struct pop3_conn *pop3c = &data->conn->proto.pop3c;
   const char *ir = (const char *) Curl_bufref_ptr(initresp);
 
+  if(!pop3c)
+    return CURLE_FAILED_INIT;
+
   if(ir) {                                  /* AUTH <mech> ...<crlf> */
     /* Send the AUTH command with the initial response */
     result = Curl_pp_sendf(data, &pop3c->pp, "AUTH %s %s", mech, ir);
@@ -558,9 +653,12 @@ static CURLcode pop3_continue_auth(struct Curl_easy *data,
                                    const char *mech,
                                    const struct bufref *resp)
 {
-  struct pop3_conn *pop3c = &data->conn->proto.pop3c;
+  struct pop3_conn *pop3c =
+    Curl_conn_meta_get(data->conn, CURL_META_POP3_CONN);
 
   (void)mech;
+  if(!pop3c)
+    return CURLE_FAILED_INIT;
 
   return Curl_pp_sendf(data, &pop3c->pp,
                        "%s", (const char *) Curl_bufref_ptr(resp));
@@ -574,9 +672,12 @@ static CURLcode pop3_continue_auth(struct Curl_easy *data,
  */
 static CURLcode pop3_cancel_auth(struct Curl_easy *data, const char *mech)
 {
-  struct pop3_conn *pop3c = &data->conn->proto.pop3c;
+  struct pop3_conn *pop3c =
+    Curl_conn_meta_get(data->conn, CURL_META_POP3_CONN);
 
   (void)mech;
+  if(!pop3c)
+    return CURLE_FAILED_INIT;
 
   return Curl_pp_sendf(data, &pop3c->pp, "*");
 }
@@ -592,10 +693,13 @@ static CURLcode pop3_cancel_auth(struct Curl_easy *data, const char *mech)
 static CURLcode pop3_perform_authentication(struct Curl_easy *data,
                                             struct connectdata *conn)
 {
+  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
   CURLcode result = CURLE_OK;
-  struct pop3_conn *pop3c = &conn->proto.pop3c;
   saslprogress progress = SASL_IDLE;
 
+  if(!pop3c)
+    return CURLE_FAILED_INIT;
+
   /* Check we have enough data to authenticate with and end the
      connect phase if we do not */
   if(!Curl_sasl_can_authenticate(&pop3c->sasl, data)) {
@@ -622,11 +726,8 @@ static CURLcode pop3_perform_authentication(struct Curl_easy *data,
     if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_CLEARTEXT)
       /* Perform clear text authentication */
       result = pop3_perform_user(data, conn);
-    else {
-      /* Other mechanisms not supported */
-      infof(data, "No known authentication mechanisms supported");
-      result = CURLE_LOGIN_DENIED;
-    }
+    else
+      result = Curl_sasl_is_blocked(&pop3c->sasl, data);
   }
 
   return result;
@@ -642,9 +743,13 @@ static CURLcode pop3_perform_command(struct Curl_easy *data)
 {
   CURLcode result = CURLE_OK;
   struct connectdata *conn = data->conn;
-  struct POP3 *pop3 = data->req.p.pop3;
+  struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY);
+  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
   const char *command = NULL;
 
+  if(!pop3 || !pop3c)
+    return CURLE_FAILED_INIT;
+
   /* Calculate the default command */
   if(pop3->id[0] == '\0' || data->set.list_only) {
     command = "LIST";
@@ -661,10 +766,9 @@ static CURLcode pop3_perform_command(struct Curl_easy *data)
 
   /* Send the command */
   if(pop3->id[0] != '\0')
-    result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "%s %s",
-                           command, pop3->id);
+    result = Curl_pp_sendf(data, &pop3c->pp, "%s %s", command, pop3->id);
   else
-    result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "%s", command);
+    result = Curl_pp_sendf(data, &pop3c->pp, "%s", command);
 
   if(!result) {
     pop3_state(data, POP3_COMMAND);
@@ -683,9 +787,14 @@ static CURLcode pop3_perform_command(struct Curl_easy *data)
 static CURLcode pop3_perform_quit(struct Curl_easy *data,
                                   struct connectdata *conn)
 {
-  /* Send the QUIT command */
-  CURLcode result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "%s", "QUIT");
+  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
+  CURLcode result;
 
+  if(!pop3c)
+    return CURLE_FAILED_INIT;
+
+  /* Send the QUIT command */
+  result = Curl_pp_sendf(data, &pop3c->pp, "%s", "QUIT");
   if(!result)
     pop3_state(data, POP3_QUIT);
 
@@ -699,11 +808,16 @@ static CURLcode pop3_state_servergreet_resp(struct Curl_easy *data,
 {
   CURLcode result = CURLE_OK;
   struct connectdata *conn = data->conn;
-  struct pop3_conn *pop3c = &conn->proto.pop3c;
-  const char *line = Curl_dyn_ptr(&data->conn->proto.pop3c.pp.recvbuf);
-  size_t len = data->conn->proto.pop3c.pp.nfinal;
+  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
+  const char *line;
+  size_t len;
 
   (void)instate; /* no use for this yet */
+  if(!pop3c)
+    return CURLE_FAILED_INIT;
+
+  line = curlx_dyn_ptr(&pop3c->pp.recvbuf);
+  len = pop3c->pp.nfinal;
 
   if(pop3code != '+') {
     failf(data, "Got unexpected pop3-server response");
@@ -749,13 +863,18 @@ static CURLcode pop3_state_capa_resp(struct Curl_easy *data, int pop3code,
 {
   CURLcode result = CURLE_OK;
   struct connectdata *conn = data->conn;
-  struct pop3_conn *pop3c = &conn->proto.pop3c;
-  const char *line = Curl_dyn_ptr(&data->conn->proto.pop3c.pp.recvbuf);
-  size_t len = data->conn->proto.pop3c.pp.nfinal;
+  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
+  const char *line;
+  size_t len;
 
   (void)instate; /* no use for this yet */
+  if(!pop3c)
+    return CURLE_FAILED_INIT;
+
+  line = curlx_dyn_ptr(&pop3c->pp.recvbuf);
+  len = pop3c->pp.nfinal;
 
-  /* Do we have a untagged continuation response? */
+  /* Do we have an untagged continuation response? */
   if(pop3code == '*') {
     /* Does the server support the STLS capability? */
     if(len >= 4 && !memcmp(line, "STLS", 4))
@@ -834,11 +953,15 @@ static CURLcode pop3_state_starttls_resp(struct Curl_easy *data,
                                          int pop3code,
                                          pop3state instate)
 {
+  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
   CURLcode result = CURLE_OK;
   (void)instate; /* no use for this yet */
 
+  if(!pop3c)
+    return CURLE_FAILED_INIT;
+
   /* Pipelining in response is forbidden. */
-  if(data->conn->proto.pop3c.pp.overflow)
+  if(pop3c->pp.overflow)
     return CURLE_WEIRD_SERVER_REPLY;
 
   if(pop3code != '+') {
@@ -862,10 +985,12 @@ static CURLcode pop3_state_auth_resp(struct Curl_easy *data,
 {
   CURLcode result = CURLE_OK;
   struct connectdata *conn = data->conn;
-  struct pop3_conn *pop3c = &conn->proto.pop3c;
+  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
   saslprogress progress;
 
   (void)instate; /* no use for this yet */
+  if(!pop3c)
+    return CURLE_FAILED_INIT;
 
   result = Curl_sasl_continue(&pop3c->sasl, data, pop3code, &progress);
   if(!result)
@@ -921,15 +1046,19 @@ static CURLcode pop3_state_user_resp(struct Curl_easy *data, int pop3code,
 {
   CURLcode result = CURLE_OK;
   struct connectdata *conn = data->conn;
+  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
   (void)instate; /* no use for this yet */
 
+  if(!pop3c)
+    return CURLE_FAILED_INIT;
+
   if(pop3code != '+') {
     failf(data, "Access denied. %c", pop3code);
     result = CURLE_LOGIN_DENIED;
   }
   else
     /* Send the PASS command */
-    result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "PASS %s",
+    result = Curl_pp_sendf(data, &pop3c->pp, "PASS %s",
                            conn->passwd ? conn->passwd : "");
   if(!result)
     pop3_state(data, POP3_PASS);
@@ -962,12 +1091,15 @@ static CURLcode pop3_state_command_resp(struct Curl_easy *data,
 {
   CURLcode result = CURLE_OK;
   struct connectdata *conn = data->conn;
-  struct POP3 *pop3 = data->req.p.pop3;
-  struct pop3_conn *pop3c = &conn->proto.pop3c;
-  struct pingpong *pp = &pop3c->pp;
+  struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY);
+  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
+  struct pingpong *pp;
 
   (void)instate; /* no use for this yet */
+  if(!pop3 || !pop3c)
+    return CURLE_FAILED_INIT;
 
+  pp = &pop3c->pp;
   if(pop3code != '+') {
     pop3_state(data, POP3_STOP);
     return CURLE_WEIRD_SERVER_REPLY;
@@ -993,18 +1125,18 @@ static CURLcode pop3_state_command_resp(struct Curl_easy *data,
          the body */
 
       /* keep only the overflow */
-      Curl_dyn_tail(&pp->recvbuf, pp->overflow);
+      curlx_dyn_tail(&pp->recvbuf, pp->overflow);
       pp->nfinal = 0; /* done */
 
       if(!data->req.no_body) {
-        result = pop3_write(data, Curl_dyn_ptr(&pp->recvbuf),
-                            Curl_dyn_len(&pp->recvbuf), FALSE);
+        result = pop3_write(data, curlx_dyn_ptr(&pp->recvbuf),
+                            curlx_dyn_len(&pp->recvbuf), FALSE);
         if(result)
           return result;
       }
 
       /* reset the buffer */
-      Curl_dyn_reset(&pp->recvbuf);
+      curlx_dyn_reset(&pp->recvbuf);
       pp->overflow = 0;
     }
   }
@@ -1020,13 +1152,17 @@ static CURLcode pop3_state_command_resp(struct Curl_easy *data,
 static CURLcode pop3_statemachine(struct Curl_easy *data,
                                   struct connectdata *conn)
 {
+  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
   CURLcode result = CURLE_OK;
   int pop3code;
-  struct pop3_conn *pop3c = &conn->proto.pop3c;
-  struct pingpong *pp = &pop3c->pp;
+  struct pingpong *pp;
   size_t nread = 0;
   (void)data;
 
+  if(!pop3c)
+    return CURLE_FAILED_INIT;
+
+  pp = &pop3c->pp;
   /* Busy upgrading the connection; right now all I/O is SSL/TLS, not POP3 */
 upgrade_tls:
   if(pop3c->state == POP3_UPGRADETLS) {
@@ -1107,8 +1243,10 @@ static CURLcode pop3_multi_statemach(struct Curl_easy *data, bool *done)
 {
   CURLcode result = CURLE_OK;
   struct connectdata *conn = data->conn;
-  struct pop3_conn *pop3c = &conn->proto.pop3c;
+  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
 
+  if(!pop3c)
+    return CURLE_FAILED_INIT;
   result = Curl_pp_statemach(data, &pop3c->pp, FALSE, FALSE);
   *done = (pop3c->state == POP3_STOP);
 
@@ -1120,7 +1258,10 @@ static CURLcode pop3_block_statemach(struct Curl_easy *data,
                                      bool disconnecting)
 {
   CURLcode result = CURLE_OK;
-  struct pop3_conn *pop3c = &conn->proto.pop3c;
+  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
+
+  if(!pop3c)
+    return CURLE_FAILED_INIT;
 
   while(pop3c->state != POP3_STOP && !result)
     result = Curl_pp_statemach(data, &pop3c->pp, TRUE, disconnecting);
@@ -1128,25 +1269,14 @@ static CURLcode pop3_block_statemach(struct Curl_easy *data,
   return result;
 }
 
-/* Allocate and initialize the POP3 struct for the current Curl_easy if
-   required */
-static CURLcode pop3_init(struct Curl_easy *data)
-{
-  CURLcode result = CURLE_OK;
-  struct POP3 *pop3;
-
-  pop3 = data->req.p.pop3 = calloc(1, sizeof(struct POP3));
-  if(!pop3)
-    result = CURLE_OUT_OF_MEMORY;
-
-  return result;
-}
-
 /* For the POP3 "protocol connect" and "doing" phases only */
 static int pop3_getsock(struct Curl_easy *data,
                         struct connectdata *conn, curl_socket_t *socks)
 {
-  return Curl_pp_getsock(data, &conn->proto.pop3c.pp, socks);
+  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
+  if(pop3c)
+    return Curl_pp_getsock(data, &pop3c->pp, socks);
+  return GETSOCK_BLANK;
 }
 
 /***********************************************************************
@@ -1163,10 +1293,12 @@ static CURLcode pop3_connect(struct Curl_easy *data, bool *done)
 {
   CURLcode result = CURLE_OK;
   struct connectdata *conn = data->conn;
-  struct pop3_conn *pop3c = &conn->proto.pop3c;
-  struct pingpong *pp = &pop3c->pp;
+  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
+  struct pingpong *pp = pop3c ? &pop3c->pp : NULL;
 
   *done = FALSE; /* default to not done yet */
+  if(!pop3c)
+    return CURLE_FAILED_INIT;
 
   /* We always support persistent connections in POP3 */
   connkeep(conn, "POP3 default");
@@ -1206,7 +1338,7 @@ static CURLcode pop3_done(struct Curl_easy *data, CURLcode status,
                           bool premature)
 {
   CURLcode result = CURLE_OK;
-  struct POP3 *pop3 = data->req.p.pop3;
+  struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY);
 
   (void)premature;
 
@@ -1240,7 +1372,10 @@ static CURLcode pop3_perform(struct Curl_easy *data, bool *connected,
 {
   /* This is POP3 and no proxy */
   CURLcode result = CURLE_OK;
-  struct POP3 *pop3 = data->req.p.pop3;
+  struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY);
+
+  if(!pop3)
+    return CURLE_FAILED_INIT;
 
   DEBUGF(infof(data, "DO phase starts"));
 
@@ -1305,9 +1440,12 @@ static CURLcode pop3_do(struct Curl_easy *data, bool *done)
 static CURLcode pop3_disconnect(struct Curl_easy *data,
                                 struct connectdata *conn, bool dead_connection)
 {
-  struct pop3_conn *pop3c = &conn->proto.pop3c;
+  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
   (void)data;
 
+  if(!pop3c)
+    return CURLE_FAILED_INIT;
+
   /* We cannot send quit unconditionally. If this connection is stale or
      bad in any way, sending quit and waiting around here will make the
      disconnect wait in vain and cause more problems than we need to. */
@@ -1388,16 +1526,42 @@ static CURLcode pop3_regular_transfer(struct Curl_easy *data,
   return result;
 }
 
+static void pop3_easy_dtor(void *key, size_t klen, void *entry)
+{
+  struct POP3 *pop3 = entry;
+  (void)key;
+  (void)klen;
+  DEBUGASSERT(pop3);
+  /* Cleanup our per-request based variables */
+  Curl_safefree(pop3->id);
+  Curl_safefree(pop3->custom);
+  free(pop3);
+}
+
+static void pop3_conn_dtor(void *key, size_t klen, void *entry)
+{
+  struct pop3_conn *pop3c = entry;
+  (void)key;
+  (void)klen;
+  DEBUGASSERT(pop3c);
+  Curl_pp_disconnect(&pop3c->pp);
+  Curl_safefree(pop3c->apoptimestamp);
+  free(pop3c);
+}
+
 static CURLcode pop3_setup_connection(struct Curl_easy *data,
                                       struct connectdata *conn)
 {
-  /* Initialise the POP3 layer */
-  CURLcode result = pop3_init(data);
-  if(result)
-    return result;
+  struct pop3_conn *pop3c;
+  struct POP3 *pop3 = calloc(1, sizeof(*pop3));
+  if(!pop3 ||
+     Curl_meta_set(data, CURL_META_POP3_EASY, pop3, pop3_easy_dtor))
+    return CURLE_OUT_OF_MEMORY;
 
-  /* Clear the TLS upgraded flag */
-  conn->bits.tls_upgraded = FALSE;
+  pop3c = calloc(1, sizeof(*pop3c));
+  if(!pop3c ||
+     Curl_conn_meta_set(conn, CURL_META_POP3_CONN, pop3c, pop3_conn_dtor))
+    return CURLE_OUT_OF_MEMORY;
 
   return CURLE_OK;
 }
@@ -1410,10 +1574,13 @@ static CURLcode pop3_setup_connection(struct Curl_easy *data,
  */
 static CURLcode pop3_parse_url_options(struct connectdata *conn)
 {
+  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
   CURLcode result = CURLE_OK;
-  struct pop3_conn *pop3c = &conn->proto.pop3c;
   const char *ptr = conn->options;
 
+  if(!pop3c)
+    return CURLE_FAILED_INIT;
+
   while(!result && ptr && *ptr) {
     const char *key = ptr;
     const char *value;
@@ -1468,9 +1635,11 @@ static CURLcode pop3_parse_url_options(struct connectdata *conn)
 static CURLcode pop3_parse_url_path(struct Curl_easy *data)
 {
   /* The POP3 struct is already initialised in pop3_connect() */
-  struct POP3 *pop3 = data->req.p.pop3;
+  struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY);
   const char *path = &data->state.up.path[1]; /* skip leading path */
 
+  if(!pop3)
+    return CURLE_FAILED_INIT;
   /* URL decode the path for the message ID */
   return Curl_urldecode(path, 0, &pop3->id, NULL, REJECT_CTRL);
 }
@@ -1484,9 +1653,11 @@ static CURLcode pop3_parse_url_path(struct Curl_easy *data)
 static CURLcode pop3_parse_custom_request(struct Curl_easy *data)
 {
   CURLcode result = CURLE_OK;
-  struct POP3 *pop3 = data->req.p.pop3;
+  struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY);
   const char *custom = data->set.str[STRING_CUSTOMREQUEST];
 
+  if(!pop3)
+    return CURLE_FAILED_INIT;
   /* URL decode the custom request */
   if(custom)
     result = Curl_urldecode(custom, 0, &pop3->custom, NULL, REJECT_CTRL);
@@ -1508,12 +1679,15 @@ static CURLcode pop3_write(struct Curl_easy *data, const char *str,
   CURLcode result = CURLE_OK;
   struct SingleRequest *k = &data->req;
   struct connectdata *conn = data->conn;
-  struct pop3_conn *pop3c = &conn->proto.pop3c;
+  struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
   bool strip_dot = FALSE;
   size_t last = 0;
   size_t i;
   (void)is_eos;
 
+  if(!pop3c)
+    return CURLE_FAILED_INIT;
+
   /* Search through the buffer looking for the end-of-body marker which is
      5 bytes (0d 0a 2e 0d 0a). Note that a line starting with a dot matches
      the eob so the server will have prefixed it with an extra dot which we
@@ -1588,11 +1762,11 @@ static CURLcode pop3_write(struct Curl_easy *data, const char *str,
         /* If the partial match was the CRLF and dot then only write the CRLF
            as the server would have inserted the dot */
         if(strip_dot && prev - 1 > 0) {
-          result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)POP3_EOB,
+          result = Curl_client_write(data, CLIENTWRITE_BODY, POP3_EOB,
                                      prev - 1);
         }
         else if(!strip_dot) {
-          result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)POP3_EOB,
+          result = Curl_client_write(data, CLIENTWRITE_BODY, POP3_EOB,
                                      prev);
         }
         else {
@@ -1612,7 +1786,7 @@ static CURLcode pop3_write(struct Curl_easy *data, const char *str,
     /* We have a full match so the transfer is done, however we must transfer
     the CRLF at the start of the EOB as this is considered to be part of the
     message as per RFC-1939, sect. 3 */
-    result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)POP3_EOB, 2);
+    result = Curl_client_write(data, CLIENTWRITE_BODY, POP3_EOB, 2);
 
     k->keepon &= ~KEEP_RECV;
     pop3c->eob = 0;
diff --git a/extra/curl/curl-8.12.1/lib/http_aws_sigv4.h b/extra/curl/curl-8.14.1/lib/pop3.h
similarity index 75%
rename from extra/curl/curl-8.12.1/lib/http_aws_sigv4.h
rename to extra/curl/curl-8.14.1/lib/pop3.h
index 57cc5706eba3..485e7c2c493d 100644
--- a/extra/curl/curl-8.12.1/lib/http_aws_sigv4.h
+++ b/extra/curl/curl-8.14.1/lib/pop3.h
@@ -1,5 +1,5 @@
-#ifndef HEADER_CURL_HTTP_AWS_SIGV4_H
-#define HEADER_CURL_HTTP_AWS_SIGV4_H
+#ifndef HEADER_CURL_POP3_H
+#define HEADER_CURL_POP3_H
 /***************************************************************************
  *                                  _   _ ____  _
  *  Project                     ___| | | |  _ \| |
@@ -11,7 +11,7 @@
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
- * are also available at https://curl.haxx.se/docs/copyright.html.
+ * are also available at https://curl.se/docs/copyright.html.
  *
  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
  * copies of the Software, and permit persons to whom the Software is
@@ -23,9 +23,8 @@
  * SPDX-License-Identifier: curl
  *
  ***************************************************************************/
-#include "curl_setup.h"
 
-/* this is for creating aws_sigv4 header output */
-CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy);
+extern const struct Curl_handler Curl_handler_pop3;
+extern const struct Curl_handler Curl_handler_pop3s;
 
-#endif /* HEADER_CURL_HTTP_AWS_SIGV4_H */
+#endif /* HEADER_CURL_POP3_H */
diff --git a/extra/curl/curl-8.12.1/lib/progress.c b/extra/curl/curl-8.14.1/lib/progress.c
similarity index 91%
rename from extra/curl/curl-8.12.1/lib/progress.c
rename to extra/curl/curl-8.14.1/lib/progress.c
index 82cbeb3770fd..8e6d98f0d980 100644
--- a/extra/curl/curl-8.12.1/lib/progress.c
+++ b/extra/curl/curl-8.14.1/lib/progress.c
@@ -28,7 +28,7 @@
 #include "sendf.h"
 #include "multiif.h"
 #include "progress.h"
-#include "timeval.h"
+#include "curlx/timeval.h"
 #include "curl_printf.h"
 
 /* check rate limits within this many recent milliseconds, at minimum. */
@@ -136,8 +136,7 @@ int Curl_pgrsDone(struct Curl_easy *data)
   if(rc)
     return rc;
 
-  if(!(data->progress.flags & PGRS_HIDE) &&
-     !data->progress.callback)
+  if(!data->progress.hide && !data->progress.callback)
     /* only output if we do not use a progress callback and we are not
      * hidden */
     fprintf(data->set.err, "\n");
@@ -181,7 +180,7 @@ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer,
   case TIMER_POSTQUEUE:
     /* Queue time is accumulative from all involved redirects */
     data->progress.t_postqueue +=
-      Curl_timediff_us(timestamp, data->progress.t_startqueue);
+      curlx_timediff_us(timestamp, data->progress.t_startqueue);
     break;
   case TIMER_STARTACCEPT:
     data->progress.t_acceptdata = timestamp;
@@ -217,13 +216,13 @@ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer,
     delta = &data->progress.t_posttransfer;
     break;
   case TIMER_REDIRECT:
-    data->progress.t_redirect = Curl_timediff_us(timestamp,
+    data->progress.t_redirect = curlx_timediff_us(timestamp,
                                                  data->progress.start);
     data->progress.t_startqueue = timestamp;
     break;
   }
   if(delta) {
-    timediff_t us = Curl_timediff_us(timestamp, data->progress.t_startsingle);
+    timediff_t us = curlx_timediff_us(timestamp, data->progress.t_startsingle);
     if(us < 1)
       us = 1; /* make sure at least one microsecond passed */
     *delta += us;
@@ -239,7 +238,7 @@ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer,
  */
 struct curltime Curl_pgrsTime(struct Curl_easy *data, timerid timer)
 {
-  struct curltime now = Curl_now();
+  struct curltime now = curlx_now();
 
   Curl_pgrsTimeWas(data, timer, now);
   return now;
@@ -247,18 +246,20 @@ struct curltime Curl_pgrsTime(struct Curl_easy *data, timerid timer)
 
 void Curl_pgrsStartNow(struct Curl_easy *data)
 {
-  data->progress.speeder_c = 0; /* reset the progress meter display */
-  data->progress.start = Curl_now();
-  data->progress.is_t_startransfer_set = FALSE;
-  data->progress.ul.limit.start = data->progress.start;
-  data->progress.dl.limit.start = data->progress.start;
-  data->progress.ul.limit.start_size = 0;
-  data->progress.dl.limit.start_size = 0;
-  data->progress.dl.cur_size = 0;
-  data->progress.ul.cur_size = 0;
-  /* clear all bits except HIDE and HEADERS_OUT */
-  data->progress.flags &= PGRS_HIDE|PGRS_HEADERS_OUT;
-  Curl_ratelimit(data, data->progress.start);
+  struct Progress *p = &data->progress;
+  p->speeder_c = 0; /* reset the progress meter display */
+  p->start = curlx_now();
+  p->is_t_startransfer_set = FALSE;
+  p->ul.limit.start = p->start;
+  p->dl.limit.start = p->start;
+  p->ul.limit.start_size = 0;
+  p->dl.limit.start_size = 0;
+  p->dl.cur_size = 0;
+  p->ul.cur_size = 0;
+  /* the sizes are unknown at start */
+  p->dl_size_known = FALSE;
+  p->ul_size_known = FALSE;
+  Curl_ratelimit(data, p->start);
 }
 
 /*
@@ -308,7 +309,7 @@ timediff_t Curl_pgrsLimitWaitTime(struct pgrs_dir *d,
    * 'actual' is the time in milliseconds it took to actually download the
    * last 'size' bytes.
    */
-  actual = Curl_timediff_ceil(now, d->limit.start);
+  actual = curlx_timediff_ceil(now, d->limit.start);
   if(actual < minimum) {
     /* if it downloaded the data faster than the limit, make it wait the
        difference */
@@ -334,14 +335,14 @@ void Curl_ratelimit(struct Curl_easy *data, struct curltime now)
 {
   /* do not set a new stamp unless the time since last update is long enough */
   if(data->set.max_recv_speed) {
-    if(Curl_timediff(now, data->progress.dl.limit.start) >=
+    if(curlx_timediff(now, data->progress.dl.limit.start) >=
        MIN_RATE_LIMIT_PERIOD) {
       data->progress.dl.limit.start = now;
       data->progress.dl.limit.start_size = data->progress.dl.cur_size;
     }
   }
   if(data->set.max_send_speed) {
-    if(Curl_timediff(now, data->progress.ul.limit.start) >=
+    if(curlx_timediff(now, data->progress.ul.limit.start) >=
        MIN_RATE_LIMIT_PERIOD) {
       data->progress.ul.limit.start = now;
       data->progress.ul.limit.start_size = data->progress.ul.cur_size;
@@ -361,11 +362,11 @@ void Curl_pgrsSetDownloadSize(struct Curl_easy *data, curl_off_t size)
 {
   if(size >= 0) {
     data->progress.dl.total_size = size;
-    data->progress.flags |= PGRS_DL_SIZE_KNOWN;
+    data->progress.dl_size_known = TRUE;
   }
   else {
     data->progress.dl.total_size = 0;
-    data->progress.flags &= ~PGRS_DL_SIZE_KNOWN;
+    data->progress.dl_size_known = FALSE;
   }
 }
 
@@ -373,11 +374,11 @@ void Curl_pgrsSetUploadSize(struct Curl_easy *data, curl_off_t size)
 {
   if(size >= 0) {
     data->progress.ul.total_size = size;
-    data->progress.flags |= PGRS_UL_SIZE_KNOWN;
+    data->progress.ul_size_known = TRUE;
   }
   else {
     data->progress.ul.total_size = 0;
-    data->progress.flags &= ~PGRS_UL_SIZE_KNOWN;
+    data->progress.ul_size_known = FALSE;
   }
 }
 
@@ -407,7 +408,7 @@ static bool progress_calc(struct Curl_easy *data, struct curltime now)
   struct Progress * const p = &data->progress;
 
   /* The time spent so far (from the start) in microseconds */
-  p->timespent = Curl_timediff_us(now, p->start);
+  p->timespent = curlx_timediff_us(now, p->start);
   p->dl.speed = trspeed(p->dl.cur_size, p->timespent);
   p->ul.speed = trspeed(p->ul.cur_size, p->timespent);
 
@@ -447,7 +448,7 @@ static bool progress_calc(struct Curl_easy *data, struct curltime now)
       checkindex = (p->speeder_c >= CURR_TIME) ? p->speeder_c%CURR_TIME : 0;
 
       /* Figure out the exact time for the time span */
-      span_ms = Curl_timediff(now, p->speeder_time[checkindex]);
+      span_ms = curlx_timediff(now, p->speeder_time[checkindex]);
       if(0 == span_ms)
         span_ms = 1; /* at least one millisecond MUST have passed */
 
@@ -509,12 +510,13 @@ static void progress_meter(struct Curl_easy *data)
   struct pgrs_estimate total_estm;
   curl_off_t total_cur_size;
   curl_off_t total_expected_size;
+  curl_off_t dl_size;
   char time_left[10];
   char time_total[10];
   char time_spent[10];
   curl_off_t cur_secs = (curl_off_t)p->timespent/1000000; /* seconds */
 
-  if(!(p->flags & PGRS_HEADERS_OUT)) {
+  if(!p->headers_out) {
     if(data->state.resume_from) {
       fprintf(data->set.err,
               "** Resuming transfer from byte position %" FMT_OFF_T "\n",
@@ -525,12 +527,12 @@ static void progress_meter(struct Curl_easy *data)
             "Time    Time     Time  Current\n"
             "                                 Dload  Upload   "
             "Total   Spent    Left  Speed\n");
-    p->flags |= PGRS_HEADERS_OUT; /* headers are shown */
+    p->headers_out = TRUE; /* headers are shown */
   }
 
   /* Figure out the estimated time of arrival for upload and download */
-  pgrs_estimates(&p->ul, (p->flags & PGRS_UL_SIZE_KNOWN), &ul_estm);
-  pgrs_estimates(&p->dl, (p->flags & PGRS_DL_SIZE_KNOWN), &dl_estm);
+  pgrs_estimates(&p->ul, (bool)p->ul_size_known, &ul_estm);
+  pgrs_estimates(&p->dl, (bool)p->dl_size_known, &dl_estm);
 
   /* Since both happen at the same time, total expected duration is max. */
   total_estm.secs = CURLMAX(ul_estm.secs, dl_estm.secs);
@@ -541,8 +543,16 @@ static void progress_meter(struct Curl_easy *data)
 
   /* Get the total amount of data expected to get transferred */
   total_expected_size =
-    ((p->flags & PGRS_UL_SIZE_KNOWN) ? p->ul.total_size : p->ul.cur_size) +
-    ((p->flags & PGRS_DL_SIZE_KNOWN) ? p->dl.total_size : p->dl.cur_size);
+    p->ul_size_known ? p->ul.total_size : p->ul.cur_size;
+
+  dl_size =
+    p->dl_size_known ? p->dl.total_size : p->dl.cur_size;
+
+  /* integer overflow check */
+  if((CURL_OFF_T_MAX - total_expected_size) < dl_size)
+    total_expected_size = CURL_OFF_T_MAX; /* capped */
+  else
+    total_expected_size += dl_size;
 
   /* We have transferred this much so far */
   total_cur_size = p->dl.cur_size + p->ul.cur_size;
@@ -584,7 +594,7 @@ static void progress_meter(struct Curl_easy *data)
  */
 static int pgrsupdate(struct Curl_easy *data, bool showprogress)
 {
-  if(!(data->progress.flags & PGRS_HIDE)) {
+  if(!data->progress.hide) {
     if(data->set.fxferinfo) {
       int result;
       /* There is a callback set, call that */
@@ -627,7 +637,7 @@ static int pgrsupdate(struct Curl_easy *data, bool showprogress)
 
 int Curl_pgrsUpdate(struct Curl_easy *data)
 {
-  struct curltime now = Curl_now(); /* what time is it */
+  struct curltime now = curlx_now(); /* what time is it */
   bool showprogress = progress_calc(data, now);
   return pgrsupdate(data, showprogress);
 }
@@ -637,6 +647,6 @@ int Curl_pgrsUpdate(struct Curl_easy *data)
  */
 void Curl_pgrsUpdate_nometer(struct Curl_easy *data)
 {
-  struct curltime now = Curl_now(); /* what time is it */
+  struct curltime now = curlx_now(); /* what time is it */
   (void)progress_calc(data, now);
 }
diff --git a/extra/curl/curl-8.12.1/lib/progress.h b/extra/curl/curl-8.14.1/lib/progress.h
similarity index 93%
rename from extra/curl/curl-8.12.1/lib/progress.h
rename to extra/curl/curl-8.14.1/lib/progress.h
index 326271ef1e4b..bbe135cdbcd7 100644
--- a/extra/curl/curl-8.12.1/lib/progress.h
+++ b/extra/curl/curl-8.14.1/lib/progress.h
@@ -24,7 +24,7 @@
  *
  ***************************************************************************/
 
-#include "timeval.h"
+#include "curlx/timeval.h"
 
 
 typedef enum {
@@ -71,9 +71,4 @@ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer,
 
 void Curl_pgrsEarlyData(struct Curl_easy *data, curl_off_t sent);
 
-#define PGRS_HIDE    (1<<4)
-#define PGRS_UL_SIZE_KNOWN (1<<5)
-#define PGRS_DL_SIZE_KNOWN (1<<6)
-#define PGRS_HEADERS_OUT (1<<7) /* set when the headers have been written */
-
 #endif /* HEADER_CURL_PROGRESS_H */
diff --git a/extra/curl/curl-8.12.1/lib/psl.c b/extra/curl/curl-8.14.1/lib/psl.c
similarity index 97%
rename from extra/curl/curl-8.12.1/lib/psl.c
rename to extra/curl/curl-8.14.1/lib/psl.c
index 0b88b05b4c73..a488a46e9315 100644
--- a/extra/curl/curl-8.12.1/lib/psl.c
+++ b/extra/curl/curl-8.14.1/lib/psl.c
@@ -40,7 +40,7 @@ void Curl_psl_destroy(struct PslCache *pslcache)
 {
   if(pslcache->psl) {
     if(pslcache->dynamic)
-      psl_free((psl_ctx_t *) pslcache->psl);
+      psl_free((psl_ctx_t *)CURL_UNCONST(pslcache->psl));
     pslcache->psl = NULL;
     pslcache->dynamic = FALSE;
   }
@@ -48,7 +48,7 @@ void Curl_psl_destroy(struct PslCache *pslcache)
 
 static time_t now_seconds(void)
 {
-  struct curltime now = Curl_now();
+  struct curltime now = curlx_now();
 
   return now.tv_sec;
 }
diff --git a/extra/curl/curl-8.12.1/lib/psl.h b/extra/curl/curl-8.14.1/lib/psl.h
similarity index 96%
rename from extra/curl/curl-8.12.1/lib/psl.h
rename to extra/curl/curl-8.14.1/lib/psl.h
index dd5bee21fbc1..dc11469a5248 100644
--- a/extra/curl/curl-8.12.1/lib/psl.h
+++ b/extra/curl/curl-8.14.1/lib/psl.h
@@ -34,7 +34,7 @@ struct Curl_easy;
 struct PslCache {
   const psl_ctx_t *psl; /* The PSL. */
   time_t expires; /* Time this PSL life expires. */
-  bool dynamic; /* PSL should be released when no longer needed. */
+  BIT(dynamic); /* PSL should be released when no longer needed. */
 };
 
 const psl_ctx_t *Curl_psl_use(struct Curl_easy *easy);
diff --git a/extra/curl/curl-8.12.1/lib/rand.c b/extra/curl/curl-8.14.1/lib/rand.c
similarity index 95%
rename from extra/curl/curl-8.12.1/lib/rand.c
rename to extra/curl/curl-8.14.1/lib/rand.c
index 8d55e260a4be..c0368dd76352 100644
--- a/extra/curl/curl-8.12.1/lib/rand.c
+++ b/extra/curl/curl-8.14.1/lib/rand.c
@@ -37,7 +37,7 @@
 #include "urldata.h"
 #include "vtls/vtls.h"
 #include "sendf.h"
-#include "timeval.h"
+#include "curlx/timeval.h"
 #include "rand.h"
 #include "escape.h"
 
@@ -130,7 +130,7 @@ static CURLcode weak_random(struct Curl_easy *data,
     static bool seeded = FALSE;
     unsigned int rnd;
     if(!seeded) {
-      struct curltime now = Curl_now();
+      struct curltime now = curlx_now();
       randseed += (unsigned int)now.tv_usec + (unsigned int)now.tv_sec;
       randseed = randseed * 1103515245 + 12345;
       randseed = randseed * 1103515245 + 12345;
@@ -191,12 +191,11 @@ static CURLcode randit(struct Curl_easy *data, unsigned int *rnd,
  * Curl_rand() stores 'num' number of random unsigned characters in the buffer
  * 'rnd' points to.
  *
- * If libcurl is built without TLS support or with a TLS backend that lacks a
- * proper random API (Rustls or mbedTLS), this function will use "weak"
- * random.
+ * If libcurl is built without TLS support or arc4random, this function will
+ * use "weak" random.
  *
- * When built *with* TLS support and a backend that offers strong random, it
- * will return error if it cannot provide strong random values.
+ * When built *with* TLS support, it will return error if it cannot provide
+ * strong random values.
  *
  * NOTE: 'data' may be passed in as NULL when coming from external API without
  * easy handle!
diff --git a/extra/curl/curl-8.12.1/lib/rand.h b/extra/curl/curl-8.14.1/lib/rand.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/rand.h
rename to extra/curl/curl-8.14.1/lib/rand.h
diff --git a/extra/curl/curl-8.12.1/lib/rename.c b/extra/curl/curl-8.14.1/lib/rename.c
similarity index 87%
rename from extra/curl/curl-8.12.1/lib/rename.c
rename to extra/curl/curl-8.14.1/lib/rename.c
index 8715a4306d98..d3a46e0e6a76 100644
--- a/extra/curl/curl-8.12.1/lib/rename.c
+++ b/extra/curl/curl-8.14.1/lib/rename.c
@@ -29,8 +29,8 @@
 #if (!defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_COOKIES)) || \
   !defined(CURL_DISABLE_ALTSVC)
 
-#include "curl_multibyte.h"
-#include "timeval.h"
+#include "curlx/multibyte.h"
+#include "curlx/timeval.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -40,14 +40,14 @@
 /* return 0 on success, 1 on error */
 int Curl_rename(const char *oldpath, const char *newpath)
 {
-#ifdef _WIN32
+#if defined(_WIN32) && !defined(UNDER_CE)
   /* rename() on Windows does not overwrite, so we cannot use it here.
      MoveFileEx() will overwrite and is usually atomic, however it fails
      when there are open handles to the file. */
   const int max_wait_ms = 1000;
-  struct curltime start = Curl_now();
-  TCHAR *tchar_oldpath = curlx_convert_UTF8_to_tchar((char *)oldpath);
-  TCHAR *tchar_newpath = curlx_convert_UTF8_to_tchar((char *)newpath);
+  struct curltime start = curlx_now();
+  TCHAR *tchar_oldpath = curlx_convert_UTF8_to_tchar(oldpath);
+  TCHAR *tchar_newpath = curlx_convert_UTF8_to_tchar(newpath);
   for(;;) {
     timediff_t diff;
     if(MoveFileEx(tchar_oldpath, tchar_newpath, MOVEFILE_REPLACE_EXISTING)) {
@@ -55,7 +55,7 @@ int Curl_rename(const char *oldpath, const char *newpath)
       curlx_unicodefree(tchar_newpath);
       break;
     }
-    diff = Curl_timediff(Curl_now(), start);
+    diff = curlx_timediff(curlx_now(), start);
     if(diff < 0 || diff > max_wait_ms) {
       curlx_unicodefree(tchar_oldpath);
       curlx_unicodefree(tchar_newpath);
diff --git a/extra/curl/curl-8.12.1/lib/rename.h b/extra/curl/curl-8.14.1/lib/rename.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/rename.h
rename to extra/curl/curl-8.14.1/lib/rename.h
diff --git a/extra/curl/curl-8.12.1/lib/request.c b/extra/curl/curl-8.14.1/lib/request.c
similarity index 94%
rename from extra/curl/curl-8.12.1/lib/request.c
rename to extra/curl/curl-8.14.1/lib/request.c
index d5f04e9f1d6f..f937a7f4bfed 100644
--- a/extra/curl/curl-8.12.1/lib/request.c
+++ b/extra/curl/curl-8.14.1/lib/request.c
@@ -26,7 +26,7 @@
 
 #include "urldata.h"
 #include "cfilters.h"
-#include "dynbuf.h"
+#include "curlx/dynbuf.h"
 #include "doh.h"
 #include "multiif.h"
 #include "progress.h"
@@ -34,6 +34,7 @@
 #include "sendf.h"
 #include "transfer.h"
 #include "url.h"
+#include "curlx/strparse.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -68,6 +69,8 @@ CURLcode Curl_req_soft_reset(struct SingleRequest *req,
   req->deductheadercount = 0;
   req->httpversion_sent = 0;
   req->httpversion = 0;
+  req->sendbuf_hds_len = 0;
+
   result = Curl_client_start(data);
   if(result)
     return result;
@@ -92,7 +95,7 @@ CURLcode Curl_req_soft_reset(struct SingleRequest *req,
 CURLcode Curl_req_start(struct SingleRequest *req,
                         struct Curl_easy *data)
 {
-  req->start = Curl_now();
+  req->start = curlx_now();
   return Curl_req_soft_reset(req, data);
 }
 
@@ -115,9 +118,6 @@ void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data)
 {
   struct curltime t0 = {0, 0};
 
-  /* This is a bit ugly. `req->p` is a union and we assume we can
-   * free this safely without leaks. */
-  Curl_safefree(req->p.ftp);
   Curl_safefree(req->newurl);
   Curl_client_reset(data);
   if(req->sendbuf_init)
@@ -140,6 +140,7 @@ void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data)
   req->httpcode = 0;
   req->keepon = 0;
   req->upgr101 = UPGR101_INIT;
+  req->sendbuf_hds_len = 0;
   req->timeofdoc = 0;
   req->location = NULL;
   req->newurl = NULL;
@@ -167,17 +168,10 @@ void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data)
 
 void Curl_req_free(struct SingleRequest *req, struct Curl_easy *data)
 {
-  /* This is a bit ugly. `req->p` is a union and we assume we can
-   * free this safely without leaks. */
-  Curl_safefree(req->p.ftp);
   Curl_safefree(req->newurl);
   if(req->sendbuf_init)
     Curl_bufq_free(&req->sendbuf);
   Curl_client_cleanup(data);
-
-#ifndef CURL_DISABLE_DOH
-  Curl_doh_cleanup(data);
-#endif
 }
 
 static CURLcode xfer_send(struct Curl_easy *data,
@@ -194,11 +188,13 @@ static CURLcode xfer_send(struct Curl_easy *data,
     /* Allow debug builds to override this logic to force short initial
        sends */
     size_t body_len = blen - hds_len;
-    char *p = getenv("CURL_SMALLREQSEND");
-    if(p) {
-      size_t body_small = (size_t)strtoul(p, NULL, 10);
-      if(body_small && body_small < body_len)
-        blen = hds_len + body_small;
+    if(body_len) {
+      const char *p = getenv("CURL_SMALLREQSEND");
+      if(p) {
+        curl_off_t body_small;
+        if(!curlx_str_number(&p, &body_small, body_len))
+          blen = hds_len + (size_t)body_small;
+      }
     }
   }
 #endif
@@ -222,11 +218,11 @@ static CURLcode xfer_send(struct Curl_easy *data,
       data->req.eos_sent = TRUE;
     if(*pnwritten) {
       if(hds_len)
-        Curl_debug(data, CURLINFO_HEADER_OUT, (char *)buf,
+        Curl_debug(data, CURLINFO_HEADER_OUT, buf,
                    CURLMIN(hds_len, *pnwritten));
       if(*pnwritten > hds_len) {
         size_t body_len = *pnwritten - hds_len;
-        Curl_debug(data, CURLINFO_DATA_OUT, (char *)buf + hds_len, body_len);
+        Curl_debug(data, CURLINFO_DATA_OUT, buf + hds_len, body_len);
         data->req.writebytecount += body_len;
         Curl_pgrsSetUploadCounter(data, data->req.writebytecount);
       }
@@ -385,8 +381,8 @@ CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *req,
     return CURLE_FAILED_INIT;
 
   data->req.httpversion_sent = httpversion;
-  buf = Curl_dyn_ptr(req);
-  blen = Curl_dyn_len(req);
+  buf = curlx_dyn_ptr(req);
+  blen = curlx_dyn_len(req);
   if(!Curl_creader_total_length(data)) {
     /* Request without body. Try to send directly from the buf given. */
     data->req.eos_read = TRUE;
diff --git a/extra/curl/curl-8.12.1/lib/request.h b/extra/curl/curl-8.14.1/lib/request.h
similarity index 94%
rename from extra/curl/curl-8.12.1/lib/request.h
rename to extra/curl/curl-8.14.1/lib/request.h
index 4c77be962f3e..74d9f5343964 100644
--- a/extra/curl/curl-8.12.1/lib/request.h
+++ b/extra/curl/curl-8.14.1/lib/request.h
@@ -32,9 +32,6 @@
 
 /* forward declarations */
 struct UserDefined;
-#ifndef CURL_DISABLE_DOH
-struct doh_probes;
-#endif
 
 enum expect100 {
   EXP100_SEND_DATA,           /* enough waiting, just send the body now */
@@ -102,24 +99,6 @@ struct SingleRequest {
   char *newurl;     /* Set to the new URL to use when a redirect or a retry is
                        wanted */
 
-  /* Allocated protocol-specific data. Each protocol handler makes sure this
-     points to data it needs. */
-  union {
-    struct FILEPROTO *file;
-    struct FTP *ftp;
-    struct IMAP *imap;
-    struct ldapreqinfo *ldap;
-    struct MQTT *mqtt;
-    struct POP3 *pop3;
-    struct RTSP *rtsp;
-    struct smb_request *smb;
-    struct SMTP *smtp;
-    struct SSHPROTO *ssh;
-    struct TELNET *telnet;
-  } p;
-#ifndef CURL_DISABLE_DOH
-  struct doh_probes *doh; /* DoH specific data for this request */
-#endif
 #ifndef CURL_DISABLE_COOKIES
   unsigned char setcookies;
 #endif
diff --git a/extra/curl/curl-8.12.1/lib/rtsp.c b/extra/curl/curl-8.14.1/lib/rtsp.c
similarity index 81%
rename from extra/curl/curl-8.12.1/lib/rtsp.c
rename to extra/curl/curl-8.14.1/lib/rtsp.c
index 4325240ee67c..ac44bec4222f 100644
--- a/extra/curl/curl-8.12.1/lib/rtsp.c
+++ b/extra/curl/curl-8.14.1/lib/rtsp.c
@@ -40,11 +40,42 @@
 #include "connect.h"
 #include "cfilters.h"
 #include "strdup.h"
+#include "curlx/strparse.h"
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
 #include "memdebug.h"
 
+
+/* meta key for storing protocol meta at easy handle */
+#define CURL_META_RTSP_EASY   "meta:proto:rtsp:easy"
+/* meta key for storing protocol meta at connection */
+#define CURL_META_RTSP_CONN   "meta:proto:rtsp:conn"
+
+typedef enum {
+  RTP_PARSE_SKIP,
+  RTP_PARSE_CHANNEL,
+  RTP_PARSE_LEN,
+  RTP_PARSE_DATA
+} rtp_parse_st;
+
+/* RTSP Connection data
+ * Currently, only used for tracking incomplete RTP data reads */
+struct rtsp_conn {
+  struct dynbuf buf;
+  int rtp_channel;
+  size_t rtp_len;
+  rtp_parse_st state;
+  BIT(in_header);
+};
+
+/* RTSP transfer data */
+struct RTSP {
+  long CSeq_sent; /* CSeq of this request */
+  long CSeq_recv; /* CSeq received */
+};
+
+
 #define RTP_PKT_LENGTH(p) ((((unsigned int)((unsigned char)((p)[2]))) << 8) | \
                             ((unsigned int)((unsigned char)((p)[3]))))
 
@@ -52,8 +83,6 @@
 static CURLcode rtsp_do(struct Curl_easy *data, bool *done);
 static CURLcode rtsp_done(struct Curl_easy *data, CURLcode, bool premature);
 static CURLcode rtsp_connect(struct Curl_easy *data, bool *done);
-static CURLcode rtsp_disconnect(struct Curl_easy *data,
-                                struct connectdata *conn, bool dead);
 static int rtsp_getsock_do(struct Curl_easy *data,
                            struct connectdata *conn, curl_socket_t *socks);
 
@@ -112,7 +141,7 @@ const struct Curl_handler Curl_handler_rtsp = {
   rtsp_getsock_do,                      /* doing_getsock */
   ZERO_NULL,                            /* domore_getsock */
   ZERO_NULL,                            /* perform_getsock */
-  rtsp_disconnect,                      /* disconnect */
+  ZERO_NULL,                            /* disconnect */
   rtsp_rtp_write_resp,                  /* write_resp */
   ZERO_NULL,                            /* write_resp_hd */
   rtsp_conncheck,                       /* connection_check */
@@ -126,17 +155,41 @@ const struct Curl_handler Curl_handler_rtsp = {
 
 #define MAX_RTP_BUFFERSIZE 1000000 /* arbitrary */
 
+static void rtsp_easy_dtor(void *key, size_t klen, void *entry)
+{
+  struct RTSP *rtsp = entry;
+  (void)key;
+  (void)klen;
+  free(rtsp);
+}
+
+static void rtsp_conn_dtor(void *key, size_t klen, void *entry)
+{
+  struct rtsp_conn *rtspc = entry;
+  (void)key;
+  (void)klen;
+  curlx_dyn_free(&rtspc->buf);
+  free(rtspc);
+}
+
 static CURLcode rtsp_setup_connection(struct Curl_easy *data,
                                       struct connectdata *conn)
 {
+  struct rtsp_conn *rtspc;
   struct RTSP *rtsp;
-  (void)conn;
 
-  data->req.p.rtsp = rtsp = calloc(1, sizeof(struct RTSP));
-  if(!rtsp)
+  rtspc = calloc(1, sizeof(*rtspc));
+  if(!rtspc)
+    return CURLE_OUT_OF_MEMORY;
+  curlx_dyn_init(&rtspc->buf, MAX_RTP_BUFFERSIZE);
+  if(Curl_conn_meta_set(conn, CURL_META_RTSP_CONN, rtspc, rtsp_conn_dtor))
+    return CURLE_OUT_OF_MEMORY;
+
+  rtsp = calloc(1, sizeof(struct RTSP));
+  if(!rtsp ||
+     Curl_meta_set(data, CURL_META_RTSP_EASY, rtsp, rtsp_easy_dtor))
     return CURLE_OUT_OF_MEMORY;
 
-  Curl_dyn_init(&conn->proto.rtspc.buf, MAX_RTP_BUFFERSIZE);
   return CURLE_OK;
 }
 
@@ -163,8 +216,13 @@ static unsigned int rtsp_conncheck(struct Curl_easy *data,
 
 static CURLcode rtsp_connect(struct Curl_easy *data, bool *done)
 {
+  struct rtsp_conn *rtspc =
+    Curl_conn_meta_get(data->conn, CURL_META_RTSP_CONN);
   CURLcode httpStatus;
 
+  if(!rtspc)
+    return CURLE_FAILED_INIT;
+
   httpStatus = Curl_http_connect(data, done);
 
   /* Initialize the CSeq if not already done */
@@ -173,34 +231,29 @@ static CURLcode rtsp_connect(struct Curl_easy *data, bool *done)
   if(data->state.rtsp_next_server_CSeq == 0)
     data->state.rtsp_next_server_CSeq = 1;
 
-  data->conn->proto.rtspc.rtp_channel = -1;
+  rtspc->rtp_channel = -1;
 
   return httpStatus;
 }
 
-static CURLcode rtsp_disconnect(struct Curl_easy *data,
-                                struct connectdata *conn, bool dead)
-{
-  (void) dead;
-  (void) data;
-  Curl_dyn_free(&conn->proto.rtspc.buf);
-  return CURLE_OK;
-}
-
-
 static CURLcode rtsp_done(struct Curl_easy *data,
                           CURLcode status, bool premature)
 {
-  struct RTSP *rtsp = data->req.p.rtsp;
+  struct rtsp_conn *rtspc =
+    Curl_conn_meta_get(data->conn, CURL_META_RTSP_CONN);
+  struct RTSP *rtsp = Curl_meta_get(data, CURL_META_RTSP_EASY);
   CURLcode httpStatus;
 
+  if(!rtspc || !rtsp)
+    return CURLE_FAILED_INIT;
+
   /* Bypass HTTP empty-reply checks on receive */
   if(data->set.rtspreq == RTSPREQ_RECEIVE)
     premature = TRUE;
 
   httpStatus = Curl_http_done(data, status, premature);
 
-  if(rtsp && !status && !httpStatus) {
+  if(!status && !httpStatus) {
     /* Check the sequence numbers */
     long CSeq_sent = rtsp->CSeq_sent;
     long CSeq_recv = rtsp->CSeq_recv;
@@ -210,8 +263,7 @@ static CURLcode rtsp_done(struct Curl_easy *data,
             CSeq_sent, CSeq_recv);
       return CURLE_RTSP_CSEQ_ERROR;
     }
-    if(data->set.rtspreq == RTSPREQ_RECEIVE &&
-       (data->conn->proto.rtspc.rtp_channel == -1)) {
+    if(data->set.rtspreq == RTSPREQ_RECEIVE && (rtspc->rtp_channel == -1)) {
       infof(data, "Got an RTP Receive with a CSeq of %ld", CSeq_recv);
     }
     if(data->set.rtspreq == RTSPREQ_RECEIVE &&
@@ -229,7 +281,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
   struct connectdata *conn = data->conn;
   CURLcode result = CURLE_OK;
   Curl_RtspReq rtspreq = data->set.rtspreq;
-  struct RTSP *rtsp = data->req.p.rtsp;
+  struct RTSP *rtsp = Curl_meta_get(data, CURL_META_RTSP_EASY);
   struct dynbuf req_buffer;
   unsigned char httpversion = 11; /* RTSP is close to HTTP/1.1, sort of... */
 
@@ -246,8 +298,11 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
   const char *p_userpwd = NULL;
 
   *done = TRUE;
+  if(!rtsp)
+    return CURLE_FAILED_INIT;
+
   /* Initialize a dynamic send buffer */
-  Curl_dyn_init(&req_buffer, DYN_RTSP_REQ_HEADER);
+  curlx_dyn_init(&req_buffer, DYN_RTSP_REQ_HEADER);
 
   rtsp->CSeq_sent = data->state.rtsp_next_client_CSeq;
   rtsp->CSeq_recv = 0;
@@ -345,8 +400,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
   if(rtspreq == RTSPREQ_SETUP && !p_transport) {
     /* New Transport: setting? */
     if(data->set.str[STRING_RTSP_TRANSPORT]) {
-      Curl_safefree(data->state.aptr.rtsp_transport);
-
+      free(data->state.aptr.rtsp_transport);
       data->state.aptr.rtsp_transport =
         aprintf("Transport: %s\r\n",
                 data->set.str[STRING_RTSP_TRANSPORT]);
@@ -372,7 +426,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
     /* Accept-Encoding header */
     if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) &&
        data->set.str[STRING_ENCODING]) {
-      Curl_safefree(data->state.aptr.accept_encoding);
+      free(data->state.aptr.accept_encoding);
       data->state.aptr.accept_encoding =
         aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]);
 
@@ -426,7 +480,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
 
     /* Check to see if there is a range set in the custom headers */
     if(!Curl_checkheaders(data, STRCONST("Range")) && data->state.range) {
-      Curl_safefree(data->state.aptr.rangeline);
+      free(data->state.aptr.rangeline);
       data->state.aptr.rangeline = aprintf("Range: %s\r\n", data->state.range);
       p_range = data->state.aptr.rangeline;
     }
@@ -447,10 +501,10 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
   }
 
   result =
-    Curl_dyn_addf(&req_buffer,
-                  "%s %s RTSP/1.0\r\n" /* Request Stream-URI RTSP/1.0 */
-                  "CSeq: %ld\r\n", /* CSeq */
-                  p_request, p_stream_uri, rtsp->CSeq_sent);
+    curlx_dyn_addf(&req_buffer,
+                   "%s %s RTSP/1.0\r\n" /* Request Stream-URI RTSP/1.0 */
+                   "CSeq: %ld\r\n", /* CSeq */
+                   p_request, p_stream_uri, rtsp->CSeq_sent);
   if(result)
     goto out;
 
@@ -459,7 +513,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
    * to make comparison easier
    */
   if(p_session_id) {
-    result = Curl_dyn_addf(&req_buffer, "Session: %s\r\n", p_session_id);
+    result = curlx_dyn_addf(&req_buffer, "Session: %s\r\n", p_session_id);
     if(result)
       goto out;
   }
@@ -467,24 +521,24 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
   /*
    * Shared HTTP-like options
    */
-  result = Curl_dyn_addf(&req_buffer,
-                         "%s" /* transport */
-                         "%s" /* accept */
-                         "%s" /* accept-encoding */
-                         "%s" /* range */
-                         "%s" /* referrer */
-                         "%s" /* user-agent */
-                         "%s" /* proxyuserpwd */
-                         "%s" /* userpwd */
-                         ,
-                         p_transport ? p_transport : "",
-                         p_accept ? p_accept : "",
-                         p_accept_encoding ? p_accept_encoding : "",
-                         p_range ? p_range : "",
-                         p_referrer ? p_referrer : "",
-                         p_uagent ? p_uagent : "",
-                         p_proxyuserpwd ? p_proxyuserpwd : "",
-                         p_userpwd ? p_userpwd : "");
+  result = curlx_dyn_addf(&req_buffer,
+                          "%s" /* transport */
+                          "%s" /* accept */
+                          "%s" /* accept-encoding */
+                          "%s" /* range */
+                          "%s" /* referrer */
+                          "%s" /* user-agent */
+                          "%s" /* proxyuserpwd */
+                          "%s" /* userpwd */
+                          ,
+                          p_transport ? p_transport : "",
+                          p_accept ? p_accept : "",
+                          p_accept_encoding ? p_accept_encoding : "",
+                          p_range ? p_range : "",
+                          p_referrer ? p_referrer : "",
+                          p_uagent ? p_uagent : "",
+                          p_proxyuserpwd ? p_proxyuserpwd : "",
+                          p_userpwd ? p_userpwd : "");
 
   /*
    * Free userpwd now --- cannot reuse this for Negotiate and possibly NTLM
@@ -540,8 +594,8 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
        * actually set a custom Content-Length in the headers */
       if(!Curl_checkheaders(data, STRCONST("Content-Length"))) {
         result =
-          Curl_dyn_addf(&req_buffer, "Content-Length: %" FMT_OFF_T"\r\n",
-                        req_clen);
+          curlx_dyn_addf(&req_buffer, "Content-Length: %" FMT_OFF_T"\r\n",
+                         req_clen);
         if(result)
           goto out;
       }
@@ -549,9 +603,9 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
       if(rtspreq == RTSPREQ_SET_PARAMETER ||
          rtspreq == RTSPREQ_GET_PARAMETER) {
         if(!Curl_checkheaders(data, STRCONST("Content-Type"))) {
-          result = Curl_dyn_addn(&req_buffer,
-                                 STRCONST("Content-Type: "
-                                          "text/parameters\r\n"));
+          result = curlx_dyn_addn(&req_buffer,
+                                  STRCONST("Content-Type: "
+                                           "text/parameters\r\n"));
           if(result)
             goto out;
         }
@@ -559,9 +613,9 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
 
       if(rtspreq == RTSPREQ_ANNOUNCE) {
         if(!Curl_checkheaders(data, STRCONST("Content-Type"))) {
-          result = Curl_dyn_addn(&req_buffer,
-                                 STRCONST("Content-Type: "
-                                          "application/sdp\r\n"));
+          result = curlx_dyn_addn(&req_buffer,
+                                  STRCONST("Content-Type: "
+                                           "application/sdp\r\n"));
           if(result)
             goto out;
         }
@@ -580,7 +634,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
   }
 
   /* Finish the request buffer */
-  result = Curl_dyn_addn(&req_buffer, STRCONST("\r\n"));
+  result = curlx_dyn_addn(&req_buffer, STRCONST("\r\n"));
   if(result)
     goto out;
 
@@ -604,7 +658,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
       result = CURLE_ABORTED_BY_CALLBACK;
   }
 out:
-  Curl_dyn_free(&req_buffer);
+  curlx_dyn_free(&req_buffer);
   return result;
 }
 
@@ -612,10 +666,10 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
  * write any BODY bytes missing to the client, ignore the rest.
  */
 static CURLcode rtp_write_body_junk(struct Curl_easy *data,
+                                    struct rtsp_conn *rtspc,
                                     const char *buf,
                                     size_t blen)
 {
-  struct rtsp_conn *rtspc = &(data->conn->proto.rtspc);
   curl_off_t body_remain;
   bool in_body;
 
@@ -627,17 +681,17 @@ static CURLcode rtp_write_body_junk(struct Curl_easy *data,
   if(body_remain) {
     if((curl_off_t)blen > body_remain)
       blen = (size_t)body_remain;
-    return Curl_client_write(data, CLIENTWRITE_BODY, (char *)buf, blen);
+    return Curl_client_write(data, CLIENTWRITE_BODY, buf, blen);
   }
   return CURLE_OK;
 }
 
 static CURLcode rtsp_filter_rtp(struct Curl_easy *data,
-                                     const char *buf,
-                                     size_t blen,
-                                     size_t *pconsumed)
+                                struct rtsp_conn *rtspc,
+                                const char *buf,
+                                size_t blen,
+                                size_t *pconsumed)
 {
-  struct rtsp_conn *rtspc = &(data->conn->proto.rtspc);
   CURLcode result = CURLE_OK;
   size_t skip_len = 0;
 
@@ -649,7 +703,7 @@ static CURLcode rtsp_filter_rtp(struct Curl_easy *data,
     switch(rtspc->state) {
 
     case RTP_PARSE_SKIP: {
-      DEBUGASSERT(Curl_dyn_len(&rtspc->buf) == 0);
+      DEBUGASSERT(curlx_dyn_len(&rtspc->buf) == 0);
       while(blen && buf[0] != '$') {
         if(!in_body && buf[0] == 'R' &&
            data->set.rtspreq != RTSPREQ_RECEIVE) {
@@ -674,13 +728,12 @@ static CURLcode rtsp_filter_rtp(struct Curl_easy *data,
         /* possible start of an RTP message, buffer */
         if(skip_len) {
           /* end of junk/BODY bytes, flush */
-          result = rtp_write_body_junk(data,
-                                       (char *)(buf - skip_len), skip_len);
+          result = rtp_write_body_junk(data, rtspc, buf - skip_len, skip_len);
           skip_len = 0;
           if(result)
             goto out;
         }
-        if(Curl_dyn_addn(&rtspc->buf, buf, 1)) {
+        if(curlx_dyn_addn(&rtspc->buf, buf, 1)) {
           result = CURLE_OUT_OF_MEMORY;
           goto out;
         }
@@ -695,7 +748,7 @@ static CURLcode rtsp_filter_rtp(struct Curl_easy *data,
     case RTP_PARSE_CHANNEL: {
       int idx = ((unsigned char)buf[0]) / 8;
       int off = ((unsigned char)buf[0]) % 8;
-      DEBUGASSERT(Curl_dyn_len(&rtspc->buf) == 1);
+      DEBUGASSERT(curlx_dyn_len(&rtspc->buf) == 1);
       if(!(data->state.rtp_channel_mask[idx] & (1 << off))) {
         /* invalid channel number, junk or BODY data */
         rtspc->state = RTP_PARSE_SKIP;
@@ -706,7 +759,8 @@ static CURLcode rtsp_filter_rtp(struct Curl_easy *data,
           /* We did not consume the initial '$' in our buffer, but had
            * it from an earlier call. We cannot un-consume it and have
            * to write it directly as BODY data */
-          result = rtp_write_body_junk(data, Curl_dyn_ptr(&rtspc->buf), 1);
+          result = rtp_write_body_junk(data, rtspc,
+                                       curlx_dyn_ptr(&rtspc->buf), 1);
           if(result)
             goto out;
         }
@@ -714,12 +768,12 @@ static CURLcode rtsp_filter_rtp(struct Curl_easy *data,
           /* count the '$' as skip and continue */
           skip_len = 1;
         }
-        Curl_dyn_free(&rtspc->buf);
+        curlx_dyn_free(&rtspc->buf);
         break;
       }
       /* a valid channel, so we expect this to be a real RTP message */
       rtspc->rtp_channel = (unsigned char)buf[0];
-      if(Curl_dyn_addn(&rtspc->buf, buf, 1)) {
+      if(curlx_dyn_addn(&rtspc->buf, buf, 1)) {
         result = CURLE_OUT_OF_MEMORY;
         goto out;
       }
@@ -731,10 +785,10 @@ static CURLcode rtsp_filter_rtp(struct Curl_easy *data,
     }
 
     case RTP_PARSE_LEN: {
-      size_t rtp_len = Curl_dyn_len(&rtspc->buf);
+      size_t rtp_len = curlx_dyn_len(&rtspc->buf);
       const char *rtp_buf;
       DEBUGASSERT(rtp_len >= 2 && rtp_len < 4);
-      if(Curl_dyn_addn(&rtspc->buf, buf, 1)) {
+      if(curlx_dyn_addn(&rtspc->buf, buf, 1)) {
         result = CURLE_OUT_OF_MEMORY;
         goto out;
       }
@@ -743,19 +797,19 @@ static CURLcode rtsp_filter_rtp(struct Curl_easy *data,
       --blen;
       if(rtp_len == 2)
         break;
-      rtp_buf = Curl_dyn_ptr(&rtspc->buf);
+      rtp_buf = curlx_dyn_ptr(&rtspc->buf);
       rtspc->rtp_len = RTP_PKT_LENGTH(rtp_buf) + 4;
       rtspc->state = RTP_PARSE_DATA;
       break;
     }
 
     case RTP_PARSE_DATA: {
-      size_t rtp_len = Curl_dyn_len(&rtspc->buf);
+      size_t rtp_len = curlx_dyn_len(&rtspc->buf);
       size_t needed;
       DEBUGASSERT(rtp_len < rtspc->rtp_len);
       needed = rtspc->rtp_len - rtp_len;
       if(needed <= blen) {
-        if(Curl_dyn_addn(&rtspc->buf, buf, needed)) {
+        if(curlx_dyn_addn(&rtspc->buf, buf, needed)) {
           result = CURLE_OUT_OF_MEMORY;
           goto out;
         }
@@ -765,15 +819,15 @@ static CURLcode rtsp_filter_rtp(struct Curl_easy *data,
         /* complete RTP message in buffer */
         DEBUGF(infof(data, "RTP write channel %d rtp_len %zu",
                      rtspc->rtp_channel, rtspc->rtp_len));
-        result = rtp_client_write(data, Curl_dyn_ptr(&rtspc->buf),
+        result = rtp_client_write(data, curlx_dyn_ptr(&rtspc->buf),
                                   rtspc->rtp_len);
-        Curl_dyn_free(&rtspc->buf);
+        curlx_dyn_free(&rtspc->buf);
         rtspc->state = RTP_PARSE_SKIP;
         if(result)
           goto out;
       }
       else {
-        if(Curl_dyn_addn(&rtspc->buf, buf, blen)) {
+        if(curlx_dyn_addn(&rtspc->buf, buf, blen)) {
           result = CURLE_OUT_OF_MEMORY;
           goto out;
         }
@@ -791,7 +845,7 @@ static CURLcode rtsp_filter_rtp(struct Curl_easy *data,
   }
 out:
   if(!result && skip_len)
-    result = rtp_write_body_junk(data, (char *)(buf - skip_len), skip_len);
+    result = rtp_write_body_junk(data, rtspc, buf - skip_len, skip_len);
   return result;
 }
 
@@ -800,10 +854,14 @@ static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data,
                                     size_t blen,
                                     bool is_eos)
 {
-  struct rtsp_conn *rtspc = &(data->conn->proto.rtspc);
+  struct rtsp_conn *rtspc =
+    Curl_conn_meta_get(data->conn, CURL_META_RTSP_CONN);
   CURLcode result = CURLE_OK;
   size_t consumed = 0;
 
+  if(!rtspc)
+    return CURLE_FAILED_INIT;
+
   if(!data->req.header)
     rtspc->in_header = FALSE;
   if(!blen) {
@@ -815,7 +873,7 @@ static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data,
 
   /* If header parsing is not ongoing, extract RTP messages */
   if(!rtspc->in_header) {
-    result = rtsp_filter_rtp(data, buf, blen, &consumed);
+    result = rtsp_filter_rtp(data, rtspc, buf, blen, &consumed);
     if(result)
       goto out;
     buf += consumed;
@@ -847,7 +905,7 @@ static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data,
         data->req.size = 0;
         data->req.download_done = TRUE;
       }
-      result = rtsp_filter_rtp(data, buf, blen, &consumed);
+      result = rtsp_filter_rtp(data, rtspc, buf, blen, &consumed);
       if(result)
         goto out;
       blen -= consumed;
@@ -865,8 +923,7 @@ static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data,
                data->req.size));
   if(!result && (is_eos || blen)) {
     result = Curl_client_write(data, CLIENTWRITE_BODY|
-                               (is_eos ? CLIENTWRITE_EOS : 0),
-                               (char *)buf, blen);
+                               (is_eos ? CLIENTWRITE_EOS : 0), buf, blen);
   }
 
 out:
@@ -906,7 +963,7 @@ CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr, size_t len)
   }
 
   Curl_set_in_callback(data, TRUE);
-  wrote = writeit((char *)ptr, 1, len, user_ptr);
+  wrote = writeit((char *)CURL_UNCONST(ptr), 1, len, user_ptr);
   Curl_set_in_callback(data, FALSE);
 
   if(CURL_WRITEFUNC_PAUSE == wrote) {
@@ -925,21 +982,18 @@ CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr, size_t len)
 CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, const char *header)
 {
   if(checkprefix("CSeq:", header)) {
-    long CSeq = 0;
-    char *endp;
+    curl_off_t CSeq = 0;
+    struct RTSP *rtsp = Curl_meta_get(data, CURL_META_RTSP_EASY);
     const char *p = &header[5];
-    while(ISBLANK(*p))
-      p++;
-    CSeq = strtol(p, &endp, 10);
-    if(p != endp) {
-      struct RTSP *rtsp = data->req.p.rtsp;
-      rtsp->CSeq_recv = CSeq; /* mark the request */
-      data->state.rtsp_CSeq_recv = CSeq; /* update the handle */
-    }
-    else {
+    if(!rtsp)
+      return CURLE_FAILED_INIT;
+    curlx_str_passblanks(&p);
+    if(curlx_str_number(&p, &CSeq, LONG_MAX)) {
       failf(data, "Unable to read the CSeq header: [%s]", header);
       return CURLE_RTSP_CSEQ_ERROR;
     }
+    rtsp->CSeq_recv = (long)CSeq; /* mark the request */
+    data->state.rtsp_CSeq_recv = (long)CSeq; /* update the handle */
   }
   else if(checkprefix("Session:", header)) {
     const char *start, *end;
@@ -947,8 +1001,7 @@ CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, const char *header)
 
     /* Find the first non-space letter */
     start = header + 8;
-    while(*start && ISBLANK(*start))
-      start++;
+    curlx_str_passblanks(&start);
 
     if(!*start) {
       failf(data, "Got a blank Session ID");
@@ -962,7 +1015,7 @@ CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, const char *header)
      * gstreamer does url-encoded session ID's not covered by the standard.
      */
     end = start;
-    while(*end && *end != ';' && !ISSPACE(*end))
+    while((*end > ' ') && (*end != ';'))
       end++;
     idlen = end - start;
 
@@ -1006,29 +1059,24 @@ CURLcode rtsp_parse_transport(struct Curl_easy *data, const char *transport)
   const char *start, *end;
   start = transport;
   while(start && *start) {
-    while(*start && ISBLANK(*start) )
-      start++;
+    curlx_str_passblanks(&start);
     end = strchr(start, ';');
     if(checkprefix("interleaved=", start)) {
-      long chan1, chan2, chan;
-      char *endp;
+      curl_off_t chan1, chan2, chan;
       const char *p = start + 12;
-      chan1 = strtol(p, &endp, 10);
-      if(p != endp && chan1 >= 0 && chan1 <= 255) {
+      if(!curlx_str_number(&p, &chan1, 255)) {
         unsigned char *rtp_channel_mask = data->state.rtp_channel_mask;
         chan2 = chan1;
-        if(*endp == '-') {
-          p = endp + 1;
-          chan2 = strtol(p, &endp, 10);
-          if(p == endp || chan2 < 0 || chan2 > 255) {
+        if(!curlx_str_single(&p, '-')) {
+          if(curlx_str_number(&p, &chan2, 255)) {
             infof(data, "Unable to read the interleaved parameter from "
                   "Transport header: [%s]", transport);
             chan2 = chan1;
           }
         }
         for(chan = chan1; chan <= chan2; chan++) {
-          long idx = chan / 8;
-          long off = chan % 8;
+          int idx = (int)chan / 8;
+          int off = (int)chan % 8;
           rtp_channel_mask[idx] |= (unsigned char)(1 << off);
         }
       }
diff --git a/extra/curl/curl-8.12.1/lib/rtsp.h b/extra/curl/curl-8.14.1/lib/rtsp.h
similarity index 69%
rename from extra/curl/curl-8.12.1/lib/rtsp.h
rename to extra/curl/curl-8.14.1/lib/rtsp.h
index 68f6f4fe01ec..59f20a9f1672 100644
--- a/extra/curl/curl-8.12.1/lib/rtsp.h
+++ b/extra/curl/curl-8.14.1/lib/rtsp.h
@@ -36,32 +36,4 @@ CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, const char *header);
 
 #endif /* CURL_DISABLE_RTSP */
 
-typedef enum {
-  RTP_PARSE_SKIP,
-  RTP_PARSE_CHANNEL,
-  RTP_PARSE_LEN,
-  RTP_PARSE_DATA
-} rtp_parse_st;
-/*
- * RTSP Connection data
- *
- * Currently, only used for tracking incomplete RTP data reads
- */
-struct rtsp_conn {
-  struct dynbuf buf;
-  int rtp_channel;
-  size_t rtp_len;
-  rtp_parse_st state;
-  BIT(in_header);
-};
-
-/****************************************************************************
- * RTSP unique setup
- ***************************************************************************/
-struct RTSP {
-  long CSeq_sent; /* CSeq of this request */
-  long CSeq_recv; /* CSeq received */
-};
-
-
 #endif /* HEADER_CURL_RTSP_H */
diff --git a/extra/curl/curl-8.12.1/lib/select.c b/extra/curl/curl-8.14.1/lib/select.c
similarity index 97%
rename from extra/curl/curl-8.12.1/lib/select.c
rename to extra/curl/curl-8.14.1/lib/select.c
index ef8d554f1cee..ee239b732176 100644
--- a/extra/curl/curl-8.12.1/lib/select.c
+++ b/extra/curl/curl-8.14.1/lib/select.c
@@ -45,8 +45,8 @@
 #include "urldata.h"
 #include "connect.h"
 #include "select.h"
-#include "timediff.h"
-#include "warnless.h"
+#include "curlx/timediff.h"
+#include "curlx/warnless.h"
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
@@ -74,7 +74,7 @@ int Curl_wait_ms(timediff_t timeout_ms)
   if(!timeout_ms)
     return 0;
   if(timeout_ms < 0) {
-    SET_SOCKERRNO(EINVAL);
+    SET_SOCKERRNO(SOCKEINVAL);
     return -1;
   }
 #if defined(MSDOS)
@@ -86,7 +86,7 @@ int Curl_wait_ms(timediff_t timeout_ms)
     timeout_ms = ULONG_MAX-1;
     /* do not use ULONG_MAX, because that is equal to INFINITE */
 #endif
-  Sleep((ULONG)timeout_ms);
+  Sleep((DWORD)timeout_ms);
 #else
   /* avoid using poll() for this since it behaves incorrectly with no sockets
      on Apple operating systems */
@@ -96,7 +96,7 @@ int Curl_wait_ms(timediff_t timeout_ms)
   }
 #endif /* _WIN32 */
   if(r) {
-    if((r == -1) && (SOCKERRNO == EINTR))
+    if((r == -1) && (SOCKERRNO == SOCKEINTR))
       /* make EINTR from select or poll not a "lethal" error */
       r = 0;
     else
@@ -197,7 +197,7 @@ int Curl_socket_check(curl_socket_t readfd0, /* two sockets to read from */
     return Curl_wait_ms(timeout_ms);
   }
 
-  /* Avoid initial timestamp, avoid Curl_now() call, when elapsed
+  /* Avoid initial timestamp, avoid curlx_now() call, when elapsed
      time in this function does not need to be measured. This happens
      when function is called with a zero timeout or a negative timeout
      value indicating a blocking call should be performed. */
@@ -292,7 +292,7 @@ int Curl_poll(struct pollfd ufds[], unsigned int nfds, timediff_t timeout_ms)
     return Curl_wait_ms(timeout_ms);
   }
 
-  /* Avoid initial timestamp, avoid Curl_now() call, when elapsed
+  /* Avoid initial timestamp, avoid curlx_now() call, when elapsed
      time in this function does not need to be measured. This happens
      when function is called with a zero timeout or a negative timeout
      value indicating a blocking call should be performed. */
@@ -312,7 +312,7 @@ int Curl_poll(struct pollfd ufds[], unsigned int nfds, timediff_t timeout_ms)
     pending_ms = 0;
   r = poll(ufds, nfds, pending_ms);
   if(r <= 0) {
-    if((r == -1) && (SOCKERRNO == EINTR))
+    if((r == -1) && (SOCKERRNO == SOCKEINTR))
       /* make EINTR from select or poll not a "lethal" error */
       r = 0;
     return r;
@@ -360,7 +360,7 @@ int Curl_poll(struct pollfd ufds[], unsigned int nfds, timediff_t timeout_ms)
   */
   r = our_select(maxfd, &fds_read, &fds_write, &fds_err, timeout_ms);
   if(r <= 0) {
-    if((r == -1) && (SOCKERRNO == EINTR))
+    if((r == -1) && (SOCKERRNO == SOCKEINTR))
       /* make EINTR from select or poll not a "lethal" error */
       r = 0;
     return r;
@@ -410,6 +410,11 @@ void Curl_pollfds_init(struct curl_pollfds *cpfds,
   }
 }
 
+void Curl_pollfds_reset(struct curl_pollfds *cpfds)
+{
+  cpfds->n = 0;
+}
+
 void Curl_pollfds_cleanup(struct curl_pollfds *cpfds)
 {
   DEBUGASSERT(cpfds);
diff --git a/extra/curl/curl-8.12.1/lib/select.h b/extra/curl/curl-8.14.1/lib/select.h
similarity index 96%
rename from extra/curl/curl-8.12.1/lib/select.h
rename to extra/curl/curl-8.14.1/lib/select.h
index 608395ff34b9..10968fab7f89 100644
--- a/extra/curl/curl-8.12.1/lib/select.h
+++ b/extra/curl/curl-8.14.1/lib/select.h
@@ -93,7 +93,7 @@ int Curl_wait_ms(timediff_t timeout_ms);
 #define FDSET_SOCK(x) 1
 #define VERIFY_SOCK(x) do { \
   if(!VALID_SOCK(x)) { \
-    SET_SOCKERRNO(WSAEINVAL); \
+    SET_SOCKERRNO(SOCKEINVAL); \
     return -1; \
   } \
 } while(0)
@@ -105,7 +105,7 @@ int Curl_wait_ms(timediff_t timeout_ms);
 
 #define VERIFY_SOCK(x) do {                     \
     if(!VALID_SOCK(x) || !FDSET_SOCK(x)) {      \
-      SET_SOCKERRNO(EINVAL);                    \
+      SET_SOCKERRNO(SOCKEINVAL);                \
       return -1;                                \
     }                                           \
   } while(0)
@@ -122,6 +122,8 @@ void Curl_pollfds_init(struct curl_pollfds *cpfds,
                        struct pollfd *static_pfds,
                        unsigned int static_count);
 
+void Curl_pollfds_reset(struct curl_pollfds *cpfds);
+
 void Curl_pollfds_cleanup(struct curl_pollfds *cpfds);
 
 CURLcode Curl_pollfds_add_ps(struct curl_pollfds *cpfds,
diff --git a/extra/curl/curl-8.12.1/lib/sendf.c b/extra/curl/curl-8.14.1/lib/sendf.c
similarity index 96%
rename from extra/curl/curl-8.12.1/lib/sendf.c
rename to extra/curl/curl-8.14.1/lib/sendf.c
index bffbd6401e91..feb4598b0634 100644
--- a/extra/curl/curl-8.12.1/lib/sendf.c
+++ b/extra/curl/curl-8.14.1/lib/sendf.c
@@ -42,6 +42,7 @@
 #include "connect.h"
 #include "content_encoding.h"
 #include "cw-out.h"
+#include "cw-pause.h"
 #include "vtls/vtls.h"
 #include "vssh/ssh.h"
 #include "easyif.h"
@@ -51,7 +52,7 @@
 #include "strdup.h"
 #include "http2.h"
 #include "progress.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
 #include "ws.h"
 
 /* The last 3 #include files should be in this order */
@@ -359,7 +360,7 @@ static CURLcode cw_raw_write(struct Curl_easy *data,
                              const char *buf, size_t nbytes)
 {
   if(type & CLIENTWRITE_BODY && data->set.verbose && !data->req.ignorebody) {
-    Curl_debug(data, CURLINFO_DATA_IN, (char *)buf, nbytes);
+    Curl_debug(data, CURLINFO_DATA_IN, buf, nbytes);
   }
   return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
 }
@@ -433,21 +434,37 @@ static CURLcode do_init_writer_stack(struct Curl_easy *data)
   if(result)
     return result;
 
-  result = Curl_cwriter_create(&writer, data, &cw_download, CURL_CW_PROTOCOL);
+  /* This places the "pause" writer behind the "download" writer that
+   * is added below. Meaning the "download" can do checks on content length
+   * and other things *before* write outs are buffered for paused transfers. */
+  result = Curl_cwriter_create(&writer, data, &Curl_cwt_pause,
+                               CURL_CW_PROTOCOL);
+  if(!result) {
+    result = Curl_cwriter_add(data, writer);
+    if(result)
+      Curl_cwriter_free(data, writer);
+  }
   if(result)
     return result;
-  result = Curl_cwriter_add(data, writer);
-  if(result) {
-    Curl_cwriter_free(data, writer);
+
+  result = Curl_cwriter_create(&writer, data, &cw_download, CURL_CW_PROTOCOL);
+  if(!result) {
+    result = Curl_cwriter_add(data, writer);
+    if(result)
+      Curl_cwriter_free(data, writer);
   }
+  if(result)
+    return result;
 
   result = Curl_cwriter_create(&writer, data, &cw_raw, CURL_CW_RAW);
+  if(!result) {
+    result = Curl_cwriter_add(data, writer);
+    if(result)
+      Curl_cwriter_free(data, writer);
+  }
   if(result)
     return result;
-  result = Curl_cwriter_add(data, writer);
-  if(result) {
-    Curl_cwriter_free(data, writer);
-  }
+
   return result;
 }
 
@@ -494,6 +511,16 @@ struct Curl_cwriter *Curl_cwriter_get_by_type(struct Curl_easy *data,
   return NULL;
 }
 
+bool Curl_cwriter_is_content_decoding(struct Curl_easy *data)
+{
+  struct Curl_cwriter *writer;
+  for(writer = data->req.writer_stack; writer; writer = writer->next) {
+    if(writer->phase == CURL_CW_CONTENT_DECODE)
+      return TRUE;
+  }
+  return FALSE;
+}
+
 bool Curl_cwriter_is_paused(struct Curl_easy *data)
 {
   return Curl_cw_out_is_paused(data);
@@ -1003,13 +1030,6 @@ static CURLcode cr_lc_read(struct Curl_easy *data,
       if(result)
         return result;
       start = i + 1;
-      if(!data->set.crlf && (data->state.infilesize != -1)) {
-        /* we are here only because FTP is in ASCII mode...
-           bump infilesize for the LF we just added */
-        data->state.infilesize++;
-        /* comment: this might work for FTP, but in HTTP we could not change
-         * the content length after having started the request... */
-      }
     }
 
     if(start < i) { /* leftover */
@@ -1286,6 +1306,15 @@ static bool cr_buf_needs_rewind(struct Curl_easy *data,
   return ctx->index > 0;
 }
 
+static CURLcode cr_buf_rewind(struct Curl_easy *data,
+                              struct Curl_creader *reader)
+{
+  struct cr_buf_ctx *ctx = reader->ctx;
+  (void)data;
+  ctx->index = 0;
+  return CURLE_OK;
+}
+
 static curl_off_t cr_buf_total_length(struct Curl_easy *data,
                                       struct Curl_creader *reader)
 {
@@ -1325,7 +1354,7 @@ static const struct Curl_crtype cr_buf = {
   cr_buf_needs_rewind,
   cr_buf_total_length,
   cr_buf_resume_from,
-  Curl_creader_def_rewind,
+  cr_buf_rewind,
   Curl_creader_def_unpause,
   Curl_creader_def_is_paused,
   Curl_creader_def_done,
diff --git a/extra/curl/curl-8.12.1/lib/sendf.h b/extra/curl/curl-8.14.1/lib/sendf.h
similarity index 99%
rename from extra/curl/curl-8.12.1/lib/sendf.h
rename to extra/curl/curl-8.14.1/lib/sendf.h
index 41ca8659c3a5..e5cc600bfe5c 100644
--- a/extra/curl/curl-8.12.1/lib/sendf.h
+++ b/extra/curl/curl-8.14.1/lib/sendf.h
@@ -182,6 +182,8 @@ CURLcode Curl_cwriter_write(struct Curl_easy *data,
  */
 bool Curl_cwriter_is_paused(struct Curl_easy *data);
 
+bool Curl_cwriter_is_content_decoding(struct Curl_easy *data);
+
 /**
  * Unpause client writer and flush any buffered date to the client.
  */
diff --git a/extra/curl/curl-8.12.1/lib/setopt.c b/extra/curl/curl-8.14.1/lib/setopt.c
similarity index 92%
rename from extra/curl/curl-8.12.1/lib/setopt.c
rename to extra/curl/curl-8.14.1/lib/setopt.c
index 8a87d2444624..d5ddf6d0c26d 100644
--- a/extra/curl/curl-8.12.1/lib/setopt.c
+++ b/extra/curl/curl-8.14.1/lib/setopt.c
@@ -43,8 +43,9 @@
 #include "strcase.h"
 #include "share.h"
 #include "vtls/vtls.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
 #include "sendf.h"
+#include "hostip.h"
 #include "http2.h"
 #include "setopt.h"
 #include "multiif.h"
@@ -255,6 +256,157 @@ static CURLcode httpauth(struct Curl_easy *data, bool proxy,
   return CURLE_OK;
 }
 
+#ifndef CURL_DISABLE_HTTP
+static CURLcode setopt_HTTP_VERSION(struct Curl_easy *data, long arg)
+{
+  /*
+   * This sets a requested HTTP version to be used. The value is one of
+   * the listed enums in curl/curl.h.
+   */
+  switch(arg) {
+  case CURL_HTTP_VERSION_NONE:
+    /* accepted */
+    break;
+  case CURL_HTTP_VERSION_1_0:
+  case CURL_HTTP_VERSION_1_1:
+    /* accepted */
+    break;
+#ifdef USE_HTTP2
+  case CURL_HTTP_VERSION_2_0:
+  case CURL_HTTP_VERSION_2TLS:
+  case CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE:
+    /* accepted */
+    break;
+#endif
+#ifdef USE_HTTP3
+  case CURL_HTTP_VERSION_3:
+  case CURL_HTTP_VERSION_3ONLY:
+    /* accepted */
+    break;
+#endif
+  default:
+    /* not accepted */
+    if(arg < CURL_HTTP_VERSION_NONE)
+      return CURLE_BAD_FUNCTION_ARGUMENT;
+    return CURLE_UNSUPPORTED_PROTOCOL;
+  }
+  data->set.httpwant = (unsigned char)arg;
+  return CURLE_OK;
+}
+#endif /* ! CURL_DISABLE_HTTP */
+
+#ifdef USE_SSL
+static CURLcode setopt_SSLVERSION(struct Curl_easy *data, CURLoption option,
+                                  long arg)
+{
+  /*
+   * Set explicit SSL version to try to connect with, as some SSL
+   * implementations are lame.
+   */
+  {
+    long version, version_max;
+    struct ssl_primary_config *primary = &data->set.ssl.primary;
+#ifndef CURL_DISABLE_PROXY
+    if(option != CURLOPT_SSLVERSION)
+      primary = &data->set.proxy_ssl.primary;
+#else
+    if(option) {}
+#endif
+    version = C_SSLVERSION_VALUE(arg);
+    version_max = (long)C_SSLVERSION_MAX_VALUE(arg);
+
+    if(version < CURL_SSLVERSION_DEFAULT ||
+       version == CURL_SSLVERSION_SSLv2 ||
+       version == CURL_SSLVERSION_SSLv3 ||
+       version >= CURL_SSLVERSION_LAST ||
+       version_max < CURL_SSLVERSION_MAX_NONE ||
+       version_max >= CURL_SSLVERSION_MAX_LAST)
+      return CURLE_BAD_FUNCTION_ARGUMENT;
+
+    primary->version = (unsigned char)version;
+    primary->version_max = (unsigned int)version_max;
+  }
+  return CURLE_OK;
+}
+#endif /* ! USE_SSL */
+
+#ifndef CURL_DISABLE_RTSP
+static CURLcode setopt_RTSP_REQUEST(struct Curl_easy *data, long arg)
+{
+  /*
+   * Set the RTSP request method (OPTIONS, SETUP, PLAY, etc...)
+   * Would this be better if the RTSPREQ_* were just moved into here?
+   */
+  Curl_RtspReq rtspreq = RTSPREQ_NONE;
+  switch(arg) {
+  case CURL_RTSPREQ_OPTIONS:
+    rtspreq = RTSPREQ_OPTIONS;
+    break;
+
+  case CURL_RTSPREQ_DESCRIBE:
+    rtspreq = RTSPREQ_DESCRIBE;
+    break;
+
+  case CURL_RTSPREQ_ANNOUNCE:
+    rtspreq = RTSPREQ_ANNOUNCE;
+    break;
+
+  case CURL_RTSPREQ_SETUP:
+    rtspreq = RTSPREQ_SETUP;
+    break;
+
+  case CURL_RTSPREQ_PLAY:
+    rtspreq = RTSPREQ_PLAY;
+    break;
+
+  case CURL_RTSPREQ_PAUSE:
+    rtspreq = RTSPREQ_PAUSE;
+    break;
+
+  case CURL_RTSPREQ_TEARDOWN:
+    rtspreq = RTSPREQ_TEARDOWN;
+    break;
+
+  case CURL_RTSPREQ_GET_PARAMETER:
+    rtspreq = RTSPREQ_GET_PARAMETER;
+    break;
+
+  case CURL_RTSPREQ_SET_PARAMETER:
+    rtspreq = RTSPREQ_SET_PARAMETER;
+    break;
+
+  case CURL_RTSPREQ_RECORD:
+    rtspreq = RTSPREQ_RECORD;
+    break;
+
+  case CURL_RTSPREQ_RECEIVE:
+    rtspreq = RTSPREQ_RECEIVE;
+    break;
+  default:
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+  }
+
+  data->set.rtspreq = rtspreq;
+  return CURLE_OK;
+}
+#endif /* ! CURL_DISABLE_RTSP */
+
+#ifdef USE_SSL
+static void set_ssl_options(struct ssl_config_data *ssl,
+                            struct ssl_primary_config *config,
+                            long arg)
+{
+  config->ssl_options = (unsigned char)(arg & 0xff);
+  ssl->enable_beast = !!(arg & CURLSSLOPT_ALLOW_BEAST);
+  ssl->no_revoke = !!(arg & CURLSSLOPT_NO_REVOKE);
+  ssl->no_partialchain = !!(arg & CURLSSLOPT_NO_PARTIALCHAIN);
+  ssl->revoke_best_effort = !!(arg & CURLSSLOPT_REVOKE_BEST_EFFORT);
+  ssl->native_ca_store = !!(arg & CURLSSLOPT_NATIVE_CA);
+  ssl->auto_client_cert = !!(arg & CURLSSLOPT_AUTO_CLIENT_CERT);
+  ssl->earlydata = !!(arg & CURLSSLOPT_EARLYDATA);
+}
+#endif
+
 static CURLcode setopt_long(struct Curl_easy *data, CURLoption option,
                             long arg)
 {
@@ -290,7 +442,7 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option,
       return CURLE_BAD_FUNCTION_ARGUMENT;
     data->set.maxconnects = (unsigned int)uarg;
     break;
-   case CURLOPT_FORBID_REUSE:
+  case CURLOPT_FORBID_REUSE:
     /*
      * When this transfer is done, it must not be left to be reused by a
      * subsequent transfer but shall be closed immediately.
@@ -321,11 +473,7 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option,
     /*
      * Shut off the internal supported progress meter
      */
-    data->set.hide_progress = enabled;
-    if(data->set.hide_progress)
-      data->progress.flags |= PGRS_HIDE;
-    else
-      data->progress.flags &= ~PGRS_HIDE;
+    data->progress.hide = enabled;
     break;
   case CURLOPT_NOBODY:
     /*
@@ -438,7 +586,7 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option,
      */
     if((arg < CURL_TIMECOND_NONE) || (arg >= CURL_TIMECOND_LAST))
       return CURLE_BAD_FUNCTION_ARGUMENT;
-    data->set.timecondition = (unsigned char)(curl_TimeCond)arg;
+    data->set.timecondition = (unsigned char)arg;
     break;
   case CURLOPT_TIMEVALUE:
     /*
@@ -451,36 +599,12 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option,
 #ifndef CURL_DISABLE_PROXY
   case CURLOPT_PROXY_SSLVERSION:
 #endif
-    /*
-     * Set explicit SSL version to try to connect with, as some SSL
-     * implementations are lame.
-     */
 #ifdef USE_SSL
-    {
-      long version, version_max;
-      struct ssl_primary_config *primary = &data->set.ssl.primary;
-#ifndef CURL_DISABLE_PROXY
-      if(option != CURLOPT_SSLVERSION)
-        primary = &data->set.proxy_ssl.primary;
-#endif
-      version = C_SSLVERSION_VALUE(arg);
-      version_max = (long)C_SSLVERSION_MAX_VALUE(arg);
-
-      if(version < CURL_SSLVERSION_DEFAULT ||
-         version == CURL_SSLVERSION_SSLv2 ||
-         version == CURL_SSLVERSION_SSLv3 ||
-         version >= CURL_SSLVERSION_LAST ||
-         version_max < CURL_SSLVERSION_MAX_NONE ||
-         version_max >= CURL_SSLVERSION_MAX_LAST)
-        return CURLE_BAD_FUNCTION_ARGUMENT;
-
-      primary->version = (unsigned char)version;
-      primary->version_max = (unsigned int)version_max;
-    }
+    return setopt_SSLVERSION(data, option, arg);
 #else
     return CURLE_NOT_BUILT_IN;
 #endif
-    break;
+
   case CURLOPT_POSTFIELDSIZE:
     /*
      * The size of the POSTFIELD data to prevent libcurl to do strlen() to
@@ -525,7 +649,9 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option,
     /*
      * Follow Location: header hints on an HTTP-server.
      */
-    data->set.http_follow_location = enabled;
+    if(uarg > 3)
+      return CURLE_BAD_FUNCTION_ARGUMENT;
+    data->set.http_follow_mode = (unsigned char)uarg;
     break;
 
   case CURLOPT_UNRESTRICTED_AUTH:
@@ -594,44 +720,7 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option,
     break;
 
   case CURLOPT_HTTP_VERSION:
-    /*
-     * This sets a requested HTTP version to be used. The value is one of
-     * the listed enums in curl/curl.h.
-     */
-    switch(arg) {
-    case CURL_HTTP_VERSION_NONE:
-#ifdef USE_HTTP2
-      /* This seems an undesirable quirk to force a behaviour on lower
-       * implementations that they should recognize independently? */
-      arg = CURL_HTTP_VERSION_2TLS;
-#endif
-      /* accepted */
-      break;
-    case CURL_HTTP_VERSION_1_0:
-    case CURL_HTTP_VERSION_1_1:
-      /* accepted */
-      break;
-#ifdef USE_HTTP2
-    case CURL_HTTP_VERSION_2_0:
-    case CURL_HTTP_VERSION_2TLS:
-    case CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE:
-      /* accepted */
-      break;
-#endif
-#ifdef USE_HTTP3
-    case CURL_HTTP_VERSION_3:
-    case CURL_HTTP_VERSION_3ONLY:
-      /* accepted */
-      break;
-#endif
-    default:
-      /* not accepted */
-      if(arg < CURL_HTTP_VERSION_NONE)
-        return CURLE_BAD_FUNCTION_ARGUMENT;
-      return CURLE_UNSUPPORTED_PROTOCOL;
-    }
-    data->set.httpwant = (unsigned char)arg;
-    break;
+    return setopt_HTTP_VERSION(data, arg);
 
   case CURLOPT_EXPECT_100_TIMEOUT_MS:
     /*
@@ -692,7 +781,7 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option,
     data->set.proxy_transfer_mode = (bool)uarg;
     break;
   case CURLOPT_SOCKS5_AUTH:
-    if(data->set.socks5auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI))
+    if(uarg & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI))
       return CURLE_NOT_BUILT_IN;
     data->set.socks5auth = (unsigned char)uarg;
     break;
@@ -1054,29 +1143,12 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option,
     data->set.use_ssl = (unsigned char)arg;
     break;
   case CURLOPT_SSL_OPTIONS:
-    data->set.ssl.primary.ssl_options = (unsigned char)(arg & 0xff);
-    data->set.ssl.enable_beast = !!(arg & CURLSSLOPT_ALLOW_BEAST);
-    data->set.ssl.no_revoke = !!(arg & CURLSSLOPT_NO_REVOKE);
-    data->set.ssl.no_partialchain = !!(arg & CURLSSLOPT_NO_PARTIALCHAIN);
-    data->set.ssl.revoke_best_effort = !!(arg & CURLSSLOPT_REVOKE_BEST_EFFORT);
-    data->set.ssl.native_ca_store = !!(arg & CURLSSLOPT_NATIVE_CA);
-    data->set.ssl.auto_client_cert = !!(arg & CURLSSLOPT_AUTO_CLIENT_CERT);
-    data->set.ssl.earlydata = !!(arg & CURLSSLOPT_EARLYDATA);
-    /* If a setting is added here it should also be added in dohprobe()
-       which sets its own CURLOPT_SSL_OPTIONS based on these settings. */
+    set_ssl_options(&data->set.ssl, &data->set.ssl.primary, arg);
     break;
 
 #ifndef CURL_DISABLE_PROXY
   case CURLOPT_PROXY_SSL_OPTIONS:
-    data->set.proxy_ssl.primary.ssl_options = (unsigned char)(arg & 0xff);
-    data->set.proxy_ssl.enable_beast = !!(arg & CURLSSLOPT_ALLOW_BEAST);
-    data->set.proxy_ssl.no_revoke = !!(arg & CURLSSLOPT_NO_REVOKE);
-    data->set.proxy_ssl.no_partialchain = !!(arg & CURLSSLOPT_NO_PARTIALCHAIN);
-    data->set.proxy_ssl.revoke_best_effort =
-      !!(arg & CURLSSLOPT_REVOKE_BEST_EFFORT);
-    data->set.proxy_ssl.native_ca_store = !!(arg & CURLSSLOPT_NATIVE_CA);
-    data->set.proxy_ssl.auto_client_cert =
-      !!(arg & CURLSSLOPT_AUTO_CLIENT_CERT);
+    set_ssl_options(&data->set.proxy_ssl, &data->set.proxy_ssl.primary, arg);
     break;
 #endif
 
@@ -1132,12 +1204,8 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option,
     /*
      * disable libcurl transfer encoding is used
      */
-#ifndef USE_HYPER
     data->set.http_te_skip = !enabled; /* reversed */
     break;
-#else
-    return CURLE_NOT_BUILT_IN; /* hyper does not support */
-#endif
 
   case CURLOPT_HTTP_CONTENT_DECODING:
     /*
@@ -1207,63 +1275,7 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option,
     break;
 #ifndef CURL_DISABLE_RTSP
   case CURLOPT_RTSP_REQUEST:
-  {
-    /*
-     * Set the RTSP request method (OPTIONS, SETUP, PLAY, etc...)
-     * Would this be better if the RTSPREQ_* were just moved into here?
-     */
-    Curl_RtspReq rtspreq = RTSPREQ_NONE;
-    switch(arg) {
-    case CURL_RTSPREQ_OPTIONS:
-      rtspreq = RTSPREQ_OPTIONS;
-      break;
-
-    case CURL_RTSPREQ_DESCRIBE:
-      rtspreq = RTSPREQ_DESCRIBE;
-      break;
-
-    case CURL_RTSPREQ_ANNOUNCE:
-      rtspreq = RTSPREQ_ANNOUNCE;
-      break;
-
-    case CURL_RTSPREQ_SETUP:
-      rtspreq = RTSPREQ_SETUP;
-      break;
-
-    case CURL_RTSPREQ_PLAY:
-      rtspreq = RTSPREQ_PLAY;
-      break;
-
-    case CURL_RTSPREQ_PAUSE:
-      rtspreq = RTSPREQ_PAUSE;
-      break;
-
-    case CURL_RTSPREQ_TEARDOWN:
-      rtspreq = RTSPREQ_TEARDOWN;
-      break;
-
-    case CURL_RTSPREQ_GET_PARAMETER:
-      rtspreq = RTSPREQ_GET_PARAMETER;
-      break;
-
-    case CURL_RTSPREQ_SET_PARAMETER:
-      rtspreq = RTSPREQ_SET_PARAMETER;
-      break;
-
-    case CURL_RTSPREQ_RECORD:
-      rtspreq = RTSPREQ_RECORD;
-      break;
-
-    case CURL_RTSPREQ_RECEIVE:
-      rtspreq = RTSPREQ_RECEIVE;
-      break;
-    default:
-      return CURLE_BAD_FUNCTION_ARGUMENT;
-    }
-
-    data->set.rtspreq = rtspreq;
-    break;
-  }
+    return setopt_RTSP_REQUEST(data, arg);
   case CURLOPT_RTSP_CLIENT_CSEQ:
     /*
      * Set the CSEQ number to issue for the next RTSP request. Useful if the
@@ -1391,7 +1403,8 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option,
 #endif /* ! CURL_DISABLE_ALTSVC */
 #ifndef CURL_DISABLE_WEBSOCKETS
   case CURLOPT_WS_OPTIONS:
-    data->set.ws_raw_mode =  (bool)(arg & CURLWS_RAW_MODE);
+    data->set.ws_raw_mode = (bool)(arg & CURLWS_RAW_MODE);
+    data->set.ws_no_auto_pong = (bool)(arg & CURLWS_NOAUTOPONG);
     break;
 #endif
   case CURLOPT_QUICK_EXIT:
@@ -1406,7 +1419,9 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option,
      */
     Curl_safefree(data->set.str[STRING_SSL_ENGINE]);
     return Curl_ssl_set_engine_default(data);
-
+  case CURLOPT_UPLOAD_FLAGS:
+    data->set.upload_flags = (unsigned char)arg;
+    break;
   default:
     /* unknown option */
     return CURLE_UNKNOWN_OPTION;
@@ -1534,7 +1549,7 @@ static CURLcode setopt_pointers(struct Curl_easy *data, CURLoption option,
 #if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_SMTP) ||       \
     !defined(CURL_DISABLE_IMAP)
 # ifndef CURL_DISABLE_MIME
-    case CURLOPT_MIMEPOST:
+  case CURLOPT_MIMEPOST:
     /*
      * Set to make us do MIME POST
      */
@@ -1570,11 +1585,6 @@ static CURLcode setopt_pointers(struct Curl_easy *data, CURLoption option,
     if(data->share) {
       Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE);
 
-      if(data->dns.hostcachetype == HCACHE_SHARED) {
-        data->dns.hostcache = NULL;
-        data->dns.hostcachetype = HCACHE_NONE;
-      }
-
 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
       if(data->share->cookies == data->cookies)
         data->cookies = NULL;
@@ -1588,6 +1598,10 @@ static CURLcode setopt_pointers(struct Curl_easy *data, CURLoption option,
       if(data->psl == &data->share->psl)
         data->psl = data->multi ? &data->multi->psl : NULL;
 #endif
+      if(data->share->specifier & (1 << CURL_LOCK_DATA_DNS)) {
+        Curl_resolv_unlink(data, &data->state.dns[0]);
+        Curl_resolv_unlink(data, &data->state.dns[1]);
+      }
 
       data->share->dirty--;
 
@@ -1604,11 +1618,6 @@ static CURLcode setopt_pointers(struct Curl_easy *data, CURLoption option,
 
       data->share->dirty++;
 
-      if(data->share->specifier & (1 << CURL_LOCK_DATA_DNS)) {
-        /* use shared host cache */
-        data->dns.hostcache = &data->share->hostcache;
-        data->dns.hostcachetype = HCACHE_SHARED;
-      }
 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
       if(data->share->cookies) {
         /* use shared cookie list, first free own one if any */
@@ -1662,8 +1671,8 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
     if(Curl_ssl_supports(data, SSLSUPP_CIPHER_LIST))
       /* set a list of cipher we want to use in the SSL connection */
       return Curl_setstropt(&data->set.str[STRING_SSL_CIPHER_LIST], ptr);
-    return CURLE_NOT_BUILT_IN;
-    break;
+    else
+      return CURLE_NOT_BUILT_IN;
 #ifndef CURL_DISABLE_PROXY
   case CURLOPT_PROXY_SSL_CIPHER_LIST:
     if(Curl_ssl_supports(data, SSLSUPP_CIPHER_LIST)) {
@@ -1673,7 +1682,6 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
     }
     else
       return CURLE_NOT_BUILT_IN;
-    break;
 #endif
   case CURLOPT_TLS13_CIPHERS:
     if(Curl_ssl_supports(data, SSLSUPP_TLS13_CIPHERSUITES)) {
@@ -1682,7 +1690,6 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
     }
     else
       return CURLE_NOT_BUILT_IN;
-    break;
 #ifndef CURL_DISABLE_PROXY
   case CURLOPT_PROXY_TLS13_CIPHERS:
     if(Curl_ssl_supports(data, SSLSUPP_TLS13_CIPHERSUITES))
@@ -1691,7 +1698,6 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
                             ptr);
     else
       return CURLE_NOT_BUILT_IN;
-    break;
 #endif
   case CURLOPT_RANDOM_FILE:
     break;
@@ -1944,7 +1950,6 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
      * to decide for us (if CURLOPT_SOCKS_PROXY setting it to NULL).
      */
     return Curl_setstropt(&data->set.str[STRING_PROXY], ptr);
-    break;
 
   case CURLOPT_PRE_PROXY:
     /*
@@ -1969,95 +1974,95 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
      * Set authentication service name for DIGEST-MD5, Kerberos 5 and SPNEGO
      */
     return Curl_setstropt(&data->set.str[STRING_SERVICE_NAME], ptr);
-    break;
 
   case CURLOPT_HEADERDATA:
     /*
      * Custom pointer to pass the header write callback function
      */
-    data->set.writeheader = (void *)ptr;
+    data->set.writeheader = ptr;
     break;
   case CURLOPT_READDATA:
     /*
      * FILE pointer to read the file to be uploaded from. Or possibly used as
      * argument to the read callback.
      */
-    data->set.in_set = (void *)ptr;
+    data->set.in_set = ptr;
     break;
   case CURLOPT_WRITEDATA:
     /*
      * FILE pointer to write to. Or possibly used as argument to the write
      * callback.
      */
-    data->set.out = (void *)ptr;
+    data->set.out = ptr;
     break;
   case CURLOPT_DEBUGDATA:
     /*
      * Set to a void * that should receive all error writes. This
      * defaults to CURLOPT_STDERR for normal operations.
      */
-    data->set.debugdata = (void *)ptr;
+    data->set.debugdata = ptr;
     break;
   case CURLOPT_PROGRESSDATA:
     /*
      * Custom client data to pass to the progress callback
      */
-    data->set.progress_client = (void *)ptr;
+    data->set.progress_client = ptr;
     break;
   case CURLOPT_SEEKDATA:
     /*
      * Seek control callback. Might be NULL.
      */
-    data->set.seek_client = (void *)ptr;
+    data->set.seek_client = ptr;
     break;
   case CURLOPT_IOCTLDATA:
     /*
      * I/O control data pointer. Might be NULL.
      */
-    data->set.ioctl_client = (void *)ptr;
+    data->set.ioctl_client = ptr;
     break;
   case CURLOPT_SSL_CTX_DATA:
     /*
-     * Set a SSL_CTX callback parameter pointer
+     * Set an SSL_CTX callback parameter pointer
      */
 #ifdef USE_SSL
-    if(Curl_ssl_supports(data, SSLSUPP_SSL_CTX))
-      data->set.ssl.fsslctxp = (void *)ptr;
+    if(Curl_ssl_supports(data, SSLSUPP_SSL_CTX)) {
+      data->set.ssl.fsslctxp = ptr;
+      break;
+    }
     else
 #endif
       return CURLE_NOT_BUILT_IN;
-    break;
   case CURLOPT_SOCKOPTDATA:
     /*
      * socket callback data pointer. Might be NULL.
      */
-    data->set.sockopt_client = (void *)ptr;
+    data->set.sockopt_client = ptr;
     break;
   case CURLOPT_OPENSOCKETDATA:
     /*
      * socket callback data pointer. Might be NULL.
      */
-    data->set.opensocket_client = (void *)ptr;
+    data->set.opensocket_client = ptr;
     break;
   case CURLOPT_RESOLVER_START_DATA:
     /*
      * resolver start callback data pointer. Might be NULL.
      */
-    data->set.resolver_start_client = (void *)ptr;
+    data->set.resolver_start_client = ptr;
     break;
   case CURLOPT_CLOSESOCKETDATA:
     /*
      * socket callback data pointer. Might be NULL.
      */
-    data->set.closesocket_client = (void *)ptr;
+    data->set.closesocket_client = ptr;
     break;
   case CURLOPT_TRAILERDATA:
 #ifndef CURL_DISABLE_HTTP
-    data->set.trailer_data = (void *)ptr;
+    data->set.trailer_data = ptr;
 #endif
     break;
   case CURLOPT_PREREQDATA:
-    data->set.prereq_userp = (void *)ptr;
+    data->set.prereq_userp = ptr;
     break;
 
   case CURLOPT_ERRORBUFFER:
@@ -2146,12 +2151,16 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
     result = setstropt_userpwd(ptr, &u, &p);
 
     /* URL decode the components */
-    if(!result && u)
+    if(!result && u) {
+      Curl_safefree(data->set.str[STRING_PROXYUSERNAME]);
       result = Curl_urldecode(u, 0, &data->set.str[STRING_PROXYUSERNAME], NULL,
                               REJECT_ZERO);
-    if(!result && p)
+    }
+    if(!result && p) {
+      Curl_safefree(data->set.str[STRING_PROXYPASSWORD]);
       result = Curl_urldecode(p, 0, &data->set.str[STRING_PROXYPASSWORD], NULL,
                               REJECT_ZERO);
+    }
     free(u);
     free(p);
   }
@@ -2220,6 +2229,7 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
      * String that holds file type of the SSL certificate to use for proxy
      */
     return Curl_setstropt(&data->set.str[STRING_CERT_TYPE_PROXY], ptr);
+
 #endif
   case CURLOPT_SSLKEY:
     /*
@@ -2240,7 +2250,7 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
      * String that holds file type of the SSL key to use
      */
     return Curl_setstropt(&data->set.str[STRING_KEY_TYPE], ptr);
-    break;
+
 #ifndef CURL_DISABLE_PROXY
   case CURLOPT_PROXY_SSLKEYTYPE:
     /*
@@ -2261,6 +2271,7 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
      * String that holds the SSL private key password for proxy.
      */
     return Curl_setstropt(&data->set.str[STRING_KEY_PASSWD_PROXY], ptr);
+
 #endif
   case CURLOPT_SSLENGINE:
     /*
@@ -2283,6 +2294,7 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
     /* enable the HAProxy protocol */
     data->set.haproxyprotocol = TRUE;
     break;
+
 #endif
   case CURLOPT_INTERFACE:
     /*
@@ -2331,8 +2343,8 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
      * CA certificate
      */
     return Curl_setstropt(&data->set.str[STRING_SSL_CAFILE_PROXY], ptr);
-#endif
 
+#endif
   case CURLOPT_CAPATH:
     /*
      * Set CA path info for SSL connection. Specify directory name of the CA
@@ -2371,6 +2383,7 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
      * CRL to check certificates revocation
      */
     return Curl_setstropt(&data->set.str[STRING_SSL_CRLFILE_PROXY], ptr);
+
 #endif
   case CURLOPT_ISSUERCERT:
     /*
@@ -2388,12 +2401,11 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
     return Curl_setstropt(&data->set.str[STRING_SSL_ISSUERCERT_PROXY], ptr);
 
 #endif
-
   case CURLOPT_PRIVATE:
     /*
      * Set private data pointer.
      */
-    data->set.private_data = (void *)ptr;
+    data->set.private_data = ptr;
     break;
 
 #ifdef USE_SSL
@@ -2403,6 +2415,16 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
      * Specify colon-delimited list of curve algorithm names.
      */
     return Curl_setstropt(&data->set.str[STRING_SSL_EC_CURVES], ptr);
+
+  case CURLOPT_SSL_SIGNATURE_ALGORITHMS:
+    /*
+     * Set accepted signature algorithms.
+     * Specify colon-delimited list of signature scheme names.
+     */
+    if(Curl_ssl_supports(data, SSLSUPP_SIGNATURE_ALGORITHMS))
+      return Curl_setstropt(&data->set.str[STRING_SSL_SIGNATURE_ALGORITHMS],
+                            ptr);
+    return CURLE_NOT_BUILT_IN;
 #endif
 #ifdef USE_SSH
   case CURLOPT_SSH_PUBLIC_KEYFILE:
@@ -2435,7 +2457,7 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
     /*
      * Custom client data to pass to the SSH keyfunc callback
      */
-    data->set.ssh_keyfunc_userp = (void *)ptr;
+    data->set.ssh_keyfunc_userp = ptr;
     break;
 #ifdef USE_LIBSSH2
   case CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256:
@@ -2450,7 +2472,7 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
     /*
      * Custom client data to pass to the SSH keyfunc callback
      */
-    data->set.ssh_hostkeyfunc_userp = (void *)ptr;
+    data->set.ssh_hostkeyfunc_userp = ptr;
     break;
 #endif /* USE_LIBSSH2 */
 #endif /* USE_SSH */
@@ -2481,7 +2503,6 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
     /* Set the SMTP auth originator */
     return Curl_setstropt(&data->set.str[STRING_MAIL_AUTH], ptr);
 #endif
-
   case CURLOPT_SASL_AUTHZID:
     /* Authorization identity (identity to act as) */
     return Curl_setstropt(&data->set.str[STRING_SASL_AUTHZID], ptr);
@@ -2500,7 +2521,6 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
      * for generic server options, the application will need to set this.
      */
     return Curl_setstropt(&data->set.str[STRING_RTSP_STREAM_URI], ptr);
-    break;
 
   case CURLOPT_RTSP_TRANSPORT:
     /*
@@ -2509,15 +2529,15 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
     return Curl_setstropt(&data->set.str[STRING_RTSP_TRANSPORT], ptr);
 
   case CURLOPT_INTERLEAVEDATA:
-    data->set.rtp_out = (void *)ptr;
+    data->set.rtp_out = ptr;
     break;
 #endif /* ! CURL_DISABLE_RTSP */
 #ifndef CURL_DISABLE_FTP
   case CURLOPT_CHUNK_DATA:
-    data->set.wildcardptr = (void *)ptr;
+    data->set.wildcardptr = ptr;
     break;
   case CURLOPT_FNMATCH_DATA:
-    data->set.fnmatch_data = (void *)ptr;
+    data->set.fnmatch_data = ptr;
     break;
 #endif
 #ifdef USE_TLS_SRP
@@ -2547,30 +2567,30 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
     break;
 #endif
 #endif
-#ifdef USE_ARES
+#ifdef CURLRES_ARES
   case CURLOPT_DNS_SERVERS:
     result = Curl_setstropt(&data->set.str[STRING_DNS_SERVERS], ptr);
     if(result)
       return result;
-    return Curl_set_dns_servers(data, data->set.str[STRING_DNS_SERVERS]);
+    return Curl_async_ares_set_dns_servers(data);
 
   case CURLOPT_DNS_INTERFACE:
     result = Curl_setstropt(&data->set.str[STRING_DNS_INTERFACE], ptr);
     if(result)
       return result;
-    return Curl_set_dns_interface(data, data->set.str[STRING_DNS_INTERFACE]);
+    return Curl_async_ares_set_dns_interface(data);
 
   case CURLOPT_DNS_LOCAL_IP4:
     result = Curl_setstropt(&data->set.str[STRING_DNS_LOCAL_IP4], ptr);
     if(result)
       return result;
-    return Curl_set_dns_local_ip4(data, data->set.str[STRING_DNS_LOCAL_IP4]);
+    return Curl_async_ares_set_dns_local_ip4(data);
 
   case CURLOPT_DNS_LOCAL_IP6:
     result = Curl_setstropt(&data->set.str[STRING_DNS_LOCAL_IP6], ptr);
     if(result)
       return result;
-    return Curl_set_dns_local_ip6(data, data->set.str[STRING_DNS_LOCAL_IP6]);
+    return Curl_async_ares_set_dns_local_ip6(data);
 
 #endif
 #ifdef USE_UNIX_SOCKETS
@@ -2592,10 +2612,10 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
 #endif
 #ifndef CURL_DISABLE_HSTS
   case CURLOPT_HSTSREADDATA:
-    data->set.hsts_read_userp = (void *)ptr;
+    data->set.hsts_read_userp = ptr;
     break;
   case CURLOPT_HSTSWRITEDATA:
-    data->set.hsts_write_userp = (void *)ptr;
+    data->set.hsts_write_userp = ptr;
     break;
   case CURLOPT_HSTS: {
     struct curl_slist *h;
@@ -2767,15 +2787,16 @@ static CURLcode setopt_func(struct Curl_easy *data, CURLoption option,
     break;
   case CURLOPT_SSL_CTX_FUNCTION:
     /*
-     * Set a SSL_CTX callback
+     * Set an SSL_CTX callback
      */
 #ifdef USE_SSL
-    if(Curl_ssl_supports(data, SSLSUPP_SSL_CTX))
+    if(Curl_ssl_supports(data, SSLSUPP_SSL_CTX)) {
       data->set.ssl.fsslctx = va_arg(param, curl_ssl_ctx_callback);
+      break;
+    }
     else
 #endif
       return CURLE_NOT_BUILT_IN;
-    break;
 
   case CURLOPT_SOCKOPTFUNCTION:
     /*
@@ -2808,7 +2829,6 @@ static CURLcode setopt_func(struct Curl_easy *data, CURLoption option,
     data->set.resolver_start = va_arg(param, curl_resolver_start_callback);
     break;
 
-
 #ifdef USE_SSH
 #ifdef USE_LIBSSH2
   case CURLOPT_SSH_HOSTKEYFUNCTION:
@@ -3073,9 +3093,7 @@ CURLcode curl_easy_setopt(CURL *d, CURLoption tag, ...)
   result = Curl_vsetopt(data, tag, arg);
 
   va_end(arg);
-#ifdef DEBUGBUILD
   if(result == CURLE_BAD_FUNCTION_ARGUMENT)
-    infof(data, "setopt arg 0x%x returned CURLE_BAD_FUNCTION_ARGUMENT", tag);
-#endif
+    failf(data, "setopt 0x%x got bad argument", tag);
   return result;
 }
diff --git a/extra/curl/curl-8.12.1/lib/setopt.h b/extra/curl/curl-8.14.1/lib/setopt.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/setopt.h
rename to extra/curl/curl-8.14.1/lib/setopt.h
diff --git a/extra/curl/curl-8.12.1/lib/setup-os400.h b/extra/curl/curl-8.14.1/lib/setup-os400.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/setup-os400.h
rename to extra/curl/curl-8.14.1/lib/setup-os400.h
diff --git a/extra/curl/curl-8.12.1/lib/setup-vms.h b/extra/curl/curl-8.14.1/lib/setup-vms.h
similarity index 91%
rename from extra/curl/curl-8.12.1/lib/setup-vms.h
rename to extra/curl/curl-8.14.1/lib/setup-vms.h
index 59b69b6806b3..d74f4ad6305a 100644
--- a/extra/curl/curl-8.12.1/lib/setup-vms.h
+++ b/extra/curl/curl-8.14.1/lib/setup-vms.h
@@ -394,51 +394,11 @@ static struct passwd *vms_getpwuid(uid_t uid)
 /* that way a newer port will also work if some one has one */
 #ifdef __VAX
 
-#   if (OPENSSL_VERSION_NUMBER < 0x00907001L)
-#       define des_set_odd_parity DES_SET_ODD_PARITY
-#       define des_set_key DES_SET_KEY
-#       define des_ecb_encrypt DES_ECB_ENCRYPT
-
-#   endif
 #   include <openssl/evp.h>
 #   ifndef OpenSSL_add_all_algorithms
 #       define OpenSSL_add_all_algorithms OPENSSL_ADD_ALL_ALGORITHMS
         void OPENSSL_ADD_ALL_ALGORITHMS(void);
 #   endif
-
-    /* Curl defines these to lower case and VAX needs them in upper case */
-    /* So we need static routines */
-#   if (OPENSSL_VERSION_NUMBER < 0x00907001L)
-
-#       undef des_set_odd_parity
-#       undef DES_set_odd_parity
-#       undef des_set_key
-#       undef DES_set_key
-#       undef des_ecb_encrypt
-#       undef DES_ecb_encrypt
-
-        static void des_set_odd_parity(des_cblock *key) {
-            DES_SET_ODD_PARITY(key);
-        }
-
-        static int des_set_key(const_des_cblock *key,
-                               des_key_schedule schedule) {
-            return DES_SET_KEY(key, schedule);
-        }
-
-        static void des_ecb_encrypt(const_des_cblock *input,
-                                    des_cblock *output,
-                                    des_key_schedule ks, int enc) {
-            DES_ECB_ENCRYPT(input, output, ks, enc);
-        }
-#endif
-/* Need this to stop a macro redefinition error */
-#if OPENSSL_VERSION_NUMBER < 0x00907000L
-#   ifdef X509_STORE_set_flags
-#       undef X509_STORE_set_flags
-#       define X509_STORE_set_flags(x,y) Curl_nop_stmt
-#   endif
-#endif
 #endif
 
 #endif /* HEADER_CURL_SETUP_VMS_H */
diff --git a/extra/curl/curl-8.12.1/lib/setup-win32.h b/extra/curl/curl-8.14.1/lib/setup-win32.h
similarity index 95%
rename from extra/curl/curl-8.12.1/lib/setup-win32.h
rename to extra/curl/curl-8.14.1/lib/setup-win32.h
index 70d83ad8e96f..35fe513a8420 100644
--- a/extra/curl/curl-8.12.1/lib/setup-win32.h
+++ b/extra/curl/curl-8.14.1/lib/setup-win32.h
@@ -93,12 +93,6 @@
  * newer symbols.
  */
 
-#ifndef _WIN32_WINNT_NT4
-#define _WIN32_WINNT_NT4            0x0400   /* Windows NT 4.0 */
-#endif
-#ifndef _WIN32_WINNT_WIN2K
-#define _WIN32_WINNT_WIN2K          0x0500   /* Windows 2000 */
-#endif
 #ifndef _WIN32_WINNT_WINXP
 #define _WIN32_WINNT_WINXP          0x0501   /* Windows XP */
 #endif
diff --git a/extra/curl/curl-8.12.1/lib/sha256.c b/extra/curl/curl-8.14.1/lib/sha256.c
similarity index 95%
rename from extra/curl/curl-8.12.1/lib/sha256.c
rename to extra/curl/curl-8.14.1/lib/sha256.c
index 4af858eb7025..d73e0ef09379 100644
--- a/extra/curl/curl-8.12.1/lib/sha256.c
+++ b/extra/curl/curl-8.14.1/lib/sha256.c
@@ -28,45 +28,20 @@
 #if !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) \
   || defined(USE_LIBSSH2) || defined(USE_SSL)
 
-#include "warnless.h"
+#include "curlx/warnless.h"
 #include "curl_sha256.h"
 #include "curl_hmac.h"
 
-#ifdef USE_WOLFSSL
-#include <wolfssl/options.h>
-#endif
-
-#if defined(USE_OPENSSL)
-
-#include <openssl/opensslv.h>
-
-#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL)
-#define USE_OPENSSL_SHA256
-#endif
-
-#endif /* USE_OPENSSL */
-
-#ifdef USE_MBEDTLS
+#ifdef USE_OPENSSL
+#include <openssl/evp.h>
+#elif defined(USE_GNUTLS)
+#include <nettle/sha.h>
+#elif defined(USE_MBEDTLS)
 #include <mbedtls/version.h>
-
 #if(MBEDTLS_VERSION_NUMBER >= 0x02070000) && \
    (MBEDTLS_VERSION_NUMBER < 0x03000000)
   #define HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS
 #endif
-#endif /* USE_MBEDTLS */
-
-#if defined(USE_OPENSSL_SHA256)
-
-/* When OpenSSL or wolfSSL is available we use their SHA256-functions. */
-#if defined(USE_OPENSSL)
-#include <openssl/evp.h>
-#elif defined(USE_WOLFSSL)
-#include <wolfssl/openssl/evp.h>
-#endif
-
-#elif defined(USE_GNUTLS)
-#include <nettle/sha.h>
-#elif defined(USE_MBEDTLS)
 #include <mbedtls/sha256.h>
 #elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \
               (__MAC_OS_X_VERSION_MAX_ALLOWED >= 1040)) || \
@@ -95,7 +70,7 @@
  * file even if multiple backends are enabled at the same time.
  */
 
-#if defined(USE_OPENSSL_SHA256)
+#ifdef USE_OPENSSL
 
 struct ossl_sha256_ctx {
   EVP_MD_CTX *openssl_ctx;
@@ -241,7 +216,11 @@ static void my_sha256_update(void *in,
                              unsigned int length)
 {
   my_sha256_ctx *ctx = (my_sha256_ctx *)in;
-  CryptHashData(ctx->hHash, (unsigned char *) data, length, 0);
+#ifdef __MINGW32CE__
+  CryptHashData(ctx->hHash, (BYTE *)CURL_UNCONST(data), length, 0);
+#else
+  CryptHashData(ctx->hHash, (const BYTE *)data, length, 0);
+#endif
 }
 
 static void my_sha256_final(unsigned char *digest, void *in)
@@ -348,7 +327,7 @@ static const unsigned long K[64] = {
 
 /* Compress 512-bits */
 static int sha256_compress(struct sha256_state *md,
-                           unsigned char *buf)
+                           const unsigned char *buf)
 {
   unsigned long S[8], W[64];
   int i;
@@ -426,7 +405,7 @@ static void my_sha256_update(void *ctx,
     return;
   while(inlen > 0) {
     if(md->curlen == 0 && inlen >= CURL_SHA256_BLOCK_SIZE) {
-      if(sha256_compress(md, (unsigned char *)in) < 0)
+      if(sha256_compress(md, in) < 0)
         return;
       md->length += CURL_SHA256_BLOCK_SIZE * 8;
       in += CURL_SHA256_BLOCK_SIZE;
@@ -533,5 +512,4 @@ const struct HMAC_params Curl_HMAC_SHA256 = {
   32                     /* Result size. */
 };
 
-
 #endif /* AWS, DIGEST, or libssh2 */
diff --git a/extra/curl/curl-8.12.1/lib/share.c b/extra/curl/curl-8.14.1/lib/share.c
similarity index 93%
rename from extra/curl/curl-8.12.1/lib/share.c
rename to extra/curl/curl-8.14.1/lib/share.c
index 4145e0c6533a..d1ab55eb2769 100644
--- a/extra/curl/curl-8.12.1/lib/share.c
+++ b/extra/curl/curl-8.14.1/lib/share.c
@@ -46,7 +46,19 @@ curl_share_init(void)
   if(share) {
     share->magic = CURL_GOOD_SHARE;
     share->specifier |= (1 << CURL_LOCK_DATA_SHARE);
-    Curl_init_dnscache(&share->hostcache, 23);
+    Curl_dnscache_init(&share->dnscache, 23);
+    share->admin = curl_easy_init();
+    if(!share->admin) {
+      free(share);
+      return NULL;
+    }
+    /* admin handles have mid 0 */
+    share->admin->mid = 0;
+    share->admin->state.internal = TRUE;
+#ifdef DEBUGBUILD
+    if(getenv("CURL_DEBUG"))
+      share->admin->set.verbose = TRUE;
+#endif
   }
 
   return share;
@@ -125,10 +137,8 @@ curl_share_setopt(CURLSH *sh, CURLSHoption option, ...)
 
     case CURL_LOCK_DATA_CONNECT:
       /* It is safe to set this option several times on a share. */
-      if(!share->cpool.idata) {
-        if(Curl_cpool_init(&share->cpool, Curl_on_disconnect,
-                           NULL, share, 103))
-          res = CURLSHE_NOMEM;
+      if(!share->cpool.initialised) {
+        Curl_cpool_init(&share->cpool, share->admin, share, 103);
       }
       break;
 
@@ -239,7 +249,8 @@ curl_share_cleanup(CURLSH *sh)
   if(share->specifier & (1 << CURL_LOCK_DATA_CONNECT)) {
     Curl_cpool_destroy(&share->cpool);
   }
-  Curl_hash_destroy(&share->hostcache);
+
+  Curl_dnscache_destroy(&share->dnscache);
 
 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
   Curl_cookie_cleanup(share->cookies);
@@ -257,6 +268,7 @@ curl_share_cleanup(CURLSH *sh)
 #endif
 
   Curl_psl_destroy(&share->psl);
+  Curl_close(&share->admin);
 
   if(share->unlockfunc)
     share->unlockfunc(NULL, CURL_LOCK_DATA_SHARE, share->clientdata);
diff --git a/extra/curl/curl-8.12.1/lib/share.h b/extra/curl/curl-8.14.1/lib/share.h
similarity index 96%
rename from extra/curl/curl-8.12.1/lib/share.h
rename to extra/curl/curl-8.14.1/lib/share.h
index d0cdb1b2680b..974c99dc204b 100644
--- a/extra/curl/curl-8.12.1/lib/share.h
+++ b/extra/curl/curl-8.14.1/lib/share.h
@@ -31,6 +31,7 @@
 #include "urldata.h"
 #include "conncache.h"
 
+struct Curl_easy;
 struct Curl_ssl_scache;
 
 #define CURL_GOOD_SHARE 0x7e117a1e
@@ -48,8 +49,9 @@ struct Curl_share {
   curl_lock_function lockfunc;
   curl_unlock_function unlockfunc;
   void *clientdata;
+  struct Curl_easy *admin;
   struct cpool cpool;
-  struct Curl_hash hostcache;
+  struct Curl_dnscache dnscache; /* DNS cache */
 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
   struct CookieInfo *cookies;
 #endif
diff --git a/extra/curl/curl-8.12.1/lib/sigpipe.h b/extra/curl/curl-8.14.1/lib/sigpipe.h
similarity index 99%
rename from extra/curl/curl-8.12.1/lib/sigpipe.h
rename to extra/curl/curl-8.14.1/lib/sigpipe.h
index c57580f434a9..1be3a111e958 100644
--- a/extra/curl/curl-8.12.1/lib/sigpipe.h
+++ b/extra/curl/curl-8.14.1/lib/sigpipe.h
@@ -31,7 +31,7 @@
 
 struct sigpipe_ignore {
   struct sigaction old_pipe_act;
-  bool no_signal;
+  BIT(no_signal);
 };
 
 #define SIGPIPE_VARIABLE(x) struct sigpipe_ignore x
diff --git a/extra/curl/curl-8.12.1/lib/slist.c b/extra/curl/curl-8.14.1/lib/slist.c
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/slist.c
rename to extra/curl/curl-8.14.1/lib/slist.c
diff --git a/extra/curl/curl-8.12.1/lib/slist.h b/extra/curl/curl-8.14.1/lib/slist.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/slist.h
rename to extra/curl/curl-8.14.1/lib/slist.h
diff --git a/extra/curl/curl-8.12.1/lib/smb.c b/extra/curl/curl-8.14.1/lib/smb.c
similarity index 79%
rename from extra/curl/curl-8.12.1/lib/smb.c
rename to extra/curl/curl-8.14.1/lib/smb.c
index d22030ccc752..ceca9564bf18 100644
--- a/extra/curl/curl-8.12.1/lib/smb.c
+++ b/extra/curl/curl-8.14.1/lib/smb.c
@@ -29,6 +29,7 @@
 
 #include "smb.h"
 #include "urldata.h"
+#include "url.h"
 #include "sendf.h"
 #include "multiif.h"
 #include "cfilters.h"
@@ -45,6 +46,58 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
+
+/* meta key for storing protocol meta at easy handle */
+#define CURL_META_SMB_EASY   "meta:proto:smb:easy"
+/* meta key for storing protocol meta at connection */
+#define CURL_META_SMB_CONN   "meta:proto:smb:conn"
+
+enum smb_conn_state {
+  SMB_NOT_CONNECTED = 0,
+  SMB_CONNECTING,
+  SMB_NEGOTIATE,
+  SMB_SETUP,
+  SMB_CONNECTED
+};
+
+/* SMB connection data, kept at connection */
+struct smb_conn {
+  enum smb_conn_state state;
+  char *user;
+  char *domain;
+  char *share;
+  unsigned char challenge[8];
+  unsigned int session_key;
+  unsigned short uid;
+  char *recv_buf;
+  char *send_buf;
+  size_t upload_size;
+  size_t send_size;
+  size_t sent;
+  size_t got;
+};
+
+/* SMB request state */
+enum smb_req_state {
+  SMB_REQUESTING,
+  SMB_TREE_CONNECT,
+  SMB_OPEN,
+  SMB_DOWNLOAD,
+  SMB_UPLOAD,
+  SMB_CLOSE,
+  SMB_TREE_DISCONNECT,
+  SMB_DONE
+};
+
+/* SMB request data, kept at easy handle */
+struct smb_request {
+  enum smb_req_state state;
+  char *path;
+  unsigned short tid; /* Even if we connect to the same tree as another */
+  unsigned short fid; /* request, the tid will be different */
+  CURLcode result;
+};
+
 /*
  * Definitions for SMB protocol data structures
  */
@@ -77,7 +130,7 @@
 
 #define SMB_FLAGS_CANONICAL_PATHNAMES 0x10
 #define SMB_FLAGS_CASELESS_PATHNAMES  0x08
-#define SMB_FLAGS2_UNICODE_STRINGS    0x8000
+/* #define SMB_FLAGS2_UNICODE_STRINGS    0x8000 */
 #define SMB_FLAGS2_IS_LONG_NAME       0x0040
 #define SMB_FLAGS2_KNOWS_LONG_NAME    0x0001
 
@@ -245,12 +298,11 @@ static CURLcode smb_connect(struct Curl_easy *data, bool *done);
 static CURLcode smb_connection_state(struct Curl_easy *data, bool *done);
 static CURLcode smb_do(struct Curl_easy *data, bool *done);
 static CURLcode smb_request_state(struct Curl_easy *data, bool *done);
-static CURLcode smb_disconnect(struct Curl_easy *data,
-                               struct connectdata *conn, bool dead);
 static int smb_getsock(struct Curl_easy *data, struct connectdata *conn,
                        curl_socket_t *socks);
 static CURLcode smb_parse_url_path(struct Curl_easy *data,
-                                   struct connectdata *conn);
+                                   struct smb_conn *smbc,
+                                   struct smb_request *req);
 
 /*
  * SMB handler interface
@@ -268,7 +320,7 @@ const struct Curl_handler Curl_handler_smb = {
   smb_getsock,                          /* doing_getsock */
   ZERO_NULL,                            /* domore_getsock */
   ZERO_NULL,                            /* perform_getsock */
-  smb_disconnect,                       /* disconnect */
+  ZERO_NULL,                            /* disconnect */
   ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
@@ -297,7 +349,7 @@ const struct Curl_handler Curl_handler_smbs = {
   smb_getsock,                          /* doing_getsock */
   ZERO_NULL,                            /* domore_getsock */
   ZERO_NULL,                            /* perform_getsock */
-  smb_disconnect,                       /* disconnect */
+  ZERO_NULL,                            /* disconnect */
   ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
@@ -341,30 +393,9 @@ static curl_off_t smb_swap64(curl_off_t x)
 #  define smb_swap64(x) (x)
 #endif
 
-/* SMB request state */
-enum smb_req_state {
-  SMB_REQUESTING,
-  SMB_TREE_CONNECT,
-  SMB_OPEN,
-  SMB_DOWNLOAD,
-  SMB_UPLOAD,
-  SMB_CLOSE,
-  SMB_TREE_DISCONNECT,
-  SMB_DONE
-};
-
-/* SMB request data */
-struct smb_request {
-  enum smb_req_state state;
-  char *path;
-  unsigned short tid; /* Even if we connect to the same tree as another */
-  unsigned short fid; /* request, the tid will be different */
-  CURLcode result;
-};
-
-static void conn_state(struct Curl_easy *data, enum smb_conn_state newstate)
+static void conn_state(struct Curl_easy *data, struct smb_conn *smbc,
+                       enum smb_conn_state newstate)
 {
-  struct smb_conn *smbc = &data->conn->proto.smbc;
 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
   /* For debug purposes */
   static const char * const names[] = {
@@ -380,34 +411,59 @@ static void conn_state(struct Curl_easy *data, enum smb_conn_state newstate)
     infof(data, "SMB conn %p state change from %s to %s",
           (void *)smbc, names[smbc->state], names[newstate]);
 #endif
-
+  (void)data;
   smbc->state = newstate;
 }
 
 static void request_state(struct Curl_easy *data,
                           enum smb_req_state newstate)
 {
-  struct smb_request *req = data->req.p.smb;
+  struct smb_request *req = Curl_meta_get(data, CURL_META_SMB_EASY);
+  if(req) {
 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
-  /* For debug purposes */
-  static const char * const names[] = {
-    "SMB_REQUESTING",
-    "SMB_TREE_CONNECT",
-    "SMB_OPEN",
-    "SMB_DOWNLOAD",
-    "SMB_UPLOAD",
-    "SMB_CLOSE",
-    "SMB_TREE_DISCONNECT",
-    "SMB_DONE",
-    /* LAST */
-  };
-
-  if(req->state != newstate)
-    infof(data, "SMB request %p state change from %s to %s",
-          (void *)req, names[req->state], names[newstate]);
+    /* For debug purposes */
+    static const char * const names[] = {
+      "SMB_REQUESTING",
+      "SMB_TREE_CONNECT",
+      "SMB_OPEN",
+      "SMB_DOWNLOAD",
+      "SMB_UPLOAD",
+      "SMB_CLOSE",
+      "SMB_TREE_DISCONNECT",
+      "SMB_DONE",
+      /* LAST */
+    };
+
+    if(req->state != newstate)
+      infof(data, "SMB request %p state change from %s to %s",
+            (void *)req, names[req->state], names[newstate]);
 #endif
 
-  req->state = newstate;
+    req->state = newstate;
+  }
+}
+
+static void smb_easy_dtor(void *key, size_t klen, void *entry)
+{
+  struct smb_request *req = entry;
+  (void)key;
+  (void)klen;
+  /* `req->path` points to somewhere in `struct smb_conn` which is
+   * kept at the connection meta. If the connection is destroyed first,
+   * req->path points to free'd memory. */
+  free(req);
+}
+
+static void smb_conn_dtor(void *key, size_t klen, void *entry)
+{
+  struct smb_conn *smbc = entry;
+  (void)key;
+  (void)klen;
+  Curl_safefree(smbc->share);
+  Curl_safefree(smbc->domain);
+  Curl_safefree(smbc->recv_buf);
+  Curl_safefree(smbc->send_buf);
+  free(smbc);
 }
 
 /* this should setup things in the connection, not in the easy
@@ -415,24 +471,34 @@ static void request_state(struct Curl_easy *data,
 static CURLcode smb_setup_connection(struct Curl_easy *data,
                                      struct connectdata *conn)
 {
+  struct smb_conn *smbc;
   struct smb_request *req;
 
+  /* Initialize the connection state */
+  smbc = calloc(1, sizeof(*smbc));
+  if(!smbc ||
+     Curl_conn_meta_set(conn, CURL_META_SMB_CONN, smbc, smb_conn_dtor))
+    return CURLE_OUT_OF_MEMORY;
+
   /* Initialize the request state */
-  data->req.p.smb = req = calloc(1, sizeof(struct smb_request));
-  if(!req)
+  req = calloc(1, sizeof(*req));
+  if(!req ||
+     Curl_meta_set(data, CURL_META_SMB_EASY, req, smb_easy_dtor))
     return CURLE_OUT_OF_MEMORY;
 
   /* Parse the URL path */
-  return smb_parse_url_path(data, conn);
+  return smb_parse_url_path(data, smbc, req);
 }
 
 static CURLcode smb_connect(struct Curl_easy *data, bool *done)
 {
   struct connectdata *conn = data->conn;
-  struct smb_conn *smbc = &conn->proto.smbc;
+  struct smb_conn *smbc = Curl_conn_meta_get(conn, CURL_META_SMB_CONN);
   char *slash;
 
   (void) done;
+  if(!smbc)
+    return CURLE_FAILED_INIT;
 
   /* Check we have a username and password to authenticate with */
   if(!data->state.aptr.user)
@@ -472,10 +538,10 @@ static CURLcode smb_connect(struct Curl_easy *data, bool *done)
   return CURLE_OK;
 }
 
-static CURLcode smb_recv_message(struct Curl_easy *data, void **msg)
+static CURLcode smb_recv_message(struct Curl_easy *data,
+                                 struct smb_conn *smbc,
+                                 void **msg)
 {
-  struct connectdata *conn = data->conn;
-  struct smb_conn *smbc = &conn->proto.smbc;
   char *buf = smbc->recv_buf;
   ssize_t bytes_read;
   size_t nbt_size;
@@ -520,20 +586,17 @@ static CURLcode smb_recv_message(struct Curl_easy *data, void **msg)
   return CURLE_OK;
 }
 
-static void smb_pop_message(struct connectdata *conn)
+static void smb_pop_message(struct smb_conn *smbc)
 {
-  struct smb_conn *smbc = &conn->proto.smbc;
-
   smbc->got = 0;
 }
 
-static void smb_format_message(struct Curl_easy *data, struct smb_header *h,
+static void smb_format_message(struct smb_conn *smbc,
+                               struct smb_request *req,
+                               struct smb_header *h,
                                unsigned char cmd, size_t len)
 {
-  struct connectdata *conn = data->conn;
-  struct smb_conn *smbc = &conn->proto.smbc;
-  struct smb_request *req = data->req.p.smb;
-  unsigned int pid;
+  const unsigned int pid = 0xbad71d; /* made up */
 
   memset(h, 0, sizeof(*h));
   h->nbt_length = htons((unsigned short) (sizeof(*h) - sizeof(unsigned int) +
@@ -544,16 +607,13 @@ static void smb_format_message(struct Curl_easy *data, struct smb_header *h,
   h->flags2 = smb_swap16(SMB_FLAGS2_IS_LONG_NAME | SMB_FLAGS2_KNOWS_LONG_NAME);
   h->uid = smb_swap16(smbc->uid);
   h->tid = smb_swap16(req->tid);
-  pid = (unsigned int)Curl_getpid();
   h->pid_high = smb_swap16((unsigned short)(pid >> 16));
   h->pid = smb_swap16((unsigned short) pid);
 }
 
-static CURLcode smb_send(struct Curl_easy *data, size_t len,
-                         size_t upload_size)
+static CURLcode smb_send(struct Curl_easy *data, struct smb_conn *smbc,
+                         size_t len, size_t upload_size)
 {
-  struct connectdata *conn = data->conn;
-  struct smb_conn *smbc = &conn->proto.smbc;
   size_t bytes_written;
   CURLcode result;
 
@@ -571,10 +631,8 @@ static CURLcode smb_send(struct Curl_easy *data, size_t len,
   return CURLE_OK;
 }
 
-static CURLcode smb_flush(struct Curl_easy *data)
+static CURLcode smb_flush(struct Curl_easy *data, struct smb_conn *smbc)
 {
-  struct connectdata *conn = data->conn;
-  struct smb_conn *smbc = &conn->proto.smbc;
   size_t bytes_written;
   size_t len = smbc->send_size - smbc->sent;
   CURLcode result;
@@ -595,39 +653,46 @@ static CURLcode smb_flush(struct Curl_easy *data)
   return CURLE_OK;
 }
 
-static CURLcode smb_send_message(struct Curl_easy *data, unsigned char cmd,
+static CURLcode smb_send_message(struct Curl_easy *data,
+                                 struct smb_conn *smbc,
+                                 struct smb_request *req,
+                                 unsigned char cmd,
                                  const void *msg, size_t msg_len)
 {
-  struct connectdata *conn = data->conn;
-  struct smb_conn *smbc = &conn->proto.smbc;
-
-  smb_format_message(data, (struct smb_header *)smbc->send_buf,
+  smb_format_message(smbc, req, (struct smb_header *)smbc->send_buf,
                      cmd, msg_len);
   DEBUGASSERT((sizeof(struct smb_header) + msg_len) <= MAX_MESSAGE_SIZE);
   memcpy(smbc->send_buf + sizeof(struct smb_header), msg, msg_len);
 
-  return smb_send(data, sizeof(struct smb_header) + msg_len, 0);
+  return smb_send(data, smbc, sizeof(struct smb_header) + msg_len, 0);
 }
 
-static CURLcode smb_send_negotiate(struct Curl_easy *data)
+static CURLcode smb_send_negotiate(struct Curl_easy *data,
+                                   struct smb_conn *smbc,
+                                   struct smb_request *req)
 {
   const char *msg = "\x00\x0c\x00\x02NT LM 0.12";
 
-  return smb_send_message(data, SMB_COM_NEGOTIATE, msg, 15);
+  return smb_send_message(data, smbc, req, SMB_COM_NEGOTIATE, msg, 15);
 }
 
 static CURLcode smb_send_setup(struct Curl_easy *data)
 {
   struct connectdata *conn = data->conn;
-  struct smb_conn *smbc = &conn->proto.smbc;
+  struct smb_conn *smbc = Curl_conn_meta_get(conn, CURL_META_SMB_CONN);
+  struct smb_request *req = Curl_meta_get(data, CURL_META_SMB_EASY);
   struct smb_setup msg;
   char *p = msg.bytes;
   unsigned char lm_hash[21];
   unsigned char lm[24];
   unsigned char nt_hash[21];
   unsigned char nt[24];
+  size_t byte_count;
+
+  if(!smbc || !req)
+    return CURLE_FAILED_INIT;
 
-  const size_t byte_count = sizeof(lm) + sizeof(nt) +
+  byte_count = sizeof(lm) + sizeof(nt) +
     strlen(smbc->user) + strlen(smbc->domain) +
     strlen(CURL_OS) + strlen(CLIENTNAME) + 4; /* 4 null chars */
   if(byte_count > sizeof(msg.bytes))
@@ -658,23 +723,24 @@ static CURLcode smb_send_setup(struct Curl_easy *data)
                  "%s%c"  /* OS */
                  "%s", /* client name */
                  smbc->user, 0, smbc->domain, 0, CURL_OS, 0, CLIENTNAME);
-  p++; /* count the final null termination */
+  p++; /* count the final null-termination */
   DEBUGASSERT(byte_count == (size_t)(p - msg.bytes));
   msg.byte_count = smb_swap16((unsigned short)byte_count);
 
-  return smb_send_message(data, SMB_COM_SETUP_ANDX, &msg,
+  return smb_send_message(data, smbc, req, SMB_COM_SETUP_ANDX, &msg,
                           sizeof(msg) - sizeof(msg.bytes) + byte_count);
 }
 
-static CURLcode smb_send_tree_connect(struct Curl_easy *data)
+static CURLcode smb_send_tree_connect(struct Curl_easy *data,
+                                      struct smb_conn *smbc,
+                                      struct smb_request *req)
 {
   struct smb_tree_connect msg;
   struct connectdata *conn = data->conn;
-  struct smb_conn *smbc = &conn->proto.smbc;
   char *p = msg.bytes;
-
   const size_t byte_count = strlen(conn->host.name) + strlen(smbc->share) +
     strlen(SERVICENAME) + 5; /* 2 nulls and 3 backslashes */
+
   if(byte_count > sizeof(msg.bytes))
     return CURLE_FILESIZE_EXCEEDED;
 
@@ -688,17 +754,18 @@ static CURLcode smb_send_tree_connect(struct Curl_easy *data)
                  "%s%c"      /* share */
                  "%s",       /* service */
                  conn->host.name, smbc->share, 0, SERVICENAME);
-  p++; /* count the final null termination */
+  p++; /* count the final null-termination */
   DEBUGASSERT(byte_count == (size_t)(p - msg.bytes));
   msg.byte_count = smb_swap16((unsigned short)byte_count);
 
-  return smb_send_message(data, SMB_COM_TREE_CONNECT_ANDX, &msg,
+  return smb_send_message(data, smbc, req, SMB_COM_TREE_CONNECT_ANDX, &msg,
                           sizeof(msg) - sizeof(msg.bytes) + byte_count);
 }
 
-static CURLcode smb_send_open(struct Curl_easy *data)
+static CURLcode smb_send_open(struct Curl_easy *data,
+                              struct smb_conn *smbc,
+                              struct smb_request *req)
 {
-  struct smb_request *req = data->req.p.smb;
   struct smb_nt_create msg;
   const size_t byte_count = strlen(req->path) + 1;
 
@@ -721,34 +788,37 @@ static CURLcode smb_send_open(struct Curl_easy *data)
   msg.byte_count = smb_swap16((unsigned short) byte_count);
   strcpy(msg.bytes, req->path);
 
-  return smb_send_message(data, SMB_COM_NT_CREATE_ANDX, &msg,
+  return smb_send_message(data, smbc, req, SMB_COM_NT_CREATE_ANDX, &msg,
                           sizeof(msg) - sizeof(msg.bytes) + byte_count);
 }
 
-static CURLcode smb_send_close(struct Curl_easy *data)
+static CURLcode smb_send_close(struct Curl_easy *data,
+                               struct smb_conn *smbc,
+                               struct smb_request *req)
 {
-  struct smb_request *req = data->req.p.smb;
   struct smb_close msg;
 
   memset(&msg, 0, sizeof(msg));
   msg.word_count = SMB_WC_CLOSE;
   msg.fid = smb_swap16(req->fid);
 
-  return smb_send_message(data, SMB_COM_CLOSE, &msg, sizeof(msg));
+  return smb_send_message(data, smbc, req, SMB_COM_CLOSE, &msg, sizeof(msg));
 }
 
-static CURLcode smb_send_tree_disconnect(struct Curl_easy *data)
+static CURLcode smb_send_tree_disconnect(struct Curl_easy *data,
+                                         struct smb_conn *smbc,
+                                         struct smb_request *req)
 {
   struct smb_tree_disconnect msg;
-
   memset(&msg, 0, sizeof(msg));
-
-  return smb_send_message(data, SMB_COM_TREE_DISCONNECT, &msg, sizeof(msg));
+  return smb_send_message(data, smbc, req, SMB_COM_TREE_DISCONNECT,
+                          &msg, sizeof(msg));
 }
 
-static CURLcode smb_send_read(struct Curl_easy *data)
+static CURLcode smb_send_read(struct Curl_easy *data,
+                              struct smb_conn *smbc,
+                              struct smb_request *req)
 {
-  struct smb_request *req = data->req.p.smb;
   curl_off_t offset = data->req.offset;
   struct smb_read msg;
 
@@ -761,15 +831,15 @@ static CURLcode smb_send_read(struct Curl_easy *data)
   msg.min_bytes = smb_swap16(MAX_PAYLOAD_SIZE);
   msg.max_bytes = smb_swap16(MAX_PAYLOAD_SIZE);
 
-  return smb_send_message(data, SMB_COM_READ_ANDX, &msg, sizeof(msg));
+  return smb_send_message(data, smbc, req, SMB_COM_READ_ANDX,
+                          &msg, sizeof(msg));
 }
 
-static CURLcode smb_send_write(struct Curl_easy *data)
+static CURLcode smb_send_write(struct Curl_easy *data,
+                               struct smb_conn *smbc,
+                               struct smb_request *req)
 {
-  struct connectdata *conn = data->conn;
-  struct smb_conn *smbc = &conn->proto.smbc;
   struct smb_write *msg;
-  struct smb_request *req = data->req.p.smb;
   curl_off_t offset = data->req.offset;
   curl_off_t upload_size = data->req.size - data->req.bytecount;
 
@@ -787,16 +857,15 @@ static CURLcode smb_send_write(struct Curl_easy *data)
   msg->data_offset = smb_swap16(sizeof(*msg) - sizeof(unsigned int));
   msg->byte_count = smb_swap16((unsigned short) (upload_size + 1));
 
-  smb_format_message(data, &msg->h, SMB_COM_WRITE_ANDX,
+  smb_format_message(smbc, req, &msg->h, SMB_COM_WRITE_ANDX,
                      sizeof(*msg) - sizeof(msg->h) + (size_t) upload_size);
 
-  return smb_send(data, sizeof(*msg), (size_t) upload_size);
+  return smb_send(data, smbc, sizeof(*msg), (size_t) upload_size);
 }
 
-static CURLcode smb_send_and_recv(struct Curl_easy *data, void **msg)
+static CURLcode smb_send_and_recv(struct Curl_easy *data,
+                                  struct smb_conn *smbc, void **msg)
 {
-  struct connectdata *conn = data->conn;
-  struct smb_conn *smbc = &conn->proto.smbc;
   CURLcode result;
   *msg = NULL; /* if it returns early */
 
@@ -819,7 +888,7 @@ static CURLcode smb_send_and_recv(struct Curl_easy *data, void **msg)
 
   /* Check if there is data to send */
   if(smbc->send_size) {
-    result = smb_flush(data);
+    result = smb_flush(data, smbc);
     if(result)
       return result;
   }
@@ -828,18 +897,22 @@ static CURLcode smb_send_and_recv(struct Curl_easy *data, void **msg)
   if(smbc->send_size || smbc->upload_size)
     return CURLE_AGAIN;
 
-  return smb_recv_message(data, msg);
+  return smb_recv_message(data, smbc, msg);
 }
 
 static CURLcode smb_connection_state(struct Curl_easy *data, bool *done)
 {
   struct connectdata *conn = data->conn;
-  struct smb_conn *smbc = &conn->proto.smbc;
+  struct smb_conn *smbc = Curl_conn_meta_get(conn, CURL_META_SMB_CONN);
+  struct smb_request *req = Curl_meta_get(data, CURL_META_SMB_EASY);
   struct smb_negotiate_response *nrsp;
   struct smb_header *h;
   CURLcode result;
   void *msg = NULL;
 
+  if(!smbc || !req)
+    return CURLE_FAILED_INIT;
+
   if(smbc->state == SMB_CONNECTING) {
 #ifdef USE_SSL
     if(Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
@@ -852,17 +925,17 @@ static CURLcode smb_connection_state(struct Curl_easy *data, bool *done)
     }
 #endif
 
-    result = smb_send_negotiate(data);
+    result = smb_send_negotiate(data, smbc, req);
     if(result) {
       connclose(conn, "SMB: failed to send negotiate message");
       return result;
     }
 
-    conn_state(data, SMB_NEGOTIATE);
+    conn_state(data, smbc, SMB_NEGOTIATE);
   }
 
   /* Send the previous message and check for a response */
-  result = smb_send_and_recv(data, &msg);
+  result = smb_send_and_recv(data, smbc, &msg);
   if(result && result != CURLE_AGAIN) {
     connclose(conn, "SMB: failed to communicate");
     return result;
@@ -897,7 +970,7 @@ static CURLcode smb_connection_state(struct Curl_easy *data, bool *done)
       connclose(conn, "SMB: failed to send setup message");
       return result;
     }
-    conn_state(data, SMB_SETUP);
+    conn_state(data, smbc, SMB_SETUP);
     break;
 
   case SMB_SETUP:
@@ -906,16 +979,16 @@ static CURLcode smb_connection_state(struct Curl_easy *data, bool *done)
       return CURLE_LOGIN_DENIED;
     }
     smbc->uid = smb_swap16(h->uid);
-    conn_state(data, SMB_CONNECTED);
+    conn_state(data, smbc, SMB_CONNECTED);
     *done = TRUE;
     break;
 
   default:
-    smb_pop_message(conn);
+    smb_pop_message(smbc);
     return CURLE_OK; /* ignore */
   }
 
-  smb_pop_message(conn);
+  smb_pop_message(smbc);
 
   return CURLE_OK;
 }
@@ -926,24 +999,28 @@ static CURLcode smb_connection_state(struct Curl_easy *data, bool *done)
  */
 static void get_posix_time(time_t *out, curl_off_t timestamp)
 {
-  timestamp -= CURL_OFF_T_C(116444736000000000);
-  timestamp /= 10000000;
+  if(timestamp >= CURL_OFF_T_C(116444736000000000)) {
+    timestamp -= CURL_OFF_T_C(116444736000000000);
+    timestamp /= 10000000;
 #if SIZEOF_TIME_T < SIZEOF_CURL_OFF_T
-  if(timestamp > TIME_T_MAX)
-    *out = TIME_T_MAX;
-  else if(timestamp < TIME_T_MIN)
-    *out = TIME_T_MIN;
-  else
+    if(timestamp > TIME_T_MAX)
+      *out = TIME_T_MAX;
+    else if(timestamp < TIME_T_MIN)
+      *out = TIME_T_MIN;
+    else
 #endif
-    *out = (time_t) timestamp;
+      *out = (time_t) timestamp;
+  }
+  else
+    *out = 0;
 }
 
 static CURLcode smb_request_state(struct Curl_easy *data, bool *done)
 {
   struct connectdata *conn = data->conn;
-  struct smb_request *req = data->req.p.smb;
+  struct smb_conn *smbc = Curl_conn_meta_get(conn, CURL_META_SMB_CONN);
+  struct smb_request *req = Curl_meta_get(data, CURL_META_SMB_EASY);
   struct smb_header *h;
-  struct smb_conn *smbc = &conn->proto.smbc;
   enum smb_req_state next_state = SMB_DONE;
   unsigned short len;
   unsigned short off;
@@ -951,6 +1028,9 @@ static CURLcode smb_request_state(struct Curl_easy *data, bool *done)
   void *msg = NULL;
   const struct smb_nt_create_response *smb_m;
 
+  if(!smbc || !req)
+    return CURLE_FAILED_INIT;
+
   if(data->state.upload && (data->state.infilesize < 0)) {
     failf(data, "SMB upload needs to know the size up front");
     return CURLE_SEND_ERROR;
@@ -958,7 +1038,7 @@ static CURLcode smb_request_state(struct Curl_easy *data, bool *done)
 
   /* Start the request */
   if(req->state == SMB_REQUESTING) {
-    result = smb_send_tree_connect(data);
+    result = smb_send_tree_connect(data, smbc, req);
     if(result) {
       connclose(conn, "SMB: failed to send tree connect message");
       return result;
@@ -968,7 +1048,7 @@ static CURLcode smb_request_state(struct Curl_easy *data, bool *done)
   }
 
   /* Send the previous message and check for a response */
-  result = smb_send_and_recv(data, &msg);
+  result = smb_send_and_recv(data, smbc, &msg);
   if(result && result != CURLE_AGAIN) {
     connclose(conn, "SMB: failed to communicate");
     return result;
@@ -1078,31 +1158,31 @@ static CURLcode smb_request_state(struct Curl_easy *data, bool *done)
     break;
 
   default:
-    smb_pop_message(conn);
+    smb_pop_message(smbc);
     return CURLE_OK; /* ignore */
   }
 
-  smb_pop_message(conn);
+  smb_pop_message(smbc);
 
   switch(next_state) {
   case SMB_OPEN:
-    result = smb_send_open(data);
+    result = smb_send_open(data, smbc, req);
     break;
 
   case SMB_DOWNLOAD:
-    result = smb_send_read(data);
+    result = smb_send_read(data, smbc, req);
     break;
 
   case SMB_UPLOAD:
-    result = smb_send_write(data);
+    result = smb_send_write(data, smbc, req);
     break;
 
   case SMB_CLOSE:
-    result = smb_send_close(data);
+    result = smb_send_close(data, smbc, req);
     break;
 
   case SMB_TREE_DISCONNECT:
-    result = smb_send_tree_disconnect(data);
+    result = smb_send_tree_disconnect(data, smbc, req);
     break;
 
   case SMB_DONE:
@@ -1124,19 +1204,6 @@ static CURLcode smb_request_state(struct Curl_easy *data, bool *done)
   return CURLE_OK;
 }
 
-static CURLcode smb_disconnect(struct Curl_easy *data,
-                               struct connectdata *conn, bool dead)
-{
-  struct smb_conn *smbc = &conn->proto.smbc;
-  (void) dead;
-  (void) data;
-  Curl_safefree(smbc->share);
-  Curl_safefree(smbc->domain);
-  Curl_safefree(smbc->recv_buf);
-  Curl_safefree(smbc->send_buf);
-  return CURLE_OK;
-}
-
 static int smb_getsock(struct Curl_easy *data,
                        struct connectdata *conn, curl_socket_t *socks)
 {
@@ -1148,26 +1215,26 @@ static int smb_getsock(struct Curl_easy *data,
 static CURLcode smb_do(struct Curl_easy *data, bool *done)
 {
   struct connectdata *conn = data->conn;
-  struct smb_conn *smbc = &conn->proto.smbc;
+  struct smb_conn *smbc = Curl_conn_meta_get(conn, CURL_META_SMB_CONN);
 
   *done = FALSE;
-  if(smbc->share) {
+  if(!smbc)
+    return CURLE_FAILED_INIT;
+  if(smbc->share)
     return CURLE_OK;
-  }
   return CURLE_URL_MALFORMAT;
 }
 
 static CURLcode smb_parse_url_path(struct Curl_easy *data,
-                                   struct connectdata *conn)
+                                   struct smb_conn *smbc,
+                                   struct smb_request *req)
 {
-  struct smb_request *req = data->req.p.smb;
-  struct smb_conn *smbc = &conn->proto.smbc;
   char *path;
   char *slash;
+  CURLcode result;
 
   /* URL decode the path */
-  CURLcode result = Curl_urldecode(data->state.up.path, 0, &path, NULL,
-                                   REJECT_CTRL);
+  result = Curl_urldecode(data->state.up.path, 0, &path, NULL, REJECT_CTRL);
   if(result)
     return result;
 
diff --git a/extra/curl/curl-8.12.1/lib/smb.h b/extra/curl/curl-8.14.1/lib/smb.h
similarity index 78%
rename from extra/curl/curl-8.12.1/lib/smb.h
rename to extra/curl/curl-8.14.1/lib/smb.h
index 9ea2a8cc31c8..eb4df6550a3e 100644
--- a/extra/curl/curl-8.12.1/lib/smb.h
+++ b/extra/curl/curl-8.14.1/lib/smb.h
@@ -25,30 +25,6 @@
  *
  ***************************************************************************/
 
-enum smb_conn_state {
-  SMB_NOT_CONNECTED = 0,
-  SMB_CONNECTING,
-  SMB_NEGOTIATE,
-  SMB_SETUP,
-  SMB_CONNECTED
-};
-
-struct smb_conn {
-  enum smb_conn_state state;
-  char *user;
-  char *domain;
-  char *share;
-  unsigned char challenge[8];
-  unsigned int session_key;
-  unsigned short uid;
-  char *recv_buf;
-  char *send_buf;
-  size_t upload_size;
-  size_t send_size;
-  size_t sent;
-  size_t got;
-};
-
 #if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) && \
     (SIZEOF_CURL_OFF_T > 4)
 
diff --git a/extra/curl/curl-8.12.1/lib/smtp.c b/extra/curl/curl-8.14.1/lib/smtp.c
similarity index 77%
rename from extra/curl/curl-8.12.1/lib/smtp.c
rename to extra/curl/curl-8.14.1/lib/smtp.c
index b763557a07d6..d39bb58d5de7 100644
--- a/extra/curl/curl-8.12.1/lib/smtp.c
+++ b/extra/curl/curl-8.14.1/lib/smtp.c
@@ -68,7 +68,6 @@
 #include "mime.h"
 #include "socks.h"
 #include "smtp.h"
-#include "strtoofft.h"
 #include "strcase.h"
 #include "vtls/vtls.h"
 #include "cfilters.h"
@@ -79,15 +78,79 @@
 #include "curl_gethostname.h"
 #include "bufref.h"
 #include "curl_sasl.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
 #include "idn.h"
+#include "curlx/strparse.h"
+
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
 #include "memdebug.h"
 
+/* meta key for storing protocol meta at easy handle */
+#define CURL_META_SMTP_EASY   "meta:proto:smtp:easy"
+/* meta key for storing protocol meta at connection */
+#define CURL_META_SMTP_CONN   "meta:proto:smtp:conn"
+
+/****************************************************************************
+ * SMTP unique setup
+ ***************************************************************************/
+typedef enum {
+  SMTP_STOP,        /* do nothing state, stops the state machine */
+  SMTP_SERVERGREET, /* waiting for the initial greeting immediately after
+                       a connect */
+  SMTP_EHLO,
+  SMTP_HELO,
+  SMTP_STARTTLS,
+  SMTP_UPGRADETLS,  /* asynchronously upgrade the connection to SSL/TLS
+                       (multi mode only) */
+  SMTP_AUTH,
+  SMTP_COMMAND,     /* VRFY, EXPN, NOOP, RSET and HELP */
+  SMTP_MAIL,        /* MAIL FROM */
+  SMTP_RCPT,        /* RCPT TO */
+  SMTP_DATA,
+  SMTP_POSTDATA,
+  SMTP_QUIT,
+  SMTP_LAST         /* never used */
+} smtpstate;
+
+/* smtp_conn is used for struct connection-oriented data in the connectdata
+   struct */
+struct smtp_conn {
+  struct pingpong pp;
+  struct SASL sasl;        /* SASL-related storage */
+  smtpstate state;         /* Always use smtp.c:state() to change state! */
+  char *domain;            /* Client address/name to send in the EHLO */
+  BIT(ssldone);            /* Is connect() over SSL done? */
+  BIT(tls_supported);      /* StartTLS capability supported by server */
+  BIT(size_supported);     /* If server supports SIZE extension according to
+                              RFC 1870 */
+  BIT(utf8_supported);     /* If server supports SMTPUTF8 extension according
+                              to RFC 6531 */
+  BIT(auth_supported);     /* AUTH capability supported by server */
+};
+
+/* This SMTP struct is used in the Curl_easy. All SMTP data that is
+   connection-oriented must be in smtp_conn to properly deal with the fact that
+   perhaps the Curl_easy is changed between the times the connection is
+   used. */
+struct SMTP {
+  curl_pp_transfer transfer;
+  char *custom;            /* Custom Request */
+  struct curl_slist *rcpt; /* Recipient list */
+  int rcpt_last_error;     /* The last error received for RCPT TO command */
+  size_t eob;              /* Number of bytes of the EOB (End Of Body) that
+                              have been received so far */
+  BIT(rcpt_had_ok);        /* Whether any of RCPT TO commands (depends on
+                              total number of recipients) succeeded so far */
+  BIT(trailing_crlf);      /* Specifies if the trailing CRLF is present */
+};
+
 /* Local API functions */
-static CURLcode smtp_regular_transfer(struct Curl_easy *data, bool *done);
+static CURLcode smtp_regular_transfer(struct Curl_easy *data,
+                                      struct smtp_conn *smtpc,
+                                      struct SMTP *smtp,
+                                      bool *done);
 static CURLcode smtp_do(struct Curl_easy *data, bool *done);
 static CURLcode smtp_done(struct Curl_easy *data, CURLcode status,
                           bool premature);
@@ -100,9 +163,12 @@ static int smtp_getsock(struct Curl_easy *data,
 static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done);
 static CURLcode smtp_setup_connection(struct Curl_easy *data,
                                       struct connectdata *conn);
-static CURLcode smtp_parse_url_options(struct connectdata *conn);
-static CURLcode smtp_parse_url_path(struct Curl_easy *data);
-static CURLcode smtp_parse_custom_request(struct Curl_easy *data);
+static CURLcode smtp_parse_url_options(struct connectdata *conn,
+                                       struct smtp_conn *smtpc);
+static CURLcode smtp_parse_url_path(struct Curl_easy *data,
+                                    struct smtp_conn *smtpc);
+static CURLcode smtp_parse_custom_request(struct Curl_easy *data,
+                                          struct SMTP *smtp);
 static CURLcode smtp_parse_address(const char *fqma,
                                    char **address, struct hostname *host);
 static CURLcode smtp_perform_auth(struct Curl_easy *data, const char *mech,
@@ -198,12 +264,16 @@ static const struct SASLproto saslsmtp = {
  * supported authentication mechanisms.
  */
 static bool smtp_endofresp(struct Curl_easy *data, struct connectdata *conn,
-                           char *line, size_t len, int *resp)
+                           const char *line, size_t len, int *resp)
 {
-  struct smtp_conn *smtpc = &conn->proto.smtpc;
+  struct smtp_conn *smtpc = Curl_conn_meta_get(conn, CURL_META_SMTP_CONN);
   bool result = FALSE;
   (void)data;
 
+  DEBUGASSERT(smtpc);
+  if(!smtpc)
+    return FALSE;
+
   /* Nothing for us */
   if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2]))
     return FALSE;
@@ -214,11 +284,14 @@ static bool smtp_endofresp(struct Curl_easy *data, struct connectdata *conn,
      only send the response code instead as per Section 4.2. */
   if(line[3] == ' ' || len == 5) {
     char tmpline[6];
-
+    curl_off_t code;
+    const char *p = tmpline;
     result = TRUE;
-    memset(tmpline, '\0', sizeof(tmpline));
     memcpy(tmpline, line, (len == 5 ? 5 : 3));
-    *resp = curlx_sltosi(strtol(tmpline, NULL, 10));
+    tmpline[len == 5 ? 5 : 3 ] = 0;
+    if(curlx_str_number(&p, &code, len == 5 ? 99999 : 999))
+      return FALSE;
+    *resp = (int) code;
 
     /* Make sure real server never sends internal value */
     if(*resp == 1)
@@ -242,9 +315,16 @@ static bool smtp_endofresp(struct Curl_easy *data, struct connectdata *conn,
  */
 static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out)
 {
-  char *message = Curl_dyn_ptr(&data->conn->proto.smtpc.pp.recvbuf);
-  size_t len = data->conn->proto.smtpc.pp.nfinal;
+  struct smtp_conn *smtpc =
+    Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
+  char *message;
+  size_t len;
+
+  if(!smtpc)
+    return CURLE_FAILED_INIT;
 
+  message = curlx_dyn_ptr(&smtpc->pp.recvbuf);
+  len = smtpc->pp.nfinal;
   if(len > 4) {
     /* Find the start of the message */
     len -= 4;
@@ -274,9 +354,10 @@ static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out)
  *
  * This is the ONLY way to change SMTP state!
  */
-static void smtp_state(struct Curl_easy *data, smtpstate newstate)
+static void smtp_state(struct Curl_easy *data,
+                       struct smtp_conn *smtpc,
+                       smtpstate newstate)
 {
-  struct smtp_conn *smtpc = &data->conn->proto.smtpc;
 #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
   /* for debug purposes */
   static const char * const names[] = {
@@ -311,11 +392,10 @@ static void smtp_state(struct Curl_easy *data, smtpstate newstate)
  * Sends the EHLO command to not only initialise communication with the ESMTP
  * server but to also obtain a list of server side supported capabilities.
  */
-static CURLcode smtp_perform_ehlo(struct Curl_easy *data)
+static CURLcode smtp_perform_ehlo(struct Curl_easy *data,
+                                  struct smtp_conn *smtpc)
 {
   CURLcode result = CURLE_OK;
-  struct connectdata *conn = data->conn;
-  struct smtp_conn *smtpc = &conn->proto.smtpc;
 
   smtpc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanism yet */
   smtpc->sasl.authused = SASL_AUTH_NONE;  /* Clear the authentication mechanism
@@ -327,7 +407,7 @@ static CURLcode smtp_perform_ehlo(struct Curl_easy *data)
   result = Curl_pp_sendf(data, &smtpc->pp, "EHLO %s", smtpc->domain);
 
   if(!result)
-    smtp_state(data, SMTP_EHLO);
+    smtp_state(data, smtpc, SMTP_EHLO);
 
   return result;
 }
@@ -339,10 +419,9 @@ static CURLcode smtp_perform_ehlo(struct Curl_easy *data)
  * Sends the HELO command to initialise communication with the SMTP server.
  */
 static CURLcode smtp_perform_helo(struct Curl_easy *data,
-                                  struct connectdata *conn)
+                                  struct smtp_conn *smtpc)
 {
   CURLcode result = CURLE_OK;
-  struct smtp_conn *smtpc = &conn->proto.smtpc;
 
   smtpc->sasl.authused = SASL_AUTH_NONE; /* No authentication mechanism used
                                             in smtp connections */
@@ -351,7 +430,7 @@ static CURLcode smtp_perform_helo(struct Curl_easy *data,
   result = Curl_pp_sendf(data, &smtpc->pp, "HELO %s", smtpc->domain);
 
   if(!result)
-    smtp_state(data, SMTP_HELO);
+    smtp_state(data, smtpc, SMTP_HELO);
 
   return result;
 }
@@ -363,14 +442,13 @@ static CURLcode smtp_perform_helo(struct Curl_easy *data,
  * Sends the STLS command to start the upgrade to TLS.
  */
 static CURLcode smtp_perform_starttls(struct Curl_easy *data,
-                                      struct connectdata *conn)
+                                      struct smtp_conn *smtpc)
 {
   /* Send the STARTTLS command */
-  CURLcode result = Curl_pp_sendf(data, &conn->proto.smtpc.pp,
-                                  "%s", "STARTTLS");
+  CURLcode result = Curl_pp_sendf(data, &smtpc->pp, "%s", "STARTTLS");
 
   if(!result)
-    smtp_state(data, SMTP_STARTTLS);
+    smtp_state(data, smtpc, SMTP_STARTTLS);
 
   return result;
 }
@@ -381,12 +459,12 @@ static CURLcode smtp_perform_starttls(struct Curl_easy *data,
  *
  * Performs the upgrade to TLS.
  */
-static CURLcode smtp_perform_upgrade_tls(struct Curl_easy *data)
+static CURLcode smtp_perform_upgrade_tls(struct Curl_easy *data,
+                                         struct smtp_conn *smtpc)
 {
 #ifdef USE_SSL
   /* Start the SSL connection */
   struct connectdata *conn = data->conn;
-  struct smtp_conn *smtpc = &conn->proto.smtpc;
   CURLcode result;
   bool ssldone = FALSE;
 
@@ -397,7 +475,6 @@ static CURLcode smtp_perform_upgrade_tls(struct Curl_easy *data)
       goto out;
     /* Change the connection handler and SMTP state */
     conn->handler = &Curl_handler_smtps;
-    conn->bits.tls_upgraded = TRUE;
   }
 
   DEBUGASSERT(!smtpc->ssldone);
@@ -406,13 +483,14 @@ static CURLcode smtp_perform_upgrade_tls(struct Curl_easy *data)
            result, ssldone));
   if(!result && ssldone) {
     smtpc->ssldone = ssldone;
-    /* perform EHLO now, changes smpt->state out of SMTP_UPGRADETLS */
-    result = smtp_perform_ehlo(data);
+    /* perform EHLO now, changes smtp->state out of SMTP_UPGRADETLS */
+    result = smtp_perform_ehlo(data, smtpc);
   }
 out:
   return result;
 #else
   (void)data;
+  (void)smtpc;
   return CURLE_NOT_BUILT_IN;
 #endif
 }
@@ -429,9 +507,13 @@ static CURLcode smtp_perform_auth(struct Curl_easy *data,
                                   const struct bufref *initresp)
 {
   CURLcode result = CURLE_OK;
-  struct smtp_conn *smtpc = &data->conn->proto.smtpc;
+  struct smtp_conn *smtpc =
+    Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
   const char *ir = (const char *) Curl_bufref_ptr(initresp);
 
+  if(!smtpc)
+    return CURLE_FAILED_INIT;
+
   if(ir) {                                  /* AUTH <mech> ...<crlf> */
     /* Send the AUTH command with the initial response */
     result = Curl_pp_sendf(data, &smtpc->pp, "AUTH %s %s", mech, ir);
@@ -454,10 +536,12 @@ static CURLcode smtp_continue_auth(struct Curl_easy *data,
                                    const char *mech,
                                    const struct bufref *resp)
 {
-  struct smtp_conn *smtpc = &data->conn->proto.smtpc;
+  struct smtp_conn *smtpc =
+    Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
 
   (void)mech;
-
+  if(!smtpc)
+    return CURLE_FAILED_INIT;
   return Curl_pp_sendf(data, &smtpc->pp,
                        "%s", (const char *) Curl_bufref_ptr(resp));
 }
@@ -470,10 +554,12 @@ static CURLcode smtp_continue_auth(struct Curl_easy *data,
  */
 static CURLcode smtp_cancel_auth(struct Curl_easy *data, const char *mech)
 {
-  struct smtp_conn *smtpc = &data->conn->proto.smtpc;
+  struct smtp_conn *smtpc =
+    Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
 
   (void)mech;
-
+  if(!smtpc)
+    return CURLE_FAILED_INIT;
   return Curl_pp_sendf(data, &smtpc->pp, "*");
 }
 
@@ -484,18 +570,17 @@ static CURLcode smtp_cancel_auth(struct Curl_easy *data, const char *mech)
  * Initiates the authentication sequence, with the appropriate SASL
  * authentication mechanism.
  */
-static CURLcode smtp_perform_authentication(struct Curl_easy *data)
+static CURLcode smtp_perform_authentication(struct Curl_easy *data,
+                                            struct smtp_conn *smtpc)
 {
   CURLcode result = CURLE_OK;
-  struct connectdata *conn = data->conn;
-  struct smtp_conn *smtpc = &conn->proto.smtpc;
   saslprogress progress;
 
   /* Check we have enough data to authenticate with, and the
      server supports authentication, and end the connect phase if not */
   if(!smtpc->auth_supported ||
      !Curl_sasl_can_authenticate(&smtpc->sasl, data)) {
-    smtp_state(data, SMTP_STOP);
+    smtp_state(data, smtpc, SMTP_STOP);
     return result;
   }
 
@@ -504,12 +589,9 @@ static CURLcode smtp_perform_authentication(struct Curl_easy *data)
 
   if(!result) {
     if(progress == SASL_INPROGRESS)
-      smtp_state(data, SMTP_AUTH);
-    else {
-      /* Other mechanisms not supported */
-      infof(data, "No known authentication mechanisms supported");
-      result = CURLE_LOGIN_DENIED;
-    }
+      smtp_state(data, smtpc, SMTP_AUTH);
+    else
+      result = Curl_sasl_is_blocked(&smtpc->sasl, data);
   }
 
   return result;
@@ -519,13 +601,13 @@ static CURLcode smtp_perform_authentication(struct Curl_easy *data)
  *
  * smtp_perform_command()
  *
- * Sends a SMTP based command.
+ * Sends an SMTP based command.
  */
-static CURLcode smtp_perform_command(struct Curl_easy *data)
+static CURLcode smtp_perform_command(struct Curl_easy *data,
+                                     struct smtp_conn *smtpc,
+                                     struct SMTP *smtp)
 {
   CURLcode result = CURLE_OK;
-  struct connectdata *conn = data->conn;
-  struct SMTP *smtp = data->req.p.smtp;
 
   if(smtp->rcpt) {
     /* We notify the server we are sending UTF-8 data if a) it supports the
@@ -547,13 +629,13 @@ static CURLcode smtp_perform_command(struct Curl_easy *data)
 
       /* Establish whether we should report SMTPUTF8 to the server for this
          mailbox as per RFC-6531 sect. 3.1 point 6 */
-      utf8 = (conn->proto.smtpc.utf8_supported) &&
+      utf8 = (smtpc->utf8_supported) &&
              ((host.encalloc) || (!Curl_is_ASCII_name(address)) ||
               (!Curl_is_ASCII_name(host.name)));
 
       /* Send the VRFY command (Note: The hostname part may be absent when the
          host is a local system) */
-      result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "VRFY %s%s%s%s",
+      result = Curl_pp_sendf(data, &smtpc->pp, "VRFY %s%s%s%s",
                              address,
                              host.name ? "@" : "",
                              host.name ? host.name : "",
@@ -565,11 +647,10 @@ static CURLcode smtp_perform_command(struct Curl_easy *data)
     else {
       /* Establish whether we should report that we support SMTPUTF8 for EXPN
          commands to the server as per RFC-6531 sect. 3.1 point 6 */
-      utf8 = (conn->proto.smtpc.utf8_supported) &&
-             (!strcmp(smtp->custom, "EXPN"));
+      utf8 = (smtpc->utf8_supported) && (!strcmp(smtp->custom, "EXPN"));
 
       /* Send the custom recipient based command such as the EXPN command */
-      result = Curl_pp_sendf(data, &conn->proto.smtpc.pp,
+      result = Curl_pp_sendf(data, &smtpc->pp,
                              "%s %s%s", smtp->custom,
                              smtp->rcpt->data,
                              utf8 ? " SMTPUTF8" : "");
@@ -577,12 +658,12 @@ static CURLcode smtp_perform_command(struct Curl_easy *data)
   }
   else
     /* Send the non-recipient based command such as HELP */
-    result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "%s",
+    result = Curl_pp_sendf(data, &smtpc->pp, "%s",
                            smtp->custom && smtp->custom[0] != '\0' ?
                            smtp->custom : "HELP");
 
   if(!result)
-    smtp_state(data, SMTP_COMMAND);
+    smtp_state(data, smtpc, SMTP_COMMAND);
 
   return result;
 }
@@ -593,13 +674,14 @@ static CURLcode smtp_perform_command(struct Curl_easy *data)
  *
  * Sends an MAIL command to initiate the upload of a message.
  */
-static CURLcode smtp_perform_mail(struct Curl_easy *data)
+static CURLcode smtp_perform_mail(struct Curl_easy *data,
+                                  struct smtp_conn *smtpc,
+                                  struct SMTP *smtp)
 {
   char *from = NULL;
   char *auth = NULL;
   char *size = NULL;
   CURLcode result = CURLE_OK;
-  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 characters, in
@@ -621,7 +703,7 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
 
     /* Establish whether we should report SMTPUTF8 to the server for this
        mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */
-    utf8 = (conn->proto.smtpc.utf8_supported) &&
+    utf8 = (smtpc->utf8_supported) &&
            ((host.encalloc) || (!Curl_is_ASCII_name(address)) ||
             (!Curl_is_ASCII_name(host.name)));
 
@@ -647,7 +729,7 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
   }
 
   /* Calculate the optional AUTH parameter */
-  if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.sasl.authused) {
+  if(data->set.str[STRING_MAIL_AUTH] && smtpc->sasl.authused) {
     if(data->set.str[STRING_MAIL_AUTH][0] != '\0') {
       char *address = NULL;
       struct hostname host = { NULL, NULL, NULL, NULL };
@@ -661,7 +743,7 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
 
       /* Establish whether we should report SMTPUTF8 to the server for this
          mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */
-      if((!utf8) && (conn->proto.smtpc.utf8_supported) &&
+      if((!utf8) && (smtpc->utf8_supported) &&
          ((host.encalloc) || (!Curl_is_ASCII_name(address)) ||
           (!Curl_is_ASCII_name(host.name))))
         utf8 = TRUE;
@@ -718,7 +800,7 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
   }
 
   /* Calculate the optional SIZE parameter */
-  if(conn->proto.smtpc.size_supported && data->state.infilesize > 0) {
+  if(smtpc->size_supported && data->state.infilesize > 0) {
     size = aprintf("%" FMT_OFF_T, data->state.infilesize);
 
     if(!size) {
@@ -731,8 +813,7 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
      based address then quickly scan through the recipient list and check if
      any there do, as we need to correctly identify our support for SMTPUTF8
      in the envelope, as per RFC-6531 sect. 3.4 */
-  if(conn->proto.smtpc.utf8_supported && !utf8) {
-    struct SMTP *smtp = data->req.p.smtp;
+  if(smtpc->utf8_supported && !utf8) {
     struct curl_slist *rcpt = smtp->rcpt;
 
     while(rcpt && !utf8) {
@@ -750,7 +831,7 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
     goto out;
 
   /* Send the MAIL command */
-  result = Curl_pp_sendf(data, &conn->proto.smtpc.pp,
+  result = Curl_pp_sendf(data, &smtpc->pp,
                          "MAIL FROM:%s%s%s%s%s%s",
                          from,                 /* Mandatory                 */
                          auth ? " AUTH=" : "", /* Optional on AUTH support  */
@@ -766,7 +847,7 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
   free(size);
 
   if(!result)
-    smtp_state(data, SMTP_MAIL);
+    smtp_state(data, smtpc, SMTP_MAIL);
 
   return result;
 }
@@ -778,11 +859,11 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
  * Sends a RCPT TO command for a given recipient as part of the message upload
  * process.
  */
-static CURLcode smtp_perform_rcpt_to(struct Curl_easy *data)
+static CURLcode smtp_perform_rcpt_to(struct Curl_easy *data,
+                                     struct smtp_conn *smtpc,
+                                     struct SMTP *smtp)
 {
   CURLcode result = CURLE_OK;
-  struct connectdata *conn = data->conn;
-  struct SMTP *smtp = data->req.p.smtp;
   char *address = NULL;
   struct hostname host = { NULL, NULL, NULL, NULL };
 
@@ -795,19 +876,18 @@ static CURLcode smtp_perform_rcpt_to(struct Curl_easy *data)
 
   /* Send the RCPT TO command */
   if(host.name)
-    result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "RCPT TO:<%s@%s>",
+    result = Curl_pp_sendf(data, &smtpc->pp, "RCPT TO:<%s@%s>",
                            address, host.name);
   else
     /* An invalid mailbox was provided but we will simply let the server worry
        about that and reply with a 501 error */
-    result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "RCPT TO:<%s>",
-                           address);
+    result = Curl_pp_sendf(data, &smtpc->pp, "RCPT TO:<%s>", address);
 
   Curl_free_idnconverted_hostname(&host);
   free(address);
 
   if(!result)
-    smtp_state(data, SMTP_RCPT);
+    smtp_state(data, smtpc, SMTP_RCPT);
 
   return result;
 }
@@ -819,19 +899,20 @@ static CURLcode smtp_perform_rcpt_to(struct Curl_easy *data)
  * Performs the quit action prior to sclose() being called.
  */
 static CURLcode smtp_perform_quit(struct Curl_easy *data,
-                                  struct connectdata *conn)
+                                  struct smtp_conn *smtpc)
 {
   /* Send the QUIT command */
-  CURLcode result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "%s", "QUIT");
+  CURLcode result = Curl_pp_sendf(data, &smtpc->pp, "%s", "QUIT");
 
   if(!result)
-    smtp_state(data, SMTP_QUIT);
+    smtp_state(data, smtpc, SMTP_QUIT);
 
   return result;
 }
 
 /* For the initial server greeting */
 static CURLcode smtp_state_servergreet_resp(struct Curl_easy *data,
+                                            struct smtp_conn *smtpc,
                                             int smtpcode,
                                             smtpstate instate)
 {
@@ -843,13 +924,14 @@ static CURLcode smtp_state_servergreet_resp(struct Curl_easy *data,
     result = CURLE_WEIRD_SERVER_REPLY;
   }
   else
-    result = smtp_perform_ehlo(data);
+    result = smtp_perform_ehlo(data, smtpc);
 
   return result;
 }
 
 /* For STARTTLS responses */
 static CURLcode smtp_state_starttls_resp(struct Curl_easy *data,
+                                         struct smtp_conn *smtpc,
                                          int smtpcode,
                                          smtpstate instate)
 {
@@ -857,7 +939,7 @@ static CURLcode smtp_state_starttls_resp(struct Curl_easy *data,
   (void)instate; /* no use for this yet */
 
   /* Pipelining in response is forbidden. */
-  if(data->conn->proto.smtpc.pp.overflow)
+  if(smtpc->pp.overflow)
     return CURLE_WEIRD_SERVER_REPLY;
 
   if(smtpcode != 220) {
@@ -866,30 +948,30 @@ static CURLcode smtp_state_starttls_resp(struct Curl_easy *data,
       result = CURLE_USE_SSL_FAILED;
     }
     else
-      result = smtp_perform_authentication(data);
+      result = smtp_perform_authentication(data, smtpc);
   }
   else
-    smtp_state(data, SMTP_UPGRADETLS);
+    smtp_state(data, smtpc, SMTP_UPGRADETLS);
 
   return result;
 }
 
 /* For EHLO responses */
 static CURLcode smtp_state_ehlo_resp(struct Curl_easy *data,
-                                     struct connectdata *conn, int smtpcode,
+                                     struct smtp_conn *smtpc,
+                                     int smtpcode,
                                      smtpstate instate)
 {
   CURLcode result = CURLE_OK;
-  struct smtp_conn *smtpc = &conn->proto.smtpc;
-  const char *line = Curl_dyn_ptr(&smtpc->pp.recvbuf);
+  const char *line = curlx_dyn_ptr(&smtpc->pp.recvbuf);
   size_t len = smtpc->pp.nfinal;
 
   (void)instate; /* no use for this yet */
 
   if(smtpcode/100 != 2 && smtpcode != 1) {
     if(data->set.use_ssl <= CURLUSESSL_TRY
-       || Curl_conn_is_ssl(conn, FIRSTSOCKET))
-      result = smtp_perform_helo(data, conn);
+       || Curl_conn_is_ssl(data->conn, FIRSTSOCKET))
+      result = smtp_perform_helo(data, smtpc);
     else {
       failf(data, "Remote access denied: %d", smtpcode);
       result = CURLE_REMOTE_ACCESS_DENIED;
@@ -953,21 +1035,21 @@ static CURLcode smtp_state_ehlo_resp(struct Curl_easy *data,
     }
 
     if(smtpcode != 1) {
-      if(data->set.use_ssl && !Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
-        /* We do not have a SSL/TLS connection yet, but SSL is requested */
+      if(data->set.use_ssl && !Curl_conn_is_ssl(data->conn, FIRSTSOCKET)) {
+        /* We do not have an SSL/TLS connection yet, but SSL is requested */
         if(smtpc->tls_supported)
           /* Switch to TLS connection now */
-          result = smtp_perform_starttls(data, conn);
+          result = smtp_perform_starttls(data, smtpc);
         else if(data->set.use_ssl == CURLUSESSL_TRY)
           /* Fallback and carry on with authentication */
-          result = smtp_perform_authentication(data);
+          result = smtp_perform_authentication(data, smtpc);
         else {
           failf(data, "STARTTLS not supported.");
           result = CURLE_USE_SSL_FAILED;
         }
       }
       else
-        result = smtp_perform_authentication(data);
+        result = smtp_perform_authentication(data, smtpc);
     }
   }
   else {
@@ -979,7 +1061,9 @@ static CURLcode smtp_state_ehlo_resp(struct Curl_easy *data,
 }
 
 /* For HELO responses */
-static CURLcode smtp_state_helo_resp(struct Curl_easy *data, int smtpcode,
+static CURLcode smtp_state_helo_resp(struct Curl_easy *data,
+                                     struct smtp_conn *smtpc,
+                                     int smtpcode,
                                      smtpstate instate)
 {
   CURLcode result = CURLE_OK;
@@ -991,19 +1075,18 @@ static CURLcode smtp_state_helo_resp(struct Curl_easy *data, int smtpcode,
   }
   else
     /* End of connect phase */
-    smtp_state(data, SMTP_STOP);
+    smtp_state(data, smtpc, SMTP_STOP);
 
   return result;
 }
 
 /* For SASL authentication responses */
 static CURLcode smtp_state_auth_resp(struct Curl_easy *data,
+                                     struct smtp_conn *smtpc,
                                      int smtpcode,
                                      smtpstate instate)
 {
   CURLcode result = CURLE_OK;
-  struct connectdata *conn = data->conn;
-  struct smtp_conn *smtpc = &conn->proto.smtpc;
   saslprogress progress;
 
   (void)instate; /* no use for this yet */
@@ -1012,7 +1095,7 @@ static CURLcode smtp_state_auth_resp(struct Curl_easy *data,
   if(!result)
     switch(progress) {
     case SASL_DONE:
-      smtp_state(data, SMTP_STOP);  /* Authenticated */
+      smtp_state(data, smtpc, SMTP_STOP);  /* Authenticated */
       break;
     case SASL_IDLE:            /* No mechanism left after cancellation */
       failf(data, "Authentication cancelled");
@@ -1026,13 +1109,15 @@ static CURLcode smtp_state_auth_resp(struct Curl_easy *data,
 }
 
 /* For command responses */
-static CURLcode smtp_state_command_resp(struct Curl_easy *data, int smtpcode,
+static CURLcode smtp_state_command_resp(struct Curl_easy *data,
+                                        struct smtp_conn *smtpc,
+                                        struct SMTP *smtp,
+                                        int smtpcode,
                                         smtpstate instate)
 {
   CURLcode result = CURLE_OK;
-  struct SMTP *smtp = data->req.p.smtp;
-  char *line = Curl_dyn_ptr(&data->conn->proto.smtpc.pp.recvbuf);
-  size_t len = data->conn->proto.smtpc.pp.nfinal;
+  char *line = curlx_dyn_ptr(&smtpc->pp.recvbuf);
+  size_t len = smtpc->pp.nfinal;
 
   (void)instate; /* no use for this yet */
 
@@ -1051,15 +1136,15 @@ static CURLcode smtp_state_command_resp(struct Curl_easy *data, int smtpcode,
 
         if(smtp->rcpt) {
           /* Send the next command */
-          result = smtp_perform_command(data);
+          result = smtp_perform_command(data, smtpc, smtp);
         }
         else
           /* End of DO phase */
-          smtp_state(data, SMTP_STOP);
+          smtp_state(data, smtpc, SMTP_STOP);
       }
       else
         /* End of DO phase */
-        smtp_state(data, SMTP_STOP);
+        smtp_state(data, smtpc, SMTP_STOP);
     }
   }
 
@@ -1067,7 +1152,10 @@ static CURLcode smtp_state_command_resp(struct Curl_easy *data, int smtpcode,
 }
 
 /* For MAIL responses */
-static CURLcode smtp_state_mail_resp(struct Curl_easy *data, int smtpcode,
+static CURLcode smtp_state_mail_resp(struct Curl_easy *data,
+                                     struct smtp_conn *smtpc,
+                                     struct SMTP *smtp,
+                                     int smtpcode,
                                      smtpstate instate)
 {
   CURLcode result = CURLE_OK;
@@ -1079,18 +1167,19 @@ static CURLcode smtp_state_mail_resp(struct Curl_easy *data, int smtpcode,
   }
   else
     /* Start the RCPT TO command */
-    result = smtp_perform_rcpt_to(data);
+    result = smtp_perform_rcpt_to(data, smtpc, smtp);
 
   return result;
 }
 
 /* For RCPT responses */
 static CURLcode smtp_state_rcpt_resp(struct Curl_easy *data,
-                                     struct connectdata *conn, int smtpcode,
+                                     struct smtp_conn *smtpc,
+                                     struct SMTP *smtp,
+                                     int smtpcode,
                                      smtpstate instate)
 {
   CURLcode result = CURLE_OK;
-  struct SMTP *smtp = data->req.p.smtp;
   bool is_smtp_err = FALSE;
   bool is_smtp_blocking_err = FALSE;
 
@@ -1122,7 +1211,7 @@ static CURLcode smtp_state_rcpt_resp(struct Curl_easy *data,
 
     if(smtp->rcpt)
       /* Send the next RCPT TO command */
-      result = smtp_perform_rcpt_to(data);
+      result = smtp_perform_rcpt_to(data, smtpc, smtp);
     else {
       /* We were not able to issue a successful RCPT TO command while going
          over recipients (potentially multiple). Sending back last error. */
@@ -1132,10 +1221,10 @@ static CURLcode smtp_state_rcpt_resp(struct Curl_easy *data,
       }
       else {
         /* Send the DATA command */
-        result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "%s", "DATA");
+        result = Curl_pp_sendf(data, &smtpc->pp, "%s", "DATA");
 
         if(!result)
-          smtp_state(data, SMTP_DATA);
+          smtp_state(data, smtpc, SMTP_DATA);
       }
     }
   }
@@ -1144,7 +1233,9 @@ static CURLcode smtp_state_rcpt_resp(struct Curl_easy *data,
 }
 
 /* For DATA response */
-static CURLcode smtp_state_data_resp(struct Curl_easy *data, int smtpcode,
+static CURLcode smtp_state_data_resp(struct Curl_easy *data,
+                                     struct smtp_conn *smtpc,
+                                     int smtpcode,
                                      smtpstate instate)
 {
   CURLcode result = CURLE_OK;
@@ -1162,7 +1253,7 @@ static CURLcode smtp_state_data_resp(struct Curl_easy *data, int smtpcode,
     Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE);
 
     /* End of DO phase */
-    smtp_state(data, SMTP_STOP);
+    smtp_state(data, smtpc, SMTP_STOP);
   }
 
   return result;
@@ -1171,6 +1262,7 @@ static CURLcode smtp_state_data_resp(struct Curl_easy *data, int smtpcode,
 /* For POSTDATA responses, which are received after the entire DATA
    part has been sent to the server */
 static CURLcode smtp_state_postdata_resp(struct Curl_easy *data,
+                                         struct smtp_conn *smtpc,
                                          int smtpcode,
                                          smtpstate instate)
 {
@@ -1182,35 +1274,39 @@ static CURLcode smtp_state_postdata_resp(struct Curl_easy *data,
     result = CURLE_WEIRD_SERVER_REPLY;
 
   /* End of DONE phase */
-  smtp_state(data, SMTP_STOP);
+  smtp_state(data, smtpc, SMTP_STOP);
 
   return result;
 }
 
-static CURLcode smtp_statemachine(struct Curl_easy *data,
-                                  struct connectdata *conn)
+static CURLcode smtp_pp_statemachine(struct Curl_easy *data,
+                                     struct connectdata *conn)
 {
   CURLcode result = CURLE_OK;
   int smtpcode;
-  struct smtp_conn *smtpc = &conn->proto.smtpc;
-  struct pingpong *pp = &smtpc->pp;
+  struct smtp_conn *smtpc = Curl_conn_meta_get(conn, CURL_META_SMTP_CONN);
+  struct SMTP *smtp = Curl_meta_get(data, CURL_META_SMTP_EASY);
   size_t nread = 0;
 
+  if(!smtpc || !smtp)
+    return CURLE_FAILED_INIT;
+
   /* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */
 upgrade_tls:
   if(smtpc->state == SMTP_UPGRADETLS) {
-    result = smtp_perform_upgrade_tls(data);
+    result = smtp_perform_upgrade_tls(data, smtpc);
     if(result || (smtpc->state == SMTP_UPGRADETLS))
       return result;
   }
 
   /* Flush any data that needs to be sent */
-  if(pp->sendleft)
-    return Curl_pp_flushsend(data, pp);
+  if(smtpc->pp.sendleft)
+    return Curl_pp_flushsend(data, &smtpc->pp);
 
   do {
     /* Read the response from the server */
-    result = Curl_pp_readresp(data, FIRSTSOCKET, pp, &smtpcode, &nread);
+    result = Curl_pp_readresp(data, FIRSTSOCKET, &smtpc->pp,
+                              &smtpcode, &nread);
     if(result)
       return result;
 
@@ -1224,19 +1320,20 @@ static CURLcode smtp_statemachine(struct Curl_easy *data,
     /* We have now received a full SMTP server response */
     switch(smtpc->state) {
     case SMTP_SERVERGREET:
-      result = smtp_state_servergreet_resp(data, smtpcode, smtpc->state);
+      result = smtp_state_servergreet_resp(data, smtpc,
+                                           smtpcode, smtpc->state);
       break;
 
     case SMTP_EHLO:
-      result = smtp_state_ehlo_resp(data, conn, smtpcode, smtpc->state);
+      result = smtp_state_ehlo_resp(data, smtpc, smtpcode, smtpc->state);
       break;
 
     case SMTP_HELO:
-      result = smtp_state_helo_resp(data, smtpcode, smtpc->state);
+      result = smtp_state_helo_resp(data, smtpc, smtpcode, smtpc->state);
       break;
 
     case SMTP_STARTTLS:
-      result = smtp_state_starttls_resp(data, smtpcode, smtpc->state);
+      result = smtp_state_starttls_resp(data, smtpc, smtpcode, smtpc->state);
       /* During UPGRADETLS, leave the read loop as we need to connect
        * (e.g. TLS handshake) before we continue sending/receiving. */
       if(!result && (smtpc->state == SMTP_UPGRADETLS))
@@ -1244,36 +1341,38 @@ static CURLcode smtp_statemachine(struct Curl_easy *data,
       break;
 
     case SMTP_AUTH:
-      result = smtp_state_auth_resp(data, smtpcode, smtpc->state);
+      result = smtp_state_auth_resp(data, smtpc, smtpcode, smtpc->state);
       break;
 
     case SMTP_COMMAND:
-      result = smtp_state_command_resp(data, smtpcode, smtpc->state);
+      result = smtp_state_command_resp(data, smtpc, smtp,
+                                       smtpcode, smtpc->state);
       break;
 
     case SMTP_MAIL:
-      result = smtp_state_mail_resp(data, smtpcode, smtpc->state);
+      result = smtp_state_mail_resp(data, smtpc, smtp, smtpcode, smtpc->state);
       break;
 
     case SMTP_RCPT:
-      result = smtp_state_rcpt_resp(data, conn, smtpcode, smtpc->state);
+      result = smtp_state_rcpt_resp(data, smtpc, smtp, smtpcode, smtpc->state);
       break;
 
     case SMTP_DATA:
-      result = smtp_state_data_resp(data, smtpcode, smtpc->state);
+      result = smtp_state_data_resp(data, smtpc, smtpcode, smtpc->state);
       break;
 
     case SMTP_POSTDATA:
-      result = smtp_state_postdata_resp(data, smtpcode, smtpc->state);
+      result = smtp_state_postdata_resp(data, smtpc, smtpcode, smtpc->state);
       break;
 
     case SMTP_QUIT:
     default:
       /* internal error */
-      smtp_state(data, SMTP_STOP);
+      smtp_state(data, smtpc, SMTP_STOP);
       break;
     }
-  } while(!result && smtpc->state != SMTP_STOP && Curl_pp_moredata(pp));
+  } while(!result && smtpc->state != SMTP_STOP &&
+          Curl_pp_moredata(&smtpc->pp));
 
   return result;
 }
@@ -1282,21 +1381,23 @@ static CURLcode smtp_statemachine(struct Curl_easy *data,
 static CURLcode smtp_multi_statemach(struct Curl_easy *data, bool *done)
 {
   CURLcode result = CURLE_OK;
-  struct connectdata *conn = data->conn;
-  struct smtp_conn *smtpc = &conn->proto.smtpc;
+  struct smtp_conn *smtpc =
+    Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
+
+  *done = FALSE;
+  if(!smtpc)
+    return CURLE_FAILED_INIT;
 
   result = Curl_pp_statemach(data, &smtpc->pp, FALSE, FALSE);
   *done = (smtpc->state == SMTP_STOP);
-
   return result;
 }
 
 static CURLcode smtp_block_statemach(struct Curl_easy *data,
-                                     struct connectdata *conn,
+                                     struct smtp_conn *smtpc,
                                      bool disconnecting)
 {
   CURLcode result = CURLE_OK;
-  struct smtp_conn *smtpc = &conn->proto.smtpc;
 
   while(smtpc->state != SMTP_STOP && !result)
     result = Curl_pp_statemach(data, &smtpc->pp, TRUE, disconnecting);
@@ -1304,25 +1405,13 @@ static CURLcode smtp_block_statemach(struct Curl_easy *data,
   return result;
 }
 
-/* Allocate and initialize the SMTP struct for the current Curl_easy if
-   required */
-static CURLcode smtp_init(struct Curl_easy *data)
-{
-  CURLcode result = CURLE_OK;
-  struct SMTP *smtp;
-
-  smtp = data->req.p.smtp = calloc(1, sizeof(struct SMTP));
-  if(!smtp)
-    result = CURLE_OUT_OF_MEMORY;
-
-  return result;
-}
-
 /* For the SMTP "protocol connect" and "doing" phases only */
 static int smtp_getsock(struct Curl_easy *data,
                         struct connectdata *conn, curl_socket_t *socks)
 {
-  return Curl_pp_getsock(data, &conn->proto.smtpc.pp, socks);
+  struct smtp_conn *smtpc = Curl_conn_meta_get(conn, CURL_META_SMTP_CONN);
+  return smtpc ?
+         Curl_pp_getsock(data, &smtpc->pp, socks) : GETSOCK_BLANK;
 }
 
 /***********************************************************************
@@ -1337,36 +1426,37 @@ static int smtp_getsock(struct Curl_easy *data,
  */
 static CURLcode smtp_connect(struct Curl_easy *data, bool *done)
 {
+  struct smtp_conn *smtpc =
+    Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
   CURLcode result = CURLE_OK;
-  struct connectdata *conn = data->conn;
-  struct smtp_conn *smtpc = &conn->proto.smtpc;
-  struct pingpong *pp = &smtpc->pp;
 
   *done = FALSE; /* default to not done yet */
+  if(!smtpc)
+    return CURLE_FAILED_INIT;
 
   /* We always support persistent connections in SMTP */
-  connkeep(conn, "SMTP default");
+  connkeep(data->conn, "SMTP default");
 
-  PINGPONG_SETUP(pp, smtp_statemachine, smtp_endofresp);
+  PINGPONG_SETUP(&smtpc->pp, smtp_pp_statemachine, smtp_endofresp);
 
   /* Initialize the SASL storage */
   Curl_sasl_init(&smtpc->sasl, data, &saslsmtp);
 
   /* Initialise the pingpong layer */
-  Curl_pp_init(pp);
+  Curl_pp_init(&smtpc->pp);
 
   /* Parse the URL options */
-  result = smtp_parse_url_options(conn);
+  result = smtp_parse_url_options(data->conn, smtpc);
   if(result)
     return result;
 
   /* Parse the URL path */
-  result = smtp_parse_url_path(data);
+  result = smtp_parse_url_path(data, smtpc);
   if(result)
     return result;
 
   /* Start off waiting for the server greeting response */
-  smtp_state(data, SMTP_SERVERGREET);
+  smtp_state(data, smtpc, SMTP_SERVERGREET);
 
   result = smtp_multi_statemach(data, done);
 
@@ -1385,12 +1475,16 @@ static CURLcode smtp_connect(struct Curl_easy *data, bool *done)
 static CURLcode smtp_done(struct Curl_easy *data, CURLcode status,
                           bool premature)
 {
+  struct smtp_conn *smtpc =
+    Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
   CURLcode result = CURLE_OK;
   struct connectdata *conn = data->conn;
-  struct SMTP *smtp = data->req.p.smtp;
+  struct SMTP *smtp = Curl_meta_get(data, CURL_META_SMTP_EASY);
 
   (void)premature;
 
+  if(!smtpc)
+    return CURLE_FAILED_INIT;
   if(!smtp)
     return CURLE_OK;
 
@@ -1404,10 +1498,10 @@ static CURLcode smtp_done(struct Curl_easy *data, CURLcode status,
   else if(!data->set.connect_only && data->set.mail_rcpt &&
           (data->state.upload || IS_MIME_POST(data))) {
 
-    smtp_state(data, SMTP_POSTDATA);
+    smtp_state(data, smtpc, SMTP_POSTDATA);
 
     /* Run the state-machine */
-    result = smtp_block_statemach(data, conn, FALSE);
+    result = smtp_block_statemach(data, smtpc, FALSE);
   }
 
   /* Clear the transfer mode for the next request */
@@ -1424,12 +1518,14 @@ static CURLcode smtp_done(struct Curl_easy *data, CURLcode status,
  * This is the actual DO function for SMTP. Transfer a mail, send a command
  * or get some data according to the options previously setup.
  */
-static CURLcode smtp_perform(struct Curl_easy *data, bool *connected,
+static CURLcode smtp_perform(struct Curl_easy *data,
+                             struct smtp_conn *smtpc,
+                             struct SMTP *smtp,
+                             bool *connected,
                              bool *dophase_done)
 {
   /* This is SMTP and no proxy */
   CURLcode result = CURLE_OK;
-  struct SMTP *smtp = data->req.p.smtp;
 
   CURL_TRC_SMTP(data, "smtp_perform(), start");
 
@@ -1457,10 +1553,10 @@ static CURLcode smtp_perform(struct Curl_easy *data, bool *connected,
   /* Start the first command in the DO phase */
   if((data->state.upload || IS_MIME_POST(data)) && data->set.mail_rcpt)
     /* MAIL transfer */
-    result = smtp_perform_mail(data);
+    result = smtp_perform_mail(data, smtpc, smtp);
   else
     /* SMTP based command (VRFY, EXPN, NOOP, RSET or HELP) */
-    result = smtp_perform_command(data);
+    result = smtp_perform_command(data, smtpc, smtp);
 
   if(result)
     goto out;
@@ -1487,17 +1583,23 @@ static CURLcode smtp_perform(struct Curl_easy *data, bool *connected,
  */
 static CURLcode smtp_do(struct Curl_easy *data, bool *done)
 {
+  struct smtp_conn *smtpc =
+    Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
+  struct SMTP *smtp = Curl_meta_get(data, CURL_META_SMTP_EASY);
   CURLcode result = CURLE_OK;
+
   DEBUGASSERT(data);
   DEBUGASSERT(data->conn);
   *done = FALSE; /* default to false */
+  if(!smtpc || !smtp)
+    return CURLE_FAILED_INIT;
 
   /* Parse the custom request */
-  result = smtp_parse_custom_request(data);
+  result = smtp_parse_custom_request(data, smtp);
   if(result)
     return result;
 
-  result = smtp_regular_transfer(data, done);
+  result = smtp_regular_transfer(data, smtpc, smtp, done);
   CURL_TRC_SMTP(data, "smtp_do() -> %d, done=%d", result, *done);
   return result;
 }
@@ -1513,36 +1615,32 @@ static CURLcode smtp_disconnect(struct Curl_easy *data,
                                 struct connectdata *conn,
                                 bool dead_connection)
 {
-  struct smtp_conn *smtpc = &conn->proto.smtpc;
+  struct smtp_conn *smtpc = Curl_conn_meta_get(conn, CURL_META_SMTP_CONN);
+
   (void)data;
+  if(!smtpc)
+    return CURLE_FAILED_INIT;
 
   /* We cannot send quit unconditionally. If this connection is stale or
      bad in any way, sending quit and waiting around here will make the
      disconnect wait in vain and cause more problems than we need to. */
 
   if(!dead_connection && conn->bits.protoconnstart) {
-    if(!smtp_perform_quit(data, conn))
-      (void)smtp_block_statemach(data, conn, TRUE); /* ignore errors on QUIT */
+    if(!smtp_perform_quit(data, smtpc))
+      (void)smtp_block_statemach(data, smtpc, TRUE); /* ignore on QUIT */
   }
 
-  /* Disconnect from the server */
-  Curl_pp_disconnect(&smtpc->pp);
-
   /* Cleanup the SASL module */
   Curl_sasl_cleanup(conn, smtpc->sasl.authused);
-
-  /* Cleanup our connection based variables */
-  Curl_safefree(smtpc->domain);
   CURL_TRC_SMTP(data, "smtp_disconnect(), finished");
-
   return CURLE_OK;
 }
 
 /* Call this when the DO phase has completed */
-static CURLcode smtp_dophase_done(struct Curl_easy *data, bool connected)
+static CURLcode smtp_dophase_done(struct Curl_easy *data,
+                                  struct SMTP *smtp,
+                                  bool connected)
 {
-  struct SMTP *smtp = data->req.p.smtp;
-
   (void)connected;
 
   if(smtp->transfer != PPTRANSFER_BODY)
@@ -1555,12 +1653,16 @@ static CURLcode smtp_dophase_done(struct Curl_easy *data, bool connected)
 /* Called from multi.c while DOing */
 static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done)
 {
-  CURLcode result = smtp_multi_statemach(data, dophase_done);
+  struct SMTP *smtp = Curl_meta_get(data, CURL_META_SMTP_EASY);
+  CURLcode result;
 
+  if(!smtp)
+    return CURLE_FAILED_INIT;
+  result = smtp_multi_statemach(data, dophase_done);
   if(result)
     DEBUGF(infof(data, "DO phase failed"));
   else if(*dophase_done) {
-    result = smtp_dophase_done(data, FALSE /* not connected */);
+    result = smtp_dophase_done(data, smtp, FALSE /* not connected */);
 
     DEBUGF(infof(data, "DO phase is complete"));
   }
@@ -1579,6 +1681,8 @@ static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done)
  * remote host.
  */
 static CURLcode smtp_regular_transfer(struct Curl_easy *data,
+                                      struct smtp_conn *smtpc,
+                                      struct SMTP *smtp,
                                       bool *dophase_done)
 {
   CURLcode result = CURLE_OK;
@@ -1594,27 +1698,56 @@ static CURLcode smtp_regular_transfer(struct Curl_easy *data,
   Curl_pgrsSetDownloadSize(data, -1);
 
   /* Carry out the perform */
-  result = smtp_perform(data, &connected, dophase_done);
+  result = smtp_perform(data, smtpc, smtp, &connected, dophase_done);
 
   /* Perform post DO phase operations if necessary */
   if(!result && *dophase_done)
-    result = smtp_dophase_done(data, connected);
+    result = smtp_dophase_done(data, smtp, connected);
 
   CURL_TRC_SMTP(data, "smtp_regular_transfer() -> %d, done=%d",
                 result, *dophase_done);
   return result;
 }
 
+
+static void smtp_easy_dtor(void *key, size_t klen, void *entry)
+{
+  struct SMTP *smtp = entry;
+  (void)key;
+  (void)klen;
+  free(smtp);
+}
+
+static void smtp_conn_dtor(void *key, size_t klen, void *entry)
+{
+  struct smtp_conn *smtpc = entry;
+  (void)key;
+  (void)klen;
+  Curl_pp_disconnect(&smtpc->pp);
+  Curl_safefree(smtpc->domain);
+  free(smtpc);
+}
+
 static CURLcode smtp_setup_connection(struct Curl_easy *data,
                                       struct connectdata *conn)
 {
-  CURLcode result;
+  struct smtp_conn *smtpc;
+  struct SMTP *smtp;
+  CURLcode result = CURLE_OK;
 
-  /* Clear the TLS upgraded flag */
-  conn->bits.tls_upgraded = FALSE;
+  smtpc = calloc(1, sizeof(*smtpc));
+  if(!smtpc ||
+     Curl_conn_meta_set(conn, CURL_META_SMTP_CONN, smtpc, smtp_conn_dtor)) {
+     result = CURLE_OUT_OF_MEMORY;
+     goto out;
+  }
 
-  /* Initialise the SMTP layer */
-  result = smtp_init(data);
+  smtp = calloc(1, sizeof(*smtp));
+  if(!smtp ||
+     Curl_meta_set(data, CURL_META_SMTP_EASY, smtp, smtp_easy_dtor))
+    result = CURLE_OUT_OF_MEMORY;
+
+out:
   CURL_TRC_SMTP(data, "smtp_setup_connection() -> %d", result);
   return result;
 }
@@ -1625,10 +1758,10 @@ static CURLcode smtp_setup_connection(struct Curl_easy *data,
  *
  * Parse the URL login options.
  */
-static CURLcode smtp_parse_url_options(struct connectdata *conn)
+static CURLcode smtp_parse_url_options(struct connectdata *conn,
+                                       struct smtp_conn *smtpc)
 {
   CURLcode result = CURLE_OK;
-  struct smtp_conn *smtpc = &conn->proto.smtpc;
   const char *ptr = conn->options;
 
   while(!result && ptr && *ptr) {
@@ -1662,11 +1795,10 @@ static CURLcode smtp_parse_url_options(struct connectdata *conn)
  *
  * Parse the URL path into separate path components.
  */
-static CURLcode smtp_parse_url_path(struct Curl_easy *data)
+static CURLcode smtp_parse_url_path(struct Curl_easy *data,
+                                    struct smtp_conn *smtpc)
 {
   /* The SMTP struct is already initialised in smtp_connect() */
-  struct connectdata *conn = data->conn;
-  struct smtp_conn *smtpc = &conn->proto.smtpc;
   const char *path = &data->state.up.path[1]; /* skip leading path */
   char localhost[HOSTNAME_MAX + 1];
 
@@ -1688,10 +1820,10 @@ static CURLcode smtp_parse_url_path(struct Curl_easy *data)
  *
  * Parse the custom request.
  */
-static CURLcode smtp_parse_custom_request(struct Curl_easy *data)
+static CURLcode smtp_parse_custom_request(struct Curl_easy *data,
+                                          struct SMTP *smtp)
 {
   CURLcode result = CURLE_OK;
-  struct SMTP *smtp = data->req.p.smtp;
   const char *custom = data->set.str[STRING_CUSTOMREQUEST];
 
   /* URL decode the custom request */
diff --git a/extra/curl/curl-8.14.1/lib/smtp.h b/extra/curl/curl-8.14.1/lib/smtp.h
new file mode 100644
index 000000000000..ed9824b14cc3
--- /dev/null
+++ b/extra/curl/curl-8.14.1/lib/smtp.h
@@ -0,0 +1,33 @@
+#ifndef HEADER_CURL_SMTP_H
+#define HEADER_CURL_SMTP_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "pingpong.h"
+#include "curl_sasl.h"
+
+extern const struct Curl_handler Curl_handler_smtp;
+extern const struct Curl_handler Curl_handler_smtps;
+
+#endif /* HEADER_CURL_SMTP_H */
diff --git a/extra/curl/curl-8.12.1/lib/sockaddr.h b/extra/curl/curl-8.14.1/lib/sockaddr.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/sockaddr.h
rename to extra/curl/curl-8.14.1/lib/sockaddr.h
diff --git a/extra/curl/curl-8.12.1/lib/socketpair.c b/extra/curl/curl-8.14.1/lib/socketpair.c
similarity index 91%
rename from extra/curl/curl-8.12.1/lib/socketpair.c
rename to extra/curl/curl-8.14.1/lib/socketpair.c
index c4f558ea6b88..1d1d5a66f30b 100644
--- a/extra/curl/curl-8.12.1/lib/socketpair.c
+++ b/extra/curl/curl-8.14.1/lib/socketpair.c
@@ -27,10 +27,9 @@
 #include "urldata.h"
 #include "rand.h"
 
-#if defined(USE_EVENTFD)
-#ifdef HAVE_SYS_EVENTFD_H
+#ifdef USE_EVENTFD
+
 #include <sys/eventfd.h>
-#endif
 
 int Curl_eventfd(curl_socket_t socks[2], bool nonblocking)
 {
@@ -42,13 +41,20 @@ int Curl_eventfd(curl_socket_t socks[2], bool nonblocking)
   socks[0] = socks[1] = efd;
   return 0;
 }
+
 #elif defined(HAVE_PIPE)
+
 #ifdef HAVE_FCNTL
 #include <fcntl.h>
 #endif
 
 int Curl_pipe(curl_socket_t socks[2], bool nonblocking)
 {
+#ifdef HAVE_PIPE2
+  int flags = nonblocking ? O_NONBLOCK | O_CLOEXEC : O_CLOEXEC;
+  if(pipe2(socks, flags))
+    return -1;
+#else
   if(pipe(socks))
     return -1;
 #ifdef HAVE_FCNTL
@@ -69,11 +75,12 @@ int Curl_pipe(curl_socket_t socks[2], bool nonblocking)
       return -1;
     }
   }
+#endif
 
   return 0;
 }
-#endif
 
+#endif /* USE_EVENTFD */
 
 #ifndef CURL_DISABLE_SOCKETPAIR
 #ifdef HAVE_SOCKETPAIR
@@ -103,7 +110,9 @@ int Curl_socketpair(int domain, int type, int protocol,
  * This is a socketpair() implementation for Windows.
  */
 #include <string.h>
+#ifdef HAVE_IO_H
 #include <io.h>
+#endif
 #else
 #ifdef HAVE_NETDB_H
 #include <netdb.h>
@@ -119,8 +128,8 @@ int Curl_socketpair(int domain, int type, int protocol,
 #endif /* !INADDR_LOOPBACK */
 #endif /* !_WIN32 */
 
-#include "nonblock.h" /* for curlx_nonblock */
-#include "timeval.h"  /* needed before select.h */
+#include "curlx/nonblock.h" /* for curlx_nonblock */
+#include "curlx/timeval.h"  /* needed before select.h */
 #include "select.h"   /* for Curl_poll */
 
 /* The last 3 #include files should be in this order */
@@ -194,7 +203,7 @@ int Curl_socketpair(int domain, int type, int protocol,
   if(socks[1] == CURL_SOCKET_BAD)
     goto error;
   else {
-    struct curltime start = Curl_now();
+    struct curltime start = curlx_now();
     char rnd[9];
     char check[sizeof(rnd)];
     char *p = &check[0];
@@ -218,18 +227,18 @@ int Curl_socketpair(int domain, int type, int protocol,
       if(nread == -1) {
         int sockerr = SOCKERRNO;
         /* Do not block forever */
-        if(Curl_timediff(Curl_now(), start) > (60 * 1000))
+        if(curlx_timediff(curlx_now(), start) > (60 * 1000))
           goto error;
         if(
-#ifdef WSAEWOULDBLOCK
+#ifdef USE_WINSOCK
           /* This is how Windows does it */
-          (WSAEWOULDBLOCK == sockerr)
+          (SOCKEWOULDBLOCK == sockerr)
 #else
           /* errno may be EWOULDBLOCK or on some systems EAGAIN when it
              returned due to its inability to send off data without
              blocking. We therefore treat both error codes the same here */
-          (EWOULDBLOCK == sockerr) || (EAGAIN == sockerr) ||
-          (EINTR == sockerr) || (EINPROGRESS == sockerr)
+          (SOCKEWOULDBLOCK == sockerr) || (EAGAIN == sockerr) ||
+          (SOCKEINTR == sockerr) || (SOCKEINPROGRESS == sockerr)
 #endif
           ) {
           continue;
diff --git a/extra/curl/curl-8.12.1/lib/socketpair.h b/extra/curl/curl-8.14.1/lib/socketpair.h
similarity index 77%
rename from extra/curl/curl-8.12.1/lib/socketpair.h
rename to extra/curl/curl-8.14.1/lib/socketpair.h
index ed69c5af826a..5541899445b6 100644
--- a/extra/curl/curl-8.12.1/lib/socketpair.h
+++ b/extra/curl/curl-8.14.1/lib/socketpair.h
@@ -26,24 +26,7 @@
 
 #include "curl_setup.h"
 
-#if defined(HAVE_EVENTFD) && \
-    (defined(__x86_64__) || \
-     defined(__aarch64__) || \
-     defined(__ia64__) || \
-     defined(__ppc64__) || \
-     defined(__mips64) || \
-     defined(__sparc64__) || \
-     defined(__riscv_64e) || \
-     defined(__s390x__))
-
-/* Use eventfd only with 64-bit CPU architectures because eventfd has a
- * stringent rule of requiring the 8-byte buffer when calling read(2) and
- * write(2) on it. In some rare cases, the C standard library implementation
- * on a 32-bit system might choose to define uint64_t as a 32-bit type for
- * various reasons (memory limitations, compatibility with older code),
- * which makes eventfd broken.
- */
-#define USE_EVENTFD 1
+#ifdef USE_EVENTFD
 
 #define wakeup_write  write
 #define wakeup_read   read
diff --git a/extra/curl/curl-8.12.1/lib/socks.c b/extra/curl/curl-8.14.1/lib/socks.c
similarity index 95%
rename from extra/curl/curl-8.12.1/lib/socks.c
rename to extra/curl/curl-8.14.1/lib/socks.c
index d16a30b90adb..08bc874f117e 100644
--- a/extra/curl/curl-8.12.1/lib/socks.c
+++ b/extra/curl/curl-8.14.1/lib/socks.c
@@ -38,10 +38,10 @@
 #include "select.h"
 #include "cfilters.h"
 #include "connect.h"
-#include "timeval.h"
+#include "curlx/timeval.h"
 #include "socks.h"
 #include "multiif.h" /* for getsock macros */
-#include "inet_pton.h"
+#include "curlx/inet_pton.h"
 #include "url.h"
 
 /* The last 3 #include files should be in this order */
@@ -321,16 +321,16 @@ static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf,
 
     /* DNS resolve only for SOCKS4, not SOCKS4a */
     if(!protocol4a) {
-      enum resolve_t rc =
-        Curl_resolv(data, sx->hostname, sx->remote_port, TRUE, &dns);
+      result = Curl_resolv(data, sx->hostname, sx->remote_port,
+                           cf->conn->ip_version, TRUE, &dns);
 
-      if(rc == CURLRESOLV_ERROR)
-        return CURLPX_RESOLVE_HOST;
-      else if(rc == CURLRESOLV_PENDING) {
+      if(result == CURLE_AGAIN) {
         sxstate(sx, data, CONNECT_RESOLVING);
         infof(data, "SOCKS4 non-blocking resolve of %s", sx->hostname);
         return CURLPX_OK;
       }
+      else if(result)
+        return CURLPX_RESOLVE_HOST;
       sxstate(sx, data, CONNECT_RESOLVED);
       goto CONNECT_RESOLVED;
     }
@@ -341,23 +341,11 @@ static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf,
 
   case CONNECT_RESOLVING:
     /* check if we have the name resolved by now */
-    dns = Curl_fetch_addr(data, sx->hostname, conn->primary.remote_port);
-
-    if(dns) {
-#ifdef CURLRES_ASYNCH
-      data->state.async.dns = dns;
-      data->state.async.done = TRUE;
-#endif
-      infof(data, "Hostname '%s' was found", sx->hostname);
-      sxstate(sx, data, CONNECT_RESOLVED);
-    }
-    else {
-      result = Curl_resolv_check(data, &dns);
-      if(!dns) {
-        if(result)
-          return CURLPX_RESOLVE_HOST;
-        return CURLPX_OK;
-      }
+    result = Curl_resolv_check(data, &dns);
+    if(!dns) {
+      if(result)
+        return CURLPX_RESOLVE_HOST;
+      return CURLPX_OK;
     }
     FALLTHROUGH();
   case CONNECT_RESOLVED:
@@ -406,7 +394,7 @@ static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf,
     /*
      * This is currently not supporting "Identification Protocol (RFC1413)".
      */
-    socksreq[8] = 0; /* ensure empty userid is NUL-terminated */
+    socksreq[8] = 0; /* ensure empty userid is null-terminated */
     if(sx->proxy_user) {
       size_t plen = strlen(sx->proxy_user);
       if(plen > 255) {
@@ -590,7 +578,6 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
   bool allow_gssapi = FALSE;
   struct Curl_dns_entry *dns = NULL;
 
-  DEBUGASSERT(auth & (CURLAUTH_BASIC | CURLAUTH_GSSAPI));
   switch(sx->state) {
   case CONNECT_SOCKS_INIT:
     if(conn->bits.httpproxy)
@@ -795,16 +782,15 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
   case CONNECT_REQ_INIT:
 CONNECT_REQ_INIT:
     if(socks5_resolve_local) {
-      enum resolve_t rc = Curl_resolv(data, sx->hostname, sx->remote_port,
-                                      TRUE, &dns);
+      result = Curl_resolv(data, sx->hostname, sx->remote_port,
+                           cf->conn->ip_version, TRUE, &dns);
 
-      if(rc == CURLRESOLV_ERROR)
-        return CURLPX_RESOLVE_HOST;
-
-      if(rc == CURLRESOLV_PENDING) {
+      if(result == CURLE_AGAIN) {
         sxstate(sx, data, CONNECT_RESOLVING);
         return CURLPX_OK;
       }
+      else if(result)
+        return CURLPX_RESOLVE_HOST;
       sxstate(sx, data, CONNECT_RESOLVED);
       goto CONNECT_RESOLVED;
     }
@@ -812,23 +798,11 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
 
   case CONNECT_RESOLVING:
     /* check if we have the name resolved by now */
-    dns = Curl_fetch_addr(data, sx->hostname, sx->remote_port);
-
-    if(dns) {
-#ifdef CURLRES_ASYNCH
-      data->state.async.dns = dns;
-      data->state.async.done = TRUE;
-#endif
-      infof(data, "SOCKS5: hostname '%s' found", sx->hostname);
-    }
-
+    result = Curl_resolv_check(data, &dns);
     if(!dns) {
-      result = Curl_resolv_check(data, &dns);
-      if(!dns) {
-        if(result)
-          return CURLPX_RESOLVE_HOST;
-        return CURLPX_OK;
-      }
+      if(result)
+        return CURLPX_RESOLVE_HOST;
+      return CURLPX_OK;
     }
     FALLTHROUGH();
   case CONNECT_RESOLVED:
@@ -912,7 +886,7 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
 #ifdef USE_IPV6
       if(conn->bits.ipv6_ip) {
         char ip6[16];
-        if(1 != Curl_inet_pton(AF_INET6, sx->hostname, ip6))
+        if(1 != curlx_inet_pton(AF_INET6, sx->hostname, ip6))
           return CURLPX_BAD_ADDRESS_TYPE;
         socksreq[len++] = 4;
         memcpy(&socksreq[len], ip6, sizeof(ip6));
@@ -920,7 +894,7 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
       }
       else
 #endif
-      if(1 == Curl_inet_pton(AF_INET, sx->hostname, ip4)) {
+      if(1 == curlx_inet_pton(AF_INET, sx->hostname, ip4)) {
         socksreq[len++] = 1;
         memcpy(&socksreq[len], ip4, sizeof(ip4));
         len += sizeof(ip4);
@@ -1128,7 +1102,7 @@ static void socks_proxy_cf_free(struct Curl_cfilter *cf)
 */
 static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf,
                                        struct Curl_easy *data,
-                                       bool blocking, bool *done)
+                                       bool *done)
 {
   CURLcode result;
   struct connectdata *conn = cf->conn;
@@ -1140,7 +1114,7 @@ static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf,
     return CURLE_OK;
   }
 
-  result = cf->next->cft->do_connect(cf->next, data, blocking, done);
+  result = cf->next->cft->do_connect(cf->next, data, done);
   if(result || !*done)
     return result;
 
diff --git a/extra/curl/curl-8.12.1/lib/socks.h b/extra/curl/curl-8.14.1/lib/socks.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/socks.h
rename to extra/curl/curl-8.14.1/lib/socks.h
diff --git a/extra/curl/curl-8.12.1/lib/socks_gssapi.c b/extra/curl/curl-8.14.1/lib/socks_gssapi.c
similarity index 98%
rename from extra/curl/curl-8.12.1/lib/socks_gssapi.c
rename to extra/curl/curl-8.14.1/lib/socks_gssapi.c
index 776dbdae80a7..697e301b7729 100644
--- a/extra/curl/curl-8.12.1/lib/socks_gssapi.c
+++ b/extra/curl/curl-8.14.1/lib/socks_gssapi.c
@@ -32,9 +32,9 @@
 #include "sendf.h"
 #include "cfilters.h"
 #include "connect.h"
-#include "timeval.h"
+#include "curlx/timeval.h"
 #include "socks.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
 #include "strdup.h"
 
 /* The last 3 #include files should be in this order */
@@ -65,7 +65,7 @@ static int check_gss_err(struct Curl_easy *data,
     gss_buffer_desc status_string = GSS_C_EMPTY_BUFFER;
     struct dynbuf dbuf;
 
-    Curl_dyn_init(&dbuf, MAX_GSS_LEN);
+    curlx_dyn_init(&dbuf, MAX_GSS_LEN);
     msg_ctx = 0;
     while(!msg_ctx) {
       /* convert major status code (GSS-API error) to text */
@@ -74,7 +74,7 @@ static int check_gss_err(struct Curl_easy *data,
                                     GSS_C_NULL_OID,
                                     &msg_ctx, &status_string);
       if(maj_stat == GSS_S_COMPLETE) {
-        if(Curl_dyn_addn(&dbuf, status_string.value,
+        if(curlx_dyn_addn(&dbuf, status_string.value,
                          status_string.length))
           return 1; /* error */
         gss_release_buffer(&min_stat, &status_string);
@@ -82,7 +82,7 @@ static int check_gss_err(struct Curl_easy *data,
       }
       gss_release_buffer(&min_stat, &status_string);
     }
-    if(Curl_dyn_addn(&dbuf, ".\n", 2))
+    if(curlx_dyn_addn(&dbuf, ".\n", 2))
       return 1; /* error */
     msg_ctx = 0;
     while(!msg_ctx) {
@@ -92,7 +92,7 @@ static int check_gss_err(struct Curl_easy *data,
                                     GSS_C_NULL_OID,
                                     &msg_ctx, &status_string);
       if(maj_stat == GSS_S_COMPLETE) {
-        if(Curl_dyn_addn(&dbuf, status_string.value,
+        if(curlx_dyn_addn(&dbuf, status_string.value,
                          status_string.length))
           return 1; /* error */
         gss_release_buffer(&min_stat, &status_string);
@@ -100,8 +100,9 @@ static int check_gss_err(struct Curl_easy *data,
       }
       gss_release_buffer(&min_stat, &status_string);
     }
-    failf(data, "GSS-API error: %s failed: %s", function, Curl_dyn_ptr(&dbuf));
-    Curl_dyn_free(&dbuf);
+    failf(data, "GSS-API error: %s failed: %s", function,
+          curlx_dyn_ptr(&dbuf));
+    curlx_dyn_free(&dbuf);
     return 1;
   }
 
diff --git a/extra/curl/curl-8.12.1/lib/socks_sspi.c b/extra/curl/curl-8.14.1/lib/socks_sspi.c
similarity index 97%
rename from extra/curl/curl-8.12.1/lib/socks_sspi.c
rename to extra/curl/curl-8.14.1/lib/socks_sspi.c
index 6d8e6ef730da..1f0846dda469 100644
--- a/extra/curl/curl-8.12.1/lib/socks_sspi.c
+++ b/extra/curl/curl-8.14.1/lib/socks_sspi.c
@@ -32,11 +32,11 @@
 #include "cfilters.h"
 #include "connect.h"
 #include "strerror.h"
-#include "timeval.h"
+#include "curlx/timeval.h"
 #include "socks.h"
 #include "curl_sspi.h"
-#include "curl_multibyte.h"
-#include "warnless.h"
+#include "curlx/multibyte.h"
+#include "curlx/warnless.h"
 #include "strdup.h"
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -140,14 +140,14 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
   cred_handle.dwUpper = 0;
 
   status = Curl_pSecFn->AcquireCredentialsHandle(NULL,
-                                              (TCHAR *) TEXT("Kerberos"),
-                                              SECPKG_CRED_OUTBOUND,
-                                              NULL,
-                                              NULL,
-                                              NULL,
-                                              NULL,
-                                              &cred_handle,
-                                              &expiry);
+                                       (TCHAR *)CURL_UNCONST(TEXT("Kerberos")),
+                                       SECPKG_CRED_OUTBOUND,
+                                       NULL,
+                                       NULL,
+                                       NULL,
+                                       NULL,
+                                       &cred_handle,
+                                       &expiry);
 
   if(check_sspi_err(data, status, "AcquireCredentialsHandle")) {
     failf(data, "Failed to acquire credentials.");
diff --git a/extra/curl/curl-8.12.1/lib/speedcheck.c b/extra/curl/curl-8.14.1/lib/speedcheck.c
similarity index 97%
rename from extra/curl/curl-8.12.1/lib/speedcheck.c
rename to extra/curl/curl-8.14.1/lib/speedcheck.c
index 580efbde7501..16d7d72562c2 100644
--- a/extra/curl/curl-8.12.1/lib/speedcheck.c
+++ b/extra/curl/curl-8.14.1/lib/speedcheck.c
@@ -52,7 +52,7 @@ CURLcode Curl_speedcheck(struct Curl_easy *data,
         data->state.keeps_speed = now;
       else {
         /* how long has it been under the limit */
-        timediff_t howlong = Curl_timediff(now, data->state.keeps_speed);
+        timediff_t howlong = curlx_timediff(now, data->state.keeps_speed);
 
         if(howlong >= data->set.low_speed_time * 1000) {
           /* too long */
diff --git a/extra/curl/curl-8.12.1/lib/speedcheck.h b/extra/curl/curl-8.14.1/lib/speedcheck.h
similarity index 98%
rename from extra/curl/curl-8.12.1/lib/speedcheck.h
rename to extra/curl/curl-8.14.1/lib/speedcheck.h
index 8b116f15289d..f54365cadfac 100644
--- a/extra/curl/curl-8.12.1/lib/speedcheck.h
+++ b/extra/curl/curl-8.14.1/lib/speedcheck.h
@@ -26,7 +26,7 @@
 
 #include "curl_setup.h"
 
-#include "timeval.h"
+#include "curlx/timeval.h"
 struct Curl_easy;
 void Curl_speedinit(struct Curl_easy *data);
 CURLcode Curl_speedcheck(struct Curl_easy *data,
diff --git a/extra/curl/curl-8.12.1/lib/splay.c b/extra/curl/curl-8.14.1/lib/splay.c
similarity index 99%
rename from extra/curl/curl-8.12.1/lib/splay.c
rename to extra/curl/curl-8.14.1/lib/splay.c
index 3f2bae023874..7a0e419ce68a 100644
--- a/extra/curl/curl-8.12.1/lib/splay.c
+++ b/extra/curl/curl-8.14.1/lib/splay.c
@@ -24,7 +24,7 @@
 
 #include "curl_setup.h"
 
-#include "timeval.h"
+#include "curlx/timeval.h"
 #include "splay.h"
 
 /*
@@ -34,7 +34,7 @@
  *  zero          : when i is equal   to   j
  *  positive when : when i is larger  than j
  */
-#define compare(i,j) Curl_timediff_us(i,j)
+#define compare(i,j) curlx_timediff_us(i,j)
 
 /*
  * Splay using the key i (which may or may not be in the tree.) The starting
diff --git a/extra/curl/curl-8.12.1/lib/splay.h b/extra/curl/curl-8.14.1/lib/splay.h
similarity index 98%
rename from extra/curl/curl-8.12.1/lib/splay.h
rename to extra/curl/curl-8.14.1/lib/splay.h
index b8c9360e5734..ccc3781ec167 100644
--- a/extra/curl/curl-8.12.1/lib/splay.h
+++ b/extra/curl/curl-8.14.1/lib/splay.h
@@ -24,7 +24,7 @@
  *
  ***************************************************************************/
 #include "curl_setup.h"
-#include "timeval.h"
+#include "curlx/timeval.h"
 
 /* only use function calls to access this struct */
 struct Curl_tree {
diff --git a/extra/curl/curl-8.12.1/lib/strcase.c b/extra/curl/curl-8.14.1/lib/strcase.c
similarity index 72%
rename from extra/curl/curl-8.12.1/lib/strcase.c
rename to extra/curl/curl-8.14.1/lib/strcase.c
index 112aedb19300..ee5b9072c4cc 100644
--- a/extra/curl/curl-8.12.1/lib/strcase.c
+++ b/extra/curl/curl-8.14.1/lib/strcase.c
@@ -82,69 +82,10 @@ char Curl_raw_tolower(char in)
   return (char)tolowermap[(unsigned char) in];
 }
 
-/*
- * curl_strequal() is for doing "raw" case insensitive strings. This is meant
- * to be locale independent and only compare strings we know are safe for
- * this. See https://daniel.haxx.se/blog/2008/10/15/strcasecmp-in-turkish/ for
- * further explanations as to why this function is necessary.
- */
-
-static int casecompare(const char *first, const char *second)
-{
-  while(*first && *second) {
-    if(Curl_raw_toupper(*first) != Curl_raw_toupper(*second))
-      /* get out of the loop as soon as they do not match */
-      return 0;
-    first++;
-    second++;
-  }
-  /* If we are here either the strings are the same or the length is different.
-     We can just test if the "current" character is non-zero for one and zero
-     for the other. Note that the characters may not be exactly the same even
-     if they match, we only want to compare zero-ness. */
-  return !*first == !*second;
-}
-
-/* --- public function --- */
-int curl_strequal(const char *first, const char *second)
-{
-  if(first && second)
-    /* both pointers point to something then compare them */
-    return casecompare(first, second);
-
-  /* if both pointers are NULL then treat them as equal */
-  return NULL == first && NULL == second;
-}
-
-static int ncasecompare(const char *first, const char *second, size_t max)
-{
-  while(*first && *second && max) {
-    if(Curl_raw_toupper(*first) != Curl_raw_toupper(*second))
-      return 0;
-    max--;
-    first++;
-    second++;
-  }
-  if(0 == max)
-    return 1; /* they are equal this far */
-
-  return Curl_raw_toupper(*first) == Curl_raw_toupper(*second);
-}
-
-/* --- public function --- */
-int curl_strnequal(const char *first, const char *second, size_t max)
-{
-  if(first && second)
-    /* both pointers point to something then compare them */
-    return ncasecompare(first, second, max);
-
-  /* if both pointers are NULL then treat them as equal if max is non-zero */
-  return NULL == first && NULL == second && max;
-}
 /* Copy an upper case version of the string from src to dest. The
  * strings may overlap. No more than n characters of the string are copied
  * (including any NUL) and the destination string will NOT be
- * NUL-terminated if that limit is reached.
+ * null-terminated if that limit is reached.
  */
 void Curl_strntoupper(char *dest, const char *src, size_t n)
 {
@@ -159,7 +100,7 @@ void Curl_strntoupper(char *dest, const char *src, size_t n)
 /* Copy a lower case version of the string from src to dest. The
  * strings may overlap. No more than n characters of the string are copied
  * (including any NUL) and the destination string will NOT be
- * NUL-terminated if that limit is reached.
+ * null-terminated if that limit is reached.
  */
 void Curl_strntolower(char *dest, const char *src, size_t n)
 {
@@ -171,7 +112,7 @@ void Curl_strntolower(char *dest, const char *src, size_t n)
   } while(*src++ && --n);
 }
 
-/* Compare case-sensitive NUL-terminated strings, taking care of possible
+/* Compare case-sensitive null-terminated strings, taking care of possible
  * null pointers. Return true if arguments match.
  */
 bool Curl_safecmp(char *a, char *b)
diff --git a/extra/curl/curl-8.12.1/lib/strcase.h b/extra/curl/curl-8.14.1/lib/strcase.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/strcase.h
rename to extra/curl/curl-8.14.1/lib/strcase.h
diff --git a/extra/curl/curl-8.12.1/lib/strdup.c b/extra/curl/curl-8.14.1/lib/strdup.c
similarity index 96%
rename from extra/curl/curl-8.12.1/lib/strdup.c
rename to extra/curl/curl-8.14.1/lib/strdup.c
index 299c9cc36b7a..69c41a5e2527 100644
--- a/extra/curl/curl-8.12.1/lib/strdup.c
+++ b/extra/curl/curl-8.14.1/lib/strdup.c
@@ -104,7 +104,7 @@ void *Curl_memdup(const void *src, size_t length)
  * Curl_memdup0(source, length)
  *
  * Copies the 'source' string to a newly allocated buffer (that is returned).
- * Copies 'length' bytes then adds a null terminator.
+ * Copies 'length' bytes then adds a null-terminator.
  *
  * Returns the new pointer or NULL on failure.
  *
@@ -114,7 +114,10 @@ void *Curl_memdup0(const char *src, size_t length)
   char *buf = malloc(length + 1);
   if(!buf)
     return NULL;
-  memcpy(buf, src, length);
+  if(length) {
+    DEBUGASSERT(src); /* must never be NULL */
+    memcpy(buf, src, length);
+  }
   buf[length] = 0;
   return buf;
 }
diff --git a/extra/curl/curl-8.12.1/lib/strdup.h b/extra/curl/curl-8.14.1/lib/strdup.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/strdup.h
rename to extra/curl/curl-8.14.1/lib/strdup.h
diff --git a/extra/curl/curl-8.14.1/lib/strequal.c b/extra/curl/curl-8.14.1/lib/strequal.c
new file mode 100644
index 000000000000..d1ba91501bbe
--- /dev/null
+++ b/extra/curl/curl-8.14.1/lib/strequal.c
@@ -0,0 +1,88 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include <curl/curl.h>
+#include "strcase.h"
+
+/*
+ * curl_strequal() is for doing "raw" case insensitive strings. This is meant
+ * to be locale independent and only compare strings we know are safe for
+ * this. See https://daniel.haxx.se/blog/2008/10/15/strcasecmp-in-turkish/ for
+ * further explanations as to why this function is necessary.
+ */
+
+static int casecompare(const char *first, const char *second)
+{
+  while(*first) {
+    if(Curl_raw_toupper(*first) != Curl_raw_toupper(*second))
+      /* get out of the loop as soon as they do not match */
+      return 0;
+    first++;
+    second++;
+  }
+  /* If we are here either the strings are the same or the length is different.
+     We can just test if the "current" character is non-zero for one and zero
+     for the other. Note that the characters may not be exactly the same even
+     if they match, we only want to compare zero-ness. */
+  return !*first == !*second;
+}
+
+static int ncasecompare(const char *first, const char *second, size_t max)
+{
+  while(*first && max) {
+    if(Curl_raw_toupper(*first) != Curl_raw_toupper(*second))
+      return 0;
+    max--;
+    first++;
+    second++;
+  }
+  if(0 == max)
+    return 1; /* they are equal this far */
+
+  return Curl_raw_toupper(*first) == Curl_raw_toupper(*second);
+}
+
+/* --- public function --- */
+int curl_strequal(const char *first, const char *second)
+{
+  if(first && second)
+    /* both pointers point to something then compare them */
+    return casecompare(first, second);
+
+  /* if both pointers are NULL then treat them as equal */
+  return NULL == first && NULL == second;
+}
+
+/* --- public function --- */
+int curl_strnequal(const char *first, const char *second, size_t max)
+{
+  if(first && second)
+    /* both pointers point to something then compare them */
+    return ncasecompare(first, second, max);
+
+  /* if both pointers are NULL then treat them as equal if max is non-zero */
+  return NULL == first && NULL == second && max;
+}
diff --git a/extra/curl/curl-8.12.1/lib/strerror.c b/extra/curl/curl-8.14.1/lib/strerror.c
similarity index 87%
rename from extra/curl/curl-8.12.1/lib/strerror.c
rename to extra/curl/curl-8.14.1/lib/strerror.c
index 20495c9791a1..9cf93dd66e97 100644
--- a/extra/curl/curl-8.12.1/lib/strerror.c
+++ b/extra/curl/curl-8.14.1/lib/strerror.c
@@ -42,16 +42,13 @@
 #include "curl_sspi.h"
 #endif
 
+#include "curlx/winapi.h"
 #include "strerror.h"
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
 #include "memdebug.h"
 
-#if defined(_WIN32) || defined(_WIN32_WCE)
-#define PRESERVE_WINDOWS_ERROR_CODE
-#endif
-
 const char *
 curl_easy_strerror(CURLcode error)
 {
@@ -335,6 +332,20 @@ curl_easy_strerror(CURLcode error)
   case CURLE_OBSOLETE62:
   case CURLE_OBSOLETE75:
   case CURLE_OBSOLETE76:
+
+    /* error codes used by curl tests */
+  case CURLE_RESERVED115:
+  case CURLE_RESERVED116:
+  case CURLE_RESERVED117:
+  case CURLE_RESERVED118:
+  case CURLE_RESERVED119:
+  case CURLE_RESERVED120:
+  case CURLE_RESERVED121:
+  case CURLE_RESERVED122:
+  case CURLE_RESERVED123:
+  case CURLE_RESERVED124:
+  case CURLE_RESERVED125:
+  case CURLE_RESERVED126:
   case CURL_LAST:
     break;
   }
@@ -768,50 +779,6 @@ get_winsock_error(int err, char *buf, size_t len)
 }
 #endif   /* USE_WINSOCK */
 
-#if defined(_WIN32) || defined(_WIN32_WCE)
-/* This is a helper function for Curl_strerror that converts Windows API error
- * codes (GetLastError) to error messages.
- * Returns NULL if no error message was found for error code.
- */
-static const char *
-get_winapi_error(int err, char *buf, size_t buflen)
-{
-  char *p;
-  wchar_t wbuf[256];
-
-  if(!buflen)
-    return NULL;
-
-  *buf = '\0';
-  *wbuf = L'\0';
-
-  /* We return the local codepage version of the error string because if it is
-     output to the user's terminal it will likely be with functions which
-     expect the local codepage (eg fprintf, failf, infof).
-     FormatMessageW -> wcstombs is used for Windows CE compatibility. */
-  if(FormatMessageW((FORMAT_MESSAGE_FROM_SYSTEM |
-                     FORMAT_MESSAGE_IGNORE_INSERTS), NULL, (DWORD)err,
-                    LANG_NEUTRAL, wbuf, sizeof(wbuf)/sizeof(wchar_t), NULL)) {
-    size_t written = wcstombs(buf, wbuf, buflen - 1);
-    if(written != (size_t)-1)
-      buf[written] = '\0';
-    else
-      *buf = '\0';
-  }
-
-  /* Truncate multiple lines */
-  p = strchr(buf, '\n');
-  if(p) {
-    if(p > buf && *(p-1) == '\r')
-      *(p-1) = '\0';
-    else
-      *p = '\0';
-  }
-
-  return *buf ? buf : NULL;
-}
-#endif /* _WIN32 || _WIN32_WCE */
-
 /*
  * Our thread-safe and smart strerror() replacement.
  *
@@ -829,11 +796,11 @@ get_winapi_error(int err, char *buf, size_t buflen)
  *
  * It may be more correct to call one of the variant functions instead:
  * Call Curl_sspi_strerror if the error code is definitely Windows SSPI.
- * Call Curl_winapi_strerror if the error code is definitely Windows API.
+ * Call curlx_winapi_strerror if the error code is definitely Windows API.
  */
 const char *Curl_strerror(int err, char *buf, size_t buflen)
 {
-#ifdef PRESERVE_WINDOWS_ERROR_CODE
+#ifdef _WIN32
   DWORD old_win_err = GetLastError();
 #endif
   int old_errno = errno;
@@ -848,20 +815,20 @@ const char *Curl_strerror(int err, char *buf, size_t buflen)
 
   *buf = '\0';
 
-#if defined(_WIN32) || defined(_WIN32_WCE)
-#if defined(_WIN32)
+#ifdef _WIN32
+#ifndef UNDER_CE
   /* 'sys_nerr' is the maximum errno number, it is not widely portable */
   if(err >= 0 && err < sys_nerr)
-    msnprintf(buf, buflen, "%s", sys_errlist[err]);
+    curl_msnprintf(buf, buflen, "%s", sys_errlist[err]);
   else
 #endif
   {
     if(
 #ifdef USE_WINSOCK
-       !get_winsock_error(err, buf, buflen) &&
+      !get_winsock_error(err, buf, buflen) &&
 #endif
-       !get_winapi_error(err, buf, buflen))
-      msnprintf(buf, buflen, "Unknown error %d (%#x)", err, err);
+      !curlx_get_winapi_error(err, buf, buflen))
+      curl_msnprintf(buf, buflen, "Unknown error %d (%#x)", err, err);
   }
 #else /* not Windows coming up */
 
@@ -873,7 +840,7 @@ const char *Curl_strerror(int err, char *buf, size_t buflen)
   */
   if(0 != strerror_r(err, buf, buflen)) {
     if('\0' == buf[0])
-      msnprintf(buf, buflen, "Unknown error %d", err);
+      curl_msnprintf(buf, buflen, "Unknown error %d", err);
   }
 #elif defined(HAVE_STRERROR_R) && defined(HAVE_GLIBC_STRERROR_R)
  /*
@@ -885,18 +852,18 @@ const char *Curl_strerror(int err, char *buf, size_t buflen)
     char buffer[256];
     char *msg = strerror_r(err, buffer, sizeof(buffer));
     if(msg)
-      msnprintf(buf, buflen, "%s", msg);
+      curl_msnprintf(buf, buflen, "%s", msg);
     else
-      msnprintf(buf, buflen, "Unknown error %d", err);
+      curl_msnprintf(buf, buflen, "Unknown error %d", err);
   }
 #else
   {
     /* !checksrc! disable BANNEDFUNC 1 */
     const char *msg = strerror(err);
     if(msg)
-      msnprintf(buf, buflen, "%s", msg);
+      curl_msnprintf(buf, buflen, "%s", msg);
     else
-      msnprintf(buf, buflen, "Unknown error %d", err);
+      curl_msnprintf(buf, buflen, "Unknown error %d", err);
   }
 #endif
 
@@ -911,56 +878,15 @@ const char *Curl_strerror(int err, char *buf, size_t buflen)
     *p = '\0';
 
   if(errno != old_errno)
-    errno = old_errno;
-
-#ifdef PRESERVE_WINDOWS_ERROR_CODE
-  if(old_win_err != GetLastError())
-    SetLastError(old_win_err);
-#endif
-
-  return buf;
-}
-
-/*
- * Curl_winapi_strerror:
- * Variant of Curl_strerror if the error code is definitely Windows API.
- */
-#if defined(_WIN32) || defined(_WIN32_WCE)
-const char *Curl_winapi_strerror(DWORD err, char *buf, size_t buflen)
-{
-#ifdef PRESERVE_WINDOWS_ERROR_CODE
-  DWORD old_win_err = GetLastError();
-#endif
-  int old_errno = errno;
-
-  if(!buflen)
-    return NULL;
-
-  *buf = '\0';
-
-#ifndef CURL_DISABLE_VERBOSE_STRINGS
-  if(!get_winapi_error((int)err, buf, buflen)) {
-    msnprintf(buf, buflen, "Unknown error %lu (0x%08lX)", err, err);
-  }
-#else
-  {
-    const char *txt = (err == ERROR_SUCCESS) ? "No error" : "Error";
-    if(strlen(txt) < buflen)
-      strcpy(buf, txt);
-  }
-#endif
-
-  if(errno != old_errno)
-    errno = old_errno;
+    CURL_SETERRNO(old_errno);
 
-#ifdef PRESERVE_WINDOWS_ERROR_CODE
+#ifdef _WIN32
   if(old_win_err != GetLastError())
     SetLastError(old_win_err);
 #endif
 
   return buf;
 }
-#endif /* _WIN32 || _WIN32_WCE */
 
 #ifdef USE_WINDOWS_SSPI
 /*
@@ -969,7 +895,7 @@ const char *Curl_winapi_strerror(DWORD err, char *buf, size_t buflen)
  */
 const char *Curl_sspi_strerror(int err, char *buf, size_t buflen)
 {
-#ifdef PRESERVE_WINDOWS_ERROR_CODE
+#ifdef _WIN32
   DWORD old_win_err = GetLastError();
 #endif
   int old_errno = errno;
@@ -1076,18 +1002,18 @@ const char *Curl_sspi_strerror(int err, char *buf, size_t buflen)
   }
 
   if(err == SEC_E_ILLEGAL_MESSAGE) {
-    msnprintf(buf, buflen,
-              "SEC_E_ILLEGAL_MESSAGE (0x%08X) - This error usually occurs "
-              "when a fatal SSL/TLS alert is received (e.g. handshake failed)."
-              " More detail may be available in the Windows System event log.",
-              err);
+    curl_msnprintf(buf, buflen,
+                   "SEC_E_ILLEGAL_MESSAGE (0x%08X) - This error usually "
+                   "occurs when a fatal SSL/TLS alert is received (e.g. "
+                   "handshake failed). More detail may be available in "
+                   "the Windows System event log.", err);
   }
   else {
     char msgbuf[256];
-    if(get_winapi_error(err, msgbuf, sizeof(msgbuf)))
-      msnprintf(buf, buflen, "%s (0x%08X) - %s", txt, err, msgbuf);
+    if(curlx_get_winapi_error(err, msgbuf, sizeof(msgbuf)))
+      curl_msnprintf(buf, buflen, "%s (0x%08X) - %s", txt, err, msgbuf);
     else
-      msnprintf(buf, buflen, "%s (0x%08X)", txt, err);
+      curl_msnprintf(buf, buflen, "%s (0x%08X)", txt, err);
   }
 
 #else
@@ -1100,9 +1026,9 @@ const char *Curl_sspi_strerror(int err, char *buf, size_t buflen)
 #endif
 
   if(errno != old_errno)
-    errno = old_errno;
+    CURL_SETERRNO(old_errno);
 
-#ifdef PRESERVE_WINDOWS_ERROR_CODE
+#ifdef _WIN32
   if(old_win_err != GetLastError())
     SetLastError(old_win_err);
 #endif
diff --git a/extra/curl/curl-8.12.1/lib/strerror.h b/extra/curl/curl-8.14.1/lib/strerror.h
similarity index 91%
rename from extra/curl/curl-8.12.1/lib/strerror.h
rename to extra/curl/curl-8.14.1/lib/strerror.h
index 680686734531..424fb5b7b5e1 100644
--- a/extra/curl/curl-8.12.1/lib/strerror.h
+++ b/extra/curl/curl-8.14.1/lib/strerror.h
@@ -29,9 +29,6 @@
 #define STRERROR_LEN 256 /* a suitable length */
 
 const char *Curl_strerror(int err, char *buf, size_t buflen);
-#if defined(_WIN32) || defined(_WIN32_WCE)
-const char *Curl_winapi_strerror(DWORD err, char *buf, size_t buflen);
-#endif
 #ifdef USE_WINDOWS_SSPI
 const char *Curl_sspi_strerror(int err, char *buf, size_t buflen);
 #endif
diff --git a/extra/curl/curl-8.12.1/lib/system_win32.c b/extra/curl/curl-8.14.1/lib/system_win32.c
similarity index 95%
rename from extra/curl/curl-8.12.1/lib/system_win32.c
rename to extra/curl/curl-8.14.1/lib/system_win32.c
index 5ab711871e50..521288758026 100644
--- a/extra/curl/curl-8.12.1/lib/system_win32.c
+++ b/extra/curl/curl-8.14.1/lib/system_win32.c
@@ -24,21 +24,18 @@
 
 #include "curl_setup.h"
 
-#if defined(_WIN32)
+#ifdef _WIN32
 
 #include <curl/curl.h>
 #include "system_win32.h"
-#include "version_win32.h"
+#include "curlx/version_win32.h"
 #include "curl_sspi.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
 
 /* The last #include files should be: */
 #include "curl_memory.h"
 #include "memdebug.h"
 
-LARGE_INTEGER Curl_freq;
-bool Curl_isVistaOrGreater;
-
 /* Handle of iphlpapp.dll */
 static HMODULE s_hIpHlpApiDll = NULL;
 
@@ -96,9 +93,15 @@ CURLcode Curl_win32_init(long flags)
   s_hIpHlpApiDll = Curl_load_library(TEXT("iphlpapi.dll"));
   if(s_hIpHlpApiDll) {
     /* Get the address of the if_nametoindex function */
+#ifdef UNDER_CE
+    #define CURL_TEXT(n) TEXT(n)
+#else
+    #define CURL_TEXT(n) (n)
+#endif
     IF_NAMETOINDEX_FN pIfNameToIndex =
       CURLX_FUNCTION_CAST(IF_NAMETOINDEX_FN,
-                          (GetProcAddress(s_hIpHlpApiDll, "if_nametoindex")));
+                          (GetProcAddress(s_hIpHlpApiDll,
+                                          CURL_TEXT("if_nametoindex"))));
 
     if(pIfNameToIndex)
       Curl_if_nametoindex = pIfNameToIndex;
@@ -150,7 +153,7 @@ typedef HMODULE (APIENTRY *LOADLIBRARYEX_FN)(LPCTSTR, HANDLE, DWORD);
 
 /* See function definitions in winbase.h */
 #ifdef UNICODE
-#  ifdef _WIN32_WCE
+#  ifdef UNDER_CE
 #    define LOADLIBARYEX  L"LoadLibraryExW"
 #  else
 #    define LOADLIBARYEX  "LoadLibraryExW"
@@ -175,7 +178,7 @@ typedef HMODULE (APIENTRY *LOADLIBRARYEX_FN)(LPCTSTR, HANDLE, DWORD);
  */
 HMODULE Curl_load_library(LPCTSTR filename)
 {
-#ifndef CURL_WINDOWS_UWP
+#if !defined(CURL_WINDOWS_UWP) && !defined(UNDER_CE)
   HMODULE hModule = NULL;
   LOADLIBRARYEX_FN pLoadLibraryEx = NULL;
 
@@ -211,7 +214,7 @@ HMODULE Curl_load_library(LPCTSTR filename)
     /* Attempt to get the Windows system path */
     UINT systemdirlen = GetSystemDirectory(NULL, 0);
     if(systemdirlen) {
-      /* Allocate space for the full DLL path (Room for the null terminator
+      /* Allocate space for the full DLL path (Room for the null-terminator
          is included in systemdirlen) */
       size_t filenamelen = _tcslen(filename);
       TCHAR *path = malloc(sizeof(TCHAR) * (systemdirlen + 1 + filenamelen));
diff --git a/extra/curl/curl-8.12.1/lib/system_win32.h b/extra/curl/curl-8.14.1/lib/system_win32.h
similarity index 98%
rename from extra/curl/curl-8.12.1/lib/system_win32.h
rename to extra/curl/curl-8.14.1/lib/system_win32.h
index 024d959f3275..b8333c647e17 100644
--- a/extra/curl/curl-8.12.1/lib/system_win32.h
+++ b/extra/curl/curl-8.14.1/lib/system_win32.h
@@ -26,7 +26,7 @@
 
 #include "curl_setup.h"
 
-#if defined(_WIN32)
+#ifdef _WIN32
 
 #include <curl/curl.h>
 
diff --git a/extra/curl/curl-8.12.1/lib/telnet.c b/extra/curl/curl-8.14.1/lib/telnet.c
similarity index 90%
rename from extra/curl/curl-8.12.1/lib/telnet.c
rename to extra/curl/curl-8.14.1/lib/telnet.c
index e383917eed9b..737db36d72c3 100644
--- a/extra/curl/curl-8.12.1/lib/telnet.c
+++ b/extra/curl/curl-8.14.1/lib/telnet.c
@@ -47,6 +47,7 @@
 #endif
 
 #include "urldata.h"
+#include "url.h"
 #include <curl/curl.h>
 #include "transfer.h"
 #include "sendf.h"
@@ -57,7 +58,8 @@
 #include "arpa_telnet.h"
 #include "select.h"
 #include "strcase.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
+#include "curlx/strparse.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -89,36 +91,6 @@
 #define printoption(a,b,c,d)  Curl_nop_stmt
 #endif
 
-static
-CURLcode telrcv(struct Curl_easy *data,
-                const unsigned char *inbuf, /* Data received from socket */
-                ssize_t count);             /* Number of bytes received */
-
-#ifndef CURL_DISABLE_VERBOSE_STRINGS
-static void printoption(struct Curl_easy *data,
-                        const char *direction,
-                        int cmd, int option);
-#endif
-
-static void negotiate(struct Curl_easy *data);
-static void send_negotiation(struct Curl_easy *data, int cmd, int option);
-static void set_local_option(struct Curl_easy *data,
-                             int option, int newstate);
-static void set_remote_option(struct Curl_easy *data,
-                              int option, int newstate);
-
-static void printsub(struct Curl_easy *data,
-                     int direction, unsigned char *pointer,
-                     size_t length);
-static void suboption(struct Curl_easy *data);
-static void sendsuboption(struct Curl_easy *data, int option);
-
-static CURLcode telnet_do(struct Curl_easy *data, bool *done);
-static CURLcode telnet_done(struct Curl_easy *data,
-                                 CURLcode, bool premature);
-static CURLcode send_telnet_data(struct Curl_easy *data,
-                                 char *buffer, ssize_t nread);
-
 /* For negotiation compliant to RFC 1143 */
 #define CURL_NO          0
 #define CURL_YES         1
@@ -128,6 +100,10 @@ static CURLcode send_telnet_data(struct Curl_easy *data,
 #define CURL_EMPTY       0
 #define CURL_OPPOSITE    1
 
+
+/* meta key for storing protocol meta at easy handle */
+#define CURL_META_TELNET_EASY   "meta:proto:telnet:easy"
+
 /*
  * Telnet receiver states for fsm
  */
@@ -168,6 +144,38 @@ struct TELNET {
 };
 
 
+static
+CURLcode telrcv(struct Curl_easy *data,
+                struct TELNET *tn,
+                const unsigned char *inbuf, /* Data received from socket */
+                ssize_t count);             /* Number of bytes received */
+
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+static void printoption(struct Curl_easy *data,
+                        const char *direction,
+                        int cmd, int option);
+#endif
+
+static void send_negotiation(struct Curl_easy *data, int cmd, int option);
+static void set_local_option(struct Curl_easy *data, struct TELNET *tn,
+                             int option, int newstate);
+static void set_remote_option(struct Curl_easy *data, struct TELNET *tn,
+                              int option, int newstate);
+
+static void printsub(struct Curl_easy *data,
+                     int direction, unsigned char *pointer,
+                     size_t length);
+static void suboption(struct Curl_easy *data, struct TELNET *tn);
+static void sendsuboption(struct Curl_easy *data,
+                          struct TELNET *tn, int option);
+
+static CURLcode telnet_do(struct Curl_easy *data, bool *done);
+static CURLcode telnet_done(struct Curl_easy *data,
+                                 CURLcode, bool premature);
+static CURLcode send_telnet_data(struct Curl_easy *data,
+                                 struct TELNET *tn,
+                                 char *buffer, ssize_t nread);
+
 /*
  * TELNET protocol handler.
  */
@@ -198,6 +206,16 @@ const struct Curl_handler Curl_handler_telnet = {
 };
 
 
+static void telnet_easy_dtor(void *key, size_t klen, void *entry)
+{
+  struct TELNET *tn = entry;
+  (void)key;
+  (void)klen;
+  curl_slist_free_all(tn->telnet_vars);
+  curlx_dyn_free(&tn->out);
+  free(tn);
+}
+
 static
 CURLcode init_telnet(struct Curl_easy *data)
 {
@@ -207,8 +225,7 @@ CURLcode init_telnet(struct Curl_easy *data)
   if(!tn)
     return CURLE_OUT_OF_MEMORY;
 
-  Curl_dyn_init(&tn->out, 0xffff);
-  data->req.p.telnet = tn; /* make us known */
+  curlx_dyn_init(&tn->out, 0xffff);
 
   tn->telrcv_state = CURL_TS_DATA;
 
@@ -247,23 +264,23 @@ CURLcode init_telnet(struct Curl_easy *data)
          based upon the terminal type information that may have been sent
          using the TERMINAL TYPE Telnet option). */
   tn->subnegotiation[CURL_TELOPT_NAWS] = CURL_YES;
-  return CURLE_OK;
+
+  return Curl_meta_set(data, CURL_META_TELNET_EASY, tn, telnet_easy_dtor);
 }
 
-static void negotiate(struct Curl_easy *data)
+static void telnet_negotiate(struct Curl_easy *data, struct TELNET *tn)
 {
   int i;
-  struct TELNET *tn = data->req.p.telnet;
 
   for(i = 0; i < CURL_NTELOPTS; i++) {
     if(i == CURL_TELOPT_ECHO)
       continue;
 
     if(tn->us_preferred[i] == CURL_YES)
-      set_local_option(data, i, CURL_YES);
+      set_local_option(data, tn, i, CURL_YES);
 
     if(tn->him_preferred[i] == CURL_YES)
-      set_remote_option(data, i, CURL_YES);
+      set_remote_option(data, tn, i, CURL_YES);
   }
 }
 
@@ -324,9 +341,9 @@ static void send_negotiation(struct Curl_easy *data, int cmd, int option)
 }
 
 static
-void set_remote_option(struct Curl_easy *data, int option, int newstate)
+void set_remote_option(struct Curl_easy *data, struct TELNET *tn,
+                       int option, int newstate)
 {
-  struct TELNET *tn = data->req.p.telnet;
   if(newstate == CURL_YES) {
     switch(tn->him[option]) {
     case CURL_NO:
@@ -398,9 +415,8 @@ void set_remote_option(struct Curl_easy *data, int option, int newstate)
 }
 
 static
-void rec_will(struct Curl_easy *data, int option)
+void rec_will(struct Curl_easy *data, struct TELNET *tn, int option)
 {
-  struct TELNET *tn = data->req.p.telnet;
   switch(tn->him[option]) {
   case CURL_NO:
     if(tn->him_preferred[option] == CURL_YES) {
@@ -446,9 +462,8 @@ void rec_will(struct Curl_easy *data, int option)
 }
 
 static
-void rec_wont(struct Curl_easy *data, int option)
+void rec_wont(struct Curl_easy *data, struct TELNET *tn, int option)
 {
-  struct TELNET *tn = data->req.p.telnet;
   switch(tn->him[option]) {
   case CURL_NO:
     /* Already disabled */
@@ -488,9 +503,9 @@ void rec_wont(struct Curl_easy *data, int option)
 }
 
 static void
-set_local_option(struct Curl_easy *data, int option, int newstate)
+set_local_option(struct Curl_easy *data, struct TELNET *tn,
+                 int option, int newstate)
 {
-  struct TELNET *tn = data->req.p.telnet;
   if(newstate == CURL_YES) {
     switch(tn->us[option]) {
     case CURL_NO:
@@ -562,9 +577,8 @@ set_local_option(struct Curl_easy *data, int option, int newstate)
 }
 
 static
-void rec_do(struct Curl_easy *data, int option)
+void rec_do(struct Curl_easy *data, struct TELNET *tn, int option)
 {
-  struct TELNET *tn = data->req.p.telnet;
   switch(tn->us[option]) {
   case CURL_NO:
     if(tn->us_preferred[option] == CURL_YES) {
@@ -572,13 +586,13 @@ void rec_do(struct Curl_easy *data, int option)
       send_negotiation(data, CURL_WILL, option);
       if(tn->subnegotiation[option] == CURL_YES)
         /* transmission of data option */
-        sendsuboption(data, option);
+        sendsuboption(data, tn, option);
     }
     else if(tn->subnegotiation[option] == CURL_YES) {
       /* send information to achieve this option */
       tn->us[option] = CURL_YES;
       send_negotiation(data, CURL_WILL, option);
-      sendsuboption(data, option);
+      sendsuboption(data, tn, option);
     }
     else
       send_negotiation(data, CURL_WONT, option);
@@ -608,7 +622,7 @@ void rec_do(struct Curl_easy *data, int option)
       tn->us[option] = CURL_YES;
       if(tn->subnegotiation[option] == CURL_YES) {
         /* transmission of data option */
-        sendsuboption(data, option);
+        sendsuboption(data, tn, option);
       }
       break;
     case CURL_OPPOSITE:
@@ -622,9 +636,8 @@ void rec_do(struct Curl_easy *data, int option)
 }
 
 static
-void rec_dont(struct Curl_easy *data, int option)
+void rec_dont(struct Curl_easy *data, struct TELNET *tn, int option)
 {
-  struct TELNET *tn = data->req.p.telnet;
   switch(tn->us[option]) {
   case CURL_NO:
     /* Already disabled */
@@ -787,11 +800,11 @@ static bool str_is_nonascii(const char *str)
   return FALSE;
 }
 
-static CURLcode check_telnet_options(struct Curl_easy *data)
+static CURLcode check_telnet_options(struct Curl_easy *data,
+                                     struct TELNET *tn)
 {
   struct curl_slist *head;
   struct curl_slist *beg;
-  struct TELNET *tn = data->req.p.telnet;
   CURLcode result = CURLE_OK;
 
   /* Add the username as an environment variable if it
@@ -862,22 +875,20 @@ static CURLcode check_telnet_options(struct Curl_easy *data)
       case 2:
         /* Window Size */
         if(strncasecompare(option, "WS", 2)) {
-          char *p;
-          unsigned long x = strtoul(arg, &p, 10);
-          unsigned long y = 0;
-          if(x && (x <= 0xffff) && Curl_raw_tolower(*p) == 'x') {
-            p++;
-            y = strtoul(p, NULL, 10);
-            if(y && (y <= 0xffff)) {
-              tn->subopt_wsx = (unsigned short)x;
-              tn->subopt_wsy = (unsigned short)y;
-              tn->us_preferred[CURL_TELOPT_NAWS] = CURL_YES;
-            }
-          }
-          if(!y) {
+          const char *p = arg;
+          curl_off_t x = 0;
+          curl_off_t y = 0;
+          if(curlx_str_number(&p, &x, 0xffff) ||
+             curlx_str_single(&p, 'x') ||
+             curlx_str_number(&p, &y, 0xffff)) {
             failf(data, "Syntax error in telnet option: %s", head->data);
             result = CURLE_SETOPT_OPTION_SYNTAX;
           }
+          else {
+            tn->subopt_wsx = (unsigned short)x;
+            tn->subopt_wsy = (unsigned short)y;
+            tn->us_preferred[CURL_TELOPT_NAWS] = CURL_YES;
+          }
         }
         else
           result = CURLE_UNKNOWN_OPTION;
@@ -922,14 +933,13 @@ static CURLcode check_telnet_options(struct Curl_easy *data)
  * side.
  */
 
-static void suboption(struct Curl_easy *data)
+static void suboption(struct Curl_easy *data, struct TELNET *tn)
 {
   struct curl_slist *v;
   unsigned char temp[2048];
   ssize_t bytes_written;
   size_t len;
   int err;
-  struct TELNET *tn = data->req.p.telnet;
   struct connectdata *conn = data->conn;
 
   printsub(data, '<', (unsigned char *)tn->subbuffer, CURL_SB_LEN(tn) + 2);
@@ -1001,13 +1011,13 @@ static void suboption(struct Curl_easy *data)
  * Send suboption information to the server side.
  */
 
-static void sendsuboption(struct Curl_easy *data, int option)
+static void sendsuboption(struct Curl_easy *data,
+                          struct TELNET *tn, int option)
 {
   ssize_t bytes_written;
   int err;
   unsigned short x, y;
   unsigned char *uc1, *uc2;
-  struct TELNET *tn = data->req.p.telnet;
   struct connectdata *conn = data->conn;
 
   switch(option) {
@@ -1044,7 +1054,7 @@ static void sendsuboption(struct Curl_easy *data, int option)
     }
     /* ... then the window size with the send_telnet_data() function
        to deal with 0xFF cases ... */
-    send_telnet_data(data, (char *)tn->subbuffer + 3, 4);
+    send_telnet_data(data, tn, (char *)tn->subbuffer + 3, 4);
     /* ... and the footer */
     bytes_written = swrite(conn->sock[FIRSTSOCKET], tn->subbuffer + 7, 2);
     if(bytes_written < 0) {
@@ -1058,6 +1068,7 @@ static void sendsuboption(struct Curl_easy *data, int option)
 
 static
 CURLcode telrcv(struct Curl_easy *data,
+                struct TELNET *tn,
                 const unsigned char *inbuf, /* Data received from socket */
                 ssize_t count)              /* Number of bytes received */
 {
@@ -1065,17 +1076,16 @@ CURLcode telrcv(struct Curl_easy *data,
   CURLcode result;
   int in = 0;
   int startwrite = -1;
-  struct TELNET *tn = data->req.p.telnet;
-
-#define startskipping()                                       \
-  if(startwrite >= 0) {                                       \
-    result = Curl_client_write(data,                          \
-                               CLIENTWRITE_BODY,              \
-                               (char *)&inbuf[startwrite],    \
-                               in-startwrite);                \
-    if(result)                                                \
-      return result;                                          \
-  }                                                           \
+
+#define startskipping()                                          \
+  if(startwrite >= 0) {                                          \
+    result = Curl_client_write(data,                             \
+                               CLIENTWRITE_BODY,                 \
+                               (const char *)&inbuf[startwrite], \
+                               in-startwrite);                   \
+    if(result)                                                   \
+      return result;                                             \
+  }                                                              \
   startwrite = -1
 
 #define writebyte() \
@@ -1145,28 +1155,28 @@ CURLcode telrcv(struct Curl_easy *data,
       case CURL_TS_WILL:
         printoption(data, "RCVD", CURL_WILL, c);
         tn->please_negotiate = 1;
-        rec_will(data, c);
+        rec_will(data, tn, c);
         tn->telrcv_state = CURL_TS_DATA;
         break;
 
       case CURL_TS_WONT:
         printoption(data, "RCVD", CURL_WONT, c);
         tn->please_negotiate = 1;
-        rec_wont(data, c);
+        rec_wont(data, tn, c);
         tn->telrcv_state = CURL_TS_DATA;
         break;
 
       case CURL_TS_DO:
         printoption(data, "RCVD", CURL_DO, c);
         tn->please_negotiate = 1;
-        rec_do(data, c);
+        rec_do(data, tn, c);
         tn->telrcv_state = CURL_TS_DATA;
         break;
 
       case CURL_TS_DONT:
         printoption(data, "RCVD", CURL_DONT, c);
         tn->please_negotiate = 1;
-        rec_dont(data, c);
+        rec_dont(data, tn, c);
         tn->telrcv_state = CURL_TS_DATA;
         break;
 
@@ -1195,7 +1205,7 @@ CURLcode telrcv(struct Curl_easy *data,
             CURL_SB_TERM(tn);
 
             printoption(data, "In SUBOPTION processing, RCVD", CURL_IAC, c);
-            suboption(data);   /* handle sub-option */
+            suboption(data, tn);   /* handle sub-option */
             tn->telrcv_state = CURL_TS_IAC;
             goto process_iac;
           }
@@ -1207,7 +1217,7 @@ CURLcode telrcv(struct Curl_easy *data,
           CURL_SB_ACCUM(tn, CURL_SE);
           tn->subpointer -= 2;
           CURL_SB_TERM(tn);
-          suboption(data);   /* handle sub-option */
+          suboption(data, tn);   /* handle sub-option */
           tn->telrcv_state = CURL_TS_DATA;
         }
         break;
@@ -1220,6 +1230,7 @@ CURLcode telrcv(struct Curl_easy *data,
 
 /* Escape and send a telnet data block */
 static CURLcode send_telnet_data(struct Curl_easy *data,
+                                 struct TELNET *tn,
                                  char *buffer, ssize_t nread)
 {
   size_t i, outlen;
@@ -1228,7 +1239,6 @@ static CURLcode send_telnet_data(struct Curl_easy *data,
   size_t bytes_written;
   size_t total_written = 0;
   struct connectdata *conn = data->conn;
-  struct TELNET *tn = data->req.p.telnet;
 
   DEBUGASSERT(tn);
   DEBUGASSERT(nread > 0);
@@ -1237,17 +1247,17 @@ static CURLcode send_telnet_data(struct Curl_easy *data,
 
   if(memchr(buffer, CURL_IAC, nread)) {
     /* only use the escape buffer when necessary */
-    Curl_dyn_reset(&tn->out);
+    curlx_dyn_reset(&tn->out);
 
     for(i = 0; i < (size_t)nread && !result; i++) {
-      result = Curl_dyn_addn(&tn->out, &buffer[i], 1);
+      result = curlx_dyn_addn(&tn->out, &buffer[i], 1);
       if(!result && ((unsigned char)buffer[i] == CURL_IAC))
         /* IAC is FF in hex */
-        result = Curl_dyn_addn(&tn->out, "\xff", 1);
+        result = curlx_dyn_addn(&tn->out, "\xff", 1);
     }
 
-    outlen = Curl_dyn_len(&tn->out);
-    outbuf = Curl_dyn_uptr(&tn->out);
+    outlen = curlx_dyn_len(&tn->out);
+    outbuf = curlx_dyn_uptr(&tn->out);
   }
   else {
     outlen = (size_t)nread;
@@ -1278,16 +1288,9 @@ static CURLcode send_telnet_data(struct Curl_easy *data,
 static CURLcode telnet_done(struct Curl_easy *data,
                             CURLcode status, bool premature)
 {
-  struct TELNET *tn = data->req.p.telnet;
   (void)status; /* unused */
   (void)premature; /* not used */
-
-  if(!tn)
-    return CURLE_OK;
-
-  curl_slist_free_all(tn->telnet_vars);
-  tn->telnet_vars = NULL;
-  Curl_dyn_free(&tn->out);
+  Curl_meta_remove(data, CURL_META_TELNET_EASY);
   return CURLE_OK;
 }
 
@@ -1324,9 +1327,11 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
   if(result)
     return result;
 
-  tn = data->req.p.telnet;
+  tn = Curl_meta_get(data, CURL_META_TELNET_EASY);
+  if(!tn)
+    return CURLE_FAILED_INIT;
 
-  result = check_telnet_options(data);
+  result = check_telnet_options(data, tn);
   if(result)
     return result;
 
@@ -1419,7 +1424,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
           }
         }
 
-        result = send_telnet_data(data, buffer, readfile_read);
+        result = send_telnet_data(data, tn, buffer, readfile_read);
         if(result) {
           keepon = FALSE;
           break;
@@ -1437,7 +1442,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
         break;
       }
 
-      result = send_telnet_data(data, buffer, readfile_read);
+      result = send_telnet_data(data, tn, buffer, readfile_read);
       if(result) {
         keepon = FALSE;
         break;
@@ -1450,7 +1455,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
       events.lNetworkEvents = 0;
       if(WSAEnumNetworkEvents(sockfd, event_handle, &events) == SOCKET_ERROR) {
         err = SOCKERRNO;
-        if(err != EINPROGRESS) {
+        if(err != SOCKEINPROGRESS) {
           infof(data, "WSAEnumNetworkEvents failed (%d)", err);
           keepon = FALSE;
           result = CURLE_READ_ERROR;
@@ -1475,7 +1480,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
           break;
         }
 
-        result = telrcv(data, (unsigned char *) buffer, nread);
+        result = telrcv(data, tn, (unsigned char *) buffer, nread);
         if(result) {
           keepon = FALSE;
           break;
@@ -1485,7 +1490,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
            otherwise do not. We do not want to speak telnet with
            non-telnet servers, like POP or SMTP. */
         if(tn->please_negotiate && !tn->already_negotiated) {
-          negotiate(data);
+          telnet_negotiate(data, tn);
           tn->already_negotiated = 1;
         }
       }
@@ -1498,8 +1503,8 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
     }
 
     if(data->set.timeout) {
-      now = Curl_now();
-      if(Curl_timediff(now, conn->created) >= data->set.timeout) {
+      now = curlx_now();
+      if(curlx_timediff(now, conn->created) >= data->set.timeout) {
         failf(data, "Time-out");
         result = CURLE_OPERATION_TIMEDOUT;
         keepon = FALSE;
@@ -1555,7 +1560,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
           /* In test 1452, macOS sees a ECONNRESET sometimes? Is this the
            * telnet test server not shutting down the socket in a clean way?
            * Seems to be timing related, happens more on slow debug build */
-          if(data->state.os_errno == ECONNRESET) {
+          if(data->state.os_errno == SOCKECONNRESET) {
             DEBUGF(infof(data, "telnet_do, unexpected ECONNRESET on recv"));
           }
           break;
@@ -1570,7 +1575,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
         total_dl += nread;
         result = Curl_pgrsSetDownloadCounter(data, total_dl);
         if(!result)
-          result = telrcv(data, (unsigned char *)buffer, nread);
+          result = telrcv(data, tn, (unsigned char *)buffer, nread);
         if(result) {
           keepon = FALSE;
           break;
@@ -1580,7 +1585,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
            otherwise do not. We do not want to speak telnet with
            non-telnet servers, like POP or SMTP. */
         if(tn->please_negotiate && !tn->already_negotiated) {
-          negotiate(data);
+          telnet_negotiate(data, tn);
           tn->already_negotiated = 1;
         }
       }
@@ -1604,7 +1609,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
       }
 
       if(nread > 0) {
-        result = send_telnet_data(data, buffer, nread);
+        result = send_telnet_data(data, tn, buffer, nread);
         if(result) {
           keepon = FALSE;
           break;
@@ -1619,8 +1624,8 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
     } /* poll switch statement */
 
     if(data->set.timeout) {
-      now = Curl_now();
-      if(Curl_timediff(now, conn->created) >= data->set.timeout) {
+      now = curlx_now();
+      if(curlx_timediff(now, conn->created) >= data->set.timeout) {
         failf(data, "Time-out");
         result = CURLE_OPERATION_TIMEDOUT;
         keepon = FALSE;
diff --git a/extra/curl/curl-8.12.1/lib/telnet.h b/extra/curl/curl-8.14.1/lib/telnet.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/telnet.h
rename to extra/curl/curl-8.14.1/lib/telnet.h
diff --git a/extra/curl/curl-8.12.1/lib/tftp.c b/extra/curl/curl-8.14.1/lib/tftp.c
similarity index 92%
rename from extra/curl/curl-8.12.1/lib/tftp.c
rename to extra/curl/curl-8.14.1/lib/tftp.c
index 3f214d55b65b..e9f4e68eccb0 100644
--- a/extra/curl/curl-8.12.1/lib/tftp.c
+++ b/extra/curl/curl-8.14.1/lib/tftp.c
@@ -62,6 +62,7 @@
 #include "speedcheck.h"
 #include "select.h"
 #include "escape.h"
+#include "curlx/strparse.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -120,7 +121,10 @@ struct tftp_packet {
   unsigned char *data;
 };
 
-struct tftp_state_data {
+/* meta key for storing protocol meta at connection */
+#define CURL_META_TFTP_CONN   "meta:proto:tftp:conn"
+
+struct tftp_conn {
   tftp_state_t    state;
   tftp_mode_t     mode;
   tftp_error_t    error;
@@ -135,9 +139,9 @@ struct tftp_state_data {
   struct Curl_sockaddr_storage   remote_addr;
   curl_socklen_t  remote_addrlen;
   int             rbytes;
-  int             sbytes;
-  int             blksize;
-  int             requested_blksize;
+  size_t          sbytes;
+  unsigned int    blksize;
+  unsigned int    requested_blksize;
   unsigned short  block;
   struct tftp_packet rpacket;
   struct tftp_packet spacket;
@@ -145,12 +149,9 @@ struct tftp_state_data {
 
 
 /* Forward declarations */
-static CURLcode tftp_rx(struct tftp_state_data *state, tftp_event_t event);
-static CURLcode tftp_tx(struct tftp_state_data *state, tftp_event_t event);
+static CURLcode tftp_rx(struct tftp_conn *state, tftp_event_t event);
+static CURLcode tftp_tx(struct tftp_conn *state, tftp_event_t event);
 static CURLcode tftp_connect(struct Curl_easy *data, bool *done);
-static CURLcode tftp_disconnect(struct Curl_easy *data,
-                                struct connectdata *conn,
-                                bool dead_connection);
 static CURLcode tftp_do(struct Curl_easy *data, bool *done);
 static CURLcode tftp_done(struct Curl_easy *data,
                           CURLcode, bool premature);
@@ -180,7 +181,7 @@ const struct Curl_handler Curl_handler_tftp = {
   tftp_getsock,                         /* doing_getsock */
   ZERO_NULL,                            /* domore_getsock */
   ZERO_NULL,                            /* perform_getsock */
-  tftp_disconnect,                      /* disconnect */
+  ZERO_NULL,                            /* disconnect */
   ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
@@ -202,7 +203,7 @@ const struct Curl_handler Curl_handler_tftp = {
  *
  *
  **********************************************************/
-static CURLcode tftp_set_timeouts(struct tftp_state_data *state)
+static CURLcode tftp_set_timeouts(struct tftp_conn *state)
 {
   time_t maxtime, timeout;
   timediff_t timeout_ms;
@@ -309,7 +310,7 @@ static const char *tftp_option_get(const char *buf, size_t len,
   return &buf[loc];
 }
 
-static CURLcode tftp_parse_option_ack(struct tftp_state_data *state,
+static CURLcode tftp_parse_option_ack(struct tftp_conn *state,
                                       const char *ptr, int len)
 {
   const char *tmp = ptr;
@@ -330,19 +331,16 @@ static CURLcode tftp_parse_option_ack(struct tftp_state_data *state,
     infof(data, "got option=(%s) value=(%s)", option, value);
 
     if(checkprefix(TFTP_OPTION_BLKSIZE, option)) {
-      long blksize;
-
-      blksize = strtol(value, NULL, 10);
-
-      if(!blksize) {
-        failf(data, "invalid blocksize value in OACK packet");
-        return CURLE_TFTP_ILLEGAL;
-      }
-      if(blksize > TFTP_BLKSIZE_MAX) {
+      curl_off_t blksize;
+      if(curlx_str_number(&value, &blksize, TFTP_BLKSIZE_MAX)) {
         failf(data, "%s (%d)", "blksize is larger than max supported",
               TFTP_BLKSIZE_MAX);
         return CURLE_TFTP_ILLEGAL;
       }
+      if(!blksize) {
+        failf(data, "invalid blocksize value in OACK packet");
+        return CURLE_TFTP_ILLEGAL;
+      }
       else if(blksize < TFTP_BLKSIZE_MIN) {
         failf(data, "%s (%d)", "blksize is smaller than min supported",
               TFTP_BLKSIZE_MIN);
@@ -352,28 +350,27 @@ static CURLcode tftp_parse_option_ack(struct tftp_state_data *state,
         /* could realloc pkt buffers here, but the spec does not call out
          * support for the server requesting a bigger blksize than the client
          * requests */
-        failf(data, "%s (%ld)",
-              "server requested blksize larger than allocated", blksize);
+        failf(data, "server requested blksize larger than allocated (%"
+              CURL_FORMAT_CURL_OFF_T ")", blksize);
         return CURLE_TFTP_ILLEGAL;
       }
 
       state->blksize = (int)blksize;
-      infof(data, "%s (%d) %s (%d)", "blksize parsed from OACK",
-            state->blksize, "requested", state->requested_blksize);
+      infof(data, "blksize parsed from OACK (%d) requested (%d)",
+            state->blksize, state->requested_blksize);
     }
     else if(checkprefix(TFTP_OPTION_TSIZE, option)) {
-      long tsize = 0;
-
-      tsize = strtol(value, NULL, 10);
-      infof(data, "%s (%ld)", "tsize parsed from OACK", tsize);
-
+      curl_off_t tsize = 0;
       /* tsize should be ignored on upload: Who cares about the size of the
          remote file? */
-      if(!data->state.upload) {
+      if(!data->state.upload &&
+         !curlx_str_number(&value, &tsize, CURL_OFF_T_MAX)) {
         if(!tsize) {
           failf(data, "invalid tsize -:%s:- value in OACK packet", value);
           return CURLE_TFTP_ILLEGAL;
         }
+        infof(data, "tsize parsed from OACK (%" CURL_FORMAT_CURL_OFF_T ")",
+              tsize);
         Curl_pgrsSetDownloadSize(data, tsize);
       }
     }
@@ -382,7 +379,7 @@ static CURLcode tftp_parse_option_ack(struct tftp_state_data *state,
   return CURLE_OK;
 }
 
-static CURLcode tftp_option_add(struct tftp_state_data *state, size_t *csize,
+static CURLcode tftp_option_add(struct tftp_conn *state, size_t *csize,
                                 char *buf, const char *option)
 {
   if(( strlen(option) + *csize + 1) > (size_t)state->blksize)
@@ -392,7 +389,7 @@ static CURLcode tftp_option_add(struct tftp_state_data *state, size_t *csize,
   return CURLE_OK;
 }
 
-static CURLcode tftp_connect_for_tx(struct tftp_state_data *state,
+static CURLcode tftp_connect_for_tx(struct tftp_conn *state,
                                     tftp_event_t event)
 {
   CURLcode result;
@@ -408,7 +405,7 @@ static CURLcode tftp_connect_for_tx(struct tftp_state_data *state,
   return tftp_tx(state, event);
 }
 
-static CURLcode tftp_connect_for_rx(struct tftp_state_data *state,
+static CURLcode tftp_connect_for_rx(struct tftp_conn *state,
                                     tftp_event_t event)
 {
   CURLcode result;
@@ -424,7 +421,7 @@ static CURLcode tftp_connect_for_rx(struct tftp_state_data *state,
   return tftp_rx(state, event);
 }
 
-static CURLcode tftp_send_first(struct tftp_state_data *state,
+static CURLcode tftp_send_first(struct tftp_conn *state,
                                 tftp_event_t event)
 {
   size_t sbytes;
@@ -523,9 +520,14 @@ static CURLcode tftp_send_first(struct tftp_state_data *state,
 
     /* the typecase for the 3rd argument is mostly for systems that do
        not have a size_t argument, like older unixes that want an 'int' */
+#ifdef __AMIGA__
+#define CURL_SENDTO_ARG5(x) CURL_UNCONST(x)
+#else
+#define CURL_SENDTO_ARG5(x) (x)
+#endif
     senddata = sendto(state->sockfd, (void *)state->spacket.data,
                       (SEND_TYPE_ARG3)sbytes, 0,
-                     (struct sockaddr *)&data->conn->remote_addr->curl_sa_addr,
+                      CURL_SENDTO_ARG5(&data->conn->remote_addr->curl_sa_addr),
                       (curl_socklen_t)data->conn->remote_addr->addrlen);
     if(senddata != (ssize_t)sbytes) {
       char buffer[STRERROR_LEN];
@@ -574,8 +576,7 @@ static CURLcode tftp_send_first(struct tftp_state_data *state,
  * Event handler for the RX state
  *
  **********************************************************/
-static CURLcode tftp_rx(struct tftp_state_data *state,
-                        tftp_event_t event)
+static CURLcode tftp_rx(struct tftp_conn *state, tftp_event_t event)
 {
   ssize_t sbytes;
   int rblock;
@@ -697,7 +698,7 @@ static CURLcode tftp_rx(struct tftp_state_data *state,
  * Event handler for the TX state
  *
  **********************************************************/
-static CURLcode tftp_tx(struct tftp_state_data *state, tftp_event_t event)
+static CURLcode tftp_tx(struct tftp_conn *state, tftp_event_t event)
 {
   struct Curl_easy *data = state->data;
   ssize_t sbytes;
@@ -776,7 +777,7 @@ static CURLcode tftp_tx(struct tftp_state_data *state, tftp_event_t event)
                                 &cb, &eos);
       if(result)
         return result;
-      state->sbytes += (int)cb;
+      state->sbytes += cb;
       bufptr += cb;
     } while(state->sbytes < state->blksize && cb);
 
@@ -899,7 +900,7 @@ static CURLcode tftp_translate_code(tftp_error_t error)
  * The tftp state machine event dispatcher
  *
  **********************************************************/
-static CURLcode tftp_state_machine(struct tftp_state_data *state,
+static CURLcode tftp_state_machine(struct tftp_conn *state,
                                    tftp_event_t event)
 {
   CURLcode result = CURLE_OK;
@@ -931,28 +932,14 @@ static CURLcode tftp_state_machine(struct tftp_state_data *state,
   return result;
 }
 
-/**********************************************************
- *
- * tftp_disconnect
- *
- * The disconnect callback
- *
- **********************************************************/
-static CURLcode tftp_disconnect(struct Curl_easy *data,
-                                struct connectdata *conn, bool dead_connection)
+static void tftp_conn_dtor(void *key, size_t klen, void *entry)
 {
-  struct tftp_state_data *state = conn->proto.tftpc;
-  (void) data;
-  (void) dead_connection;
-
-  /* done, free dynamically allocated pkt buffers */
-  if(state) {
-    Curl_safefree(state->rpacket.data);
-    Curl_safefree(state->spacket.data);
-    free(state);
-  }
-
-  return CURLE_OK;
+  struct tftp_conn *state = entry;
+  (void)key;
+  (void)klen;
+  Curl_safefree(state->rpacket.data);
+  Curl_safefree(state->spacket.data);
+  free(state);
 }
 
 /**********************************************************
@@ -964,15 +951,16 @@ static CURLcode tftp_disconnect(struct Curl_easy *data,
  **********************************************************/
 static CURLcode tftp_connect(struct Curl_easy *data, bool *done)
 {
-  struct tftp_state_data *state;
+  struct tftp_conn *state;
   int blksize;
   int need_blksize;
   struct connectdata *conn = data->conn;
 
   blksize = TFTP_BLKSIZE_DEFAULT;
 
-  state = conn->proto.tftpc = calloc(1, sizeof(struct tftp_state_data));
-  if(!state)
+  state = calloc(1, sizeof(*state));
+  if(!state ||
+     Curl_conn_meta_set(conn, CURL_META_TFTP_CONN, state, tftp_conn_dtor))
     return CURLE_OUT_OF_MEMORY;
 
   /* alloc pkt buffers based on specified blksize */
@@ -1059,7 +1047,7 @@ static CURLcode tftp_done(struct Curl_easy *data, CURLcode status,
 {
   CURLcode result = CURLE_OK;
   struct connectdata *conn = data->conn;
-  struct tftp_state_data *state = conn->proto.tftpc;
+  struct tftp_conn *state = Curl_conn_meta_get(conn, CURL_META_TFTP_CONN);
 
   (void)status; /* unused */
   (void)premature; /* not used */
@@ -1096,12 +1084,11 @@ static int tftp_getsock(struct Curl_easy *data,
  * Called once select fires and data is ready on the socket
  *
  **********************************************************/
-static CURLcode tftp_receive_packet(struct Curl_easy *data)
+static CURLcode tftp_receive_packet(struct Curl_easy *data,
+                                    struct tftp_conn *state)
 {
   curl_socklen_t        fromlen;
   CURLcode              result = CURLE_OK;
-  struct connectdata *conn = data->conn;
-  struct tftp_state_data *state = conn->proto.tftpc;
 
   /* Receive the packet */
   fromlen = sizeof(state->remote_addr);
@@ -1180,12 +1167,10 @@ static CURLcode tftp_receive_packet(struct Curl_easy *data)
  * Check if timeouts have been reached
  *
  **********************************************************/
-static timediff_t tftp_state_timeout(struct Curl_easy *data,
+static timediff_t tftp_state_timeout(struct tftp_conn *state,
                                      tftp_event_t *event)
 {
   time_t current;
-  struct connectdata *conn = data->conn;
-  struct tftp_state_data *state = conn->proto.tftpc;
   timediff_t timeout_ms;
 
   if(event)
@@ -1220,11 +1205,14 @@ static CURLcode tftp_multi_statemach(struct Curl_easy *data, bool *done)
   tftp_event_t event;
   CURLcode result = CURLE_OK;
   struct connectdata *conn = data->conn;
-  struct tftp_state_data *state = conn->proto.tftpc;
-  timediff_t timeout_ms = tftp_state_timeout(data, &event);
+  struct tftp_conn *state = Curl_conn_meta_get(conn, CURL_META_TFTP_CONN);
+  timediff_t timeout_ms;
 
   *done = FALSE;
+  if(!state)
+    return CURLE_FAILED_INIT;
 
+  timeout_ms = tftp_state_timeout(state, &event);
   if(timeout_ms < 0) {
     failf(data, "TFTP response timeout");
     return CURLE_OPERATION_TIMEDOUT;
@@ -1250,7 +1238,7 @@ static CURLcode tftp_multi_statemach(struct Curl_easy *data, bool *done)
       state->event = TFTP_EVENT_ERROR;
     }
     else if(rc) {
-      result = tftp_receive_packet(data);
+      result = tftp_receive_packet(data, state);
       if(result)
         return result;
       result = tftp_state_machine(state, state->event);
@@ -1289,7 +1277,7 @@ static CURLcode tftp_doing(struct Curl_easy *data, bool *dophase_done)
     if(Curl_pgrsUpdate(data))
       result = CURLE_ABORTED_BY_CALLBACK;
     else
-      result = Curl_speedcheck(data, Curl_now());
+      result = Curl_speedcheck(data, curlx_now());
   }
   return result;
 }
@@ -1305,9 +1293,11 @@ static CURLcode tftp_perform(struct Curl_easy *data, bool *dophase_done)
 {
   CURLcode result = CURLE_OK;
   struct connectdata *conn = data->conn;
-  struct tftp_state_data *state = conn->proto.tftpc;
+  struct tftp_conn *state = Curl_conn_meta_get(conn, CURL_META_TFTP_CONN);
 
   *dophase_done = FALSE;
+  if(!state)
+    return CURLE_FAILED_INIT;
 
   result = tftp_state_machine(state, TFTP_EVENT_INIT);
 
@@ -1335,21 +1325,21 @@ static CURLcode tftp_perform(struct Curl_easy *data, bool *dophase_done)
 
 static CURLcode tftp_do(struct Curl_easy *data, bool *done)
 {
-  struct tftp_state_data *state;
-  CURLcode result;
   struct connectdata *conn = data->conn;
+  struct tftp_conn *state = Curl_conn_meta_get(conn, CURL_META_TFTP_CONN);
+  CURLcode result;
 
   *done = FALSE;
 
-  if(!conn->proto.tftpc) {
+  if(!state) {
     result = tftp_connect(data, done);
     if(result)
       return result;
-  }
 
-  state = conn->proto.tftpc;
-  if(!state)
-    return CURLE_TFTP_ILLEGAL;
+    state = Curl_conn_meta_get(conn, CURL_META_TFTP_CONN);
+    if(!state)
+      return CURLE_TFTP_ILLEGAL;
+  }
 
   result = tftp_perform(data, done);
 
diff --git a/extra/curl/curl-8.12.1/lib/tftp.h b/extra/curl/curl-8.14.1/lib/tftp.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/tftp.h
rename to extra/curl/curl-8.14.1/lib/tftp.h
diff --git a/extra/curl/curl-8.12.1/lib/transfer.c b/extra/curl/curl-8.14.1/lib/transfer.c
similarity index 98%
rename from extra/curl/curl-8.12.1/lib/transfer.c
rename to extra/curl/curl-8.14.1/lib/transfer.c
index 742828a763b2..ef7d7f19af4b 100644
--- a/extra/curl/curl-8.12.1/lib/transfer.c
+++ b/extra/curl/curl-8.14.1/lib/transfer.c
@@ -23,7 +23,6 @@
  ***************************************************************************/
 
 #include "curl_setup.h"
-#include "strtoofft.h"
 
 #ifdef HAVE_NETINET_IN_H
 #include <netinet/in.h>
@@ -40,7 +39,9 @@
 #ifdef HAVE_SYS_IOCTL_H
 #include <sys/ioctl.h>
 #endif
+#ifndef UNDER_CE
 #include <signal.h>
+#endif
 
 #ifdef HAVE_SYS_PARAM_H
 #include <sys/param.h>
@@ -469,13 +470,13 @@ CURLcode Curl_sendrecv(struct Curl_easy *data, struct curltime *nowp)
         failf(data, "Operation timed out after %" FMT_TIMEDIFF_T
               " milliseconds with %" FMT_OFF_T " out of %"
               FMT_OFF_T " bytes received",
-              Curl_timediff(*nowp, data->progress.t_startsingle),
+              curlx_timediff(*nowp, data->progress.t_startsingle),
               k->bytecount, k->size);
       }
       else {
         failf(data, "Operation timed out after %" FMT_TIMEDIFF_T
               " milliseconds with %" FMT_OFF_T " bytes received",
-              Curl_timediff(*nowp, data->progress.t_startsingle),
+              curlx_timediff(*nowp, data->progress.t_startsingle),
               k->bytecount);
       }
       result = CURLE_OPERATION_TIMEDOUT;
@@ -571,8 +572,9 @@ CURLcode Curl_pretransfer(struct Curl_easy *data)
   data->state.followlocation = 0; /* reset the location-follow counter */
   data->state.this_is_a_follow = FALSE; /* reset this */
   data->state.errorbuf = FALSE; /* no error has occurred */
-  data->state.httpwant = data->set.httpwant;
-  data->state.httpversion = 0;
+#ifndef CURL_DISABLE_HTTP
+  Curl_http_neg_init(data, &data->state.http_neg);
+#endif
   data->state.authproblem = FALSE;
   data->state.authhost.want = data->set.httpauth;
   data->state.authproxy.want = data->set.proxyauth;
@@ -654,7 +656,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data)
    * protocol.
    */
   if(data->set.str[STRING_USERAGENT]) {
-    Curl_safefree(data->state.aptr.uagent);
+    free(data->state.aptr.uagent);
     data->state.aptr.uagent =
       aprintf("User-Agent: %s\r\n", data->set.str[STRING_USERAGENT]);
     if(!data->state.aptr.uagent)
@@ -880,6 +882,11 @@ CURLcode Curl_xfer_write_resp(struct Curl_easy *data,
   return result;
 }
 
+bool Curl_xfer_write_is_paused(struct Curl_easy *data)
+{
+  return Curl_cwriter_is_paused(data);
+}
+
 CURLcode Curl_xfer_write_resp_hd(struct Curl_easy *data,
                                  const char *hd0, size_t hdlen, bool is_eos)
 {
diff --git a/extra/curl/curl-8.12.1/lib/transfer.h b/extra/curl/curl-8.14.1/lib/transfer.h
similarity index 97%
rename from extra/curl/curl-8.12.1/lib/transfer.h
rename to extra/curl/curl-8.14.1/lib/transfer.h
index b67f8a894742..2c355e0e9d81 100644
--- a/extra/curl/curl-8.12.1/lib/transfer.h
+++ b/extra/curl/curl-8.14.1/lib/transfer.h
@@ -55,9 +55,11 @@ CURLcode Curl_xfer_write_resp(struct Curl_easy *data,
                               const char *buf, size_t blen,
                               bool is_eos);
 
+bool Curl_xfer_write_is_paused(struct Curl_easy *data);
+
 /**
  * Write a single "header" line from a server response.
- * @param hd0      the 0-terminated, single header line
+ * @param hd0      the null-terminated, single header line
  * @param hdlen    the length of the header line
  * @param is_eos   TRUE iff this is the end of the response
  */
diff --git a/extra/curl/curl-8.14.1/lib/uint-bset.c b/extra/curl/curl-8.14.1/lib/uint-bset.c
new file mode 100644
index 000000000000..2560b3736341
--- /dev/null
+++ b/extra/curl/curl-8.14.1/lib/uint-bset.c
@@ -0,0 +1,238 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+#include "uint-bset.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#ifdef DEBUGBUILD
+#define CURL_UINT_BSET_MAGIC  0x62757473
+#endif
+
+void Curl_uint_bset_init(struct uint_bset *bset)
+{
+  memset(bset, 0, sizeof(*bset));
+#ifdef DEBUGBUILD
+  bset->init = CURL_UINT_BSET_MAGIC;
+#endif
+}
+
+
+CURLcode Curl_uint_bset_resize(struct uint_bset *bset, unsigned int nmax)
+{
+  unsigned int nslots = (nmax + 63) / 64;
+
+  DEBUGASSERT(bset->init == CURL_UINT_BSET_MAGIC);
+  if(nslots != bset->nslots) {
+    curl_uint64_t *slots = calloc(nslots, sizeof(curl_uint64_t));
+    if(!slots)
+      return CURLE_OUT_OF_MEMORY;
+
+    if(bset->slots) {
+      memcpy(slots, bset->slots,
+             (CURLMIN(nslots, bset->nslots) * sizeof(curl_uint64_t)));
+      free(bset->slots);
+    }
+    bset->slots = slots;
+    bset->nslots = nslots;
+  }
+  return CURLE_OK;
+}
+
+
+void Curl_uint_bset_destroy(struct uint_bset *bset)
+{
+  DEBUGASSERT(bset->init == CURL_UINT_BSET_MAGIC);
+  free(bset->slots);
+  memset(bset, 0, sizeof(*bset));
+}
+
+
+unsigned int Curl_uint_bset_capacity(struct uint_bset *bset)
+{
+  return bset->nslots * 64;
+}
+
+
+unsigned int Curl_uint_bset_count(struct uint_bset *bset)
+{
+  unsigned int i;
+  unsigned int n = 0;
+  for(i = 0; i < bset->nslots; ++i) {
+    if(bset->slots[i])
+      n += CURL_POPCOUNT64(bset->slots[i]);
+  }
+  return n;
+}
+
+
+bool Curl_uint_bset_empty(struct uint_bset *bset)
+{
+  unsigned int i;
+  for(i = 0; i < bset->nslots; ++i) {
+    if(bset->slots[i])
+      return FALSE;
+  }
+  return TRUE;
+}
+
+
+void Curl_uint_bset_clear(struct uint_bset *bset)
+{
+  if(bset->nslots)
+    memset(bset->slots, 0, bset->nslots * sizeof(curl_uint64_t));
+}
+
+
+bool Curl_uint_bset_add(struct uint_bset *bset, unsigned int i)
+{
+  unsigned int islot = i / 64;
+  if(islot >= bset->nslots)
+    return FALSE;
+  bset->slots[islot] |= ((curl_uint64_t)1 << (i % 64));
+  return TRUE;
+}
+
+
+void Curl_uint_bset_remove(struct uint_bset *bset, unsigned int i)
+{
+  size_t islot = i / 64;
+  if(islot < bset->nslots)
+    bset->slots[islot] &= ~((curl_uint64_t)1 << (i % 64));
+}
+
+
+bool Curl_uint_bset_contains(struct uint_bset *bset, unsigned int i)
+{
+  unsigned int islot = i / 64;
+  if(islot >= bset->nslots)
+    return FALSE;
+  return (bset->slots[islot] & ((curl_uint64_t)1 << (i % 64))) != 0;
+}
+
+
+bool Curl_uint_bset_first(struct uint_bset *bset, unsigned int *pfirst)
+{
+  unsigned int i;
+  for(i = 0; i < bset->nslots; ++i) {
+    if(bset->slots[i]) {
+      *pfirst = (i * 64) + CURL_CTZ64(bset->slots[i]);
+      return TRUE;
+    }
+  }
+  *pfirst = UINT_MAX; /* a value we cannot store */
+  return FALSE;
+}
+
+bool Curl_uint_bset_next(struct uint_bset *bset, unsigned int last,
+                         unsigned int *pnext)
+{
+  unsigned int islot;
+  curl_uint64_t x;
+
+  ++last; /* look for number one higher than last */
+  islot = last / 64; /* the slot this would be in */
+  if(islot < bset->nslots) {
+    /* shift away the bits we already iterated in this slot */
+    x = (bset->slots[islot] >> (last % 64));
+    if(x) {
+      /* more bits set, next is `last` + trailing0s of the shifted slot */
+      *pnext = last + CURL_CTZ64(x);
+      return TRUE;
+    }
+    /* no more bits set in the last slot, scan forward */
+    for(islot = islot + 1; islot < bset->nslots; ++islot) {
+      if(bset->slots[islot]) {
+        *pnext = (islot * 64) + CURL_CTZ64(bset->slots[islot]);
+        return TRUE;
+      }
+    }
+  }
+  *pnext = UINT_MAX; /* a value we cannot store */
+  return FALSE;
+}
+
+#ifdef CURL_POPCOUNT64_IMPLEMENT
+unsigned int Curl_popcount64(curl_uint64_t x)
+{
+  /* Compute the "Hamming Distance" between 'x' and 0,
+   * which is the number of set bits in 'x'.
+   * See: https://en.wikipedia.org/wiki/Hamming_weight */
+  const curl_uint64_t m1  = CURL_OFF_TU_C(0x5555555555555555); /* 0101+ */
+  const curl_uint64_t m2  = CURL_OFF_TU_C(0x3333333333333333); /* 00110011+ */
+  const curl_uint64_t m4  = CURL_OFF_TU_C(0x0f0f0f0f0f0f0f0f); /* 00001111+ */
+   /* 1 + 256^1 + 256^2 + 256^3 + ... + 256^7 */
+  const curl_uint64_t h01 = CURL_OFF_TU_C(0x0101010101010101);
+  x -= (x >> 1) & m1;             /* replace every 2 bits with bits present */
+  x = (x & m2) + ((x >> 2) & m2); /* replace every nibble with bits present */
+  x = (x + (x >> 4)) & m4;        /* replace every byte with bits present */
+  /* top 8 bits of x + (x<<8) + (x<<16) + (x<<24) + ... which makes the
+   * top byte the sum of all individual 8 bytes, throw away the rest */
+  return (unsigned int)((x * h01) >> 56);
+}
+#endif /* CURL_POPCOUNT64_IMPLEMENT */
+
+
+#ifdef CURL_CTZ64_IMPLEMENT
+unsigned int Curl_ctz64(curl_uint64_t x)
+{
+  /* count trailing zeros in a curl_uint64_t.
+   * divide and conquer to find the number of lower 0 bits */
+  const curl_uint64_t ml32 = CURL_OFF_TU_C(0xFFFFFFFF); /* lower 32 bits */
+  const curl_uint64_t ml16 = CURL_OFF_TU_C(0x0000FFFF); /* lower 16 bits */
+  const curl_uint64_t ml8  = CURL_OFF_TU_C(0x000000FF); /* lower 8 bits */
+  const curl_uint64_t ml4  = CURL_OFF_TU_C(0x0000000F); /* lower 4 bits */
+  const curl_uint64_t ml2  = CURL_OFF_TU_C(0x00000003); /* lower 2 bits */
+  unsigned int n;
+
+  if(!x)
+    return 64;
+  n = 1;
+  if(!(x & ml32)) {
+    n = n + 32;
+    x = x >> 32;
+  }
+  if(!(x & ml16)) {
+    n = n + 16;
+    x = x >> 16;
+  }
+  if(!(x & ml8)) {
+    n = n + 8;
+    x = x >> 8;
+  }
+  if(!(x & ml4)) {
+    n = n + 4;
+    x = x >> 4;
+  }
+  if(!(x & ml2)) {
+    n = n + 2;
+    x = x >> 2;
+  }
+  return n - (unsigned int)(x & 1);
+}
+#endif /* CURL_CTZ64_IMPLEMENT */
diff --git a/extra/curl/curl-8.14.1/lib/uint-bset.h b/extra/curl/curl-8.14.1/lib/uint-bset.h
new file mode 100644
index 000000000000..d998dccdfe5e
--- /dev/null
+++ b/extra/curl/curl-8.14.1/lib/uint-bset.h
@@ -0,0 +1,114 @@
+#ifndef HEADER_CURL_UINT_BSET_H
+#define HEADER_CURL_UINT_BSET_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "curl_setup.h"
+
+#include <curl/curl.h>
+
+/* A bitset for unsigned int values.
+ * It can hold the numbers from 0 - (nmax - 1),
+ * rounded to the next 64 multiple.
+ *
+ * Optimized for high efficiency in adding/removing numbers.
+ * Efficient storage when the set is (often) relatively full.
+ *
+ * If the set's cardinality is only expected to be a fraction of nmax,
+ * uint_spbset offers a "sparse" variant with more memory efficiency at
+ * the price of slightly slower operations.
+ */
+
+struct uint_bset {
+  curl_uint64_t *slots;
+  unsigned int nslots;
+#ifdef DEBUGBUILD
+  int init;
+#endif
+};
+
+/* Initialize the bitset with capacity 0. */
+void Curl_uint_bset_init(struct uint_bset *bset);
+
+/* Resize the bitset capacity to hold numbers from 0 to `nmax`,
+ * which rounds up `nmax` to the next multiple of 64. */
+CURLcode Curl_uint_bset_resize(struct uint_bset *bset, unsigned int nmax);
+
+/* Destroy the bitset, freeing all resources. */
+void Curl_uint_bset_destroy(struct uint_bset *bset);
+
+/* Get the bitset capacity, e.g. can hold numbers from 0 to capacity - 1. */
+unsigned int Curl_uint_bset_capacity(struct uint_bset *bset);
+
+/* Get the cardinality of the bitset, e.g. numbers present in the set. */
+unsigned int Curl_uint_bset_count(struct uint_bset *bset);
+
+/* TRUE of bitset is empty */
+bool Curl_uint_bset_empty(struct uint_bset *bset);
+
+/* Clear the bitset, making it empty. */
+void Curl_uint_bset_clear(struct uint_bset *bset);
+
+/* Add the number `i` to the bitset. Return FALSE if the number is
+ * outside the set's capacity.
+ * Numbers can be added more than once, without making a difference. */
+bool Curl_uint_bset_add(struct uint_bset *bset, unsigned int i);
+
+/* Remove the number `i` from the bitset. */
+void Curl_uint_bset_remove(struct uint_bset *bset, unsigned int i);
+
+/* Return TRUE if the bitset contains number `i`. */
+bool Curl_uint_bset_contains(struct uint_bset *bset, unsigned int i);
+
+/* Get the first number in the bitset, e.g. the smallest.
+ * Returns FALSE when the bitset is empty. */
+bool Curl_uint_bset_first(struct uint_bset *bset, unsigned int *pfirst);
+
+/* Get the next number in the bitset, following `last` in natural order.
+ * Put another way, this is the smallest number greater than `last` in
+ * the bitset. `last` does not have to be present in the set.
+ *
+ * Returns FALSE when no such number is in the set.
+ *
+ * This allows to iterate the set while being modified:
+ * - added numbers higher than 'last' will be picked up by the iteration.
+ * - added numbers lower than 'last' will not show up.
+ * - removed numbers lower or equal to 'last' will not show up.
+ * - removed numbers higher than 'last' will not be visited. */
+bool Curl_uint_bset_next(struct uint_bset *bset, unsigned int last,
+                         unsigned int *pnext);
+
+
+#ifndef CURL_POPCOUNT64
+#define CURL_POPCOUNT64(x)   Curl_popcount64(x)
+#define CURL_POPCOUNT64_IMPLEMENT
+unsigned int Curl_popcount64(curl_uint64_t x);
+#endif /* !CURL_POPCOUNT64 */
+
+#ifndef CURL_CTZ64
+#define CURL_CTZ64(x)  Curl_ctz64(x)
+#define CURL_CTZ64_IMPLEMENT
+unsigned int Curl_ctz64(curl_uint64_t x);
+#endif /* !CURL_CTZ64 */
+
+#endif /* HEADER_CURL_UINT_BSET_H */
diff --git a/extra/curl/curl-8.14.1/lib/uint-hash.c b/extra/curl/curl-8.14.1/lib/uint-hash.c
new file mode 100644
index 000000000000..afeb684d0cab
--- /dev/null
+++ b/extra/curl/curl-8.14.1/lib/uint-hash.c
@@ -0,0 +1,246 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include <curl/curl.h>
+
+#include "uint-hash.h"
+#include "curl_memory.h"
+
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/* random patterns for API verification */
+#ifdef DEBUGBUILD
+#define CURL_UINTHASHINIT 0x7117e779
+#endif
+
+static unsigned int uint_hash_hash(unsigned int id, unsigned int slots)
+{
+  return (id % slots);
+}
+
+
+struct uint_hash_entry {
+  struct uint_hash_entry *next;
+  void   *value;
+  unsigned int id;
+};
+
+void Curl_uint_hash_init(struct uint_hash *h,
+                         unsigned int slots,
+                         Curl_uint_hash_dtor *dtor)
+{
+  DEBUGASSERT(h);
+  DEBUGASSERT(slots);
+
+  h->table = NULL;
+  h->dtor = dtor;
+  h->size = 0;
+  h->slots = slots;
+#ifdef DEBUGBUILD
+  h->init = CURL_UINTHASHINIT;
+#endif
+}
+
+static struct uint_hash_entry *uint_hash_mk_entry(unsigned int id, void *value)
+{
+  struct uint_hash_entry *e;
+
+  /* allocate the struct for the hash entry */
+  e = malloc(sizeof(*e));
+  if(e) {
+    e->id = id;
+    e->next = NULL;
+    e->value = value;
+  }
+  return e;
+}
+
+static void uint_hash_entry_clear(struct uint_hash *h,
+                                  struct uint_hash_entry *e)
+{
+  DEBUGASSERT(h);
+  DEBUGASSERT(e);
+  if(e->value) {
+    if(h->dtor)
+      h->dtor(e->id, e->value);
+    e->value = NULL;
+  }
+}
+
+static void uint_hash_entry_destroy(struct uint_hash *h,
+                                    struct uint_hash_entry *e)
+{
+  uint_hash_entry_clear(h, e);
+  free(e);
+}
+
+static void uint_hash_entry_unlink(struct uint_hash *h,
+                                   struct uint_hash_entry **he_anchor,
+                                   struct uint_hash_entry *he)
+{
+  *he_anchor = he->next;
+  --h->size;
+}
+
+static void uint_hash_elem_link(struct uint_hash *h,
+                                 struct uint_hash_entry **he_anchor,
+                                 struct uint_hash_entry *he)
+{
+  he->next = *he_anchor;
+  *he_anchor = he;
+  ++h->size;
+}
+
+#define CURL_UINT_HASH_SLOT(h,id)  h->table[uint_hash_hash(id, h->slots)]
+#define CURL_UINT_HASH_SLOT_ADDR(h,id) &CURL_UINT_HASH_SLOT(h,id)
+
+bool Curl_uint_hash_set(struct uint_hash *h, unsigned int id, void *value)
+{
+  struct uint_hash_entry *he, **slot;
+
+  DEBUGASSERT(h);
+  DEBUGASSERT(h->slots);
+  DEBUGASSERT(h->init == CURL_UINTHASHINIT);
+  if(!h->table) {
+    h->table = calloc(h->slots, sizeof(*he));
+    if(!h->table)
+      return FALSE; /* OOM */
+  }
+
+  slot = CURL_UINT_HASH_SLOT_ADDR(h, id);
+  for(he = *slot; he; he = he->next) {
+    if(he->id == id) {
+      /* existing key entry, overwrite by clearing old pointer */
+      uint_hash_entry_clear(h, he);
+      he->value = value;
+      return TRUE;
+    }
+  }
+
+  he = uint_hash_mk_entry(id, value);
+  if(!he)
+    return FALSE; /* OOM */
+
+  uint_hash_elem_link(h, slot, he);
+  return TRUE;
+}
+
+bool Curl_uint_hash_remove(struct uint_hash *h, unsigned int id)
+{
+  DEBUGASSERT(h);
+  DEBUGASSERT(h->slots);
+  DEBUGASSERT(h->init == CURL_UINTHASHINIT);
+  if(h->table) {
+    struct uint_hash_entry *he, **he_anchor;
+
+    he_anchor = CURL_UINT_HASH_SLOT_ADDR(h, id);
+    while(*he_anchor) {
+      he = *he_anchor;
+      if(id == he->id) {
+        uint_hash_entry_unlink(h, he_anchor, he);
+        uint_hash_entry_destroy(h, he);
+        return TRUE;
+      }
+      he_anchor = &he->next;
+    }
+  }
+  return FALSE;
+}
+
+void *Curl_uint_hash_get(struct uint_hash *h, unsigned int id)
+{
+  DEBUGASSERT(h);
+  DEBUGASSERT(h->init == CURL_UINTHASHINIT);
+  if(h->table) {
+    struct uint_hash_entry *he;
+    DEBUGASSERT(h->slots);
+    he = CURL_UINT_HASH_SLOT(h, id);
+    while(he) {
+      if(id == he->id) {
+        return he->value;
+      }
+      he = he->next;
+    }
+  }
+  return NULL;
+}
+
+static void uint_hash_clear(struct uint_hash *h)
+{
+  if(h && h->table) {
+    struct uint_hash_entry *he, **he_anchor;
+    size_t i;
+    DEBUGASSERT(h->init == CURL_UINTHASHINIT);
+    for(i = 0; i < h->slots; ++i) {
+      he_anchor = &h->table[i];
+      while(*he_anchor) {
+        he = *he_anchor;
+        uint_hash_entry_unlink(h, he_anchor, he);
+        uint_hash_entry_destroy(h, he);
+      }
+    }
+  }
+}
+
+void Curl_uint_hash_clear(struct uint_hash *h)
+{
+  uint_hash_clear(h);
+}
+
+void Curl_uint_hash_destroy(struct uint_hash *h)
+{
+  DEBUGASSERT(h->init == CURL_UINTHASHINIT);
+  if(h->table) {
+    uint_hash_clear(h);
+    Curl_safefree(h->table);
+  }
+  DEBUGASSERT(h->size == 0);
+  h->slots = 0;
+}
+
+unsigned int Curl_uint_hash_count(struct uint_hash *h)
+{
+  DEBUGASSERT(h->init == CURL_UINTHASHINIT);
+  return h->size;
+}
+
+void Curl_uint_hash_visit(struct uint_hash *h,
+                          Curl_uint_hash_visit_cb *cb,
+                          void *user_data)
+{
+  if(h && h->table && cb) {
+    struct uint_hash_entry *he;
+    size_t i;
+    DEBUGASSERT(h->init == CURL_UINTHASHINIT);
+    for(i = 0; i < h->slots; ++i) {
+      for(he = h->table[i]; he; he = he->next) {
+        if(!cb(he->id, he->value, user_data))
+          return;
+      }
+    }
+  }
+}
diff --git a/extra/curl/curl-8.14.1/lib/uint-hash.h b/extra/curl/curl-8.14.1/lib/uint-hash.h
new file mode 100644
index 000000000000..1b52dba4c470
--- /dev/null
+++ b/extra/curl/curl-8.14.1/lib/uint-hash.h
@@ -0,0 +1,68 @@
+#ifndef HEADER_CURL_UINT_HASH_H
+#define HEADER_CURL_UINT_HASH_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include <stddef.h>
+
+#include "llist.h"
+
+/* A version with unsigned int as key */
+typedef void Curl_uint_hash_dtor(unsigned int id, void *value);
+struct uint_hash_entry;
+
+/* Hash for `unsigned int` as key */
+struct uint_hash {
+  struct uint_hash_entry **table;
+  Curl_uint_hash_dtor *dtor;
+  unsigned int slots;
+  unsigned int size;
+#ifdef DEBUGBUILD
+  int init;
+#endif
+};
+
+
+void Curl_uint_hash_init(struct uint_hash *h,
+                         unsigned int slots,
+                         Curl_uint_hash_dtor *dtor);
+void Curl_uint_hash_destroy(struct uint_hash *h);
+void Curl_uint_hash_clear(struct uint_hash *h);
+
+bool Curl_uint_hash_set(struct uint_hash *h, unsigned int id, void *value);
+bool Curl_uint_hash_remove(struct uint_hash *h, unsigned int id);
+void *Curl_uint_hash_get(struct uint_hash *h, unsigned int id);
+unsigned int Curl_uint_hash_count(struct uint_hash *h);
+
+
+typedef bool Curl_uint_hash_visit_cb(unsigned int id, void *value,
+                                     void *user_data);
+
+void Curl_uint_hash_visit(struct uint_hash *h,
+                          Curl_uint_hash_visit_cb *cb,
+                          void *user_data);
+
+#endif /* HEADER_CURL_UINT_HASH_H */
diff --git a/extra/curl/curl-8.14.1/lib/uint-spbset.c b/extra/curl/curl-8.14.1/lib/uint-spbset.c
new file mode 100644
index 000000000000..578b9bd07c14
--- /dev/null
+++ b/extra/curl/curl-8.14.1/lib/uint-spbset.c
@@ -0,0 +1,273 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+#include "uint-bset.h"
+#include "uint-spbset.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#ifdef DEBUGBUILD
+#define CURL_UINT_SPBSET_MAGIC  0x70737362
+#endif
+
+void Curl_uint_spbset_init(struct uint_spbset *bset)
+{
+  memset(bset, 0, sizeof(*bset));
+#ifdef DEBUGBUILD
+  bset->init = CURL_UINT_SPBSET_MAGIC;
+#endif
+}
+
+void Curl_uint_spbset_destroy(struct uint_spbset *bset)
+{
+  DEBUGASSERT(bset->init == CURL_UINT_SPBSET_MAGIC);
+  Curl_uint_spbset_clear(bset);
+}
+
+unsigned int Curl_uint_spbset_count(struct uint_spbset *bset)
+{
+  struct uint_spbset_chunk *chunk;
+  unsigned int i, n = 0;
+
+  for(chunk = &bset->head; chunk; chunk = chunk->next) {
+    for(i = 0; i < CURL_UINT_SPBSET_CH_SLOTS; ++i) {
+      if(chunk->slots[i])
+        n += CURL_POPCOUNT64(chunk->slots[i]);
+    }
+  }
+  return n;
+}
+
+bool Curl_uint_spbset_empty(struct uint_spbset *bset)
+{
+  struct uint_spbset_chunk *chunk;
+  unsigned int i;
+
+  for(chunk = &bset->head; chunk; chunk = chunk->next) {
+    for(i = 0; i < CURL_UINT_SPBSET_CH_SLOTS; ++i) {
+      if(chunk->slots[i])
+        return FALSE;
+    }
+  }
+  return TRUE;
+}
+
+void Curl_uint_spbset_clear(struct uint_spbset *bset)
+{
+  struct uint_spbset_chunk *next, *chunk;
+
+  for(chunk = bset->head.next; chunk; chunk = next) {
+    next = chunk->next;
+    free(chunk);
+  }
+  memset(&bset->head, 0, sizeof(bset->head));
+}
+
+
+static struct uint_spbset_chunk *
+uint_spbset_get_chunk(struct uint_spbset *bset, unsigned int i, bool grow)
+{
+  struct uint_spbset_chunk *chunk, **panchor = NULL;
+  unsigned int i_offset = (i & ~CURL_UINT_SPBSET_CH_MASK);
+
+  if(!bset)
+    return NULL;
+
+  for(chunk = &bset->head; chunk;
+      panchor = &chunk->next, chunk = chunk->next) {
+    if(chunk->offset == i_offset) {
+      return chunk;
+    }
+    else if(chunk->offset > i_offset) {
+      /* need new chunk here */
+      chunk = NULL;
+      break;
+    }
+  }
+
+  if(!grow)
+    return NULL;
+
+  /* need a new one */
+  chunk = calloc(1, sizeof(*chunk));
+  if(!chunk)
+    return NULL;
+
+  if(panchor) {  /* insert between panchor and *panchor */
+    chunk->next = *panchor;
+    *panchor = chunk;
+  }
+  else {  /* prepend to head, switching places */
+    memcpy(chunk, &bset->head, sizeof(*chunk));
+    memset(&bset->head, 0, sizeof(bset->head));
+    bset->head.next = chunk;
+  }
+  chunk->offset = i_offset;
+  return chunk;
+}
+
+
+bool Curl_uint_spbset_add(struct uint_spbset *bset, unsigned int i)
+{
+  struct uint_spbset_chunk *chunk;
+  unsigned int i_chunk;
+
+  chunk = uint_spbset_get_chunk(bset, i, TRUE);
+  if(!chunk)
+    return FALSE;
+
+  DEBUGASSERT(i >= chunk->offset);
+  i_chunk = (i - chunk->offset);
+  DEBUGASSERT((i_chunk / 64) < CURL_UINT_SPBSET_CH_SLOTS);
+  chunk->slots[(i_chunk / 64)] |= ((curl_uint64_t)1 << (i_chunk % 64));
+  return TRUE;
+}
+
+
+void Curl_uint_spbset_remove(struct uint_spbset *bset, unsigned int i)
+{
+  struct uint_spbset_chunk *chunk;
+  unsigned int i_chunk;
+
+  chunk = uint_spbset_get_chunk(bset, i, FALSE);
+  if(chunk) {
+    DEBUGASSERT(i >= chunk->offset);
+    i_chunk = (i - chunk->offset);
+    DEBUGASSERT((i_chunk / 64) < CURL_UINT_SPBSET_CH_SLOTS);
+    chunk->slots[(i_chunk / 64)] &= ~((curl_uint64_t)1 << (i_chunk % 64));
+  }
+}
+
+
+bool Curl_uint_spbset_contains(struct uint_spbset *bset, unsigned int i)
+{
+  struct uint_spbset_chunk *chunk;
+  unsigned int i_chunk;
+
+  chunk = uint_spbset_get_chunk(bset, i, FALSE);
+  if(chunk) {
+    DEBUGASSERT(i >= chunk->offset);
+    i_chunk = (i - chunk->offset);
+    DEBUGASSERT((i_chunk / 64) < CURL_UINT_SPBSET_CH_SLOTS);
+    return (chunk->slots[i_chunk / 64] &
+            ((curl_uint64_t)1 << (i_chunk % 64))) != 0;
+  }
+  return FALSE;
+}
+
+bool Curl_uint_spbset_first(struct uint_spbset *bset, unsigned int *pfirst)
+{
+  struct uint_spbset_chunk *chunk;
+  unsigned int i;
+
+  for(chunk = &bset->head; chunk; chunk = chunk->next) {
+    for(i = 0; i < CURL_UINT_SPBSET_CH_SLOTS; ++i) {
+      if(chunk->slots[i]) {
+        *pfirst = chunk->offset + ((i * 64) + CURL_CTZ64(chunk->slots[i]));
+        return TRUE;
+      }
+    }
+  }
+  *pfirst = 0; /* give it a defined value even if it should not be used */
+  return FALSE;
+}
+
+
+static bool uint_spbset_chunk_first(struct uint_spbset_chunk *chunk,
+                                    unsigned int *pfirst)
+{
+  unsigned int i;
+  for(i = 0; i < CURL_UINT_SPBSET_CH_SLOTS; ++i) {
+    if(chunk->slots[i]) {
+      *pfirst = chunk->offset + ((i * 64) + CURL_CTZ64(chunk->slots[i]));
+      return TRUE;
+    }
+  }
+  *pfirst = UINT_MAX; /* a value we cannot store */
+  return FALSE;
+}
+
+
+static bool uint_spbset_chunk_next(struct uint_spbset_chunk *chunk,
+                                   unsigned int last,
+                                   unsigned int *pnext)
+{
+  if(chunk->offset <= last) {
+    curl_uint64_t x;
+    unsigned int i = ((last - chunk->offset) / 64);
+    if(i < CURL_UINT_SPBSET_CH_SLOTS) {
+      x = (chunk->slots[i] >> (last % 64));
+      if(x) {
+        /* more bits set, next is `last` + trailing0s of the shifted slot */
+        *pnext = last + CURL_CTZ64(x);
+        return TRUE;
+      }
+      /* no more bits set in the last slot, scan forward */
+      for(i = i + 1; i < CURL_UINT_SPBSET_CH_SLOTS; ++i) {
+        if(chunk->slots[i]) {
+          *pnext = chunk->offset + ((i * 64) + CURL_CTZ64(chunk->slots[i]));
+          return TRUE;
+        }
+      }
+    }
+  }
+  *pnext = UINT_MAX;
+  return FALSE;
+}
+
+bool Curl_uint_spbset_next(struct uint_spbset *bset, unsigned int last,
+                           unsigned int *pnext)
+{
+  struct uint_spbset_chunk *chunk;
+  unsigned int last_offset;
+
+  ++last; /* look for the next higher number */
+  last_offset = (last & ~CURL_UINT_SPBSET_CH_MASK);
+
+  for(chunk = &bset->head; chunk; chunk = chunk->next) {
+    if(chunk->offset >= last_offset) {
+      break;
+    }
+  }
+
+  if(chunk && (chunk->offset == last_offset)) {
+    /* is there a number higher than last in this chunk? */
+    if(uint_spbset_chunk_next(chunk, last, pnext))
+      return TRUE;
+    /* not in this chunk */
+    chunk = chunk->next;
+  }
+  /* look for the first in the "higher" chunks, if there are any. */
+  while(chunk) {
+    if(uint_spbset_chunk_first(chunk, pnext))
+      return TRUE;
+    chunk = chunk->next;
+  }
+  *pnext = UINT_MAX;
+  return FALSE;
+}
diff --git a/extra/curl/curl-8.14.1/lib/uint-spbset.h b/extra/curl/curl-8.14.1/lib/uint-spbset.h
new file mode 100644
index 000000000000..571d56753cdc
--- /dev/null
+++ b/extra/curl/curl-8.14.1/lib/uint-spbset.h
@@ -0,0 +1,99 @@
+#ifndef HEADER_CURL_UINT_SPBSET_H
+#define HEADER_CURL_UINT_SPBSET_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "curl_setup.h"
+
+#include <curl/curl.h>
+
+/* A "sparse" bitset for unsigned int values.
+ * It can hold any unsigned int value.
+ *
+ * Optimized for the case where only a small set of numbers need
+ * to be kept, especially when "close" together. Then storage space
+ * is most efficient, deteriorating when many number are far apart.
+ */
+
+/* 4 slots = 256 bits, keep this a 2^n value. */
+#define CURL_UINT_SPBSET_CH_SLOTS  4
+#define CURL_UINT_SPBSET_CH_MASK   ((CURL_UINT_SPBSET_CH_SLOTS * 64) - 1)
+
+/* store the uint value from offset to
+ * (offset + (CURL_UINT_SPBSET_CHUNK_SLOTS * 64) - 1 */
+struct uint_spbset_chunk {
+  struct uint_spbset_chunk *next;
+  curl_uint64_t slots[CURL_UINT_SPBSET_CH_SLOTS];
+  unsigned int offset;
+};
+
+struct uint_spbset {
+  struct uint_spbset_chunk head;
+#ifdef DEBUGBUILD
+  int init;
+#endif
+};
+
+void Curl_uint_spbset_init(struct uint_spbset *bset);
+
+void Curl_uint_spbset_destroy(struct uint_spbset *bset);
+
+/* Get the cardinality of the bitset, e.g. numbers present in the set. */
+unsigned int Curl_uint_spbset_count(struct uint_spbset *bset);
+
+/* TRUE of bitset is empty */
+bool Curl_uint_spbset_empty(struct uint_spbset *bset);
+
+/* Clear the bitset, making it empty. */
+void Curl_uint_spbset_clear(struct uint_spbset *bset);
+
+/* Add the number `i` to the bitset.
+ * Numbers can be added more than once, without making a difference.
+ * Returns FALSE if allocations failed. */
+bool Curl_uint_spbset_add(struct uint_spbset *bset, unsigned int i);
+
+/* Remove the number `i` from the bitset. */
+void Curl_uint_spbset_remove(struct uint_spbset *bset, unsigned int i);
+
+/* Return TRUE if the bitset contains number `i`. */
+bool Curl_uint_spbset_contains(struct uint_spbset *bset, unsigned int i);
+
+/* Get the first number in the bitset, e.g. the smallest.
+ * Returns FALSE when the bitset is empty. */
+bool Curl_uint_spbset_first(struct uint_spbset *bset, unsigned int *pfirst);
+
+/* Get the next number in the bitset, following `last` in natural order.
+ * Put another way, this is the smallest number greater than `last` in
+ * the bitset. `last` does not have to be present in the set.
+ *
+ * Returns FALSE when no such number is in the set.
+ *
+ * This allows to iterate the set while being modified:
+ * - added numbers higher than 'last' will be picked up by the iteration.
+ * - added numbers lower than 'last' will not show up.
+ * - removed numbers lower or equal to 'last' will not show up.
+ * - removed numbers higher than 'last' will not be visited. */
+bool Curl_uint_spbset_next(struct uint_spbset *bset, unsigned int last,
+                           unsigned int *pnext);
+
+#endif /* HEADER_CURL_UINT_SPBSET_H */
diff --git a/extra/curl/curl-8.14.1/lib/uint-table.c b/extra/curl/curl-8.14.1/lib/uint-table.c
new file mode 100644
index 000000000000..d8de1b128ab3
--- /dev/null
+++ b/extra/curl/curl-8.14.1/lib/uint-table.c
@@ -0,0 +1,214 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+#include "uint-table.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#ifdef DEBUGBUILD
+#define CURL_UINT_TBL_MAGIC  0x62757473
+#endif
+
+void Curl_uint_tbl_init(struct uint_tbl *tbl,
+                        Curl_uint_tbl_entry_dtor *entry_dtor)
+{
+  memset(tbl, 0, sizeof(*tbl));
+  tbl->entry_dtor = entry_dtor;
+  tbl->last_key_added = UINT_MAX;
+#ifdef DEBUGBUILD
+  tbl->init = CURL_UINT_TBL_MAGIC;
+#endif
+}
+
+
+static void uint_tbl_clear_rows(struct uint_tbl *tbl,
+                                unsigned int from,
+                                unsigned int upto_excluding)
+{
+  unsigned int i, end;
+
+  end = CURLMIN(upto_excluding, tbl->nrows);
+  for(i = from; i < end; ++i) {
+    if(tbl->rows[i]) {
+      if(tbl->entry_dtor)
+        tbl->entry_dtor(i, tbl->rows[i]);
+      tbl->rows[i] = NULL;
+      tbl->nentries--;
+    }
+  }
+}
+
+
+CURLcode Curl_uint_tbl_resize(struct uint_tbl *tbl, unsigned int nrows)
+{
+  /* we use `tbl->nrows + 1` during iteration, want that to work */
+  DEBUGASSERT(tbl->init == CURL_UINT_TBL_MAGIC);
+  if(!nrows || (nrows == UINT_MAX))
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+  if(nrows != tbl->nrows) {
+    void **rows = calloc(nrows, sizeof(void *));
+    if(!rows)
+      return CURLE_OUT_OF_MEMORY;
+    if(tbl->rows) {
+      memcpy(rows, tbl->rows, (CURLMIN(nrows, tbl->nrows) * sizeof(void *)));
+      if(nrows < tbl->nrows)
+        uint_tbl_clear_rows(tbl, nrows, tbl->nrows);
+      free(tbl->rows);
+    }
+    tbl->rows = rows;
+    tbl->nrows = nrows;
+  }
+  return CURLE_OK;
+}
+
+
+void Curl_uint_tbl_destroy(struct uint_tbl *tbl)
+{
+  DEBUGASSERT(tbl->init == CURL_UINT_TBL_MAGIC);
+  Curl_uint_tbl_clear(tbl);
+  free(tbl->rows);
+  memset(tbl, 0, sizeof(*tbl));
+}
+
+
+void Curl_uint_tbl_clear(struct uint_tbl *tbl)
+{
+  DEBUGASSERT(tbl->init == CURL_UINT_TBL_MAGIC);
+  uint_tbl_clear_rows(tbl, 0, tbl->nrows);
+  DEBUGASSERT(!tbl->nentries);
+  tbl->last_key_added = UINT_MAX;
+}
+
+
+unsigned int Curl_uint_tbl_capacity(struct uint_tbl *tbl)
+{
+  return tbl->nrows;
+}
+
+
+unsigned int Curl_uint_tbl_count(struct uint_tbl *tbl)
+{
+  return tbl->nentries;
+}
+
+
+void *Curl_uint_tbl_get(struct uint_tbl *tbl, unsigned int key)
+{
+  return (key < tbl->nrows) ? tbl->rows[key] : NULL;
+}
+
+
+bool Curl_uint_tbl_add(struct uint_tbl *tbl, void *entry, unsigned int *pkey)
+{
+  unsigned int key, start_pos;
+
+  DEBUGASSERT(tbl->init == CURL_UINT_TBL_MAGIC);
+  if(!entry || !pkey)
+    return FALSE;
+  *pkey = UINT_MAX; /* always invalid */
+  if(tbl->nentries == tbl->nrows)  /* full */
+    return FALSE;
+
+  start_pos = CURLMIN(tbl->last_key_added, tbl->nrows) + 1;
+  for(key = start_pos; key < tbl->nrows; ++key) {
+    if(!tbl->rows[key]) {
+      tbl->rows[key] = entry;
+      tbl->nentries++;
+      tbl->last_key_added = key;
+      *pkey = key;
+      return TRUE;
+    }
+  }
+  /* no free entry at or above tbl->maybe_next_key, wrap around */
+  for(key = 0; key < start_pos; ++key) {
+    if(!tbl->rows[key]) {
+      tbl->rows[key] = entry;
+      tbl->nentries++;
+      tbl->last_key_added = key;
+      *pkey = key;
+      return TRUE;
+    }
+  }
+  /* Did not find any free row? Should not happen */
+  DEBUGASSERT(0);
+  return FALSE;
+}
+
+
+void Curl_uint_tbl_remove(struct uint_tbl *tbl, unsigned int key)
+{
+  uint_tbl_clear_rows(tbl, key, key + 1);
+}
+
+
+bool Curl_uint_tbl_contains(struct uint_tbl *tbl, unsigned int key)
+{
+  return (key < tbl->nrows) ? !!tbl->rows[key] : FALSE;
+}
+
+
+static bool uint_tbl_next_at(struct uint_tbl *tbl, unsigned int key,
+                             unsigned int *pkey, void **pentry)
+{
+  for(; key < tbl->nrows; ++key) {
+    if(tbl->rows[key]) {
+      *pkey = key;
+      *pentry = tbl->rows[key];
+      return TRUE;
+    }
+  }
+  *pkey = UINT_MAX;  /* always invalid */
+  *pentry = NULL;
+  return FALSE;
+}
+
+bool Curl_uint_tbl_first(struct uint_tbl *tbl,
+                         unsigned int *pkey, void **pentry)
+{
+  if(!pkey || !pentry)
+    return FALSE;
+  if(tbl->nentries && uint_tbl_next_at(tbl, 0, pkey, pentry))
+    return TRUE;
+  DEBUGASSERT(!tbl->nentries);
+  *pkey = UINT_MAX;  /* always invalid */
+  *pentry = NULL;
+  return FALSE;
+}
+
+
+bool Curl_uint_tbl_next(struct uint_tbl *tbl, unsigned int last_key,
+                        unsigned int *pkey, void **pentry)
+{
+  if(!pkey || !pentry)
+    return FALSE;
+  if(uint_tbl_next_at(tbl, last_key + 1, pkey, pentry))
+    return TRUE;
+  *pkey = UINT_MAX;  /* always invalid */
+  *pentry = NULL;
+  return FALSE;
+}
diff --git a/extra/curl/curl-8.14.1/lib/uint-table.h b/extra/curl/curl-8.14.1/lib/uint-table.h
new file mode 100644
index 000000000000..2c05b1de1ab9
--- /dev/null
+++ b/extra/curl/curl-8.14.1/lib/uint-table.h
@@ -0,0 +1,101 @@
+#ifndef HEADER_CURL_UINT_TABLE_H
+#define HEADER_CURL_UINT_TABLE_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "curl_setup.h"
+
+#include <curl/curl.h>
+
+/* Destructor for a single table entry */
+typedef void Curl_uint_tbl_entry_dtor(unsigned int key, void *entry);
+
+struct uint_tbl {
+  void **rows;  /* array of void* holding entries */
+  Curl_uint_tbl_entry_dtor *entry_dtor;
+  unsigned int nrows;  /* length of `rows` array */
+  unsigned int nentries; /* entries in table */
+  unsigned int last_key_added; /* UINT_MAX or last key added */
+#ifdef DEBUGBUILD
+  int init;
+#endif
+};
+
+/* Initialize the table with 0 capacity.
+ * The optional `entry_dtor` is called when a table entry is removed,
+ * Passing NULL means no action is taken on removal. */
+void Curl_uint_tbl_init(struct uint_tbl *tbl,
+                        Curl_uint_tbl_entry_dtor *entry_dtor);
+
+/* Resize the table to change capacity `nmax`. When `nmax` is reduced,
+ * all present entries with key equal or larger to `nmax` are removed. */
+CURLcode Curl_uint_tbl_resize(struct uint_tbl *tbl, unsigned int nmax);
+
+/* Destroy the table, freeing all entries. */
+void Curl_uint_tbl_destroy(struct uint_tbl *tbl);
+
+/* Get the table capacity. */
+unsigned int Curl_uint_tbl_capacity(struct uint_tbl *tbl);
+
+/* Get the number of entries in the table. */
+unsigned int Curl_uint_tbl_count(struct uint_tbl *tbl);
+
+/* Clear the table, making it empty. */
+void Curl_uint_tbl_clear(struct uint_tbl *tbl);
+
+/* Get the entry for key or NULL if not present */
+void *Curl_uint_tbl_get(struct uint_tbl *tbl, unsigned int key);
+
+/* Add a new entry to the table and assign it a free key.
+ * Returns FALSE if the table is full.
+ *
+ * Keys are assigned in a round-robin manner.
+ * No matter the capacity, UINT_MAX is never assigned. */
+bool Curl_uint_tbl_add(struct uint_tbl *tbl, void *entry, unsigned int *pkey);
+
+/* Remove the entry with `key`. */
+void Curl_uint_tbl_remove(struct uint_tbl *tbl, unsigned int key);
+
+/* Return TRUE if the table contains an tryn with that keys. */
+bool Curl_uint_tbl_contains(struct uint_tbl *tbl, unsigned int key);
+
+/* Get the first entry in the table (with the smallest `key`).
+ * Returns FALSE if the table is empty. */
+bool Curl_uint_tbl_first(struct uint_tbl *tbl,
+                         unsigned int *pkey, void **pentry);
+
+/* Get the next key in the table, following `last_key` in natural order.
+ * Put another way, this is the smallest key greater than `last_key` in
+ * the table. `last_key` does not have to be present in the table.
+ *
+ * Returns FALSE when no such entry is in the table.
+ *
+ * This allows to iterate the table while being modified:
+ * - added keys higher than 'last_key' will be picked up by the iteration.
+ * - added keys lower than 'last_key' will not show up.
+ * - removed keys lower or equal to 'last_key' will not show up.
+ * - removed keys higher than 'last_key' will not be visited. */
+bool Curl_uint_tbl_next(struct uint_tbl *tbl, unsigned int last_key,
+                        unsigned int *pkey, void **pentry);
+
+#endif /* HEADER_CURL_UINT_TABLE_H */
diff --git a/extra/curl/curl-8.12.1/lib/url.c b/extra/curl/curl-8.14.1/lib/url.c
similarity index 83%
rename from extra/curl/curl-8.12.1/lib/url.c
rename to extra/curl/curl-8.14.1/lib/url.c
index 516ee08a1aa0..10e37ec67f43 100644
--- a/extra/curl/curl-8.12.1/lib/url.c
+++ b/extra/curl/curl-8.14.1/lib/url.c
@@ -75,7 +75,6 @@
 #include "strcase.h"
 #include "strerror.h"
 #include "escape.h"
-#include "strtok.h"
 #include "share.h"
 #include "content_encoding.h"
 #include "http_digest.h"
@@ -84,13 +83,15 @@
 #include "multiif.h"
 #include "easyif.h"
 #include "speedcheck.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
 #include "getinfo.h"
+#include "pop3.h"
 #include "urlapi-int.h"
 #include "system_win32.h"
 #include "hsts.h"
 #include "noproxy.h"
 #include "cfilters.h"
+#include "curl_krb5.h"
 #include "idn.h"
 
 /* And now for the protocols */
@@ -117,9 +118,9 @@
 #include "strdup.h"
 #include "setopt.h"
 #include "altsvc.h"
-#include "dynbuf.h"
+#include "curlx/dynbuf.h"
 #include "headers.h"
-
+#include "curlx/strparse.h"
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
@@ -271,7 +272,7 @@ CURLcode Curl_close(struct Curl_easy **datap)
   data->state.referer = NULL;
 
   up_free(data);
-  Curl_dyn_free(&data->state.headerb);
+  curlx_dyn_free(&data->state.headerb);
   Curl_flush_cookies(data, TRUE);
 #ifndef CURL_DISABLE_ALTSVC
   Curl_altsvc_save(data, data->asi, data->set.str[STRING_ALTSVC]);
@@ -286,12 +287,14 @@ CURLcode Curl_close(struct Curl_easy **datap)
 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH)
   Curl_http_auth_cleanup_digest(data);
 #endif
+  Curl_safefree(data->state.most_recent_ftp_entrypath);
   Curl_safefree(data->info.contenttype);
   Curl_safefree(data->info.wouldredirect);
 
-  /* this destroys the channel and we cannot use it anymore after this */
-  Curl_resolver_cancel(data);
-  Curl_resolver_cleanup(data->state.async.resolver);
+  /* release any resolve information this transfer kept */
+  Curl_async_destroy(data);
+  Curl_resolv_unlink(data, &data->state.dns[0]); /* done with this */
+  Curl_resolv_unlink(data, &data->state.dns[1]);
 
   data_priority_cleanup(data);
 
@@ -302,6 +305,7 @@ CURLcode Curl_close(struct Curl_easy **datap)
     Curl_share_unlock(data, CURL_LOCK_DATA_SHARE);
   }
 
+  Curl_hash_destroy(&data->meta_hash);
 #ifndef CURL_DISABLE_PROXY
   Curl_safefree(data->state.aptr.proxyuserpwd);
 #endif
@@ -391,9 +395,6 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
   set->socks5auth = CURLAUTH_BASIC | CURLAUTH_GSSAPI;
 #endif
 
-  /* make libcurl quiet by default: */
-  set->hide_progress = TRUE;  /* CURLOPT_NOPROGRESS changes these */
-
   Curl_mime_initpart(&set->mimepost);
 
   Curl_ssl_easy_config_init(data);
@@ -428,7 +429,7 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
    */
   if(Curl_ssl_backend() != CURLSSLBACKEND_SCHANNEL &&
      Curl_ssl_backend() != CURLSSLBACKEND_SECURETRANSPORT) {
-#if defined(CURL_CA_BUNDLE)
+#ifdef CURL_CA_BUNDLE
     result = Curl_setstropt(&set->str[STRING_SSL_CAFILE], CURL_CA_BUNDLE);
     if(result)
       return result;
@@ -439,7 +440,7 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
       return result;
 #endif
 #endif
-#if defined(CURL_CA_PATH)
+#ifdef CURL_CA_PATH
     result = Curl_setstropt(&set->str[STRING_SSL_CAPATH], CURL_CA_PATH);
     if(result)
       return result;
@@ -474,19 +475,31 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
   set->maxage_conn = 118;
   set->maxlifetime_conn = 0;
   set->http09_allowed = FALSE;
-#ifdef USE_HTTP2
-  set->httpwant = CURL_HTTP_VERSION_2TLS
-#else
-  set->httpwant = CURL_HTTP_VERSION_1_1
-#endif
+  set->httpwant = CURL_HTTP_VERSION_NONE
     ;
 #if defined(USE_HTTP2) || defined(USE_HTTP3)
   memset(&set->priority, 0, sizeof(set->priority));
 #endif
   set->quick_exit = 0L;
+#ifndef CURL_DISABLE_WEBSOCKETS
+  set->ws_raw_mode = FALSE;
+  set->ws_no_auto_pong = FALSE;
+#endif
+
   return result;
 }
 
+/* easy->meta_hash destructor. Should never be called as elements
+ * MUST be added with their own destructor */
+static void easy_meta_freeentry(void *p)
+{
+  (void)p;
+  /* Will always be FALSE. Cannot use a 0 assert here since compilers
+   * are not in agreement if they then want a NORETURN attribute or
+   * not. *sigh* */
+  DEBUGASSERT(p == NULL);
+}
+
 /**
  * Curl_open()
  *
@@ -509,45 +522,34 @@ CURLcode Curl_open(struct Curl_easy **curl)
   }
 
   data->magic = CURLEASY_MAGIC_NUMBER;
-
+  /* most recent connection is not yet defined */
+  data->state.lastconnect_id = -1;
+  data->state.recent_conn_id = -1;
+  /* and not assigned an id yet */
+  data->id = -1;
+  data->mid = UINT_MAX;
+  data->master_mid = UINT_MAX;
+  data->progress.hide = TRUE;
+  data->state.current_speed = -1; /* init to negative == impossible */
+
+  Curl_hash_init(&data->meta_hash, 23,
+                 Curl_hash_str, curlx_str_key_compare, easy_meta_freeentry);
+  curlx_dyn_init(&data->state.headerb, CURL_MAX_HTTP_HEADER);
   Curl_req_init(&data->req);
-
-  result = Curl_resolver_init(data, &data->state.async.resolver);
-  if(result) {
-    DEBUGF(fprintf(stderr, "Error: resolver_init failed\n"));
-    Curl_req_free(&data->req, data);
-    free(data);
-    return result;
-  }
-
-  result = Curl_init_userdefined(data);
-  if(!result) {
-    Curl_dyn_init(&data->state.headerb, CURL_MAX_HTTP_HEADER);
-    Curl_initinfo(data);
-
-    /* most recent connection is not yet defined */
-    data->state.lastconnect_id = -1;
-    data->state.recent_conn_id = -1;
-    /* and not assigned an id yet */
-    data->id = -1;
-    data->mid = -1;
-#ifndef CURL_DISABLE_DOH
-    data->set.dohfor_mid = -1;
-#endif
-
-    data->progress.flags |= PGRS_HIDE;
-    data->state.current_speed = -1; /* init to negative == impossible */
+  Curl_initinfo(data);
 #ifndef CURL_DISABLE_HTTP
-    Curl_llist_init(&data->state.httphdrs, NULL);
+  Curl_llist_init(&data->state.httphdrs, NULL);
 #endif
-    Curl_netrc_init(&data->state.netrc);
-  }
+  Curl_netrc_init(&data->state.netrc);
+
+  result = Curl_init_userdefined(data);
 
   if(result) {
-    Curl_resolver_cleanup(data->state.async.resolver);
-    Curl_dyn_free(&data->state.headerb);
+    curlx_dyn_free(&data->state.headerb);
     Curl_freeset(data);
     Curl_req_free(&data->req, data);
+    Curl_hash_destroy(&data->meta_hash);
+    data->magic = 0;
     free(data);
     data = NULL;
   }
@@ -562,6 +564,10 @@ void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn)
 
   DEBUGASSERT(conn);
 
+  if(conn->handler && conn->handler->disconnect &&
+     !conn->bits.shutdown_handler)
+    conn->handler->disconnect(data, conn, TRUE);
+
   for(i = 0; i < CURL_ARRAYSIZE(conn->cfilter); ++i) {
     Curl_conn_cf_discard_all(data, conn, (int)i);
   }
@@ -578,6 +584,7 @@ void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn)
   Curl_safefree(conn->http_proxy.host.rawalloc); /* http proxy name buffer */
   Curl_safefree(conn->socks_proxy.host.rawalloc); /* socks proxy name buffer */
 #endif
+  Curl_sec_conn_destroy(conn);
   Curl_safefree(conn->user);
   Curl_safefree(conn->passwd);
   Curl_safefree(conn->sasl_authzid);
@@ -594,56 +601,12 @@ void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn)
   Curl_safefree(conn->unix_domain_socket);
 #endif
   Curl_safefree(conn->destination);
+  Curl_uint_spbset_destroy(&conn->xfers_attached);
+  Curl_hash_destroy(&conn->meta_hash);
 
   free(conn); /* free all the connection oriented data */
 }
 
-/*
- * Disconnects the given connection. Note the connection may not be the
- * primary connection, like when freeing room in the connection pool or
- * killing of a dead old connection.
- *
- * A connection needs an easy handle when closing down. We support this passed
- * in separately since the connection to get closed here is often already
- * disassociated from an easy handle.
- *
- * This function MUST NOT reset state in the Curl_easy struct if that
- * is not strictly bound to the life-time of *this* particular connection.
- */
-bool Curl_on_disconnect(struct Curl_easy *data,
-                        struct connectdata *conn, bool aborted)
-{
-  /* there must be a connection to close */
-  DEBUGASSERT(conn);
-
-  /* it must be removed from the connection pool */
-  DEBUGASSERT(!conn->bits.in_cpool);
-
-  /* there must be an associated transfer */
-  DEBUGASSERT(data);
-
-  /* the transfer must be detached from the connection */
-  DEBUGASSERT(!data->conn);
-
-  DEBUGF(infof(data, "Curl_disconnect(conn #%" FMT_OFF_T ", aborted=%d)",
-         conn->connection_id, aborted));
-
-  if(conn->dns_entry)
-    Curl_resolv_unlink(data, &conn->dns_entry);
-
-  /* Cleanup NTLM connection-related data */
-  Curl_http_auth_cleanup_ntlm(conn);
-
-  /* Cleanup NEGOTIATE connection-related data */
-  Curl_http_auth_cleanup_negotiate(conn);
-
-  if(conn->connect_only)
-    /* treat the connection as aborted in CONNECT_ONLY situations */
-    aborted = TRUE;
-
-  return aborted;
-}
-
 /*
  * xfer_may_multiplex()
  *
@@ -653,15 +616,20 @@ bool Curl_on_disconnect(struct Curl_easy *data,
 static bool xfer_may_multiplex(const struct Curl_easy *data,
                                const struct connectdata *conn)
 {
+#ifndef CURL_DISABLE_HTTP
   /* If an HTTP protocol and multiplexing is enabled */
   if((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
      (!conn->bits.protoconnstart || !conn->bits.close)) {
 
     if(Curl_multiplex_wanted(data->multi) &&
-       (data->state.httpwant >= CURL_HTTP_VERSION_2))
+       (data->state.http_neg.allowed & (CURL_HTTP_V2x|CURL_HTTP_V3x)))
       /* allows HTTP/2 or newer */
       return TRUE;
   }
+#else
+  (void)data;
+  (void)conn;
+#endif
   return FALSE;
 }
 
@@ -712,7 +680,7 @@ static bool conn_maxage(struct Curl_easy *data,
 {
   timediff_t idletime, lifetime;
 
-  idletime = Curl_timediff(now, conn->lastused);
+  idletime = curlx_timediff(now, conn->lastused);
   idletime /= 1000; /* integer seconds is fine */
 
   if(idletime > data->set.maxage_conn) {
@@ -721,7 +689,7 @@ static bool conn_maxage(struct Curl_easy *data,
     return TRUE;
   }
 
-  lifetime = Curl_timediff(now, conn->created);
+  lifetime = curlx_timediff(now, conn->created);
   lifetime /= 1000; /* integer seconds is fine */
 
   if(data->set.maxlifetime_conn && lifetime > data->set.maxlifetime_conn) {
@@ -749,7 +717,7 @@ bool Curl_conn_seems_dead(struct connectdata *conn,
     bool dead;
     struct curltime now;
     if(!pnow) {
-      now = Curl_now();
+      now = curlx_now();
       pnow = &now;
     }
 
@@ -807,7 +775,7 @@ CURLcode Curl_conn_upkeep(struct Curl_easy *data,
                           struct curltime *now)
 {
   CURLcode result = CURLE_OK;
-  if(Curl_timediff(*now, conn->keepalive) <= data->set.upkeep_interval_ms)
+  if(curlx_timediff(*now, conn->keepalive) <= data->set.upkeep_interval_ms)
     return result;
 
   /* briefly attach for action */
@@ -833,11 +801,13 @@ CURLcode Curl_conn_upkeep(struct Curl_easy *data,
 static bool ssh_config_matches(struct connectdata *one,
                                struct connectdata *two)
 {
-  return Curl_safecmp(one->proto.sshc.rsa, two->proto.sshc.rsa) &&
-         Curl_safecmp(one->proto.sshc.rsa_pub, two->proto.sshc.rsa_pub);
+  struct ssh_conn *sshc1, *sshc2;
+
+  sshc1 = Curl_conn_meta_get(one, CURL_META_SSH_CONN);
+  sshc2 = Curl_conn_meta_get(two, CURL_META_SSH_CONN);
+  return (sshc1 && sshc2 && Curl_safecmp(sshc1->rsa, sshc2->rsa) &&
+          Curl_safecmp(sshc1->rsa_pub, sshc2->rsa_pub));
 }
-#else
-#define ssh_config_matches(x,y) FALSE
 #endif
 
 struct url_conn_match {
@@ -855,25 +825,19 @@ struct url_conn_match {
   BIT(seen_multiplex_conn);
 };
 
-static bool url_match_conn(struct connectdata *conn, void *userdata)
+static bool url_match_connect_config(struct connectdata *conn,
+                                     struct url_conn_match *m)
 {
-  struct url_conn_match *match = userdata;
-  struct Curl_easy *data = match->data;
-  struct connectdata *needle = match->needle;
-
-  /* Check if `conn` can be used for transfer `data` */
-
+  /* connect-only or to-be-closed connections will not be reused */
   if(conn->connect_only || conn->bits.close)
-    /* connect-only or to-be-closed connections will not be reused */
     return FALSE;
 
-  if(data->set.ipver != CURL_IPRESOLVE_WHATEVER
-     && data->set.ipver != conn->ip_version) {
-    /* skip because the connection is not via the requested IP version */
+  /* ip_version must match */
+  if(m->data->set.ipver != CURL_IPRESOLVE_WHATEVER
+     && m->data->set.ipver != conn->ip_version)
     return FALSE;
-  }
 
-  if(needle->localdev || needle->localport) {
+  if(m->needle->localdev || m->needle->localport) {
     /* If we are bound to a specific local end (IP+port), we must not reuse a
        random other one, although if we did not ask for a particular one we
        can reuse one that was bound.
@@ -885,105 +849,155 @@ static bool url_match_conn(struct connectdata *conn, void *userdata)
        likely also reuse the exact same binding parameters and missing out a
        few edge cases should not hurt anyone much.
     */
-    if((conn->localport != needle->localport) ||
-       (conn->localportrange != needle->localportrange) ||
-       (needle->localdev &&
-        (!conn->localdev || strcmp(conn->localdev, needle->localdev))))
+    if((conn->localport != m->needle->localport) ||
+       (conn->localportrange != m->needle->localportrange) ||
+       (m->needle->localdev &&
+        (!conn->localdev || strcmp(conn->localdev, m->needle->localdev))))
       return FALSE;
   }
 
-  if(needle->bits.conn_to_host != conn->bits.conn_to_host)
+  if(m->needle->bits.conn_to_host != conn->bits.conn_to_host)
     /* do not mix connections that use the "connect to host" feature and
      * connections that do not use this feature */
     return FALSE;
 
-  if(needle->bits.conn_to_port != conn->bits.conn_to_port)
+  if(m->needle->bits.conn_to_port != conn->bits.conn_to_port)
     /* do not mix connections that use the "connect to port" feature and
      * connections that do not use this feature */
     return FALSE;
 
+  /* Does `conn` use the correct protocol? */
+#ifdef USE_UNIX_SOCKETS
+  if(m->needle->unix_domain_socket) {
+    if(!conn->unix_domain_socket)
+      return FALSE;
+    if(strcmp(m->needle->unix_domain_socket, conn->unix_domain_socket))
+      return FALSE;
+    if(m->needle->bits.abstract_unix_socket != conn->bits.abstract_unix_socket)
+      return FALSE;
+  }
+  else if(conn->unix_domain_socket)
+    return FALSE;
+#endif
+
+  return TRUE;
+}
+
+static bool url_match_fully_connected(struct connectdata *conn,
+                                      struct url_conn_match *m)
+{
   if(!Curl_conn_is_connected(conn, FIRSTSOCKET) ||
      conn->bits.asks_multiplex) {
     /* Not yet connected, or not yet decided if it multiplexes. The later
      * happens for HTTP/2 Upgrade: requests that need a response. */
-    if(match->may_multiplex) {
-      match->seen_pending_conn = TRUE;
+    if(m->may_multiplex) {
+      m->seen_pending_conn = TRUE;
       /* Do not pick a connection that has not connected yet */
-      infof(data, "Connection #%" FMT_OFF_T
+      infof(m->data, "Connection #%" FMT_OFF_T
             " is not open enough, cannot reuse", conn->connection_id);
     }
     /* Do not pick a connection that has not connected yet */
     return FALSE;
   }
-  /* `conn` is connected. If it has transfers, can we add ours to it? */
+  return TRUE;
+}
 
+static bool url_match_multi(struct connectdata *conn,
+                            struct url_conn_match *m)
+{
+  if(CONN_INUSE(conn)) {
+    DEBUGASSERT(conn->attached_multi);
+    if(conn->attached_multi != m->data->multi)
+      return FALSE;
+  }
+  return TRUE;
+}
+
+static bool url_match_multiplex_needs(struct connectdata *conn,
+                                      struct url_conn_match *m)
+{
   if(CONN_INUSE(conn)) {
     if(!conn->bits.multiplex) {
       /* conn busy and conn cannot take more transfers */
-      match->seen_single_use_conn = TRUE;
+      m->seen_single_use_conn = TRUE;
       return FALSE;
     }
-    match->seen_multiplex_conn = TRUE;
-    if(!match->may_multiplex)
+    m->seen_multiplex_conn = TRUE;
+    if(!m->may_multiplex || !url_match_multi(conn, m))
       /* conn busy and transfer cannot be multiplexed */
       return FALSE;
-    else {
-      /* transfer and conn multiplex. Are they on the same multi? */
-      struct Curl_llist_node *e = Curl_llist_head(&conn->easyq);
-      struct Curl_easy *entry = Curl_node_elem(e);
-      if(entry->multi != data->multi)
-        return FALSE;
-    }
   }
-  /* `conn` is connected and we could add the transfer to it, if
-   * all the other criteria do match. */
+  return TRUE;
+}
 
-  /* Does `conn` use the correct protocol? */
-#ifdef USE_UNIX_SOCKETS
-  if(needle->unix_domain_socket) {
-    if(!conn->unix_domain_socket)
-      return FALSE;
-    if(strcmp(needle->unix_domain_socket, conn->unix_domain_socket))
+static bool url_match_multiplex_limits(struct connectdata *conn,
+                                       struct url_conn_match *m)
+{
+  if(CONN_INUSE(conn) && m->may_multiplex) {
+    DEBUGASSERT(conn->bits.multiplex);
+    /* If multiplexed, make sure we do not go over concurrency limit */
+    if(CONN_ATTACHED(conn) >=
+            Curl_multi_max_concurrent_streams(m->data->multi)) {
+      infof(m->data, "client side MAX_CONCURRENT_STREAMS reached"
+            ", skip (%u)", CONN_ATTACHED(conn));
       return FALSE;
-    if(needle->bits.abstract_unix_socket != conn->bits.abstract_unix_socket)
+    }
+    if(CONN_ATTACHED(conn) >=
+            Curl_conn_get_max_concurrent(m->data, conn, FIRSTSOCKET)) {
+      infof(m->data, "MAX_CONCURRENT_STREAMS reached, skip (%u)",
+            CONN_ATTACHED(conn));
       return FALSE;
+    }
+    /* When not multiplexed, we have a match here! */
+    infof(m->data, "Multiplexed connection found");
   }
-  else if(conn->unix_domain_socket)
-    return FALSE;
-#endif
+  return TRUE;
+}
 
-  if((!(needle->handler->flags&PROTOPT_SSL) !=
-      !Curl_conn_is_ssl(conn, FIRSTSOCKET)) &&
-     !(get_protocol_family(conn->handler) == needle->handler->protocol &&
-       conn->bits.tls_upgraded))
-    /* Deny `conn` if it is not fit for `needle`'s SSL needs,
-     * UNLESS `conn` is the same protocol family and was upgraded to SSL. */
+static bool url_match_ssl_use(struct connectdata *conn,
+                              struct url_conn_match *m)
+{
+  if(m->needle->handler->flags&PROTOPT_SSL) {
+    /* We are looking for SSL, if `conn` does not do it, not a match. */
+    if(!Curl_conn_is_ssl(conn, FIRSTSOCKET))
+      return FALSE;
+  }
+  else if(Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
+    /* We are not *requiring* SSL, however `conn` has it. If the
+     * protocol *family* is not the same, not a match. */
+    if(get_protocol_family(conn->handler) != m->needle->handler->protocol)
       return FALSE;
+  }
+  return TRUE;
+}
 
 #ifndef CURL_DISABLE_PROXY
-  if(needle->bits.httpproxy != conn->bits.httpproxy ||
-     needle->bits.socksproxy != conn->bits.socksproxy)
+static bool url_match_proxy_use(struct connectdata *conn,
+                                struct url_conn_match *m)
+{
+  if(m->needle->bits.httpproxy != conn->bits.httpproxy ||
+     m->needle->bits.socksproxy != conn->bits.socksproxy)
     return FALSE;
 
-  if(needle->bits.socksproxy &&
-    !socks_proxy_info_matches(&needle->socks_proxy,
+  if(m->needle->bits.socksproxy &&
+    !socks_proxy_info_matches(&m->needle->socks_proxy,
                               &conn->socks_proxy))
     return FALSE;
 
-  if(needle->bits.httpproxy) {
-    if(needle->bits.tunnel_proxy != conn->bits.tunnel_proxy)
+  if(m->needle->bits.httpproxy) {
+    if(m->needle->bits.tunnel_proxy != conn->bits.tunnel_proxy)
       return FALSE;
 
-    if(!proxy_info_matches(&needle->http_proxy, &conn->http_proxy))
+    if(!proxy_info_matches(&m->needle->http_proxy, &conn->http_proxy))
       return FALSE;
 
-    if(IS_HTTPS_PROXY(needle->http_proxy.proxytype)) {
+    if(IS_HTTPS_PROXY(m->needle->http_proxy.proxytype)) {
       /* https proxies come in different types, http/1.1, h2, ... */
-      if(needle->http_proxy.proxytype != conn->http_proxy.proxytype)
+      if(m->needle->http_proxy.proxytype != conn->http_proxy.proxytype)
         return FALSE;
       /* match SSL config to proxy */
-      if(!Curl_ssl_conn_config_match(data, conn, TRUE)) {
-        DEBUGF(infof(data,
+      if(!Curl_ssl_conn_config_match(m->data, conn, TRUE)) {
+        DEBUGF(infof(m->data,
           "Connection #%" FMT_OFF_T
           " has different SSL proxy parameters, cannot reuse",
           conn->connection_id));
@@ -993,132 +1007,187 @@ static bool url_match_conn(struct connectdata *conn, void *userdata)
        * further below */
     }
   }
+  return TRUE;
+}
+#else
+#define url_match_proxy_use(c,m)    ((void)c, (void)m, TRUE)
 #endif
 
-  if(match->may_multiplex &&
-     (data->state.httpwant == CURL_HTTP_VERSION_2_0) &&
-     (needle->handler->protocol & CURLPROTO_HTTP) &&
+#ifndef CURL_DISABLE_HTTP
+static bool url_match_http_multiplex(struct connectdata *conn,
+                                     struct url_conn_match *m)
+{
+  if(m->may_multiplex &&
+     (m->data->state.http_neg.allowed & (CURL_HTTP_V2x|CURL_HTTP_V3x)) &&
+     (m->needle->handler->protocol & CURLPROTO_HTTP) &&
      !conn->httpversion_seen) {
-    if(data->set.pipewait) {
-      infof(data, "Server upgrade does not support multiplex yet, wait");
-      match->found = NULL;
-      match->wait_pipe = TRUE;
+    if(m->data->set.pipewait) {
+      infof(m->data, "Server upgrade does not support multiplex yet, wait");
+      m->found = NULL;
+      m->wait_pipe = TRUE;
       return TRUE; /* stop searching, we want to wait */
     }
-    infof(data, "Server upgrade cannot be used");
+    infof(m->data, "Server upgrade cannot be used");
     return FALSE;
   }
+  return TRUE;
+}
 
-  if(!(needle->handler->flags & PROTOPT_CREDSPERREQUEST)) {
-    /* This protocol requires credentials per connection,
-       so verify that we are using the same name and password as well */
-    if(Curl_timestrcmp(needle->user, conn->user) ||
-       Curl_timestrcmp(needle->passwd, conn->passwd) ||
-       Curl_timestrcmp(needle->sasl_authzid, conn->sasl_authzid) ||
-       Curl_timestrcmp(needle->oauth_bearer, conn->oauth_bearer)) {
-      /* one of them was different */
-      return FALSE;
+static bool url_match_http_version(struct connectdata *conn,
+                                   struct url_conn_match *m)
+{
+  /* If looking for HTTP and the HTTP versions allowed do not include
+   * the HTTP version of conn, continue looking. */
+  if((m->needle->handler->protocol & PROTO_FAMILY_HTTP)) {
+    switch(Curl_conn_http_version(m->data, conn)) {
+    case 30:
+      if(!(m->data->state.http_neg.allowed & CURL_HTTP_V3x)) {
+        DEBUGF(infof(m->data, "not reusing conn #%" CURL_FORMAT_CURL_OFF_T
+               ", we do not want h3", conn->connection_id));
+        return FALSE;
+      }
+      break;
+    case 20:
+      if(!(m->data->state.http_neg.allowed & CURL_HTTP_V2x)) {
+        DEBUGF(infof(m->data, "not reusing conn #%" CURL_FORMAT_CURL_OFF_T
+               ", we do not want h2", conn->connection_id));
+        return FALSE;
+      }
+      break;
+    default:
+      if(!(m->data->state.http_neg.allowed & CURL_HTTP_V1x)) {
+        DEBUGF(infof(m->data, "not reusing conn #%" CURL_FORMAT_CURL_OFF_T
+               ", we do not want h1", conn->connection_id));
+        return FALSE;
+      }
+      break;
     }
   }
+  return TRUE;
+}
+#else
+#define url_match_http_multiplex(c,m)    ((void)c, (void)m, TRUE)
+#define url_match_http_version(c,m)      ((void)c, (void)m, TRUE)
+#endif
 
-#ifdef HAVE_GSSAPI
-  /* GSS delegation differences do not actually affect every connection
-     and auth method, but this check takes precaution before efficiency */
-  if(needle->gssapi_delegation != conn->gssapi_delegation)
+static bool url_match_proto_config(struct connectdata *conn,
+                                   struct url_conn_match *m)
+{
+  if(!url_match_http_version(conn, m))
     return FALSE;
-#endif
 
-  /* If looking for HTTP and the HTTP version we want is less
-   * than the HTTP version of conn, continue looking.
-   * CURL_HTTP_VERSION_2TLS is default which indicates no preference,
-   * so we take any existing connection. */
-  if((needle->handler->protocol & PROTO_FAMILY_HTTP) &&
-     (data->state.httpwant != CURL_HTTP_VERSION_2TLS)) {
-    unsigned char httpversion = Curl_conn_http_version(data);
-    if((httpversion >= 20) &&
-       (data->state.httpwant < CURL_HTTP_VERSION_2_0)) {
-      DEBUGF(infof(data, "nor reusing conn #%" CURL_FORMAT_CURL_OFF_T
-             " with httpversion=%d, we want a version less than h2",
-             conn->connection_id, httpversion));
-    }
-    if((httpversion >= 30) &&
-       (data->state.httpwant < CURL_HTTP_VERSION_3)) {
-      DEBUGF(infof(data, "nor reusing conn #%" CURL_FORMAT_CURL_OFF_T
-             " with httpversion=%d, we want a version less than h3",
-             conn->connection_id, httpversion));
-      return FALSE;
-    }
-  }
 #ifdef USE_SSH
-  else if(get_protocol_family(needle->handler) & PROTO_FAMILY_SSH) {
-    if(!ssh_config_matches(needle, conn))
+  if(get_protocol_family(m->needle->handler) & PROTO_FAMILY_SSH) {
+    if(!ssh_config_matches(m->needle, conn))
       return FALSE;
   }
 #endif
 #ifndef CURL_DISABLE_FTP
-  else if(get_protocol_family(needle->handler) & PROTO_FAMILY_FTP) {
-    /* Also match ACCOUNT, ALTERNATIVE-TO-USER, USE_SSL and CCC options */
-    if(Curl_timestrcmp(needle->proto.ftpc.account,
-                       conn->proto.ftpc.account) ||
-       Curl_timestrcmp(needle->proto.ftpc.alternative_to_user,
-                       conn->proto.ftpc.alternative_to_user) ||
-       (needle->proto.ftpc.use_ssl != conn->proto.ftpc.use_ssl) ||
-       (needle->proto.ftpc.ccc != conn->proto.ftpc.ccc))
+  else if(get_protocol_family(m->needle->handler) & PROTO_FAMILY_FTP) {
+    if(!ftp_conns_match(m->needle, conn))
       return FALSE;
   }
 #endif
+  return TRUE;
+}
+
+static bool url_match_auth(struct connectdata *conn,
+                           struct url_conn_match *m)
+{
+  if(!(m->needle->handler->flags & PROTOPT_CREDSPERREQUEST)) {
+    /* This protocol requires credentials per connection,
+       so verify that we are using the same name and password as well */
+    if(Curl_timestrcmp(m->needle->user, conn->user) ||
+       Curl_timestrcmp(m->needle->passwd, conn->passwd) ||
+       Curl_timestrcmp(m->needle->sasl_authzid, conn->sasl_authzid) ||
+       Curl_timestrcmp(m->needle->oauth_bearer, conn->oauth_bearer)) {
+      /* one of them was different */
+      return FALSE;
+    }
+  }
+#ifdef HAVE_GSSAPI
+  /* GSS delegation differences do not actually affect every connection
+     and auth method, but this check takes precaution before efficiency */
+  if(m->needle->gssapi_delegation != conn->gssapi_delegation)
+    return FALSE;
+#endif
+
+  return TRUE;
+}
 
+static bool url_match_destination(struct connectdata *conn,
+                                  struct url_conn_match *m)
+{
   /* Additional match requirements if talking TLS OR
    * not talking to an HTTP proxy OR using a tunnel through a proxy */
-  if((needle->handler->flags&PROTOPT_SSL)
+  if((m->needle->handler->flags&PROTOPT_SSL)
 #ifndef CURL_DISABLE_PROXY
-     || !needle->bits.httpproxy || needle->bits.tunnel_proxy
+     || !m->needle->bits.httpproxy || m->needle->bits.tunnel_proxy
 #endif
     ) {
-    /* Talking the same protocol scheme or a TLS upgraded protocol in the
-     * same protocol family? */
-    if(!strcasecompare(needle->handler->scheme, conn->handler->scheme) &&
-       (get_protocol_family(conn->handler) !=
-        needle->handler->protocol || !conn->bits.tls_upgraded))
-      return FALSE;
+    if(!strcasecompare(m->needle->handler->scheme, conn->handler->scheme)) {
+      /* `needle` and `conn` do not have the same scheme... */
+      if(get_protocol_family(conn->handler) != m->needle->handler->protocol) {
+        /* and `conn`s protocol family is not the protocol `needle` wants.
+         * IMAPS would work for IMAP, but no vice versa. */
+        return FALSE;
+      }
+      /* We are in an IMAPS vs IMAP like case. We expect `conn` to have SSL */
+      if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
+        DEBUGF(infof(m->data,
+          "Connection #%" FMT_OFF_T " has compatible protocol family, "
+          "but no SSL, no match", conn->connection_id));
+        return FALSE;
+      }
+    }
 
     /* If needle has "conn_to_*" set, conn must match this */
-    if((needle->bits.conn_to_host && !strcasecompare(
-        needle->conn_to_host.name, conn->conn_to_host.name)) ||
-       (needle->bits.conn_to_port &&
-         needle->conn_to_port != conn->conn_to_port))
+    if((m->needle->bits.conn_to_host && !strcasecompare(
+        m->needle->conn_to_host.name, conn->conn_to_host.name)) ||
+       (m->needle->bits.conn_to_port &&
+         m->needle->conn_to_port != conn->conn_to_port))
       return FALSE;
 
     /* hostname and port must match */
-    if(!strcasecompare(needle->host.name, conn->host.name) ||
-       needle->remote_port != conn->remote_port)
+    if(!strcasecompare(m->needle->host.name, conn->host.name) ||
+       m->needle->remote_port != conn->remote_port)
       return FALSE;
+  }
+  return TRUE;
+}
 
-    /* If talking TLS, conn needs to use the same SSL options. */
-    if((needle->handler->flags & PROTOPT_SSL) &&
-       !Curl_ssl_conn_config_match(data, conn, FALSE)) {
-      DEBUGF(infof(data,
-                   "Connection #%" FMT_OFF_T
-                   " has different SSL parameters, cannot reuse",
-                   conn->connection_id));
-      return FALSE;
-    }
+static bool url_match_ssl_config(struct connectdata *conn,
+                                 struct url_conn_match *m)
+{
+  /* If talking TLS, conn needs to use the same SSL options. */
+  if((m->needle->handler->flags & PROTOPT_SSL) &&
+     !Curl_ssl_conn_config_match(m->data, conn, FALSE)) {
+    DEBUGF(infof(m->data,
+                 "Connection #%" FMT_OFF_T
+                 " has different SSL parameters, cannot reuse",
+                 conn->connection_id));
+    return FALSE;
   }
+  return TRUE;
+}
 
-#if defined(USE_NTLM)
+#ifdef USE_NTLM
+static bool url_match_auth_ntlm(struct connectdata *conn,
+                                struct url_conn_match *m)
+{
   /* If we are looking for an HTTP+NTLM connection, check if this is
      already authenticating with the right credentials. If not, keep
      looking so that we can reuse NTLM connections if
      possible. (Especially we must not reuse the same connection if
      partway through a handshake!) */
-  if(match->want_ntlm_http) {
-    if(Curl_timestrcmp(needle->user, conn->user) ||
-       Curl_timestrcmp(needle->passwd, conn->passwd)) {
+  if(m->want_ntlm_http) {
+    if(Curl_timestrcmp(m->needle->user, conn->user) ||
+       Curl_timestrcmp(m->needle->passwd, conn->passwd)) {
 
       /* we prefer a credential match, but this is at least a connection
          that can be reused and "upgraded" to NTLM */
       if(conn->http_ntlm_state == NTLMSTATE_NONE)
-        match->found = conn;
+        m->found = conn;
       return FALSE;
     }
   }
@@ -1129,15 +1198,15 @@ static bool url_match_conn(struct connectdata *conn, void *userdata)
 
 #ifndef CURL_DISABLE_PROXY
   /* Same for Proxy NTLM authentication */
-  if(match->want_proxy_ntlm_http) {
+  if(m->want_proxy_ntlm_http) {
     /* Both conn->http_proxy.user and conn->http_proxy.passwd can be
      * NULL */
     if(!conn->http_proxy.user || !conn->http_proxy.passwd)
       return FALSE;
 
-    if(Curl_timestrcmp(needle->http_proxy.user,
+    if(Curl_timestrcmp(m->needle->http_proxy.user,
                        conn->http_proxy.user) ||
-       Curl_timestrcmp(needle->http_proxy.passwd,
+       Curl_timestrcmp(m->needle->http_proxy.passwd,
                        conn->http_proxy.passwd))
       return FALSE;
   }
@@ -1146,53 +1215,83 @@ static bool url_match_conn(struct connectdata *conn, void *userdata)
     return FALSE;
   }
 #endif
-  if(match->want_ntlm_http || match->want_proxy_ntlm_http) {
+  if(m->want_ntlm_http || m->want_proxy_ntlm_http) {
     /* Credentials are already checked, we may use this connection.
      * With NTLM being weird as it is, we MUST use a
      * connection where it has already been fully negotiated.
      * If it has not, we keep on looking for a better one. */
-    match->found = conn;
+    m->found = conn;
 
-    if((match->want_ntlm_http &&
+    if((m->want_ntlm_http &&
        (conn->http_ntlm_state != NTLMSTATE_NONE)) ||
-        (match->want_proxy_ntlm_http &&
+        (m->want_proxy_ntlm_http &&
          (conn->proxy_ntlm_state != NTLMSTATE_NONE))) {
       /* We must use this connection, no other */
-      match->force_reuse = TRUE;
+      m->force_reuse = TRUE;
       return TRUE;
     }
     /* Continue look up for a better connection */
     return FALSE;
   }
+  return TRUE;
+}
+#else
+#define url_match_auth_ntlm(c,m)    ((void)c, (void)m, TRUE)
 #endif
 
-  if(CONN_INUSE(conn)) {
-    DEBUGASSERT(match->may_multiplex);
-    DEBUGASSERT(conn->bits.multiplex);
-    /* If multiplexed, make sure we do not go over concurrency limit */
-    if(CONN_INUSE(conn) >=
-            Curl_multi_max_concurrent_streams(data->multi)) {
-      infof(data, "client side MAX_CONCURRENT_STREAMS reached"
-            ", skip (%zu)", CONN_INUSE(conn));
-      return FALSE;
-    }
-    if(CONN_INUSE(conn) >=
-            Curl_conn_get_max_concurrent(data, conn, FIRSTSOCKET)) {
-      infof(data, "MAX_CONCURRENT_STREAMS reached, skip (%zu)",
-            CONN_INUSE(conn));
-      return FALSE;
-    }
-    /* When not multiplexed, we have a match here! */
-    infof(data, "Multiplexed connection found");
-  }
-  else if(Curl_conn_seems_dead(conn, data, NULL)) {
-    /* removed and disconnect. Do not treat as aborted. */
-    Curl_cpool_disconnect(data, conn, FALSE);
+static bool url_match_conn(struct connectdata *conn, void *userdata)
+{
+  struct url_conn_match *m = userdata;
+  /* Check if `conn` can be used for transfer `m->data` */
+
+  /* general connect config setting match? */
+  if(!url_match_connect_config(conn, m))
+    return FALSE;
+
+  if(!url_match_destination(conn, m))
+    return FALSE;
+
+  if(!url_match_fully_connected(conn, m))
+    return FALSE;
+
+  if(!url_match_multiplex_needs(conn, m))
+    return FALSE;
+
+  if(!url_match_ssl_use(conn, m))
+    return FALSE;
+  if(!url_match_proxy_use(conn, m))
+    return FALSE;
+  if(!url_match_ssl_config(conn, m))
+    return FALSE;
+
+  if(!url_match_http_multiplex(conn, m))
+    return FALSE;
+  else if(m->wait_pipe)
+    /* we decided to wait on PIPELINING */
+    return TRUE;
+
+  if(!url_match_auth(conn, m))
+    return FALSE;
+
+  if(!url_match_proto_config(conn, m))
+    return FALSE;
+
+  if(!url_match_auth_ntlm(conn, m))
+    return FALSE;
+  else if(m->force_reuse)
+    return TRUE;
+
+  if(!url_match_multiplex_limits(conn, m))
+    return FALSE;
+
+  if(!CONN_INUSE(conn) && Curl_conn_seems_dead(conn, m->data, NULL)) {
+    /* remove and disconnect. */
+    Curl_conn_terminate(m->data, conn, FALSE);
     return FALSE;
   }
 
-  /* We have found a connection. Let's stop searching. */
-  match->found = conn;
+  /* conn matches our needs. */
+  m->found = conn;
   return TRUE;
 }
 
@@ -1260,7 +1359,7 @@ ConnectionExists(struct Curl_easy *data,
 
   /* Find a connection in the pool that matches what "data + needle"
    * requires. If a suitable candidate is found, it is attached to "data". */
-  result = Curl_cpool_find(data, needle->destination, needle->destination_len,
+  result = Curl_cpool_find(data, needle->destination,
                            url_match_conn, url_match_result, &match);
 
   /* wait_pipe is TRUE if we encounter a bundle that is undecided. There
@@ -1285,7 +1384,7 @@ void Curl_verboseconnect(struct Curl_easy *data,
     infof(data, "Connected to %s (%s) port %u",
           CURL_CONN_HOST_DISPNAME(conn), conn->primary.remote_ip,
           conn->primary.remote_port);
-#if !defined(CURL_DISABLE_HTTP)
+#ifndef CURL_DISABLE_HTTP
     if(conn->handler->protocol & PROTO_FAMILY_HTTP) {
       switch(conn->alpn) {
       case CURL_HTTP_VERSION_3:
@@ -1328,7 +1427,7 @@ static struct connectdata *allocate_conn(struct Curl_easy *data)
   connclose(conn, "Default to force-close");
 
   /* Store creation time to help future close decision making */
-  conn->created = Curl_now();
+  conn->created = curlx_now();
 
   /* Store current time to give a baseline to keepalive connection times. */
   conn->keepalive = conn->created;
@@ -1364,8 +1463,8 @@ static struct connectdata *allocate_conn(struct Curl_easy *data)
   conn->connect_only = data->set.connect_only;
   conn->transport = TRNSPRT_TCP; /* most of them are TCP streams */
 
-  /* Initialize the easy handle list */
-  Curl_llist_init(&conn->easyq, NULL);
+  /* Initialize the attached xfers bitset */
+  Curl_uint_spbset_init(&conn->xfers_attached);
 
 #ifdef HAVE_GSSAPI
   conn->data_prot = PROT_CLEAR;
@@ -1440,7 +1539,7 @@ const struct Curl_handler *Curl_getn_scheme_handler(const char *scheme,
 #else
     NULL,
 #endif
-#if defined(USE_SSH)
+#ifdef USE_SSH
     &Curl_handler_sftp,
 #else
     NULL,
@@ -1682,12 +1781,12 @@ static void zonefrom_url(CURLU *uh, struct Curl_easy *data,
 #endif
 
   if(!uc && zoneid) {
-    char *endp;
-    unsigned long scope = strtoul(zoneid, &endp, 10);
-    if(!*endp && (scope < UINT_MAX))
+    const char *p = zoneid;
+    curl_off_t scope;
+    if(!curlx_str_number(&p, &scope, UINT_MAX))
       /* A plain number, use it directly as a scope id. */
       conn->scope_id = (unsigned int)scope;
-#if defined(HAVE_IF_NAMETOINDEX)
+#ifdef HAVE_IF_NAMETOINDEX
     else {
 #elif defined(_WIN32)
     else if(Curl_if_nametoindex) {
@@ -1696,7 +1795,7 @@ static void zonefrom_url(CURLU *uh, struct Curl_easy *data,
 #if defined(HAVE_IF_NAMETOINDEX) || defined(_WIN32)
       /* Zone identifier is not numeric */
       unsigned int scopeidx = 0;
-#if defined(_WIN32)
+#ifdef _WIN32
       scopeidx = Curl_if_nametoindex(zoneid);
 #else
       scopeidx = if_nametoindex(zoneid);
@@ -1921,10 +2020,17 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data,
       return CURLE_OUT_OF_MEMORY;
   }
   else {
-    unsigned long port = strtoul(data->state.up.port, NULL, 10);
-    conn->primary.remote_port = conn->remote_port =
-      (data->set.use_port && data->state.allow_port) ?
-      data->set.use_port : curlx_ultous(port);
+    curl_off_t port;
+    bool valid = TRUE;
+    if(data->set.use_port && data->state.allow_port)
+      port = data->set.use_port;
+    else {
+      const char *p = data->state.up.port;
+      if(curlx_str_number(&p, &port, 0xffff))
+        valid = FALSE;
+    }
+    if(valid)
+      conn->primary.remote_port = conn->remote_port = (unsigned short)port;
   }
 
   (void)curl_url_get(uh, CURLUPART_QUERY, &data->state.up.query, 0);
@@ -2029,9 +2135,8 @@ static CURLcode setup_connection_internals(struct Curl_easy *data,
   if(!conn->destination)
     return CURLE_OUT_OF_MEMORY;
 
-  conn->destination_len = strlen(conn->destination) + 1;
   Curl_strntolower(conn->destination, conn->destination,
-                   conn->destination_len - 1);
+                   strlen(conn->destination));
 
   return CURLE_OK;
 }
@@ -2068,7 +2173,7 @@ static char *detect_proxy(struct Curl_easy *data,
    * checked if the lowercase versions do not exist.
    */
   char proxy_env[20];
-  char *envp = proxy_env;
+  const char *envp = proxy_env;
 #ifdef CURL_DISABLE_VERBOSE_STRINGS
   (void)data;
 #endif
@@ -2109,10 +2214,10 @@ static char *detect_proxy(struct Curl_easy *data,
     }
     if(!proxy) {
 #endif
-      envp = (char *)"all_proxy";
+      envp = "all_proxy";
       proxy = curl_getenv(envp); /* default proxy to use */
       if(!proxy) {
-        envp = (char *)"ALL_PROXY";
+        envp = "ALL_PROXY";
         proxy = curl_getenv(envp);
       }
 #ifndef CURL_DISABLE_WEBSOCKETS
@@ -2228,7 +2333,7 @@ static CURLcode parse_proxy(struct Curl_easy *data,
     goto error;
 
   if(proxyuser || proxypasswd) {
-    Curl_safefree(proxyinfo->user);
+    free(proxyinfo->user);
     proxyinfo->user = proxyuser;
     result = Curl_setstropt(&data->state.aptr.proxyuser, proxyuser);
     proxyuser = NULL;
@@ -2253,7 +2358,10 @@ static CURLcode parse_proxy(struct Curl_easy *data,
   (void)curl_url_get(uhp, CURLUPART_PORT, &portptr, 0);
 
   if(portptr) {
-    port = (int)strtol(portptr, NULL, 10);
+    curl_off_t num;
+    const char *p = portptr;
+    if(!curlx_str_number(&p, &num, 0xffff))
+      port = (int)num;
     free(portptr);
   }
   else {
@@ -2297,7 +2405,7 @@ static CURLcode parse_proxy(struct Curl_easy *data,
         result = CURLE_OUT_OF_MEMORY;
         goto error;
       }
-      Curl_safefree(proxyinfo->host.rawalloc);
+      free(proxyinfo->host.rawalloc);
       proxyinfo->host.rawalloc = host;
       proxyinfo->host.name = host;
       host = NULL;
@@ -2306,7 +2414,7 @@ static CURLcode parse_proxy(struct Curl_easy *data,
 
   if(!is_unix_proxy) {
 #endif
-    Curl_safefree(proxyinfo->host.rawalloc);
+    free(proxyinfo->host.rawalloc);
     proxyinfo->host.rawalloc = host;
     if(host[0] == '[') {
       /* this is a numerical IPv6, strip off the brackets */
@@ -2493,7 +2601,7 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data,
         if(!conn->socks_proxy.user) {
           conn->socks_proxy.user = conn->http_proxy.user;
           conn->http_proxy.user = NULL;
-          Curl_safefree(conn->socks_proxy.passwd);
+          free(conn->socks_proxy.passwd);
           conn->socks_proxy.passwd = conn->http_proxy.passwd;
           conn->http_proxy.passwd = NULL;
         }
@@ -2728,7 +2836,7 @@ static CURLcode override_login(struct Curl_easy *data,
       }
     }
     if(url_provided) {
-      Curl_safefree(conn->user);
+      free(conn->user);
       conn->user = strdup(*userp);
       if(!conn->user)
         return CURLE_OUT_OF_MEMORY;
@@ -2836,7 +2944,7 @@ static CURLcode parse_connect_to_host_port(struct Curl_easy *data,
   int port = -1;
   CURLcode result = CURLE_OK;
 
-#if defined(CURL_DISABLE_VERBOSE_STRINGS)
+#ifdef CURL_DISABLE_VERBOSE_STRINGS
   (void) data;
 #endif
 
@@ -2891,19 +2999,18 @@ static CURLcode parse_connect_to_host_port(struct Curl_easy *data,
   /* Get port number off server.com:1080 */
   host_portno = strchr(portptr, ':');
   if(host_portno) {
-    char *endp = NULL;
     *host_portno = '\0'; /* cut off number from hostname */
     host_portno++;
     if(*host_portno) {
-      long portparse = strtol(host_portno, &endp, 10);
-      if((endp && *endp) || (portparse < 0) || (portparse > 65535)) {
+      curl_off_t portparse;
+      const char *p = host_portno;
+      if(curlx_str_number(&p, &portparse, 0xffff)) {
         failf(data, "No valid port number in connect to host string (%s)",
               host_portno);
         result = CURLE_SETOPT_OPTION_SYNTAX;
         goto error;
       }
-      else
-        port = (int)portparse; /* we know it will fit */
+      port = (int)portparse; /* we know it will fit */
     }
   }
 
@@ -2974,12 +3081,11 @@ static CURLcode parse_connect_to_string(struct Curl_easy *data,
       /* check whether the URL's port matches */
       char *ptr_next = strchr(ptr, ':');
       if(ptr_next) {
-        char *endp = NULL;
-        long port_to_match = strtol(ptr, &endp, 10);
-        if((endp == ptr_next) && (port_to_match == conn->remote_port)) {
+        curl_off_t port_to_match;
+        if(!curlx_str_number(&ptr, &port_to_match, 0xffff) &&
+           (port_to_match == (curl_off_t)conn->remote_port))
           port_match = TRUE;
-          ptr = ptr_next + 1;
-        }
+        ptr = ptr_next + 1;
       }
     }
   }
@@ -3049,75 +3155,54 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data,
        )) {
     /* no connect_to match, try alt-svc! */
     enum alpnid srcalpnid = ALPN_none;
-    bool use_alt_svc = FALSE;
     bool hit = FALSE;
     struct altsvc *as = NULL;
-    const int allowed_versions = ( ALPN_h1
-#ifdef USE_HTTP2
-                                   | ALPN_h2
-#endif
-#ifdef USE_HTTP3
-                                   | ALPN_h3
-#endif
-      ) & data->asi->flags;
-    static enum alpnid alpn_ids[] = {
+    int allowed_alpns = ALPN_none;
+    struct http_negotiation *neg = &data->state.http_neg;
+
+    DEBUGF(infof(data, "Alt-svc check wanted=%x, allowed=%x",
+                 neg->wanted, neg->allowed));
 #ifdef USE_HTTP3
-      ALPN_h3,
+    if(neg->allowed & CURL_HTTP_V3x)
+      allowed_alpns |= ALPN_h3;
 #endif
 #ifdef USE_HTTP2
-      ALPN_h2,
+    if(neg->allowed & CURL_HTTP_V2x)
+      allowed_alpns |= ALPN_h2;
 #endif
-      ALPN_h1,
-    };
-    size_t i;
-
-    switch(data->state.httpwant) {
-    case CURL_HTTP_VERSION_1_0:
-      break;
-    case CURL_HTTP_VERSION_1_1:
-      use_alt_svc = TRUE;
-      srcalpnid = ALPN_h1; /* only regard alt-svc advice for http/1.1 */
-      break;
-    case CURL_HTTP_VERSION_2_0:
-      use_alt_svc = TRUE;
-      srcalpnid = ALPN_h2; /* only regard alt-svc advice for h2 */
-      break;
-    case CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE:
-      break;
-    case CURL_HTTP_VERSION_3:
-      use_alt_svc = TRUE;
-      srcalpnid = ALPN_h3; /* only regard alt-svc advice for h3 */
-      break;
-    case CURL_HTTP_VERSION_3ONLY:
-      break;
-    default: /* no specific HTTP version wanted, look at all of alt-svc */
-      use_alt_svc = TRUE;
-      srcalpnid = ALPN_none;
-      break;
-    }
-    if(!use_alt_svc)
-      return CURLE_OK;
+    if(neg->allowed & CURL_HTTP_V1x)
+      allowed_alpns |= ALPN_h1;
+    allowed_alpns &= (int)data->asi->flags;
 
     host = conn->host.rawalloc;
     DEBUGF(infof(data, "check Alt-Svc for host %s", host));
-    if(srcalpnid == ALPN_none) {
-      /* scan all alt-svc protocol ids in order or relevance */
-      for(i = 0; !hit && (i < CURL_ARRAYSIZE(alpn_ids)); ++i) {
-        srcalpnid = alpn_ids[i];
-        hit = Curl_altsvc_lookup(data->asi,
-                                 srcalpnid, host, conn->remote_port, /* from */
-                                 &as /* to */,
-                                 allowed_versions);
-      }
+#ifdef USE_HTTP3
+    if(!hit && (neg->wanted & CURL_HTTP_V3x)) {
+      srcalpnid = ALPN_h3;
+      hit = Curl_altsvc_lookup(data->asi,
+                               ALPN_h3, host, conn->remote_port, /* from */
+                               &as /* to */,
+                               allowed_alpns);
     }
-    else {
-      /* look for a specific alt-svc protocol id */
+ #endif
+ #ifdef USE_HTTP2
+    if(!hit && (neg->wanted & CURL_HTTP_V2x) &&
+       !neg->h2_prior_knowledge) {
+      srcalpnid = ALPN_h2;
       hit = Curl_altsvc_lookup(data->asi,
-                               srcalpnid, host, conn->remote_port, /* from */
+                               ALPN_h2, host, conn->remote_port, /* from */
                                &as /* to */,
-                               allowed_versions);
+                               allowed_alpns);
+    }
+ #endif
+    if(!hit && (neg->wanted & CURL_HTTP_V1x) &&
+       !neg->only_10) {
+      srcalpnid = ALPN_h1;
+      hit = Curl_altsvc_lookup(data->asi,
+                               ALPN_h1, host, conn->remote_port, /* from */
+                               &as /* to */,
+                               allowed_alpns);
     }
-
 
     if(hit) {
       char *hostd = strdup((char *)as->dst.host);
@@ -3136,14 +3221,15 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data,
         /* protocol version switch */
         switch(as->dst.alpnid) {
         case ALPN_h1:
-          data->state.httpwant = CURL_HTTP_VERSION_1_1;
+          neg->wanted = neg->allowed = CURL_HTTP_V1x;
+          neg->only_10 = FALSE;
           break;
         case ALPN_h2:
-          data->state.httpwant = CURL_HTTP_VERSION_2_0;
+          neg->wanted = neg->allowed = CURL_HTTP_V2x;
           break;
         case ALPN_h3:
           conn->transport = TRNSPRT_QUIC;
-          data->state.httpwant = CURL_HTTP_VERSION_3;
+          neg->wanted = neg->allowed = CURL_HTTP_V3x;
           break;
         default: /* should not be possible */
           break;
@@ -3159,13 +3245,14 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data,
 #ifdef USE_UNIX_SOCKETS
 static CURLcode resolve_unix(struct Curl_easy *data,
                              struct connectdata *conn,
-                             char *unix_path)
+                             char *unix_path,
+                             struct Curl_dns_entry **pdns)
 {
-  struct Curl_dns_entry *hostaddr = NULL;
+  struct Curl_dns_entry *hostaddr;
   bool longpath = FALSE;
 
   DEBUGASSERT(unix_path);
-  DEBUGASSERT(conn->dns_entry == NULL);
+  *pdns = NULL;
 
   /* Unix domain sockets are local. The host gets ignored, just use the
    * specified domain socket address. Do not cache "DNS entries". There is
@@ -3185,7 +3272,7 @@ static CURLcode resolve_unix(struct Curl_easy *data,
   }
 
   hostaddr->refcount = 1; /* connection is the only one holding this */
-  conn->dns_entry = hostaddr;
+  *pdns = hostaddr;
   return CURLE_OK;
 }
 #endif
@@ -3195,31 +3282,35 @@ static CURLcode resolve_unix(struct Curl_easy *data,
  *************************************************************/
 static CURLcode resolve_server(struct Curl_easy *data,
                                struct connectdata *conn,
-                               bool *async)
+                               bool *async,
+                               struct Curl_dns_entry **pdns)
 {
   struct hostname *ehost;
   timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
   const char *peertype = "host";
-  int rc;
+  CURLcode result;
+
+  *pdns = NULL;
+
 #ifdef USE_UNIX_SOCKETS
-  char *unix_path = conn->unix_domain_socket;
+  {
+    char *unix_path = conn->unix_domain_socket;
 
 #ifndef CURL_DISABLE_PROXY
-  if(!unix_path && CONN_IS_PROXIED(conn) && conn->socks_proxy.host.name &&
-     !strncmp(UNIX_SOCKET_PREFIX"/",
-              conn->socks_proxy.host.name, sizeof(UNIX_SOCKET_PREFIX)))
-    unix_path = conn->socks_proxy.host.name + sizeof(UNIX_SOCKET_PREFIX) - 1;
+    if(!unix_path && CONN_IS_PROXIED(conn) && conn->socks_proxy.host.name &&
+       !strncmp(UNIX_SOCKET_PREFIX"/",
+                conn->socks_proxy.host.name, sizeof(UNIX_SOCKET_PREFIX)))
+      unix_path = conn->socks_proxy.host.name + sizeof(UNIX_SOCKET_PREFIX) - 1;
 #endif
 
-  if(unix_path) {
-    /* This only works if previous transport is TRNSPRT_TCP. Check it? */
-    conn->transport = TRNSPRT_UNIX;
-    return resolve_unix(data, conn, unix_path);
+    if(unix_path) {
+      /* This only works if previous transport is TRNSPRT_TCP. Check it? */
+      conn->transport = TRNSPRT_UNIX;
+      return resolve_unix(data, conn, unix_path, pdns);
+    }
   }
 #endif
 
-  DEBUGASSERT(conn->dns_entry == NULL);
-
 #ifndef CURL_DISABLE_PROXY
   if(CONN_IS_PROXIED(conn)) {
     ehost = conn->bits.socksproxy ? &conn->socks_proxy.host :
@@ -3241,22 +3332,25 @@ static CURLcode resolve_server(struct Curl_easy *data,
   if(!conn->hostname_resolve)
     return CURLE_OUT_OF_MEMORY;
 
-  rc = Curl_resolv_timeout(data, conn->hostname_resolve,
-                           conn->primary.remote_port,
-                           &conn->dns_entry, timeout_ms);
-  if(rc == CURLRESOLV_PENDING)
+  result = Curl_resolv_timeout(data, conn->hostname_resolve,
+                               conn->primary.remote_port, conn->ip_version,
+                               pdns, timeout_ms);
+  DEBUGASSERT(!result || !*pdns);
+  if(result == CURLE_AGAIN) {
     *async = TRUE;
-  else if(rc == CURLRESOLV_TIMEDOUT) {
+    return CURLE_OK;
+  }
+  else if(result == CURLE_OPERATION_TIMEDOUT) {
     failf(data, "Failed to resolve %s '%s' with timeout after %"
           FMT_TIMEDIFF_T " ms", peertype, ehost->dispname,
-          Curl_timediff(Curl_now(), data->progress.t_startsingle));
+          curlx_timediff(curlx_now(), data->progress.t_startsingle));
     return CURLE_OPERATION_TIMEDOUT;
   }
-  else if(!conn->dns_entry) {
+  else if(result) {
     failf(data, "Could not resolve %s: %s", peertype, ehost->dispname);
-    return CURLE_COULDNT_RESOLVE_HOST;
+    return result;
   }
-
+  DEBUGASSERT(*pdns);
   return CURLE_OK;
 }
 
@@ -3273,8 +3367,8 @@ static void reuse_conn(struct Curl_easy *data,
    * be new for this request even when we reuse an existing connection */
   if(temp->user) {
     /* use the new username and password though */
-    Curl_safefree(existing->user);
-    Curl_safefree(existing->passwd);
+    free(existing->user);
+    free(existing->passwd);
     existing->user = temp->user;
     existing->passwd = temp->passwd;
     temp->user = NULL;
@@ -3285,10 +3379,10 @@ static void reuse_conn(struct Curl_easy *data,
   existing->bits.proxy_user_passwd = temp->bits.proxy_user_passwd;
   if(existing->bits.proxy_user_passwd) {
     /* use the new proxy username and proxy password though */
-    Curl_safefree(existing->http_proxy.user);
-    Curl_safefree(existing->socks_proxy.user);
-    Curl_safefree(existing->http_proxy.passwd);
-    Curl_safefree(existing->socks_proxy.passwd);
+    free(existing->http_proxy.user);
+    free(existing->socks_proxy.user);
+    free(existing->http_proxy.passwd);
+    free(existing->socks_proxy.passwd);
     existing->http_proxy.user = temp->http_proxy.user;
     existing->socks_proxy.user = temp->socks_proxy.user;
     existing->http_proxy.passwd = temp->http_proxy.passwd;
@@ -3325,8 +3419,7 @@ static void reuse_conn(struct Curl_easy *data,
   temp->conn_to_host.rawalloc = NULL;
   existing->conn_to_port = temp->conn_to_port;
   existing->remote_port = temp->remote_port;
-  Curl_safefree(existing->hostname_resolve);
-
+  free(existing->hostname_resolve);
   existing->hostname_resolve = temp->hostname_resolve;
   temp->hostname_resolve = NULL;
 
@@ -3336,6 +3429,15 @@ static void reuse_conn(struct Curl_easy *data,
   Curl_conn_free(data, temp);
 }
 
+static void conn_meta_freeentry(void *p)
+{
+  (void)p;
+  /* Will always be FALSE. Cannot use a 0 assert here since compilers
+   * are not in agreement if they then want a NORETURN attribute or
+   * not. *sigh* */
+  DEBUGASSERT(p == NULL);
+}
+
 /**
  * create_conn() sets up a new connectdata struct, or reuses an already
  * existing one, and resolves hostname.
@@ -3346,14 +3448,14 @@ static void reuse_conn(struct Curl_easy *data,
  *
  * @param data The sessionhandle pointer
  * @param in_connect is set to the next connection data pointer
- * @param async is set TRUE when an async DNS resolution is pending
+ * @param reusedp is set to to TRUE if connection was reused
  * @see Curl_setup_conn()
  *
  */
 
 static CURLcode create_conn(struct Curl_easy *data,
                             struct connectdata **in_connect,
-                            bool *async)
+                            bool *reusedp)
 {
   CURLcode result = CURLE_OK;
   struct connectdata *conn;
@@ -3363,7 +3465,7 @@ static CURLcode create_conn(struct Curl_easy *data,
   bool force_reuse = FALSE;
   bool waitpipe = FALSE;
 
-  *async = FALSE;
+  *reusedp = FALSE;
   *in_connect = NULL;
 
   /*************************************************************
@@ -3390,6 +3492,13 @@ static CURLcode create_conn(struct Curl_easy *data,
      any failure */
   *in_connect = conn;
 
+  /* Do the unfailable inits first, before checks that may early return */
+  Curl_hash_init(&conn->meta_hash, 23,
+               Curl_hash_str, curlx_str_key_compare, conn_meta_freeentry);
+
+  /* GSSAPI related inits */
+  Curl_sec_conn_init(conn);
+
   result = parseurlandfillconn(data, conn);
   if(result)
     goto out;
@@ -3530,30 +3639,26 @@ static CURLcode create_conn(struct Curl_easy *data,
     /* conn_protocol can only provide "old" protocols */
     data->info.conn_protocol = (conn->handler->protocol) & CURLPROTO_MASK;
     result = conn->handler->connect_it(data, &done);
+    if(result)
+      goto out;
 
     /* Setup a "faked" transfer that will do nothing */
+    Curl_attach_connection(data, conn);
+    result = Curl_cpool_add(data, conn);
     if(!result) {
-      Curl_attach_connection(data, conn);
-      result = Curl_cpool_add_conn(data, conn);
-      if(result)
-        goto out;
-
-      /*
-       * Setup whatever necessary for a resumed transfer
-       */
+      /* Setup whatever necessary for a resumed transfer */
       result = setup_range(data);
-      if(result) {
-        DEBUGASSERT(conn->handler->done);
-        /* we ignore the return code for the protocol-specific DONE */
-        (void)conn->handler->done(data, result, FALSE);
-        goto out;
+      if(!result) {
+        Curl_xfer_setup_nop(data);
+        result = Curl_init_do(data, conn);
       }
-      Curl_xfer_setup_nop(data);
     }
 
-    /* since we skip do_init() */
-    Curl_init_do(data, conn);
-
+    if(result) {
+      DEBUGASSERT(conn->handler->done);
+      /* we ignore the return code for the protocol-specific DONE */
+      (void)conn->handler->done(data, result, FALSE);
+    }
     goto out;
   }
 #endif
@@ -3597,18 +3702,25 @@ static CURLcode create_conn(struct Curl_easy *data,
      * `existing` and thus we need to cleanup the one we just
      * allocated before we can move along and use `existing`.
      */
+    bool tls_upgraded = (!(conn->given->flags & PROTOPT_SSL) &&
+                         Curl_conn_is_ssl(conn, FIRSTSOCKET));
+
     reuse_conn(data, conn, existing);
     conn = existing;
     *in_connect = conn;
 
 #ifndef CURL_DISABLE_PROXY
-    infof(data, "Re-using existing connection with %s %s",
+    infof(data, "Re-using existing %s: connection%s with %s %s",
+          conn->given->scheme,
+          tls_upgraded ? " (upgraded to SSL)" : "",
           conn->bits.proxy ? "proxy" : "host",
           conn->socks_proxy.host.name ? conn->socks_proxy.host.dispname :
           conn->http_proxy.host.name ? conn->http_proxy.host.dispname :
           conn->host.dispname);
 #else
-    infof(data, "Re-using existing connection with host %s",
+    infof(data, "Re-using existing %s: connection%s with host %s",
+          conn->given->scheme,
+          tls_upgraded ? " (upgraded to SSL)" : "",
           conn->host.dispname);
 #endif
   }
@@ -3624,10 +3736,12 @@ static CURLcode create_conn(struct Curl_easy *data,
         conn->bits.tls_enable_alpn = TRUE;
     }
 
-    if(waitpipe)
+    if(waitpipe) {
       /* There is a connection that *might* become usable for multiplexing
          "soon", and we wait for that */
+      infof(data, "Waiting on connection to negotiate possible multiplexing.");
       connections_available = FALSE;
+    }
     else {
       switch(Curl_cpool_check_limits(data, conn)) {
       case CPOOL_LIMIT_DEST:
@@ -3635,13 +3749,12 @@ static CURLcode create_conn(struct Curl_easy *data,
         connections_available = FALSE;
         break;
       case CPOOL_LIMIT_TOTAL:
-#ifndef CURL_DISABLE_DOH
-        if(data->set.dohfor_mid >= 0)
-          infof(data, "Allowing DoH to override max connection limit");
-        else
-#endif
-        {
-          infof(data, "No connections available in cache");
+        if(data->master_mid != UINT_MAX)
+          CURL_TRC_M(data, "Allowing sub-requests (like DoH) to override "
+                     "max connection limit");
+        else {
+          infof(data, "No connections available, total of %ld reached.",
+                data->multi->max_total_connections);
           connections_available = FALSE;
         }
         break;
@@ -3651,8 +3764,6 @@ static CURLcode create_conn(struct Curl_easy *data,
     }
 
     if(!connections_available) {
-      infof(data, "No connections available.");
-
       Curl_conn_free(data, conn);
       *in_connect = NULL;
 
@@ -3671,12 +3782,12 @@ static CURLcode create_conn(struct Curl_easy *data,
       }
 
       Curl_attach_connection(data, conn);
-      result = Curl_cpool_add_conn(data, conn);
+      result = Curl_cpool_add(data, conn);
       if(result)
         goto out;
     }
 
-#if defined(USE_NTLM)
+#ifdef USE_NTLM
     /* If NTLM is requested in a part of this connection, make sure we do not
        assume the state is fine as this is a fresh connection and NTLM is
        connection based. */
@@ -3697,7 +3808,9 @@ static CURLcode create_conn(struct Curl_easy *data,
   }
 
   /* Setup and init stuff before DO starts, in preparing for the transfer. */
-  Curl_init_do(data, conn);
+  result = Curl_init_do(data, conn);
+  if(result)
+    goto out;
 
   /*
    * Setup whatever necessary for a resumed transfer
@@ -3712,15 +3825,7 @@ static CURLcode create_conn(struct Curl_easy *data,
     /* We are reusing the connection - no need to resolve anything, and
        idnconvert_hostname() was called already in create_conn() for the reuse
        case. */
-    *async = FALSE;
-  }
-  else {
-    /*************************************************************
-     * Resolve the address of the server or proxy
-     *************************************************************/
-    result = resolve_server(data, conn, async);
-    if(result)
-      goto out;
+    *reusedp = TRUE;
   }
 
   /* persist the scheme and handler the transfer is using */
@@ -3749,24 +3854,17 @@ static CURLcode create_conn(struct Curl_easy *data,
  * Curl_setup_conn() also handles reused connections
  */
 CURLcode Curl_setup_conn(struct Curl_easy *data,
+                         struct Curl_dns_entry *dns,
                          bool *protocol_done)
 {
   CURLcode result = CURLE_OK;
   struct connectdata *conn = data->conn;
 
+  DEBUGASSERT(dns);
   Curl_pgrsTime(data, TIMER_NAMELOOKUP);
 
-  if(conn->handler->flags & PROTOPT_NONETWORK) {
-    /* nothing to setup when not using a network */
-    *protocol_done = TRUE;
-    return result;
-  }
-
-  /* set start time here for timeout purposes in the connect procedure, it
-     is later set again for the progress meter purpose */
-  conn->now = Curl_now();
   if(!conn->bits.reuse)
-    result = Curl_conn_setup(data, conn, FIRSTSOCKET, conn->dns_entry,
+    result = Curl_conn_setup(data, conn, FIRSTSOCKET, dns,
                              CURL_CF_SSL_DEFAULT);
   if(!result)
     result = Curl_headers_init(data);
@@ -3782,35 +3880,56 @@ CURLcode Curl_connect(struct Curl_easy *data,
 {
   CURLcode result;
   struct connectdata *conn;
+  bool reused = FALSE;
 
   *asyncp = FALSE; /* assume synchronous resolves by default */
+  *protocol_done = FALSE;
 
   /* Set the request to virgin state based on transfer settings */
   Curl_req_hard_reset(&data->req, data);
 
   /* call the stuff that needs to be called */
-  result = create_conn(data, &conn, asyncp);
+  result = create_conn(data, &conn, &reused);
+
+  if(result == CURLE_NO_CONNECTION_AVAILABLE) {
+    DEBUGASSERT(!conn);
+    return result;
+  }
 
   if(!result) {
-    if(CONN_INUSE(conn) > 1)
-      /* multiplexed */
+    DEBUGASSERT(conn);
+    if(reused) {
+      if(CONN_ATTACHED(conn) > 1)
+        /* multiplexed */
+        *protocol_done = TRUE;
+    }
+    else if(conn->handler->flags & PROTOPT_NONETWORK) {
+      *asyncp = FALSE;
+      Curl_pgrsTime(data, TIMER_NAMELOOKUP);
       *protocol_done = TRUE;
-    else if(!*asyncp) {
-      /* DNS resolution is done: that is either because this is a reused
-         connection, in which case DNS was unnecessary, or because DNS
-         really did finish already (synch resolver/fast async resolve) */
-      result = Curl_setup_conn(data, protocol_done);
+    }
+    else {
+      /*************************************************************
+       * Resolve the address of the server or proxy
+       *************************************************************/
+      struct Curl_dns_entry *dns;
+      result = resolve_server(data, conn, asyncp, &dns);
+      if(!result) {
+        *asyncp = !dns;
+        if(dns)
+          /* DNS resolution is done: that is either because this is a reused
+             connection, in which case DNS was unnecessary, or because DNS
+             really did finish already (synch resolver/fast async resolve) */
+          result = Curl_setup_conn(data, dns, protocol_done);
+      }
     }
   }
 
-  if(result == CURLE_NO_CONNECTION_AVAILABLE) {
-    return result;
-  }
-  else if(result && conn) {
+  if(result && conn) {
     /* We are not allowed to return failure with memory left allocated in the
        connectdata struct, free those here */
     Curl_detach_connection(data);
-    Curl_cpool_disconnect(data, conn, TRUE);
+    Curl_conn_terminate(data, conn, TRUE);
   }
 
   return result;
@@ -3953,3 +4072,25 @@ void Curl_data_priority_clear_state(struct Curl_easy *data)
 }
 
 #endif /* defined(USE_HTTP2) || defined(USE_HTTP3) */
+
+
+CURLcode Curl_conn_meta_set(struct connectdata *conn, const char *key,
+                            void *meta_data, Curl_meta_dtor *meta_dtor)
+{
+  if(!Curl_hash_add2(&conn->meta_hash, CURL_UNCONST(key), strlen(key) + 1,
+                     meta_data, meta_dtor)) {
+    meta_dtor(CURL_UNCONST(key), strlen(key) + 1, meta_data);
+    return CURLE_OUT_OF_MEMORY;
+  }
+  return CURLE_OK;
+}
+
+void Curl_conn_meta_remove(struct connectdata *conn, const char *key)
+{
+  Curl_hash_delete(&conn->meta_hash, CURL_UNCONST(key), strlen(key) + 1);
+}
+
+void *Curl_conn_meta_get(struct connectdata *conn, const char *key)
+{
+  return Curl_hash_pick(&conn->meta_hash, CURL_UNCONST(key), strlen(key) + 1);
+}
diff --git a/extra/curl/curl-8.12.1/lib/url.h b/extra/curl/curl-8.14.1/lib/url.h
similarity index 75%
rename from extra/curl/curl-8.12.1/lib/url.h
rename to extra/curl/curl-8.14.1/lib/url.h
index 47c1db44f3ec..7aba98dbb936 100644
--- a/extra/curl/curl-8.12.1/lib/url.h
+++ b/extra/curl/curl-8.14.1/lib/url.h
@@ -37,15 +37,36 @@ void Curl_freeset(struct Curl_easy *data);
 CURLcode Curl_uc_to_curlcode(CURLUcode uc);
 CURLcode Curl_close(struct Curl_easy **datap); /* opposite of curl_open() */
 CURLcode Curl_connect(struct Curl_easy *, bool *async, bool *protocol_connect);
-bool Curl_on_disconnect(struct Curl_easy *data,
-                        struct connectdata *, bool aborted);
 CURLcode Curl_setup_conn(struct Curl_easy *data,
+                         struct Curl_dns_entry *dns,
                          bool *protocol_done);
 void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn);
 CURLcode Curl_parse_login_details(const char *login, const size_t len,
                                   char **userptr, char **passwdptr,
                                   char **optionsptr);
 
+/* Attach/Clear/Get meta data for an easy handle. Needs to provide
+ * a destructor, will be automatically called when the easy handle
+ * is reset or closed. */
+typedef void Curl_meta_dtor(void *key, size_t key_len, void *meta_data);
+
+/* Set the transfer meta data for the key. Any existing entry for that
+ * key will be destroyed.
+ * Takes ownership of `meta_data` and destroys it when the call fails. */
+CURLcode Curl_meta_set(struct Curl_easy *data, const char *key,
+                       void *meta_data, Curl_meta_dtor *meta_dtor);
+void Curl_meta_remove(struct Curl_easy *data, const char *key);
+void *Curl_meta_get(struct Curl_easy *data, const char *key);
+void Curl_meta_reset(struct Curl_easy *data);
+
+/* Set connection meta data for the key. Any existing entry for that
+ * key will be destroyed.
+ * Takes ownership of `meta_data` and destroys it when the call fails. */
+CURLcode Curl_conn_meta_set(struct connectdata *conn, const char *key,
+                            void *meta_data, Curl_meta_dtor *meta_dtor);
+void Curl_conn_meta_remove(struct connectdata *conn, const char *key);
+void *Curl_conn_meta_get(struct connectdata *conn, const char *key);
+
 /* Get protocol handler for a URI scheme
  * @param scheme URI scheme, case-insensitive
  * @return NULL of handler not found
diff --git a/extra/curl/curl-8.12.1/lib/urlapi-int.h b/extra/curl/curl-8.14.1/lib/urlapi-int.h
similarity index 95%
rename from extra/curl/curl-8.12.1/lib/urlapi-int.h
rename to extra/curl/curl-8.14.1/lib/urlapi-int.h
index fcffab2e9542..fbce1837ff94 100644
--- a/extra/curl/curl-8.12.1/lib/urlapi-int.h
+++ b/extra/curl/curl-8.14.1/lib/urlapi-int.h
@@ -30,6 +30,8 @@ size_t Curl_is_absolute_url(const char *url, char *buf, size_t buflen,
 
 CURLUcode Curl_url_set_authority(CURLU *u, const char *authority);
 
+CURLUcode Curl_junkscan(const char *url, size_t *urllen, bool allowspace);
+
 #ifdef UNITTESTS
 UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host,
                                    bool has_scheme);
diff --git a/extra/curl/curl-8.12.1/lib/urlapi.c b/extra/curl/curl-8.14.1/lib/urlapi.c
similarity index 76%
rename from extra/curl/curl-8.12.1/lib/urlapi.c
rename to extra/curl/curl-8.14.1/lib/urlapi.c
index 2368bd79b588..6a7ab1a575e5 100644
--- a/extra/curl/curl-8.12.1/lib/urlapi.c
+++ b/extra/curl/curl-8.14.1/lib/urlapi.c
@@ -30,10 +30,12 @@
 #include "url.h"
 #include "escape.h"
 #include "curl_ctype.h"
-#include "inet_pton.h"
+#include "curlx/inet_pton.h"
 #include "inet_ntop.h"
 #include "strdup.h"
 #include "idn.h"
+#include "curlx/strparse.h"
+#include "curl_memrchr.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -109,37 +111,24 @@ static void free_urlhandle(struct Curl_URL *u)
  */
 static const char *find_host_sep(const char *url)
 {
-  const char *sep;
-  const char *query;
-
   /* Find the start of the hostname */
-  sep = strstr(url, "//");
+  const char *sep = strstr(url, "//");
   if(!sep)
     sep = url;
   else
     sep += 2;
 
-  query = strchr(sep, '?');
-  sep = strchr(sep, '/');
-
-  if(!sep)
-    sep = url + strlen(url);
-
-  if(!query)
-    query = url + strlen(url);
+  /* Find first / or ? */
+  while(*sep && *sep != '/' && *sep != '?')
+    sep++;
 
-  return sep < query ? sep : query;
+  return sep;
 }
 
 /* convert CURLcode to CURLUcode */
 #define cc2cu(x) ((x) == CURLE_TOO_LARGE ? CURLUE_TOO_LARGE :   \
                   CURLUE_OUT_OF_MEMORY)
-/*
- * Decide whether a character in a URL must be escaped.
- */
-#define urlchar_needs_escaping(c) (!(ISCNTRL(c) || ISSPACE(c) || ISGRAPH(c)))
 
-static const char hexdigits[] = "0123456789abcdef";
 /* urlencode_str() writes data into an output dynbuf and URL-encodes the
  * spaces in the source URL accordingly.
  *
@@ -154,46 +143,39 @@ static CURLUcode urlencode_str(struct dynbuf *o, const char *url,
   bool left = !query;
   const unsigned char *iptr;
   const unsigned char *host_sep = (const unsigned char *) url;
-  CURLcode result;
+  CURLcode result = CURLE_OK;
 
-  if(!relative)
+  if(!relative) {
+    size_t n;
     host_sep = (const unsigned char *) find_host_sep(url);
 
-  for(iptr = (unsigned char *)url;    /* read from here */
-      len; iptr++, len--) {
-
-    if(iptr < host_sep) {
-      result = Curl_dyn_addn(o, iptr, 1);
-      if(result)
-        return cc2cu(result);
-      continue;
-    }
+    /* output the first piece as-is */
+    n = (const char *)host_sep - url;
+    result = curlx_dyn_addn(o, url, n);
+    len -= n;
+  }
 
+  for(iptr = host_sep; len && !result; iptr++, len--) {
     if(*iptr == ' ') {
       if(left)
-        result = Curl_dyn_addn(o, "%20", 3);
+        result = curlx_dyn_addn(o, "%20", 3);
       else
-        result = Curl_dyn_addn(o, "+", 1);
-      if(result)
-        return cc2cu(result);
-      continue;
+        result = curlx_dyn_addn(o, "+", 1);
     }
-
-    if(*iptr == '?')
-      left = FALSE;
-
-    if(urlchar_needs_escaping(*iptr)) {
-      char out[3]={'%'};
-      out[1] = hexdigits[*iptr >> 4];
-      out[2] = hexdigits[*iptr & 0xf];
-      result = Curl_dyn_addn(o, out, 3);
+    else if((*iptr < ' ') || (*iptr >= 0x7f)) {
+      unsigned char out[3]={'%'};
+      Curl_hexbyte(&out[1], *iptr, TRUE);
+      result = curlx_dyn_addn(o, out, 3);
+    }
+    else {
+      result = curlx_dyn_addn(o, iptr, 1);
+      if(*iptr == '?')
+        left = FALSE;
     }
-    else
-      result = Curl_dyn_addn(o, iptr, 1);
-    if(result)
-      return cc2cu(result);
   }
 
+  if(result)
+    return cc2cu(result);
   return CURLUE_OK;
 }
 
@@ -246,113 +228,95 @@ size_t Curl_is_absolute_url(const char *url, char *buf, size_t buflen,
 }
 
 /*
- * Concatenate a relative URL to a base URL making it absolute.
- *
- * Note that this function destroys the 'base' string.
+ * Concatenate a relative URL onto a base URL making it absolute.
  */
-static CURLUcode redirect_url(char *base, const char *relurl,
+static CURLUcode redirect_url(const char *base, const char *relurl,
                               CURLU *u, unsigned int flags)
 {
   struct dynbuf urlbuf;
   bool host_changed = FALSE;
   const char *useurl = relurl;
-  CURLcode result = CURLE_OK;
+  const char *cutoff = NULL;
+  size_t prelen;
   CURLUcode uc;
-  /* protsep points to the start of the hostname */
-  char *protsep = strstr(base, "//");
-  DEBUGASSERT(protsep);
-  if(!protsep)
-    protsep = base;
-  else
-    protsep += 2; /* pass the slashes */
-
-  if(('/' != relurl[0]) && ('#' != relurl[0])) {
-    /* First we need to find out if there is a ?-letter in the original URL,
-       and cut it and the right-side of that off */
-    char *pathsep = strchr(protsep, '?');
-    if(pathsep)
-      *pathsep = 0;
-    else {
-      /* if not, cut off the potential fragment */
-      pathsep = strchr(protsep, '#');
-      if(pathsep)
-        *pathsep = 0;
-    }
 
-    /* if the redirect-to piece is not just a query, cut the path after the
-       last slash */
-    if(useurl[0] != '?') {
-      pathsep = strrchr(protsep, '/');
-      if(pathsep)
-        pathsep[1] = 0; /* leave the slash */
-    }
-  }
-  else if('/' == relurl[0]) {
-    /* We got a new absolute path for this server */
+  /* protsep points to the start of the hostname, after [scheme]:// */
+  const char *protsep = base + strlen(u->scheme) + 3;
+  DEBUGASSERT(base && relurl && u); /* all set here */
+  if(!base)
+    return CURLUE_MALFORMED_INPUT; /* should never happen */
 
+  /* handle different relative URL types */
+  switch(relurl[0]) {
+  case '/':
     if(relurl[1] == '/') {
-      /* the new URL starts with //, just keep the protocol part from the
-         original one */
-      *protsep = 0;
-      useurl = &relurl[2]; /* we keep the slashes from the original, so we
-                              skip the new ones */
+      /* protocol-relative URL: //example.com/path */
+      cutoff = protsep;
+      useurl = &relurl[2];
       host_changed = TRUE;
     }
-    else {
-      /* cut the original URL at first slash */
-      char *pathsep = strchr(protsep, '/');
-      if(pathsep)
-        *pathsep = 0;
+    else
+      /* absolute /path */
+      cutoff = strchr(protsep, '/');
+    break;
+
+  case '#':
+    /* fragment-only change */
+    if(u->fragment)
+      cutoff = strchr(protsep, '#');
+    break;
+
+  default:
+    /* path or query-only change */
+    if(u->query && u->query[0])
+      /* remove existing query */
+      cutoff = strchr(protsep, '?');
+    else if(u->fragment && u->fragment[0])
+      /* Remove existing fragment */
+      cutoff = strchr(protsep, '#');
+
+    if(relurl[0] != '?') {
+      /* append a relative path after the last slash */
+      cutoff = memrchr(protsep, '/',
+                       cutoff ? (size_t)(cutoff - protsep) : strlen(protsep));
+      if(cutoff)
+        cutoff++; /* truncate after last slash */
     }
+    break;
   }
-  else {
-    /* the relative piece starts with '#' */
 
-    /* If there is a fragment in the original URL, cut it off */
-    char *pathsep = strchr(protsep, '#');
-    if(pathsep)
-      *pathsep = 0;
-  }
+  prelen = cutoff ? (size_t)(cutoff - base) : strlen(base);
 
-  Curl_dyn_init(&urlbuf, CURL_MAX_INPUT_LENGTH);
+  /* build new URL */
+  curlx_dyn_init(&urlbuf, CURL_MAX_INPUT_LENGTH);
 
-  /* copy over the root URL part */
-  result = Curl_dyn_add(&urlbuf, base);
-  if(result)
-    return cc2cu(result);
+  if(!curlx_dyn_addn(&urlbuf, base, prelen) &&
+     !urlencode_str(&urlbuf, useurl, strlen(useurl), !host_changed, FALSE)) {
+    uc = parseurl_and_replace(curlx_dyn_ptr(&urlbuf), u,
+                              flags & ~CURLU_PATH_AS_IS);
+  }
+  else
+    uc = CURLUE_OUT_OF_MEMORY;
 
-  /* then append the new piece on the right side */
-  uc = urlencode_str(&urlbuf, useurl, strlen(useurl), !host_changed,
-                     FALSE);
-  if(!uc)
-    uc = parseurl_and_replace(Curl_dyn_ptr(&urlbuf), u,
-                              flags&~CURLU_PATH_AS_IS);
-  Curl_dyn_free(&urlbuf);
+  curlx_dyn_free(&urlbuf);
   return uc;
 }
 
 /* scan for byte values <= 31, 127 and sometimes space */
-static CURLUcode junkscan(const char *url, size_t *urllen, unsigned int flags)
+CURLUcode Curl_junkscan(const char *url, size_t *urllen, bool allowspace)
 {
-  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(url);
-  size_t nfine;
-
+  size_t i;
+  unsigned char control;
+  const unsigned char *p = (const unsigned char *)url;
   if(n > CURL_MAX_INPUT_LENGTH)
-    /* excessive input length */
-    return CURLUE_MALFORMED_INPUT;
-
-  nfine = strcspn(url, badbytes);
-  if((nfine != n) ||
-     (!(flags & CURLU_ALLOW_SPACE) && strchr(url, ' ')))
     return CURLUE_MALFORMED_INPUT;
 
+  control = allowspace ? 0x1f : 0x20;
+  for(i = 0; i < n; i++) {
+    if(p[i] <= control || p[i] == 127)
+      return CURLUE_MALFORMED_INPUT;
+  }
   *urllen = n;
   return CURLUE_OK;
 }
@@ -452,8 +416,8 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u,
 UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host,
                                    bool has_scheme)
 {
-  char *portptr;
-  char *hostname = Curl_dyn_ptr(host);
+  const char *portptr;
+  char *hostname = curlx_dyn_ptr(host);
   /*
    * Find the end of an IPv6 address on the ']' ending bracket.
    */
@@ -474,8 +438,7 @@ UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host,
     portptr = strchr(hostname, ':');
 
   if(portptr) {
-    char *rest = NULL;
-    unsigned long port;
+    curl_off_t port;
     size_t keep = portptr - hostname;
 
     /* Browser behavior adaptation. If there is a colon with no digits after,
@@ -485,24 +448,18 @@ UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host,
        Do not do it if the URL has no scheme, to make something that looks like
        a scheme not work!
     */
-    Curl_dyn_setlen(host, keep);
+    curlx_dyn_setlen(host, keep);
     portptr++;
     if(!*portptr)
       return has_scheme ? CURLUE_OK : CURLUE_BAD_PORT_NUMBER;
 
-    if(!ISDIGIT(*portptr))
-      return CURLUE_BAD_PORT_NUMBER;
-
-    errno = 0;
-    port = strtoul(portptr, &rest, 10);  /* Port number must be decimal */
-
-    if(errno || (port > 0xffff) || *rest)
+    if(curlx_str_number(&portptr, &port, 0xffff) || *portptr)
       return CURLUE_BAD_PORT_NUMBER;
 
     u->portnum = (unsigned short) port;
     /* generate a new port number string to get rid of leading zeroes etc */
     free(u->port);
-    u->port = aprintf("%ld", port);
+    u->port = aprintf("%" CURL_FORMAT_CURL_OFF_T, port);
     if(!u->port)
       return CURLUE_OUT_OF_MEMORY;
   }
@@ -554,7 +511,7 @@ static CURLUcode ipv6_parse(struct Curl_URL *u, char *hostname,
   {
     char dest[16]; /* fits a binary IPv6 address */
     hostname[hlen] = 0; /* end the address there */
-    if(1 != Curl_inet_pton(AF_INET6, hostname, dest))
+    if(1 != curlx_inet_pton(AF_INET6, hostname, dest))
       return CURLUE_BAD_IPV6;
     if(Curl_inet_ntop(AF_INET6, dest, hostname, hlen)) {
       hlen = strlen(hostname); /* might be shorter now */
@@ -608,31 +565,31 @@ static int ipv4_normalize(struct dynbuf *host)
 {
   bool done = FALSE;
   int n = 0;
-  const char *c = Curl_dyn_ptr(host);
-  unsigned long parts[4] = {0, 0, 0, 0};
+  const char *c = curlx_dyn_ptr(host);
+  unsigned int parts[4] = {0, 0, 0, 0};
   CURLcode result = CURLE_OK;
 
   if(*c == '[')
     return HOST_IPV6;
 
-  errno = 0; /* for strtoul */
   while(!done) {
-    char *endp = NULL;
-    unsigned long l;
-    if(!ISDIGIT(*c))
-      /* most importantly this does not allow a leading plus or minus */
-      return HOST_NAME;
-    l = strtoul(c, &endp, 0);
-    if(errno)
-      return HOST_NAME;
-#if SIZEOF_LONG > 4
-    /* a value larger than 32 bits */
-    if(l > UINT_MAX)
+    int rc;
+    curl_off_t l;
+    if(*c == '0') {
+      if(c[1] == 'x') {
+        c += 2; /* skip the prefix */
+        rc = curlx_str_hex(&c, &l, UINT_MAX);
+      }
+      else
+        rc = curlx_str_octal(&c, &l, UINT_MAX);
+    }
+    else
+      rc = curlx_str_number(&c, &l, UINT_MAX);
+
+    if(rc)
       return HOST_NAME;
-#endif
 
-    parts[n] = l;
-    c = endp;
+    parts[n] = (unsigned int)l;
 
     switch(*c) {
     case '.':
@@ -653,44 +610,44 @@ static int ipv4_normalize(struct dynbuf *host)
 
   switch(n) {
   case 0: /* a -- 32 bits */
-    Curl_dyn_reset(host);
+    curlx_dyn_reset(host);
 
-    result = Curl_dyn_addf(host, "%u.%u.%u.%u",
-                           (unsigned int)(parts[0] >> 24),
-                           (unsigned int)((parts[0] >> 16) & 0xff),
-                           (unsigned int)((parts[0] >> 8) & 0xff),
-                           (unsigned int)(parts[0] & 0xff));
+    result = curlx_dyn_addf(host, "%u.%u.%u.%u",
+                            (parts[0] >> 24),
+                            ((parts[0] >> 16) & 0xff),
+                            ((parts[0] >> 8) & 0xff),
+                            (parts[0] & 0xff));
     break;
   case 1: /* a.b -- 8.24 bits */
     if((parts[0] > 0xff) || (parts[1] > 0xffffff))
       return HOST_NAME;
-    Curl_dyn_reset(host);
-    result = Curl_dyn_addf(host, "%u.%u.%u.%u",
-                           (unsigned int)(parts[0]),
-                           (unsigned int)((parts[1] >> 16) & 0xff),
-                           (unsigned int)((parts[1] >> 8) & 0xff),
-                           (unsigned int)(parts[1] & 0xff));
+    curlx_dyn_reset(host);
+    result = curlx_dyn_addf(host, "%u.%u.%u.%u",
+                            (parts[0]),
+                            ((parts[1] >> 16) & 0xff),
+                            ((parts[1] >> 8) & 0xff),
+                            (parts[1] & 0xff));
     break;
   case 2: /* a.b.c -- 8.8.16 bits */
     if((parts[0] > 0xff) || (parts[1] > 0xff) || (parts[2] > 0xffff))
       return HOST_NAME;
-    Curl_dyn_reset(host);
-    result = Curl_dyn_addf(host, "%u.%u.%u.%u",
-                           (unsigned int)(parts[0]),
-                           (unsigned int)(parts[1]),
-                           (unsigned int)((parts[2] >> 8) & 0xff),
-                           (unsigned int)(parts[2] & 0xff));
+    curlx_dyn_reset(host);
+    result = curlx_dyn_addf(host, "%u.%u.%u.%u",
+                            (parts[0]),
+                            (parts[1]),
+                            ((parts[2] >> 8) & 0xff),
+                            (parts[2] & 0xff));
     break;
   case 3: /* a.b.c.d -- 8.8.8.8 bits */
     if((parts[0] > 0xff) || (parts[1] > 0xff) || (parts[2] > 0xff) ||
        (parts[3] > 0xff))
       return HOST_NAME;
-    Curl_dyn_reset(host);
-    result = Curl_dyn_addf(host, "%u.%u.%u.%u",
-                           (unsigned int)(parts[0]),
-                           (unsigned int)(parts[1]),
-                           (unsigned int)(parts[2]),
-                           (unsigned int)(parts[3]));
+    curlx_dyn_reset(host);
+    result = curlx_dyn_addf(host, "%u.%u.%u.%u",
+                            (parts[0]),
+                            (parts[1]),
+                            (parts[2]),
+                            (parts[3]));
     break;
   }
   if(result)
@@ -702,7 +659,7 @@ static int ipv4_normalize(struct dynbuf *host)
 static CURLUcode urldecode_host(struct dynbuf *host)
 {
   char *per = NULL;
-  const char *hostname = Curl_dyn_ptr(host);
+  const char *hostname = curlx_dyn_ptr(host);
   per = strchr(hostname, '%');
   if(!per)
     /* nothing to decode */
@@ -715,8 +672,8 @@ static CURLUcode urldecode_host(struct dynbuf *host)
                                      REJECT_CTRL);
     if(result)
       return CURLUE_BAD_HOSTNAME;
-    Curl_dyn_reset(host);
-    result = Curl_dyn_addn(host, decoded, dlen);
+    curlx_dyn_reset(host);
+    result = curlx_dyn_addn(host, decoded, dlen);
     free(decoded);
     if(result)
       return cc2cu(result);
@@ -742,7 +699,7 @@ static CURLUcode parse_authority(struct Curl_URL *u,
   if(uc)
     goto out;
 
-  result = Curl_dyn_addn(host, auth + offset, authlen - offset);
+  result = curlx_dyn_addn(host, auth + offset, authlen - offset);
   if(result) {
     uc = cc2cu(result);
     goto out;
@@ -752,19 +709,19 @@ static CURLUcode parse_authority(struct Curl_URL *u,
   if(uc)
     goto out;
 
-  if(!Curl_dyn_len(host))
+  if(!curlx_dyn_len(host))
     return CURLUE_NO_HOST;
 
   switch(ipv4_normalize(host)) {
   case HOST_IPV4:
     break;
   case HOST_IPV6:
-    uc = ipv6_parse(u, Curl_dyn_ptr(host), Curl_dyn_len(host));
+    uc = ipv6_parse(u, curlx_dyn_ptr(host), curlx_dyn_len(host));
     break;
   case HOST_NAME:
     uc = urldecode_host(host);
     if(!uc)
-      uc = hostname_check(u, Curl_dyn_ptr(host), Curl_dyn_len(host));
+      uc = hostname_check(u, curlx_dyn_ptr(host), curlx_dyn_len(host));
     break;
   case HOST_ERROR:
     uc = CURLUE_OUT_OF_MEMORY;
@@ -785,15 +742,15 @@ CURLUcode Curl_url_set_authority(CURLU *u, const char *authority)
   struct dynbuf host;
 
   DEBUGASSERT(authority);
-  Curl_dyn_init(&host, CURL_MAX_INPUT_LENGTH);
+  curlx_dyn_init(&host, CURL_MAX_INPUT_LENGTH);
 
   result = parse_authority(u, authority, strlen(authority),
                            CURLU_DISALLOW_USER, &host, !!u->scheme);
   if(result)
-    Curl_dyn_free(&host);
+    curlx_dyn_free(&host);
   else {
     free(u->host);
-    u->host = Curl_dyn_ptr(&host);
+    u->host = curlx_dyn_ptr(&host);
   }
   return result;
 }
@@ -803,6 +760,25 @@ CURLUcode Curl_url_set_authority(CURLU *u, const char *authority)
  * https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.4
  */
 
+static bool is_dot(const char **str, size_t *clen)
+{
+  const char *p = *str;
+  if(*p == '.') {
+    (*str)++;
+    (*clen)--;
+    return TRUE;
+  }
+  else if((*clen >= 3) &&
+          (p[0] == '%') && (p[1] == '2') && ((p[2] | 0x20) == 'e')) {
+    *str += 3;
+    *clen -= 3;
+    return TRUE;
+  }
+  return FALSE;
+}
+
+#define ISSLASH(x) ((x) == '/')
+
 /*
  * dedotdotify()
  * @unittest: 1395
@@ -811,8 +787,7 @@ CURLUcode Curl_url_set_authority(CURLU *u, const char *authority)
  * passed in and strips them off according to the rules in RFC 3986 section
  * 5.2.4.
  *
- * The function handles a query part ('?' + stuff) appended but it expects
- * that fragments ('#' + stuff) have already been cut off.
+ * The function handles a path. It should not contain the query nor fragment.
  *
  * RETURNS
  *
@@ -821,112 +796,109 @@ CURLUcode Curl_url_set_authority(CURLU *u, const char *authority)
 UNITTEST int dedotdotify(const char *input, size_t clen, char **outp);
 UNITTEST int dedotdotify(const char *input, size_t clen, char **outp)
 {
-  char *outptr;
-  const char *endp = &input[clen];
-  char *out;
+  struct dynbuf out;
+  CURLcode result = CURLE_OK;
 
   *outp = NULL;
   /* the path always starts with a slash, and a slash has not dot */
-  if((clen < 2) || !memchr(input, '.', clen))
+  if(clen < 2)
     return 0;
 
-  out = malloc(clen + 1);
-  if(!out)
-    return 1; /* out of memory */
-
-  *out = 0; /* null-terminates, for inputs like "./" */
-  outptr = out;
-
-  do {
-    bool dotdot = TRUE;
-    if(*input == '.') {
-      /*  A. If the input buffer begins with a prefix of "../" or "./", then
-          remove that prefix from the input buffer; otherwise, */
-
-      if(!strncmp("./", input, 2)) {
-        input += 2;
-        clen -= 2;
-      }
-      else if(!strncmp("../", input, 3)) {
-        input += 3;
-        clen -= 3;
-      }
-      /*  D. if the input buffer consists only of "." or "..", then remove
-          that from the input buffer; otherwise, */
+  curlx_dyn_init(&out, clen + 1);
+
+  /*  A. If the input buffer begins with a prefix of "../" or "./", then
+      remove that prefix from the input buffer; otherwise, */
+  if(is_dot(&input, &clen)) {
+    const char *p = input;
+    size_t blen = clen;
+
+    if(!clen)
+      /* . [end] */
+      goto end;
+    else if(ISSLASH(*p)) {
+      /* one dot followed by a slash */
+      input = p + 1;
+      clen--;
+    }
 
-      else if(!strcmp(".", input) || !strcmp("..", input) ||
-              !strncmp(".?", input, 2) || !strncmp("..?", input, 3)) {
-        *out = 0;
-        break;
+    /*  D. if the input buffer consists only of "." or "..", then remove
+        that from the input buffer; otherwise, */
+    else if(is_dot(&p, &blen)) {
+      if(!blen)
+        /* .. [end] */
+        goto end;
+      else if(ISSLASH(*p)) {
+        /* ../ */
+        input = p + 1;
+        clen = blen - 1;
       }
-      else
-        dotdot = FALSE;
     }
-    else if(*input == '/') {
+  }
+
+  while(clen && !result) { /* until end of path content */
+    if(ISSLASH(*input)) {
+      const char *p = &input[1];
+      size_t blen = clen - 1;
       /*  B. if the input buffer begins with a prefix of "/./" or "/.", where
           "."  is a complete path segment, then replace that prefix with "/" in
           the input buffer; otherwise, */
-      if(!strncmp("/./", input, 3)) {
-        input += 2;
-        clen -= 2;
-      }
-      else if(!strcmp("/.", input) || !strncmp("/.?", input, 3)) {
-        *outptr++ = '/';
-        *outptr = 0;
-        break;
-      }
-
-      /*  C. if the input buffer begins with a prefix of "/../" or "/..",
-          where ".." is a complete path segment, then replace that prefix with
-          "/" in the input buffer and remove the last segment and its
-          preceding "/" (if any) from the output buffer; otherwise, */
-
-      else if(!strncmp("/../", input, 4)) {
-        input += 3;
-        clen -= 3;
-        /* remove the last segment from the output buffer */
-        while(outptr > out) {
-          outptr--;
-          if(*outptr == '/')
-            break;
+      if(is_dot(&p, &blen)) {
+        if(!blen) { /* /. */
+          result = curlx_dyn_addn(&out, "/", 1);
+          break;
         }
-        *outptr = 0; /* null-terminate where it stops */
-      }
-      else if(!strcmp("/..", input) || !strncmp("/..?", input, 4)) {
-        /* remove the last segment from the output buffer */
-        while(outptr > out) {
-          outptr--;
-          if(*outptr == '/')
-            break;
+        else if(ISSLASH(*p)) { /* /./ */
+          input = p;
+          clen = blen;
+          continue;
+        }
+
+        /*  C. if the input buffer begins with a prefix of "/../" or "/..",
+            where ".." is a complete path segment, then replace that prefix
+            with "/" in the input buffer and remove the last segment and its
+            preceding "/" (if any) from the output buffer; otherwise, */
+        else if(is_dot(&p, &blen) && (ISSLASH(*p) || !blen)) {
+          /* remove the last segment from the output buffer */
+          size_t len = curlx_dyn_len(&out);
+          if(len) {
+            char *ptr = curlx_dyn_ptr(&out);
+            char *last = memrchr(ptr, '/', len);
+            if(last)
+              /* trim the output at the slash */
+              curlx_dyn_setlen(&out, last - ptr);
+          }
+
+          if(blen) { /* /../ */
+            input = p;
+            clen = blen;
+            continue;
+          }
+          result = curlx_dyn_addn(&out, "/", 1);
+          break;
         }
-        *outptr++ = '/';
-        *outptr = 0; /* null-terminate where it stops */
-        break;
       }
-      else
-        dotdot = FALSE;
-    }
-    else
-      dotdot = FALSE;
-
-    if(!dotdot) {
-      /*  E. move the first path segment in the input buffer to the end of
-          the output buffer, including the initial "/" character (if any) and
-          any subsequent characters up to, but not including, the next "/"
-          character or the end of the input buffer. */
-
-      do {
-        *outptr++ = *input++;
-        clen--;
-      } while(*input && (*input != '/') && (*input != '?'));
-      *outptr = 0;
     }
 
-    /* continue until end of path */
-  } while(input < endp);
+    /*  E. move the first path segment in the input buffer to the end of
+        the output buffer, including the initial "/" character (if any) and
+        any subsequent characters up to, but not including, the next "/"
+        character or the end of the input buffer. */
 
-  *outp = out;
-  return 0; /* success */
+    result = curlx_dyn_addn(&out, input, 1);
+    input++;
+    clen--;
+  }
+end:
+  if(!result) {
+    if(curlx_dyn_len(&out))
+      *outp = curlx_dyn_ptr(&out);
+    else {
+      *outp = strdup("");
+      if(!*outp)
+        return 1;
+    }
+  }
+  return result ? 1 : 0; /* success */
 }
 
 static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
@@ -944,9 +916,9 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
 
   DEBUGASSERT(url);
 
-  Curl_dyn_init(&host, CURL_MAX_INPUT_LENGTH);
+  curlx_dyn_init(&host, CURL_MAX_INPUT_LENGTH);
 
-  result = junkscan(url, &urllen, flags);
+  result = Curl_junkscan(url, &urllen, !!(flags & CURLU_ALLOW_SPACE));
   if(result)
     goto fail;
 
@@ -964,7 +936,7 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
     }
 
     /* path has been allocated large enough to hold this */
-    path = (char *)&url[5];
+    path = &url[5];
     pathlen = urllen - 5;
 
     u->scheme = strdup("file");
@@ -1010,7 +982,7 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
           ptr += 9; /* now points to the slash after the host */
         }
         else {
-#if defined(_WIN32)
+#ifdef _WIN32
           size_t len;
 
           /* the hostname, NetBIOS computer name, can not contain disallowed
@@ -1024,7 +996,7 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
 
           len = path - ptr;
           if(len) {
-            CURLcode code = Curl_dyn_addn(&host, ptr, len);
+            CURLcode code = curlx_dyn_addn(&host, ptr, len);
             if(code) {
               result = cc2cu(code);
               goto fail;
@@ -1048,7 +1020,7 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
 
     if(!uncpath)
       /* no host for file: URLs by default */
-      Curl_dyn_reset(&host);
+      curlx_dyn_reset(&host);
 
 #if !defined(_WIN32) && !defined(MSDOS) && !defined(__CYGWIN__)
     /* Do not allow Windows drive letters when not in Windows.
@@ -1134,7 +1106,7 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
         goto fail;
 
       if((flags & CURLU_GUESS_SCHEME) && !schemep) {
-        const char *hostname = Curl_dyn_ptr(&host);
+        const char *hostname = curlx_dyn_ptr(&host);
         /* legacy curl-style guess based on hostname */
         if(checkprefix("ftp.", hostname))
           schemep = "ftp";
@@ -1161,7 +1133,7 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
     }
     else if(flags & CURLU_NO_AUTHORITY) {
       /* allowed to be empty. */
-      if(Curl_dyn_add(&host, "")) {
+      if(curlx_dyn_add(&host, "")) {
         result = CURLUE_OUT_OF_MEMORY;
         goto fail;
       }
@@ -1180,11 +1152,11 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
       /* skip the leading '#' in the copy but include the terminating null */
       if(flags & CURLU_URLENCODE) {
         struct dynbuf enc;
-        Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
+        curlx_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
         result = urlencode_str(&enc, fragment + 1, fraglen - 1, TRUE, FALSE);
         if(result)
           goto fail;
-        u->fragment = Curl_dyn_ptr(&enc);
+        u->fragment = curlx_dyn_ptr(&enc);
       }
       else {
         u->fragment = Curl_memdup0(fragment + 1, fraglen - 1);
@@ -1207,12 +1179,12 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
     if(qlen > 1) {
       if(flags & CURLU_URLENCODE) {
         struct dynbuf enc;
-        Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
+        curlx_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
         /* skip the leading question mark */
         result = urlencode_str(&enc, query + 1, qlen - 1, TRUE, TRUE);
         if(result)
           goto fail;
-        u->query = Curl_dyn_ptr(&enc);
+        u->query = curlx_dyn_ptr(&enc);
       }
       else {
         u->query = Curl_memdup0(query + 1, qlen - 1);
@@ -1234,12 +1206,12 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
 
   if(pathlen && (flags & CURLU_URLENCODE)) {
     struct dynbuf enc;
-    Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
+    curlx_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
     result = urlencode_str(&enc, path, pathlen, TRUE, FALSE);
     if(result)
       goto fail;
-    pathlen = Curl_dyn_len(&enc);
-    path = u->path = Curl_dyn_ptr(&enc);
+    pathlen = curlx_dyn_len(&enc);
+    path = u->path = curlx_dyn_ptr(&enc);
   }
 
   if(pathlen <= 1) {
@@ -1262,7 +1234,7 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
     if(!(flags & CURLU_PATH_AS_IS)) {
       /* remove ../ and ./ sequences according to RFC3986 */
       char *dedot;
-      int err = dedotdotify((char *)path, pathlen, &dedot);
+      int err = dedotdotify(path, pathlen, &dedot);
       if(err) {
         result = CURLUE_OUT_OF_MEMORY;
         goto fail;
@@ -1274,11 +1246,11 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
     }
   }
 
-  u->host = Curl_dyn_ptr(&host);
+  u->host = curlx_dyn_ptr(&host);
 
   return result;
 fail:
-  Curl_dyn_free(&host);
+  curlx_dyn_free(&host);
   free_urlhandle(u);
   return result;
 }
@@ -1440,7 +1412,7 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what,
     break;
   case CURLUPART_URL: {
     char *url;
-    char *scheme;
+    const char *scheme;
     char *options = u->options;
     char *port = u->port;
     char *allochost = NULL;
@@ -1452,8 +1424,10 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what,
     punycode = (flags & CURLU_PUNYCODE) ? 1 : 0;
     depunyfy = (flags & CURLU_PUNY2IDN) ? 1 : 0;
     if(u->scheme && strcasecompare("file", u->scheme)) {
-      url = aprintf("file://%s%s%s",
+      url = aprintf("file://%s%s%s%s%s",
                     u->path,
+                    show_query ? "?": "",
+                    u->query ? u->query : "",
                     show_fragment ? "#": "",
                     u->fragment ? u->fragment : "");
     }
@@ -1465,7 +1439,7 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what,
       if(u->scheme)
         scheme = u->scheme;
       else if(flags & CURLU_DEFAULT_SCHEME)
-        scheme = (char *) DEFAULT_SCHEME;
+        scheme = DEFAULT_SCHEME;
       else
         return CURLUE_NO_SCHEME;
 
@@ -1494,11 +1468,11 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what,
           /* make it '[ host %25 zoneid ]' */
           struct dynbuf enc;
           size_t hostlen = strlen(u->host);
-          Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
-          if(Curl_dyn_addf(&enc, "%.*s%%25%s]", (int)hostlen - 1, u->host,
-                           u->zoneid))
+          curlx_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
+          if(curlx_dyn_addf(&enc, "%.*s%%25%s]", (int)hostlen - 1, u->host,
+                            u->zoneid))
             return CURLUE_OUT_OF_MEMORY;
-          allochost = Curl_dyn_ptr(&enc);
+          allochost = curlx_dyn_ptr(&enc);
         }
       }
       else if(urlencode) {
@@ -1519,7 +1493,7 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what,
         }
       }
       else if(depunyfy) {
-        if(Curl_is_ASCII_name(u->host) && !strncmp("xn--", u->host, 4)) {
+        if(Curl_is_ASCII_name(u->host)) {
 #ifndef USE_IDN
           return CURLUE_LACKS_IDN;
 #else
@@ -1595,12 +1569,12 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what,
     if(urlencode) {
       struct dynbuf enc;
       CURLUcode uc;
-      Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
+      curlx_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
       uc = urlencode_str(&enc, *part, partlen, TRUE, what == CURLUPART_QUERY);
       if(uc)
         return uc;
       free(*part);
-      *part = Curl_dyn_ptr(&enc);
+      *part = curlx_dyn_ptr(&enc);
     }
     else if(punycode) {
       if(!Curl_is_ASCII_name(u->host)) {
@@ -1618,7 +1592,7 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what,
       }
     }
     else if(depunyfy) {
-      if(Curl_is_ASCII_name(u->host)  && !strncmp("xn--", u->host, 4)) {
+      if(Curl_is_ASCII_name(u->host)) {
 #ifndef USE_IDN
         return CURLUE_LACKS_IDN;
 #else
@@ -1639,6 +1613,89 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what,
     return ifmissing;
 }
 
+static CURLUcode set_url_scheme(CURLU *u, const char *scheme,
+        unsigned int flags)
+{
+    size_t plen = strlen(scheme);
+    const char *s = scheme;
+    if((plen > MAX_SCHEME_LEN) || (plen < 1))
+      /* too long or too short */
+      return CURLUE_BAD_SCHEME;
+   /* verify that it is a fine scheme */
+    if(!(flags & CURLU_NON_SUPPORT_SCHEME) && !Curl_get_scheme_handler(scheme))
+      return CURLUE_UNSUPPORTED_SCHEME;
+    if(ISALPHA(*s)) {
+      /* ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) */
+      while(--plen) {
+        if(ISALNUM(*s) || (*s == '+') || (*s == '-') || (*s == '.'))
+          s++; /* fine */
+        else
+          return CURLUE_BAD_SCHEME;
+      }
+    }
+    else
+      return CURLUE_BAD_SCHEME;
+    u->guessed_scheme = FALSE;
+    return CURLUE_OK;
+}
+
+static CURLUcode set_url_port(CURLU *u, const char *provided_port)
+{
+  char *tmp;
+  curl_off_t port;
+  if(!ISDIGIT(provided_port[0]))
+    /* not a number */
+    return CURLUE_BAD_PORT_NUMBER;
+  if(curlx_str_number(&provided_port, &port, 0xffff) || *provided_port)
+    /* weirdly provided number, not good! */
+    return CURLUE_BAD_PORT_NUMBER;
+  tmp = aprintf("%" CURL_FORMAT_CURL_OFF_T, port);
+  if(!tmp)
+    return CURLUE_OUT_OF_MEMORY;
+  free(u->port);
+  u->port = tmp;
+  u->portnum = (unsigned short)port;
+  return CURLUE_OK;
+}
+
+static CURLUcode set_url(CURLU *u, const char *url, size_t part_size,
+        unsigned int flags)
+{
+  /*
+   * Allow a new URL to replace the existing (if any) contents.
+   *
+   * If the existing contents is enough for a URL, allow a relative URL to
+   * replace it.
+   */
+  CURLUcode uc;
+  char *oldurl = NULL;
+
+  if(!part_size) {
+    /* a blank URL is not a valid URL unless we already have a complete one
+       and this is a redirect */
+    if(!curl_url_get(u, CURLUPART_URL, &oldurl, flags)) {
+      /* success, meaning the "" is a fine relative URL, but nothing
+         changes */
+      free(oldurl);
+      return CURLUE_OK;
+    }
+    return CURLUE_MALFORMED_INPUT;
+  }
+
+  /* if the new thing is absolute or the old one is not (we could not get an
+   * absolute URL in 'oldurl'), then replace the existing with the new. */
+  if(Curl_is_absolute_url(url, NULL, 0,
+                          flags & (CURLU_GUESS_SCHEME|CURLU_DEFAULT_SCHEME))
+     || curl_url_get(u, CURLUPART_URL, &oldurl, flags)) {
+    return parseurl_and_replace(url, u, flags);
+  }
+  DEBUGASSERT(oldurl); /* it is set here */
+  /* apply the relative part to create a new URL */
+  uc = redirect_url(oldurl, url, u, flags);
+  free(oldurl);
+  return uc;
+}
+
 CURLUcode curl_url_set(CURLU *u, CURLUPart what,
                        const char *part, unsigned int flags)
 {
@@ -1712,28 +1769,11 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
 
   switch(what) {
   case CURLUPART_SCHEME: {
-    size_t plen = strlen(part);
-    const char *s = part;
-    if((plen > MAX_SCHEME_LEN) || (plen < 1))
-      /* too long or too short */
-      return CURLUE_BAD_SCHEME;
-   /* verify that it is a fine scheme */
-    if(!(flags & CURLU_NON_SUPPORT_SCHEME) && !Curl_get_scheme_handler(part))
-      return CURLUE_UNSUPPORTED_SCHEME;
+    CURLUcode status = set_url_scheme(u, part, flags);
+    if(status)
+      return status;
     storep = &u->scheme;
     urlencode = FALSE; /* never */
-    if(ISALPHA(*s)) {
-      /* ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) */
-      while(--plen) {
-        if(ISALNUM(*s) || (*s == '+') || (*s == '-') || (*s == '.'))
-          s++; /* fine */
-        else
-          return CURLUE_BAD_SCHEME;
-      }
-    }
-    else
-      return CURLUE_BAD_SCHEME;
-    u->guessed_scheme = FALSE;
     break;
   }
   case CURLUPART_USER:
@@ -1753,26 +1793,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
     storep = &u->zoneid;
     break;
   case CURLUPART_PORT:
-    if(!ISDIGIT(part[0]))
-      /* not a number */
-      return CURLUE_BAD_PORT_NUMBER;
-    else {
-      char *tmp;
-      char *endp;
-      unsigned long port;
-      errno = 0;
-      port = strtoul(part, &endp, 10);  /* must be decimal */
-      if(errno || (port > 0xffff) || *endp)
-        /* weirdly provided number, not good! */
-        return CURLUE_BAD_PORT_NUMBER;
-      tmp = strdup(part);
-      if(!tmp)
-        return CURLUE_OUT_OF_MEMORY;
-      free(u->port);
-      u->port = tmp;
-      u->portnum = (unsigned short)port;
-      return CURLUE_OK;
-    }
+    return set_url_port(u, part);
   case CURLUPART_PATH:
     urlskipslash = TRUE;
     leadingslash = TRUE; /* enforce */
@@ -1790,31 +1811,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
     u->fragment_present = TRUE;
     break;
   case CURLUPART_URL: {
-    /*
-     * Allow a new URL to replace the existing (if any) contents.
-     *
-     * If the existing contents is enough for a URL, allow a relative URL to
-     * replace it.
-     */
-    CURLUcode uc;
-    char *oldurl;
-
-    if(!nalloc)
-      /* a blank URL is not a valid URL */
-      return CURLUE_MALFORMED_INPUT;
-
-    /* if the new thing is absolute or the old one is not (we could not get an
-     * absolute URL in 'oldurl'), then replace the existing with the new. */
-    if(Curl_is_absolute_url(part, NULL, 0,
-                            flags & (CURLU_GUESS_SCHEME|CURLU_DEFAULT_SCHEME))
-       || curl_url_get(u, CURLUPART_URL, &oldurl, flags)) {
-      return parseurl_and_replace(part, u, flags);
-    }
-
-    /* apply the relative part to create a new URL */
-    uc = redirect_url(oldurl, part, u, flags);
-    free(oldurl);
-    return uc;
+    return set_url(u, part, nalloc, flags);
   }
   default:
     return CURLUE_UNKNOWN_PART;
@@ -1823,10 +1820,10 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
   {
     const char *newp;
     struct dynbuf enc;
-    Curl_dyn_init(&enc, nalloc * 3 + 1 + leadingslash);
+    curlx_dyn_init(&enc, nalloc * 3 + 1 + leadingslash);
 
     if(leadingslash && (part[0] != '/')) {
-      CURLcode result = Curl_dyn_addn(&enc, "/", 1);
+      CURLcode result = curlx_dyn_addn(&enc, "/", 1);
       if(result)
         return cc2cu(result);
     }
@@ -1836,7 +1833,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
       for(i = (const unsigned char *)part; *i; i++) {
         CURLcode result;
         if((*i == ' ') && plusencode) {
-          result = Curl_dyn_addn(&enc, "+", 1);
+          result = curlx_dyn_addn(&enc, "+", 1);
           if(result)
             return CURLUE_OUT_OF_MEMORY;
         }
@@ -1846,15 +1843,14 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
           if((*i == '=') && equalsencode)
             /* only skip the first equals sign */
             equalsencode = FALSE;
-          result = Curl_dyn_addn(&enc, i, 1);
+          result = curlx_dyn_addn(&enc, i, 1);
           if(result)
             return cc2cu(result);
         }
         else {
-          char out[3]={'%'};
-          out[1] = hexdigits[*i >> 4];
-          out[2] = hexdigits[*i & 0xf];
-          result = Curl_dyn_addn(&enc, out, 3);
+          unsigned char out[3]={'%'};
+          Curl_hexbyte(&out[1], *i, TRUE);
+          result = curlx_dyn_addn(&enc, out, 3);
           if(result)
             return cc2cu(result);
         }
@@ -1862,10 +1858,10 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
     }
     else {
       char *p;
-      CURLcode result = Curl_dyn_add(&enc, part);
+      CURLcode result = curlx_dyn_add(&enc, part);
       if(result)
         return cc2cu(result);
-      p = Curl_dyn_ptr(&enc);
+      p = curlx_dyn_ptr(&enc);
       while(*p) {
         /* make sure percent encoded are lower case */
         if((*p == '%') && ISXDIGIT(p[1]) && ISXDIGIT(p[2]) &&
@@ -1878,7 +1874,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
           p++;
       }
     }
-    newp = Curl_dyn_ptr(&enc);
+    newp = curlx_dyn_ptr(&enc);
 
     if(appendquery && newp) {
       /* Append the 'newp' string onto the old query. Add a '&' separator if
@@ -1888,29 +1884,29 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
       bool addamperand = querylen && (u->query[querylen -1] != '&');
       if(querylen) {
         struct dynbuf qbuf;
-        Curl_dyn_init(&qbuf, CURL_MAX_INPUT_LENGTH);
+        curlx_dyn_init(&qbuf, CURL_MAX_INPUT_LENGTH);
 
-        if(Curl_dyn_addn(&qbuf, u->query, querylen)) /* add original query */
+        if(curlx_dyn_addn(&qbuf, u->query, querylen)) /* add original query */
           goto nomem;
 
         if(addamperand) {
-          if(Curl_dyn_addn(&qbuf, "&", 1))
+          if(curlx_dyn_addn(&qbuf, "&", 1))
             goto nomem;
         }
-        if(Curl_dyn_add(&qbuf, newp))
+        if(curlx_dyn_add(&qbuf, newp))
           goto nomem;
-        Curl_dyn_free(&enc);
+        curlx_dyn_free(&enc);
         free(*storep);
-        *storep = Curl_dyn_ptr(&qbuf);
+        *storep = curlx_dyn_ptr(&qbuf);
         return CURLUE_OK;
 nomem:
-        Curl_dyn_free(&enc);
+        curlx_dyn_free(&enc);
         return CURLUE_OUT_OF_MEMORY;
       }
     }
 
     else if(what == CURLUPART_HOST) {
-      size_t n = Curl_dyn_len(&enc);
+      size_t n = curlx_dyn_len(&enc);
       if(!n && (flags & CURLU_NO_AUTHORITY)) {
         /* Skip hostname check, it is allowed to be empty. */
       }
@@ -1929,17 +1925,17 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
             bad = TRUE;
           free(decoded);
         }
-        else if(hostname_check(u, (char *)newp, n))
+        else if(hostname_check(u, (char *)CURL_UNCONST(newp), n))
           bad = TRUE;
         if(bad) {
-          Curl_dyn_free(&enc);
+          curlx_dyn_free(&enc);
           return CURLUE_BAD_HOSTNAME;
         }
       }
     }
 
     free(*storep);
-    *storep = (char *)newp;
+    *storep = (char *)CURL_UNCONST(newp);
   }
   return CURLUE_OK;
 }
diff --git a/extra/curl/curl-8.12.1/lib/urldata.h b/extra/curl/curl-8.14.1/lib/urldata.h
similarity index 94%
rename from extra/curl/curl-8.12.1/lib/urldata.h
rename to extra/curl/curl-8.14.1/lib/urldata.h
index d9acb2b7bf36..45052e84b18b 100644
--- a/extra/curl/curl-8.12.1/lib/urldata.h
+++ b/extra/curl/curl-8.14.1/lib/urldata.h
@@ -95,13 +95,6 @@ typedef unsigned int curl_prot_t;
    in the API */
 #define CURLPROTO_MASK   (0x3ffffff)
 
-#define DICT_MATCH "/MATCH:"
-#define DICT_MATCH2 "/M:"
-#define DICT_MATCH3 "/FIND:"
-#define DICT_DEFINE "/DEFINE:"
-#define DICT_DEFINE2 "/D:"
-#define DICT_DEFINE3 "/LOOKUP:"
-
 #define CURL_DEFAULT_USER "anonymous"
 #define CURL_DEFAULT_PASSWORD "ftp@example.com"
 
@@ -152,7 +145,7 @@ typedef unsigned int curl_prot_t;
 #include <netinet/in6.h>
 #endif
 
-#include "timeval.h"
+#include "curlx/timeval.h"
 
 #include <curl/curl.h>
 
@@ -160,7 +153,7 @@ typedef unsigned int curl_prot_t;
 #include "hostip.h"
 #include "hash.h"
 #include "splay.h"
-#include "dynbuf.h"
+#include "curlx/dynbuf.h"
 #include "dynhds.h"
 #include "request.h"
 #include "netrc.h"
@@ -182,7 +175,6 @@ typedef ssize_t (Curl_recv)(struct Curl_easy *data,   /* transfer */
 
 #include "mime.h"
 #include "imap.h"
-#include "pop3.h"
 #include "smtp.h"
 #include "ftp.h"
 #include "file.h"
@@ -272,6 +264,7 @@ struct ssl_primary_config {
   char *clientcert;
   char *cipher_list;     /* list of ciphers to use */
   char *cipher_list13;   /* list of TLS 1.3 cipher suites to use */
+  char *signature_algorithms; /* list of signature algorithms to use */
   char *pinned_key;
   char *CRLfile;         /* CRL to check certificate revocation */
   struct curl_blob *cert_blob;
@@ -282,8 +275,8 @@ struct ssl_primary_config {
   char *password; /* TLS password (for, e.g., SRP) */
 #endif
   char *curves;          /* list of curves to use */
-  unsigned char ssl_options;  /* the CURLOPT_SSL_OPTIONS bitmask */
   unsigned int version_max; /* max supported version the client wants to use */
+  unsigned char ssl_options;  /* the CURLOPT_SSL_OPTIONS bitmask */
   unsigned char version;    /* what version the client wants to use */
   BIT(verifypeer);       /* set TRUE if this is desired */
   BIT(verifyhost);       /* set TRUE if CN/SAN must match hostname */
@@ -513,7 +506,6 @@ struct ConnectBits {
 #ifdef USE_UNIX_SOCKETS
   BIT(abstract_unix_socket);
 #endif
-  BIT(tls_upgraded);
   BIT(sock_accepted); /* TRUE if the SECONDARYSOCKET was created with
                          accept() */
   BIT(parallel_connect); /* set TRUE when a parallel connect attempt has
@@ -563,21 +555,6 @@ struct hostname {
 #define CURL_WANT_RECV(data) \
   (((data)->req.keepon & KEEP_RECVBITS) == KEEP_RECV)
 
-#if defined(CURLRES_ASYNCH) || !defined(CURL_DISABLE_DOH)
-#define USE_CURL_ASYNC
-struct Curl_async {
-  char *hostname;
-  struct Curl_dns_entry *dns;
-  struct thread_data *tdata;
-  void *resolver; /* resolver state, if it is used in the URL state -
-                     ares_channel e.g. */
-  int port;
-  int status; /* if done is TRUE, this is the status from the callback */
-  BIT(done);  /* set TRUE when the lookup is complete */
-};
-
-#endif
-
 #define FIRSTSOCKET     0
 #define SECONDARYSOCKET 1
 
@@ -742,8 +719,6 @@ struct proxy_info {
   char *passwd;  /* proxy password string, allocated */
 };
 
-struct ldapconninfo;
-
 #define TRNSPRT_TCP 3
 #define TRNSPRT_UDP 4
 #define TRNSPRT_QUIC 5
@@ -755,6 +730,7 @@ struct ldapconninfo;
  */
 struct connectdata {
   struct Curl_llist_node cpool_node; /* conncache lists */
+  struct Curl_llist_node cshutdn_node; /* cshutdn list */
 
   curl_closesocket_callback fclosesocket; /* function closing the socket(s) */
   void *closesocket_client;
@@ -763,19 +739,19 @@ struct connectdata {
      handle is still used by one or more easy handles and can only used by any
      other easy handle without careful consideration (== only for
      multiplexing) and it cannot be used by another multi handle! */
-#define CONN_INUSE(c) Curl_llist_count(&(c)->easyq)
+#define CONN_INUSE(c) (!Curl_uint_spbset_empty(&(c)->xfers_attached))
+#define CONN_ATTACHED(c) Curl_uint_spbset_count(&(c)->xfers_attached)
 
   /**** Fields set when inited and not modified again */
   curl_off_t connection_id; /* Contains a unique number to make it easier to
                                track the connections in the log output */
   char *destination; /* string carrying normalized hostname+port+scope */
-  size_t destination_len; /* strlen(destination) + 1 */
 
-  /* 'dns_entry' is the particular host we use. This points to an entry in the
-     DNS cache and it will not get pruned while locked. It gets unlocked in
-     multi_done(). This entry will be NULL if the connection is reused as then
-     there is no name resolve done. */
-  struct Curl_dns_entry *dns_entry;
+  /* `meta_hash` is a general key-value store for implementations
+   * with the lifetime of the connection.
+   * Elements need to be added with their own destructor to be invoked when
+   * the connection is cleaned up (see Curl_hash_add2()).*/
+  struct Curl_hash meta_hash;
 
   /* 'remote_addr' is the particular IP we connected to. it is owned, set
    * and NULLed by the connected socket filter (if there is one). */
@@ -803,7 +779,6 @@ struct connectdata {
   char *options; /* options string, allocated */
   char *sasl_authzid;     /* authorization identity string, allocated */
   char *oauth_bearer; /* OAUTH2 bearer, allocated */
-  struct curltime now;     /* "current" time */
   struct curltime created; /* creation time */
   struct curltime lastused; /* when returned to the connection poolas idle */
   curl_socket_t sock[2]; /* two sockets, the second is used for the data
@@ -815,9 +790,6 @@ struct connectdata {
     struct curltime start[2]; /* when filter shutdown started */
     unsigned int timeout_ms; /* 0 means no timeout */
   } shutdown;
-  /* Last pollset used in connection shutdown. Used to detect changes
-   * for multi_socket API. */
-  struct easy_pollset shutdown_poll;
 
   struct ssl_primary_config ssl_config;
 #ifndef CURL_DISABLE_PROXY
@@ -855,7 +827,14 @@ struct connectdata {
   struct kerberos5data krb5;  /* variables into the structure definition, */
 #endif                        /* however, some of them are ftp specific. */
 
-  struct Curl_llist easyq;    /* List of easy handles using this connection */
+  struct uint_spbset xfers_attached; /* mids of attached transfers */
+  /* A connection cache from a SHARE might be used in several multi handles.
+   * We MUST not reuse connections that are running in another multi,
+   * for concurrency reasons. That multi might run in another thread.
+   * `attached_multi` is set by the first transfer attached and cleared
+   * when the last one is detached.
+   * NEVER call anything on this multi, just check for equality. */
+  struct Curl_multi *attached_multi;
 
   /*************** Request - specific items ************/
 #if defined(USE_WINDOWS_SSPI) && defined(SECPKG_ATTR_ENDPOINT_BINDINGS)
@@ -884,46 +863,6 @@ struct connectdata {
   struct negotiatedata proxyneg; /* state data for proxy Negotiate auth */
 #endif
 
-  union {
-#ifndef CURL_DISABLE_FTP
-    struct ftp_conn ftpc;
-#endif
-#ifdef USE_SSH
-    struct ssh_conn sshc;
-#endif
-#ifndef CURL_DISABLE_TFTP
-    struct tftp_state_data *tftpc;
-#endif
-#ifndef CURL_DISABLE_IMAP
-    struct imap_conn imapc;
-#endif
-#ifndef CURL_DISABLE_POP3
-    struct pop3_conn pop3c;
-#endif
-#ifndef CURL_DISABLE_SMTP
-    struct smtp_conn smtpc;
-#endif
-#ifndef CURL_DISABLE_RTSP
-    struct rtsp_conn rtspc;
-#endif
-#ifndef CURL_DISABLE_SMB
-    struct smb_conn smbc;
-#endif
-#ifdef USE_LIBRTMP
-    void *rtmp;
-#endif
-#ifdef USE_OPENLDAP
-    struct ldapconninfo *ldapc;
-#endif
-#ifndef CURL_DISABLE_MQTT
-    struct mqtt_conn mqtt;
-#endif
-#ifndef CURL_DISABLE_WEBSOCKETS
-    struct websocket *ws;
-#endif
-    unsigned int unused:1; /* avoids empty union */
-  } proto;
-
 #ifdef USE_UNIX_SOCKETS
   char *unix_domain_socket;
 #endif
@@ -1039,9 +978,6 @@ struct Progress {
   curl_off_t current_speed; /* uses the currently fastest transfer */
   curl_off_t earlydata_sent;
 
-  int width; /* screen width at download start */
-  int flags; /* see progress.h */
-
   timediff_t timespent;
 
   timediff_t t_postqueue;
@@ -1063,7 +999,11 @@ struct Progress {
 
   curl_off_t speeder[ CURR_TIME ];
   struct curltime speeder_time[ CURR_TIME ];
-  int speeder_c;
+  unsigned char speeder_c;
+  BIT(hide);
+  BIT(ul_size_known);
+  BIT(dl_size_known);
+  BIT(headers_out); /* when the headers have been written */
   BIT(callback);  /* set when progress callback is used */
   BIT(is_t_startransfer_set);
 };
@@ -1138,6 +1078,7 @@ typedef enum {
   EXPIRE_QUIC,
   EXPIRE_FTP_ACCEPT,
   EXPIRE_ALPN_EYEBALLS,
+  EXPIRE_SHUTDOWN,
   EXPIRE_LAST /* not an actual timer, used as a marker only */
 } expire_id;
 
@@ -1211,6 +1152,8 @@ struct UrlState {
 #endif
   struct auth authhost;  /* auth details for host */
   struct auth authproxy; /* auth details for proxy */
+
+  struct Curl_dns_entry *dns[2]; /* DNS to connect FIRST/SECONDARY */
 #ifdef USE_CURL_ASYNC
   struct Curl_async async;  /* asynchronous name resolver data */
 #endif
@@ -1218,10 +1161,13 @@ struct UrlState {
 #if defined(USE_OPENSSL)
   /* void instead of ENGINE to avoid bleeding OpenSSL into this header */
   void *engine;
-  /* this is just a flag -- we do not need to reference the provider in any
-   * way as OpenSSL takes care of that */
-  BIT(provider);
-  BIT(provider_failed);
+  /* void instead of OSSL_PROVIDER */
+  void *provider;
+  void *baseprov;
+  void *libctx;
+  char *propq; /* for a provider */
+
+  BIT(provider_loaded);
 #endif /* USE_OPENSSL */
   struct curltime expiretime; /* set this with Curl_expire() only */
   struct Curl_tree timenode; /* for the splay stuff */
@@ -1312,10 +1258,9 @@ struct UrlState {
     char *proxypasswd;
 #endif
   } aptr;
-  unsigned char httpwant; /* when non-zero, a specific HTTP version requested
-                             to be used in the library's request(s) */
-  unsigned char httpversion; /* the lowest HTTP version*10 reported by any
-                                server involved in this request */
+#ifndef CURL_DISABLE_HTTP
+  struct http_negotiation http_neg;
+#endif
   unsigned char httpreq; /* Curl_HttpReq; what kind of HTTP request (if any)
                             is this */
   unsigned char select_bits; /* != 0 -> bitmask of socket events for this
@@ -1360,6 +1305,7 @@ struct UrlState {
   BIT(internal); /* internal: true if this easy handle was created for
                     internal use and the user does not have ownership of the
                     handle. */
+  BIT(http_ignorecustom); /* ignore custom method from now */
 };
 
 /*
@@ -1496,6 +1442,7 @@ enum dupstring {
 #endif
   STRING_ECH_CONFIG,            /* CURLOPT_ECH_CONFIG */
   STRING_ECH_PUBLIC,            /* CURLOPT_ECH_PUBLIC */
+  STRING_SSL_SIGNATURE_ALGORITHMS, /* CURLOPT_SSL_SIGNATURE_ALGORITHMS */
 
   /* -- end of null-terminated strings -- */
 
@@ -1522,10 +1469,6 @@ enum dupblob {
   BLOB_LAST
 };
 
-/* callback that gets called when this easy handle is completed within a multi
-   handle. Only used for internally created transfers, like for example
-   DoH. */
-typedef int (*multidone_func)(struct Curl_easy *easy, CURLcode result);
 
 struct UserDefined {
   FILE *err;         /* the stderr user data goes here */
@@ -1681,10 +1624,6 @@ struct UserDefined {
                                                   before resolver start */
   void *resolver_start_client; /* pointer to pass to resolver start callback */
   long upkeep_interval_ms;      /* Time between calls for connection upkeep. */
-  multidone_func fmultidone;
-#ifndef CURL_DISABLE_DOH
-  curl_off_t dohfor_mid; /* this is a DoH request for that transfer */
-#endif
   CURLU *uh; /* URL handle for the current parsed URL */
 #ifndef CURL_DISABLE_HTTP
   void *trailer_data; /* pointer to pass to trailer data callback */
@@ -1694,6 +1633,9 @@ struct UserDefined {
   struct curl_slist *mail_rcpt; /* linked list of mail recipients */
 #endif
   unsigned int maxconnects; /* Max idle connections in the connection cache */
+#ifdef USE_ECH
+  int tls_ech;      /* TLS ECH configuration  */
+#endif
   unsigned short use_port; /* which port to use (when not using default) */
 #ifndef CURL_DISABLE_BINDLOCAL
   unsigned short localport; /* local port number to bind to */
@@ -1721,11 +1663,13 @@ struct UserDefined {
                              to be used in the library's request(s) */
   unsigned char ipver; /* the CURL_IPRESOLVE_* defines in the public header
                           file 0 - whatever, 1 - v2, 2 - v6 */
+  unsigned char upload_flags; /* flags set by CURLOPT_UPLOAD_FLAGS */
 #ifdef HAVE_GSSAPI
   /* GSS-API credential delegation, see the documentation of
      CURLOPT_GSSAPI_DELEGATION */
   unsigned char gssapi_delegation;
 #endif
+  unsigned char http_follow_mode; /* follow HTTP redirects */
   BIT(connect_only); /* make connection/request, then let application use the
                         socket */
   BIT(connect_only_ws); /* special websocket connect-only level */
@@ -1774,10 +1718,8 @@ struct UserDefined {
                             us */
   BIT(wildcard_enabled); /* enable wildcard matching */
 #endif
-  BIT(hide_progress);    /* do not use the progress meter */
   BIT(http_fail_on_error);  /* fail on HTTP error codes >= 400 */
   BIT(http_keep_sending_on_error); /* for HTTP status codes >= 300 */
-  BIT(http_follow_location); /* follow HTTP redirects */
   BIT(http_transfer_encoding); /* request compressed HTTP transfer-encoding */
   BIT(allow_auth_to_other_hosts);
   BIT(include_header); /* include received protocol headers in data output */
@@ -1830,9 +1772,7 @@ struct UserDefined {
   BIT(http09_allowed); /* allow HTTP/0.9 responses */
 #ifndef CURL_DISABLE_WEBSOCKETS
   BIT(ws_raw_mode);
-#endif
-#ifdef USE_ECH
-  int tls_ech;      /* TLS ECH configuration  */
+  BIT(ws_no_auto_pong);
 #endif
 };
 
@@ -1842,14 +1782,11 @@ struct UserDefined {
 #define IS_MIME_POST(a) FALSE
 #endif
 
-struct Names {
-  struct Curl_hash *hostcache;
-  enum {
-    HCACHE_NONE,    /* not pointing to anything */
-    HCACHE_MULTI,   /* points to a shared one in the multi handle */
-    HCACHE_SHARED   /* points to a shared one in a shared object */
-  } hostcachetype;
-};
+/* callback that gets called when a sub easy (data->master_mid set) is
+   DONE. Called on the master easy. */
+typedef void multi_sub_xfer_done_cb(struct Curl_easy *master_easy,
+                                    struct Curl_easy *sub_easy,
+                                    CURLcode result);
 
 /*
  * The 'connectdata' struct MUST have all the connection oriented stuff as we
@@ -1875,24 +1812,17 @@ struct Curl_easy {
   /* once an easy handle is added to a multi, either explicitly by the
    * libcurl application or implicitly during `curl_easy_perform()`,
    * a unique identifier inside this one multi instance. */
-  curl_off_t mid;
+  unsigned int mid;
+  unsigned int master_mid; /* if set, this transfer belongs to a master */
+  multi_sub_xfer_done_cb *sub_xfer_done;
 
   struct connectdata *conn;
-  struct Curl_llist_node multi_queue; /* for multihandle list management */
-  struct Curl_llist_node conn_queue; /* list per connectdata */
 
   CURLMstate mstate;  /* the handle's state */
   CURLcode result;   /* previous result */
 
   struct Curl_message msg; /* A single posted message. */
 
-  /* Array with the plain socket numbers this handle takes care of, in no
-     particular order. Note that all sockets are added to the sockhash, where
-     the state etc are also kept. This array is mostly used to detect when a
-     socket is to be removed from the hash. See singlesocket(). */
-  struct easy_pollset last_poll;
-
-  struct Names dns;
   struct Curl_multi *multi;    /* if non-NULL, points to the multi handle
                                   struct to which this "belongs" when used by
                                   the multi interface */
@@ -1900,6 +1830,13 @@ struct Curl_easy {
                                     struct to which this "belongs" when used
                                     by the easy interface */
   struct Curl_share *share;    /* Share, handles global variable mutexing */
+
+  /* `meta_hash` is a general key-value store for implementations
+   * with the lifetime of the easy handle.
+   * Elements need to be added with their own destructor to be invoked when
+   * the easy handle is cleaned up (see Curl_hash_add2()).*/
+  struct Curl_hash meta_hash;
+
 #ifdef USE_LIBPSL
   struct PslCache *psl;        /* The associated PSL cache. */
 #endif
diff --git a/extra/curl/curl-8.12.1/lib/vauth/.checksrc b/extra/curl/curl-8.14.1/lib/vauth/.checksrc
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/vauth/.checksrc
rename to extra/curl/curl-8.14.1/lib/vauth/.checksrc
diff --git a/extra/curl/curl-8.12.1/lib/vauth/cleartext.c b/extra/curl/curl-8.14.1/lib/vauth/cleartext.c
similarity index 94%
rename from extra/curl/curl-8.12.1/lib/vauth/cleartext.c
rename to extra/curl/curl-8.14.1/lib/vauth/cleartext.c
index cf8108ac5b6a..719abd196067 100644
--- a/extra/curl/curl-8.12.1/lib/vauth/cleartext.c
+++ b/extra/curl/curl-8.14.1/lib/vauth/cleartext.c
@@ -25,24 +25,23 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_SMTP) ||       \
   !defined(CURL_DISABLE_POP3) || \
   (!defined(CURL_DISABLE_LDAP) && defined(USE_OPENLDAP))
 
 #include <curl/curl.h>
-#include "urldata.h"
+#include "../urldata.h"
 
-#include "vauth/vauth.h"
-#include "warnless.h"
-#include "strtok.h"
-#include "sendf.h"
-#include "curl_printf.h"
+#include "vauth.h"
+#include "../curlx/warnless.h"
+#include "../sendf.h"
+#include "../curl_printf.h"
 
 /* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
 
 /*
  * Curl_auth_create_plain_message()
diff --git a/extra/curl/curl-8.12.1/lib/vauth/cram.c b/extra/curl/curl-8.14.1/lib/vauth/cram.c
similarity index 92%
rename from extra/curl/curl-8.12.1/lib/vauth/cram.c
rename to extra/curl/curl-8.14.1/lib/vauth/cram.c
index c51c7285b478..3586e1012d0a 100644
--- a/extra/curl/curl-8.12.1/lib/vauth/cram.c
+++ b/extra/curl/curl-8.14.1/lib/vauth/cram.c
@@ -24,22 +24,22 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #ifndef CURL_DISABLE_DIGEST_AUTH
 
 #include <curl/curl.h>
-#include "urldata.h"
+#include "../urldata.h"
 
-#include "vauth/vauth.h"
-#include "curl_hmac.h"
-#include "curl_md5.h"
-#include "warnless.h"
-#include "curl_printf.h"
+#include "vauth.h"
+#include "../curl_hmac.h"
+#include "../curl_md5.h"
+#include "../curlx/warnless.h"
+#include "../curl_printf.h"
 
 /* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
 
 
 /*
diff --git a/extra/curl/curl-8.12.1/lib/vauth/digest.c b/extra/curl/curl-8.14.1/lib/vauth/digest.c
similarity index 93%
rename from extra/curl/curl-8.12.1/lib/vauth/digest.c
rename to extra/curl/curl-8.14.1/lib/vauth/digest.c
index 0acfcace1d13..ec4e82256c4e 100644
--- a/extra/curl/curl-8.12.1/lib/vauth/digest.c
+++ b/extra/curl/curl-8.14.1/lib/vauth/digest.c
@@ -25,31 +25,32 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #ifndef CURL_DISABLE_DIGEST_AUTH
 
 #include <curl/curl.h>
 
-#include "vauth/vauth.h"
-#include "vauth/digest.h"
-#include "urldata.h"
-#include "curl_base64.h"
-#include "curl_hmac.h"
-#include "curl_md5.h"
-#include "curl_sha256.h"
-#include "curl_sha512_256.h"
-#include "vtls/vtls.h"
-#include "warnless.h"
-#include "strtok.h"
-#include "strcase.h"
-#include "curl_printf.h"
-#include "rand.h"
+#include "vauth.h"
+#include "digest.h"
+#include "../urldata.h"
+#include "../curlx/base64.h"
+#include "../curl_hmac.h"
+#include "../curl_md5.h"
+#include "../curl_sha256.h"
+#include "../curl_sha512_256.h"
+#include "../vtls/vtls.h"
+#include "../curlx/warnless.h"
+#include "../curlx/strparse.h"
+#include "../strcase.h"
+#include "../curl_printf.h"
+#include "../rand.h"
 
 /* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
 
+#ifndef USE_WINDOWS_SSPI
 #define SESSION_ALGO 1 /* for algos with this bit set */
 
 #define ALGO_MD5 0
@@ -59,7 +60,6 @@
 #define ALGO_SHA512_256 4
 #define ALGO_SHA512_256SESS (ALGO_SHA512_256 | SESSION_ALGO)
 
-#if !defined(USE_WINDOWS_SSPI)
 #define DIGEST_QOP_VALUE_AUTH             (1 << 0)
 #define DIGEST_QOP_VALUE_AUTH_INT         (1 << 1)
 #define DIGEST_QOP_VALUE_AUTH_CONF        (1 << 2)
@@ -141,8 +141,8 @@ bool Curl_auth_digest_get_pair(const char *str, char *value, char *content,
   return TRUE;
 }
 
-#if !defined(USE_WINDOWS_SSPI)
-/* Convert md5 chunk to RFC2617 (section 3.1.3) -suitable ASCII string */
+#ifndef USE_WINDOWS_SSPI
+/* Convert MD5 chunk to RFC2617 (section 3.1.3) -suitable ASCII string */
 static void auth_digest_md5_to_ascii(unsigned char *source, /* 16 bytes */
                                      unsigned char *dest) /* 33 bytes */
 {
@@ -165,7 +165,7 @@ static char *auth_digest_string_quoted(const char *source)
 {
   char *dest;
   const char *s = source;
-  size_t n = 1; /* null terminator */
+  size_t n = 1; /* null-terminator */
 
   /* Calculate size needed */
   while(*s) {
@@ -219,33 +219,21 @@ static bool auth_digest_get_key_value(const char *chlg,
 
 static CURLcode auth_digest_get_qop_values(const char *options, int *value)
 {
-  char *tmp;
-  char *token;
-  char *tok_buf = NULL;
-
+  struct Curl_str out;
   /* Initialise the output */
   *value = 0;
 
-  /* Tokenise the list of qop values. Use a temporary clone of the buffer since
-     Curl_strtok_r() ruins it. */
-  tmp = strdup(options);
-  if(!tmp)
-    return CURLE_OUT_OF_MEMORY;
-
-  token = Curl_strtok_r(tmp, ",", &tok_buf);
-  while(token) {
-    if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH))
+  while(!curlx_str_until(&options, &out, 32, ',')) {
+    if(curlx_str_casecompare(&out, DIGEST_QOP_VALUE_STRING_AUTH))
       *value |= DIGEST_QOP_VALUE_AUTH;
-    else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_INT))
+    else if(curlx_str_casecompare(&out, DIGEST_QOP_VALUE_STRING_AUTH_INT))
       *value |= DIGEST_QOP_VALUE_AUTH_INT;
-    else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_CONF))
+    else if(curlx_str_casecompare(&out, DIGEST_QOP_VALUE_STRING_AUTH_CONF))
       *value |= DIGEST_QOP_VALUE_AUTH_CONF;
-
-    token = Curl_strtok_r(NULL, ",", &tok_buf);
+    if(curlx_str_single(&options, ','))
+      break;
   }
 
-  free(tmp);
-
   return CURLE_OK;
 }
 
@@ -504,10 +492,6 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg,
                                               struct digestdata *digest)
 {
   bool before = FALSE; /* got a nonce before */
-  bool foundAuth = FALSE;
-  bool foundAuthInt = FALSE;
-  char *token = NULL;
-  char *tmp = NULL;
 
   /* If we already have received a nonce, keep that in mind */
   if(digest->nonce)
@@ -551,29 +535,25 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg,
           return CURLE_OUT_OF_MEMORY;
       }
       else if(strcasecompare(value, "qop")) {
-        char *tok_buf = NULL;
-        /* Tokenize the list and choose auth if possible, use a temporary
-           clone of the buffer since Curl_strtok_r() ruins it */
-        tmp = strdup(content);
-        if(!tmp)
-          return CURLE_OUT_OF_MEMORY;
-
-        token = Curl_strtok_r(tmp, ",", &tok_buf);
-        while(token) {
-          /* Pass additional spaces here */
-          while(*token && ISBLANK(*token))
-            token++;
-          if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH)) {
+        const char *token = content;
+        struct Curl_str out;
+        bool foundAuth = FALSE;
+        bool foundAuthInt = FALSE;
+        /* Pass leading spaces */
+        while(*token && ISBLANK(*token))
+          token++;
+        while(!curlx_str_until(&token, &out, 32, ',')) {
+          if(curlx_str_casecompare(&out, DIGEST_QOP_VALUE_STRING_AUTH))
             foundAuth = TRUE;
-          }
-          else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_INT)) {
+          else if(curlx_str_casecompare(&out,
+                                       DIGEST_QOP_VALUE_STRING_AUTH_INT))
             foundAuthInt = TRUE;
-          }
-          token = Curl_strtok_r(NULL, ",", &tok_buf);
+          if(curlx_str_single(&token, ','))
+            break;
+          while(*token && ISBLANK(*token))
+            token++;
         }
 
-        free(tmp);
-
         /* Select only auth or auth-int. Otherwise, ignore */
         if(foundAuth) {
           free(digest->qop);
@@ -719,8 +699,8 @@ static CURLcode auth_create_digest_http_message(
     if(result)
       return result;
 
-    result = Curl_base64_encode(cnoncebuf, sizeof(cnoncebuf),
-                                &cnonce, &cnonce_sz);
+    result = curlx_base64_encode(cnoncebuf, sizeof(cnoncebuf),
+                                 &cnonce, &cnonce_sz);
     if(result)
       return result;
 
diff --git a/extra/curl/curl-8.12.1/lib/vauth/digest.h b/extra/curl/curl-8.14.1/lib/vauth/digest.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/vauth/digest.h
rename to extra/curl/curl-8.14.1/lib/vauth/digest.h
diff --git a/extra/curl/curl-8.12.1/lib/vauth/digest_sspi.c b/extra/curl/curl-8.14.1/lib/vauth/digest_sspi.c
similarity index 91%
rename from extra/curl/curl-8.12.1/lib/vauth/digest_sspi.c
rename to extra/curl/curl-8.14.1/lib/vauth/digest_sspi.c
index c06ee1064f47..2761c599650f 100644
--- a/extra/curl/curl-8.12.1/lib/vauth/digest_sspi.c
+++ b/extra/curl/curl-8.14.1/lib/vauth/digest_sspi.c
@@ -25,25 +25,25 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #if defined(USE_WINDOWS_SSPI) && !defined(CURL_DISABLE_DIGEST_AUTH)
 
 #include <curl/curl.h>
 
-#include "vauth/vauth.h"
-#include "vauth/digest.h"
-#include "urldata.h"
-#include "warnless.h"
-#include "curl_multibyte.h"
-#include "sendf.h"
-#include "strdup.h"
-#include "strcase.h"
-#include "strerror.h"
+#include "vauth.h"
+#include "digest.h"
+#include "../urldata.h"
+#include "../curlx/warnless.h"
+#include "../curlx/multibyte.h"
+#include "../sendf.h"
+#include "../strdup.h"
+#include "../strcase.h"
+#include "../strerror.h"
 
 /* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
 
 /*
 * Curl_auth_is_digest_supported()
@@ -61,8 +61,9 @@ bool Curl_auth_is_digest_supported(void)
 
   /* Query the security package for Digest */
   status =
-    Curl_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
-                                          &SecurityPackage);
+    Curl_pSecFn->QuerySecurityPackageInfo(
+                                   (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_DIGEST)),
+                                   &SecurityPackage);
 
   /* Release the package buffer as it is not required anymore */
   if(status == SEC_E_OK) {
@@ -121,8 +122,9 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
 
   /* Query the security package for DigestSSP */
   status =
-    Curl_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
-                                          &SecurityPackage);
+    Curl_pSecFn->QuerySecurityPackageInfo(
+                                   (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_DIGEST)),
+                                   &SecurityPackage);
   if(status != SEC_E_OK) {
     failf(data, "SSPI: could not get auth info");
     return CURLE_AUTH_ERROR;
@@ -163,10 +165,10 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
 
   /* Acquire our credentials handle */
   status = Curl_pSecFn->AcquireCredentialsHandle(NULL,
-                                              (TCHAR *) TEXT(SP_NAME_DIGEST),
-                                              SECPKG_CRED_OUTBOUND, NULL,
-                                              p_identity, NULL, NULL,
-                                              &credentials, &expiry);
+                                   (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_DIGEST)),
+                                   SECPKG_CRED_OUTBOUND, NULL,
+                                   p_identity, NULL, NULL,
+                                   &credentials, &expiry);
 
   if(status != SEC_E_OK) {
     Curl_sspi_free_identity(p_identity);
@@ -180,7 +182,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
   chlg_desc.cBuffers  = 1;
   chlg_desc.pBuffers  = &chlg_buf;
   chlg_buf.BufferType = SECBUFFER_TOKEN;
-  chlg_buf.pvBuffer   = (void *) Curl_bufref_ptr(chlg);
+  chlg_buf.pvBuffer   = CURL_UNCONST(Curl_bufref_ptr(chlg));
   chlg_buf.cbBuffer   = curlx_uztoul(Curl_bufref_len(chlg));
 
   /* Setup the response "output" security buffer */
@@ -240,7 +242,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
 /*
  * Curl_override_sspi_http_realm()
  *
- * This is used to populate the domain in a SSPI identity structure
+ * This is used to populate the domain in an SSPI identity structure
  * The realm is extracted from the challenge message and used as the
  * domain if it is not already explicitly set.
  *
@@ -271,7 +273,7 @@ CURLcode Curl_override_sspi_http_realm(const char *chlg,
         if(strcasecompare(value, "realm")) {
 
           /* Setup identity's domain and length */
-          domain.tchar_ptr = curlx_convert_UTF8_to_tchar((char *) content);
+          domain.tchar_ptr = curlx_convert_UTF8_to_tchar(content);
           if(!domain.tchar_ptr)
             return CURLE_OUT_OF_MEMORY;
 
@@ -413,8 +415,9 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
 
   /* Query the security package for DigestSSP */
   status =
-    Curl_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
-                                          &SecurityPackage);
+    Curl_pSecFn->QuerySecurityPackageInfo(
+                                   (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_DIGEST)),
+                                   &SecurityPackage);
   if(status != SEC_E_OK) {
     failf(data, "SSPI: could not get auth info");
     return CURLE_AUTH_ERROR;
@@ -454,10 +457,10 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
     chlg_buf[0].pvBuffer   = NULL;
     chlg_buf[0].cbBuffer   = 0;
     chlg_buf[1].BufferType = SECBUFFER_PKG_PARAMS;
-    chlg_buf[1].pvBuffer   = (void *) request;
+    chlg_buf[1].pvBuffer   = CURL_UNCONST(request);
     chlg_buf[1].cbBuffer   = curlx_uztoul(strlen((const char *) request));
     chlg_buf[2].BufferType = SECBUFFER_PKG_PARAMS;
-    chlg_buf[2].pvBuffer   = (void *) uripath;
+    chlg_buf[2].pvBuffer   = CURL_UNCONST(uripath);
     chlg_buf[2].cbBuffer   = curlx_uztoul(strlen((const char *) uripath));
     chlg_buf[3].BufferType = SECBUFFER_PKG_PARAMS;
     chlg_buf[3].pvBuffer   = NULL;
@@ -534,10 +537,10 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
 
     /* Acquire our credentials handle */
     status = Curl_pSecFn->AcquireCredentialsHandle(NULL,
-                                                (TCHAR *) TEXT(SP_NAME_DIGEST),
-                                                SECPKG_CRED_OUTBOUND, NULL,
-                                                p_identity, NULL, NULL,
-                                                &credentials, &expiry);
+                                   (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_DIGEST)),
+                                   SECPKG_CRED_OUTBOUND, NULL,
+                                   p_identity, NULL, NULL,
+                                   &credentials, &expiry);
     if(status != SEC_E_OK) {
       Curl_sspi_free_identity(p_identity);
       free(output_token);
@@ -553,7 +556,7 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
     chlg_buf[0].pvBuffer   = digest->input_token;
     chlg_buf[0].cbBuffer   = curlx_uztoul(digest->input_token_len);
     chlg_buf[1].BufferType = SECBUFFER_PKG_PARAMS;
-    chlg_buf[1].pvBuffer   = (void *) request;
+    chlg_buf[1].pvBuffer   = CURL_UNCONST(request);
     chlg_buf[1].cbBuffer   = curlx_uztoul(strlen((const char *) request));
     chlg_buf[2].BufferType = SECBUFFER_PKG_PARAMS;
     chlg_buf[2].pvBuffer   = NULL;
@@ -567,7 +570,7 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
     resp_buf.pvBuffer   = output_token;
     resp_buf.cbBuffer   = curlx_uztoul(token_max);
 
-    spn = curlx_convert_UTF8_to_tchar((char *) uripath);
+    spn = curlx_convert_UTF8_to_tchar((const char *) uripath);
     if(!spn) {
       Curl_pSecFn->FreeCredentialsHandle(&credentials);
 
@@ -579,8 +582,12 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
 
     /* Allocate our new context handle */
     digest->http_context = calloc(1, sizeof(CtxtHandle));
-    if(!digest->http_context)
+    if(!digest->http_context) {
+      curlx_unicodefree(spn);
+      Curl_sspi_free_identity(p_identity);
+      free(output_token);
       return CURLE_OUT_OF_MEMORY;
+    }
 
     /* Generate our response message */
     status = Curl_pSecFn->InitializeSecurityContext(&credentials, NULL,
diff --git a/extra/curl/curl-8.12.1/lib/vauth/gsasl.c b/extra/curl/curl-8.14.1/lib/vauth/gsasl.c
similarity index 95%
rename from extra/curl/curl-8.12.1/lib/vauth/gsasl.c
rename to extra/curl/curl-8.14.1/lib/vauth/gsasl.c
index ee11b6039d69..3684c8f4b247 100644
--- a/extra/curl/curl-8.12.1/lib/vauth/gsasl.c
+++ b/extra/curl/curl-8.14.1/lib/vauth/gsasl.c
@@ -24,22 +24,22 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #ifdef USE_GSASL
 
 #include <curl/curl.h>
 
-#include "vauth/vauth.h"
-#include "urldata.h"
-#include "sendf.h"
+#include "vauth.h"
+#include "../urldata.h"
+#include "../sendf.h"
 
 #include <gsasl.h>
 
 /* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_printf.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
 
 bool Curl_auth_gsasl_is_supported(struct Curl_easy *data,
                                   const char *mech,
diff --git a/extra/curl/curl-8.12.1/lib/vauth/krb5_gssapi.c b/extra/curl/curl-8.14.1/lib/vauth/krb5_gssapi.c
similarity index 96%
rename from extra/curl/curl-8.12.1/lib/vauth/krb5_gssapi.c
rename to extra/curl/curl-8.14.1/lib/vauth/krb5_gssapi.c
index beaf02729723..b5590406175c 100644
--- a/extra/curl/curl-8.12.1/lib/vauth/krb5_gssapi.c
+++ b/extra/curl/curl-8.14.1/lib/vauth/krb5_gssapi.c
@@ -25,22 +25,22 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #if defined(HAVE_GSSAPI) && defined(USE_KERBEROS5)
 
 #include <curl/curl.h>
 
-#include "vauth/vauth.h"
-#include "curl_sasl.h"
-#include "urldata.h"
-#include "curl_gssapi.h"
-#include "sendf.h"
-#include "curl_printf.h"
+#include "vauth.h"
+#include "../curl_sasl.h"
+#include "../urldata.h"
+#include "../curl_gssapi.h"
+#include "../sendf.h"
+#include "../curl_printf.h"
 
 /* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
 
 #if defined(__GNUC__) && defined(__APPLE__)
 #pragma GCC diagnostic push
@@ -133,7 +133,7 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data,
       infof(data, "GSSAPI handshake failure (empty challenge message)");
       return CURLE_BAD_CONTENT_ENCODING;
     }
-    input_token.value = (void *) Curl_bufref_ptr(chlg);
+    input_token.value = CURL_UNCONST(Curl_bufref_ptr(chlg));
     input_token.length = Curl_bufref_len(chlg);
   }
 
@@ -210,7 +210,7 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
   }
 
   /* Setup the challenge "input" security buffer */
-  input_token.value = (void *) Curl_bufref_ptr(chlg);
+  input_token.value = CURL_UNCONST(Curl_bufref_ptr(chlg));
   input_token.length = Curl_bufref_len(chlg);
 
   /* Decrypt the inbound challenge and obtain the qop */
diff --git a/extra/curl/curl-8.12.1/lib/vauth/krb5_sspi.c b/extra/curl/curl-8.14.1/lib/vauth/krb5_sspi.c
similarity index 93%
rename from extra/curl/curl-8.12.1/lib/vauth/krb5_sspi.c
rename to extra/curl/curl-8.14.1/lib/vauth/krb5_sspi.c
index 00a5db125d07..a29358569d9c 100644
--- a/extra/curl/curl-8.12.1/lib/vauth/krb5_sspi.c
+++ b/extra/curl/curl-8.14.1/lib/vauth/krb5_sspi.c
@@ -24,21 +24,21 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #if defined(USE_WINDOWS_SSPI) && defined(USE_KERBEROS5)
 
 #include <curl/curl.h>
 
-#include "vauth/vauth.h"
-#include "urldata.h"
-#include "warnless.h"
-#include "curl_multibyte.h"
-#include "sendf.h"
+#include "vauth.h"
+#include "../urldata.h"
+#include "../curlx/warnless.h"
+#include "../curlx/multibyte.h"
+#include "../sendf.h"
 
 /* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
 
 /*
  * Curl_auth_is_gssapi_supported()
@@ -55,9 +55,9 @@ bool Curl_auth_is_gssapi_supported(void)
   SECURITY_STATUS status;
 
   /* Query the security package for Kerberos */
-  status = Curl_pSecFn->QuerySecurityPackageInfo((TCHAR *)
-                                              TEXT(SP_NAME_KERBEROS),
-                                              &SecurityPackage);
+  status = Curl_pSecFn->QuerySecurityPackageInfo(
+                                 (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_KERBEROS)),
+                                 &SecurityPackage);
 
   /* Release the package buffer as it is not required anymore */
   if(status == SEC_E_OK) {
@@ -118,9 +118,9 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data,
 
   if(!krb5->output_token) {
     /* Query the security package for Kerberos */
-    status = Curl_pSecFn->QuerySecurityPackageInfo((TCHAR *)
-                                                TEXT(SP_NAME_KERBEROS),
-                                                &SecurityPackage);
+    status = Curl_pSecFn->QuerySecurityPackageInfo(
+                                 (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_KERBEROS)),
+                                 &SecurityPackage);
     if(status != SEC_E_OK) {
       failf(data, "SSPI: could not get auth info");
       return CURLE_AUTH_ERROR;
@@ -159,11 +159,10 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data,
 
     /* Acquire our credentials handle */
     status = Curl_pSecFn->AcquireCredentialsHandle(NULL,
-                                                (TCHAR *)
-                                                TEXT(SP_NAME_KERBEROS),
-                                                SECPKG_CRED_OUTBOUND, NULL,
-                                                krb5->p_identity, NULL, NULL,
-                                                krb5->credentials, &expiry);
+                                 (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_KERBEROS)),
+                                 SECPKG_CRED_OUTBOUND, NULL,
+                                 krb5->p_identity, NULL, NULL,
+                                 krb5->credentials, &expiry);
     if(status != SEC_E_OK)
       return CURLE_LOGIN_DENIED;
 
@@ -184,7 +183,7 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data,
     chlg_desc.cBuffers  = 1;
     chlg_desc.pBuffers  = &chlg_buf;
     chlg_buf.BufferType = SECBUFFER_TOKEN;
-    chlg_buf.pvBuffer   = (void *) Curl_bufref_ptr(chlg);
+    chlg_buf.pvBuffer   = CURL_UNCONST(Curl_bufref_ptr(chlg));
     chlg_buf.cbBuffer   = curlx_uztoul(Curl_bufref_len(chlg));
   }
 
@@ -297,7 +296,7 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
   input_desc.cBuffers = 2;
   input_desc.pBuffers = input_buf;
   input_buf[0].BufferType = SECBUFFER_STREAM;
-  input_buf[0].pvBuffer = (void *) Curl_bufref_ptr(chlg);
+  input_buf[0].pvBuffer = CURL_UNCONST(Curl_bufref_ptr(chlg));
   input_buf[0].cbBuffer = curlx_uztoul(Curl_bufref_len(chlg));
   input_buf[1].BufferType = SECBUFFER_DATA;
   input_buf[1].pvBuffer = NULL;
diff --git a/extra/curl/curl-8.12.1/lib/vauth/ntlm.c b/extra/curl/curl-8.14.1/lib/vauth/ntlm.c
similarity index 84%
rename from extra/curl/curl-8.12.1/lib/vauth/ntlm.c
rename to extra/curl/curl-8.14.1/lib/vauth/ntlm.c
index f8f6aea0e9e2..5cda790dda33 100644
--- a/extra/curl/curl-8.12.1/lib/vauth/ntlm.c
+++ b/extra/curl/curl-8.14.1/lib/vauth/ntlm.c
@@ -22,7 +22,7 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #if defined(USE_NTLM) && !defined(USE_WINDOWS_SSPI)
 
@@ -35,26 +35,130 @@
 
 #define DEBUG_ME 0
 
-#include "urldata.h"
-#include "sendf.h"
-#include "curl_ntlm_core.h"
-#include "curl_gethostname.h"
-#include "curl_multibyte.h"
-#include "curl_md5.h"
-#include "warnless.h"
-#include "rand.h"
-#include "vtls/vtls.h"
-#include "strdup.h"
-
-#define BUILDING_CURL_NTLM_MSGS_C
-#include "vauth/vauth.h"
-#include "vauth/ntlm.h"
-#include "curl_endian.h"
-#include "curl_printf.h"
+#include "../urldata.h"
+#include "../sendf.h"
+#include "../curl_ntlm_core.h"
+#include "../curl_gethostname.h"
+#include "../curlx/multibyte.h"
+#include "../curl_md5.h"
+#include "../curlx/warnless.h"
+#include "../rand.h"
+#include "../vtls/vtls.h"
+#include "../strdup.h"
+
+#include "vauth.h"
+#include "../curl_endian.h"
+#include "../curl_printf.h"
 
 /* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
+
+
+/* NTLM buffer fixed size, large enough for long user + host + domain */
+#define NTLM_BUFSIZE 1024
+
+/* Flag bits definitions based on
+   https://davenport.sourceforge.net/ntlm.html */
+
+#define NTLMFLAG_NEGOTIATE_UNICODE               (1<<0)
+/* Indicates that Unicode strings are supported for use in security buffer
+   data. */
+
+#define NTLMFLAG_NEGOTIATE_OEM                   (1<<1)
+/* Indicates that OEM strings are supported for use in security buffer data. */
+
+#define NTLMFLAG_REQUEST_TARGET                  (1<<2)
+/* Requests that the server's authentication realm be included in the Type 2
+   message. */
+
+/* unknown (1<<3) */
+#define NTLMFLAG_NEGOTIATE_SIGN                  (1<<4)
+/* Specifies that authenticated communication between the client and server
+   should carry a digital signature (message integrity). */
+
+#define NTLMFLAG_NEGOTIATE_SEAL                  (1<<5)
+/* Specifies that authenticated communication between the client and server
+   should be encrypted (message confidentiality). */
+
+#define NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE        (1<<6)
+/* Indicates that datagram authentication is being used. */
+
+#define NTLMFLAG_NEGOTIATE_LM_KEY                (1<<7)
+/* Indicates that the LAN Manager session key should be used for signing and
+   sealing authenticated communications. */
+
+#define NTLMFLAG_NEGOTIATE_NTLM_KEY              (1<<9)
+/* Indicates that NTLM authentication is being used. */
+
+/* unknown (1<<10) */
+
+#define NTLMFLAG_NEGOTIATE_ANONYMOUS             (1<<11)
+/* Sent by the client in the Type 3 message to indicate that an anonymous
+   context has been established. This also affects the response fields. */
+
+#define NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED       (1<<12)
+/* Sent by the client in the Type 1 message to indicate that a desired
+   authentication realm is included in the message. */
+
+#define NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED  (1<<13)
+/* Sent by the client in the Type 1 message to indicate that the client
+   workstation's name is included in the message. */
+
+#define NTLMFLAG_NEGOTIATE_LOCAL_CALL            (1<<14)
+/* Sent by the server to indicate that the server and client are on the same
+   machine. Implies that the client may use a pre-established local security
+   context rather than responding to the challenge. */
+
+#define NTLMFLAG_NEGOTIATE_ALWAYS_SIGN           (1<<15)
+/* Indicates that authenticated communication between the client and server
+   should be signed with a "dummy" signature. */
+
+#define NTLMFLAG_TARGET_TYPE_DOMAIN              (1<<16)
+/* Sent by the server in the Type 2 message to indicate that the target
+   authentication realm is a domain. */
+
+#define NTLMFLAG_TARGET_TYPE_SERVER              (1<<17)
+/* Sent by the server in the Type 2 message to indicate that the target
+   authentication realm is a server. */
+
+#define NTLMFLAG_TARGET_TYPE_SHARE               (1<<18)
+/* Sent by the server in the Type 2 message to indicate that the target
+   authentication realm is a share. Presumably, this is for share-level
+   authentication. Usage is unclear. */
+
+#define NTLMFLAG_NEGOTIATE_NTLM2_KEY             (1<<19)
+/* Indicates that the NTLM2 signing and sealing scheme should be used for
+   protecting authenticated communications. */
+
+#define NTLMFLAG_REQUEST_INIT_RESPONSE           (1<<20)
+/* unknown purpose */
+
+#define NTLMFLAG_REQUEST_ACCEPT_RESPONSE         (1<<21)
+/* unknown purpose */
+
+#define NTLMFLAG_REQUEST_NONNT_SESSION_KEY       (1<<22)
+/* unknown purpose */
+
+#define NTLMFLAG_NEGOTIATE_TARGET_INFO           (1<<23)
+/* Sent by the server in the Type 2 message to indicate that it is including a
+   Target Information block in the message. */
+
+/* unknown (1<24) */
+/* unknown (1<25) */
+/* unknown (1<26) */
+/* unknown (1<27) */
+/* unknown (1<28) */
+
+#define NTLMFLAG_NEGOTIATE_128                   (1<<29)
+/* Indicates that 128-bit encryption is supported. */
+
+#define NTLMFLAG_NEGOTIATE_KEY_EXCHANGE          (1<<30)
+/* Indicates that the client will provide an encrypted master key in
+   the "Session Key" field of the Type 3 message. */
+
+#define NTLMFLAG_NEGOTIATE_56                    (1<<31)
+/* Indicates that 56-bit encryption is supported. */
 
 /* "NTLMSSP" signature is always in ASCII regardless of the platform */
 #define NTLMSSP_SIGNATURE "\x4e\x54\x4c\x4d\x53\x53\x50"
diff --git a/extra/curl/curl-8.12.1/lib/vauth/ntlm_sspi.c b/extra/curl/curl-8.14.1/lib/vauth/ntlm_sspi.c
similarity index 92%
rename from extra/curl/curl-8.12.1/lib/vauth/ntlm_sspi.c
rename to extra/curl/curl-8.14.1/lib/vauth/ntlm_sspi.c
index 6421c6ac1b44..86b4bccfdf47 100644
--- a/extra/curl/curl-8.12.1/lib/vauth/ntlm_sspi.c
+++ b/extra/curl/curl-8.14.1/lib/vauth/ntlm_sspi.c
@@ -22,23 +22,23 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #if defined(USE_WINDOWS_SSPI) && defined(USE_NTLM)
 
 #include <curl/curl.h>
 
-#include "vauth/vauth.h"
-#include "urldata.h"
-#include "curl_ntlm_core.h"
-#include "warnless.h"
-#include "curl_multibyte.h"
-#include "sendf.h"
-#include "strdup.h"
+#include "vauth.h"
+#include "../urldata.h"
+#include "../curl_ntlm_core.h"
+#include "../curlx/warnless.h"
+#include "../curlx/multibyte.h"
+#include "../sendf.h"
+#include "../strdup.h"
 
 /* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
 
 /*
  * Curl_auth_is_ntlm_supported()
@@ -55,8 +55,9 @@ bool Curl_auth_is_ntlm_supported(void)
   SECURITY_STATUS status;
 
   /* Query the security package for NTLM */
-  status = Curl_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NTLM),
-                                              &SecurityPackage);
+  status = Curl_pSecFn->QuerySecurityPackageInfo(
+                                     (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_NTLM)),
+                                     &SecurityPackage);
 
   /* Release the package buffer as it is not required anymore */
   if(status == SEC_E_OK) {
@@ -103,8 +104,9 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data,
   Curl_auth_cleanup_ntlm(ntlm);
 
   /* Query the security package for NTLM */
-  status = Curl_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NTLM),
-                                              &SecurityPackage);
+  status = Curl_pSecFn->QuerySecurityPackageInfo(
+                                     (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_NTLM)),
+                                     &SecurityPackage);
   if(status != SEC_E_OK) {
     failf(data, "SSPI: could not get auth info");
     return CURLE_AUTH_ERROR;
@@ -142,10 +144,10 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data,
 
   /* Acquire our credentials handle */
   status = Curl_pSecFn->AcquireCredentialsHandle(NULL,
-                                              (TCHAR *) TEXT(SP_NAME_NTLM),
-                                              SECPKG_CRED_OUTBOUND, NULL,
-                                              ntlm->p_identity, NULL, NULL,
-                                              ntlm->credentials, &expiry);
+                                     (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_NTLM)),
+                                     SECPKG_CRED_OUTBOUND, NULL,
+                                     ntlm->p_identity, NULL, NULL,
+                                     ntlm->credentials, &expiry);
   if(status != SEC_E_OK)
     return CURLE_LOGIN_DENIED;
 
diff --git a/extra/curl/curl-8.12.1/lib/vauth/oauth2.c b/extra/curl/curl-8.14.1/lib/vauth/oauth2.c
similarity index 94%
rename from extra/curl/curl-8.12.1/lib/vauth/oauth2.c
rename to extra/curl/curl-8.14.1/lib/vauth/oauth2.c
index dc94afa365dc..76e77c6a2f42 100644
--- a/extra/curl/curl-8.12.1/lib/vauth/oauth2.c
+++ b/extra/curl/curl-8.14.1/lib/vauth/oauth2.c
@@ -24,22 +24,22 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_SMTP) || \
   !defined(CURL_DISABLE_POP3) || \
   (!defined(CURL_DISABLE_LDAP) && defined(USE_OPENLDAP))
 
 #include <curl/curl.h>
-#include "urldata.h"
+#include "../urldata.h"
 
-#include "vauth/vauth.h"
-#include "warnless.h"
-#include "curl_printf.h"
+#include "vauth.h"
+#include "../curlx/warnless.h"
+#include "../curl_printf.h"
 
 /* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
 
 /*
  * Curl_auth_create_oauth_bearer_message()
diff --git a/extra/curl/curl-8.12.1/lib/vauth/spnego_gssapi.c b/extra/curl/curl-8.14.1/lib/vauth/spnego_gssapi.c
similarity index 92%
rename from extra/curl/curl-8.12.1/lib/vauth/spnego_gssapi.c
rename to extra/curl/curl-8.14.1/lib/vauth/spnego_gssapi.c
index 55232a8e4afb..b17ee46d175b 100644
--- a/extra/curl/curl-8.12.1/lib/vauth/spnego_gssapi.c
+++ b/extra/curl/curl-8.14.1/lib/vauth/spnego_gssapi.c
@@ -24,23 +24,23 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #if defined(HAVE_GSSAPI) && defined(USE_SPNEGO)
 
 #include <curl/curl.h>
 
-#include "vauth/vauth.h"
-#include "urldata.h"
-#include "curl_base64.h"
-#include "curl_gssapi.h"
-#include "warnless.h"
-#include "curl_multibyte.h"
-#include "sendf.h"
+#include "vauth.h"
+#include "../urldata.h"
+#include "../curlx/base64.h"
+#include "../curl_gssapi.h"
+#include "../curlx/warnless.h"
+#include "../curlx/multibyte.h"
+#include "../sendf.h"
 
 /* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
 
 #if defined(__GNUC__) && defined(__APPLE__)
 #pragma GCC diagnostic push
@@ -139,7 +139,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
   if(chlg64 && *chlg64) {
     /* Decode the base-64 encoded challenge message */
     if(*chlg64 != '=') {
-      result = Curl_base64_decode(chlg64, &chlg, &chlglen);
+      result = curlx_base64_decode(chlg64, &chlg, &chlglen);
       if(result)
         return result;
     }
@@ -156,10 +156,10 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
   }
 
   /* Set channel binding data if available */
-  if(nego->channel_binding_data.leng > 0) {
+  if(curlx_dyn_len(&nego->channel_binding_data)) {
     memset(&chan, 0, sizeof(struct gss_channel_bindings_struct));
-    chan.application_data.length = nego->channel_binding_data.leng;
-    chan.application_data.value = nego->channel_binding_data.bufr;
+    chan.application_data.length = curlx_dyn_len(&nego->channel_binding_data);
+    chan.application_data.value = curlx_dyn_ptr(&nego->channel_binding_data);
     chan_bindings = &chan;
   }
 
@@ -228,9 +228,9 @@ CURLcode Curl_auth_create_spnego_message(struct negotiatedata *nego,
   OM_uint32 minor_status;
 
   /* Base64 encode the already generated response */
-  result = Curl_base64_encode(nego->output_token.value,
-                              nego->output_token.length,
-                              outptr, outlen);
+  result = curlx_base64_encode(nego->output_token.value,
+                               nego->output_token.length,
+                               outptr, outlen);
 
   if(result) {
     gss_release_buffer(&minor_status, &nego->output_token);
diff --git a/extra/curl/curl-8.12.1/lib/vauth/spnego_sspi.c b/extra/curl/curl-8.14.1/lib/vauth/spnego_sspi.c
similarity index 90%
rename from extra/curl/curl-8.12.1/lib/vauth/spnego_sspi.c
rename to extra/curl/curl-8.14.1/lib/vauth/spnego_sspi.c
index 2439e7319f5e..c19a1ff4236a 100644
--- a/extra/curl/curl-8.12.1/lib/vauth/spnego_sspi.c
+++ b/extra/curl/curl-8.14.1/lib/vauth/spnego_sspi.c
@@ -24,23 +24,23 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #if defined(USE_WINDOWS_SSPI) && defined(USE_SPNEGO)
 
 #include <curl/curl.h>
 
-#include "vauth/vauth.h"
-#include "urldata.h"
-#include "curl_base64.h"
-#include "warnless.h"
-#include "curl_multibyte.h"
-#include "sendf.h"
-#include "strerror.h"
+#include "vauth.h"
+#include "../urldata.h"
+#include "../curlx/base64.h"
+#include "../curlx/warnless.h"
+#include "../curlx/multibyte.h"
+#include "../sendf.h"
+#include "../strerror.h"
 
 /* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
 
 /*
  * Curl_auth_is_spnego_supported()
@@ -57,9 +57,9 @@ bool Curl_auth_is_spnego_supported(void)
   SECURITY_STATUS status;
 
   /* Query the security package for Negotiate */
-  status = Curl_pSecFn->QuerySecurityPackageInfo((TCHAR *)
-                                              TEXT(SP_NAME_NEGOTIATE),
-                                              &SecurityPackage);
+  status = Curl_pSecFn->QuerySecurityPackageInfo(
+                                (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_NEGOTIATE)),
+                                &SecurityPackage);
 
   /* Release the package buffer as it is not required anymore */
   if(status == SEC_E_OK) {
@@ -128,9 +128,9 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
 
   if(!nego->output_token) {
     /* Query the security package for Negotiate */
-    nego->status = (DWORD)Curl_pSecFn->QuerySecurityPackageInfo((TCHAR *)
-                                                       TEXT(SP_NAME_NEGOTIATE),
-                                                       &SecurityPackage);
+    nego->status = (DWORD)Curl_pSecFn->QuerySecurityPackageInfo(
+                                (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_NEGOTIATE)),
+                                &SecurityPackage);
     if(nego->status != SEC_E_OK) {
       failf(data, "SSPI: could not get auth info");
       return CURLE_AUTH_ERROR;
@@ -170,10 +170,10 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
     /* Acquire our credentials handle */
     nego->status = (DWORD)
       Curl_pSecFn->AcquireCredentialsHandle(NULL,
-                                         (TCHAR *)TEXT(SP_NAME_NEGOTIATE),
-                                         SECPKG_CRED_OUTBOUND, NULL,
-                                         nego->p_identity, NULL, NULL,
-                                         nego->credentials, &expiry);
+                                (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_NEGOTIATE)),
+                                SECPKG_CRED_OUTBOUND, NULL,
+                                nego->p_identity, NULL, NULL,
+                                nego->credentials, &expiry);
     if(nego->status != SEC_E_OK)
       return CURLE_AUTH_ERROR;
 
@@ -186,7 +186,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
   if(chlg64 && *chlg64) {
     /* Decode the base-64 encoded challenge message */
     if(*chlg64 != '=') {
-      result = Curl_base64_decode(chlg64, &chlg, &chlglen);
+      result = curlx_base64_decode(chlg64, &chlg, &chlglen);
       if(result)
         return result;
     }
@@ -308,9 +308,9 @@ CURLcode Curl_auth_create_spnego_message(struct negotiatedata *nego,
                                          char **outptr, size_t *outlen)
 {
   /* Base64 encode the already generated response */
-  CURLcode result = Curl_base64_encode((const char *) nego->output_token,
-                                       nego->output_token_length, outptr,
-                                       outlen);
+  CURLcode result = curlx_base64_encode((const char *) nego->output_token,
+                                        nego->output_token_length, outptr,
+                                        outlen);
   if(!result && (!*outptr || !*outlen)) {
     free(*outptr);
     result = CURLE_REMOTE_ACCESS_DENIED;
diff --git a/extra/curl/curl-8.12.1/lib/vauth/vauth.c b/extra/curl/curl-8.14.1/lib/vauth/vauth.c
similarity index 95%
rename from extra/curl/curl-8.12.1/lib/vauth/vauth.c
rename to extra/curl/curl-8.14.1/lib/vauth/vauth.c
index 171e53fb13e0..e2872b2522a5 100644
--- a/extra/curl/curl-8.12.1/lib/vauth/vauth.c
+++ b/extra/curl/curl-8.14.1/lib/vauth/vauth.c
@@ -22,24 +22,24 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #include <curl/curl.h>
 
 #include "vauth.h"
-#include "urldata.h"
-#include "strcase.h"
-#include "curl_multibyte.h"
-#include "curl_printf.h"
+#include "../urldata.h"
+#include "../strcase.h"
+#include "../curlx/multibyte.h"
+#include "../curl_printf.h"
 
 /* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
 
 /*
  * Curl_auth_build_spn()
  *
- * This is used to build a SPN string in the following formats:
+ * This is used to build an SPN string in the following formats:
  *
  * service/host@realm (Not currently used)
  * service/host       (Not used by GSS-API)
diff --git a/extra/curl/curl-8.12.1/lib/vauth/vauth.h b/extra/curl/curl-8.14.1/lib/vauth/vauth.h
similarity index 98%
rename from extra/curl/curl-8.12.1/lib/vauth/vauth.h
rename to extra/curl/curl-8.14.1/lib/vauth/vauth.h
index 7e823484f68d..58b13f582c7b 100644
--- a/extra/curl/curl-8.12.1/lib/vauth/vauth.h
+++ b/extra/curl/curl-8.14.1/lib/vauth/vauth.h
@@ -26,7 +26,7 @@
 
 #include <curl/curl.h>
 
-#include "bufref.h"
+#include "../bufref.h"
 
 struct Curl_easy;
 
@@ -60,7 +60,7 @@ struct gsasldata;
  */
 bool Curl_auth_allowed_to_host(struct Curl_easy *data);
 
-/* This is used to build a SPN string */
+/* This is used to build an SPN string */
 #if !defined(USE_WINDOWS_SSPI)
 char *Curl_auth_build_spn(const char *service, const char *host,
                           const char *realm);
@@ -167,6 +167,8 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
 
 /* This is used to clean up the NTLM specific data */
 void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm);
+#else
+#define Curl_auth_is_ntlm_supported()     FALSE
 #endif /* USE_NTLM */
 
 /* This is used to generate a base64 encoded OAuth 2.0 message */
@@ -207,6 +209,8 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
 
 /* This is used to clean up the GSSAPI specific data */
 void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5);
+#else
+#define Curl_auth_is_gssapi_supported()       FALSE
 #endif /* USE_KERBEROS5 */
 
 #if defined(USE_SPNEGO)
diff --git a/extra/curl/curl-8.12.1/lib/version.c b/extra/curl/curl-8.14.1/lib/version.c
similarity index 99%
rename from extra/curl/curl-8.12.1/lib/version.c
rename to extra/curl/curl-8.14.1/lib/version.c
index 6543dcd0bd5f..bf4c76d016c5 100644
--- a/extra/curl/curl-8.12.1/lib/version.c
+++ b/extra/curl/curl-8.14.1/lib/version.c
@@ -422,8 +422,6 @@ static int idn_present(curl_version_info_data *info)
   return info->libidn != NULL;
 #endif
 }
-#else
-#define idn_present     NULL
 #endif
 
 #if defined(USE_SSL) && !defined(CURL_DISABLE_PROXY) && \
diff --git a/extra/curl/curl-8.12.1/lib/vssh/.checksrc b/extra/curl/curl-8.14.1/lib/vquic/.checksrc
similarity index 60%
rename from extra/curl/curl-8.12.1/lib/vssh/.checksrc
rename to extra/curl/curl-8.14.1/lib/vquic/.checksrc
index eff4bc0a3f72..770fad0dfcea 100644
--- a/extra/curl/curl-8.12.1/lib/vssh/.checksrc
+++ b/extra/curl/curl-8.14.1/lib/vquic/.checksrc
@@ -1,3 +1,5 @@
 banfunc strerror
 banfunc strncpy
 banfunc sscanf
+banfunc strtoul
+banfunc strtol
diff --git a/extra/curl/curl-8.12.1/lib/vquic/curl_msh3.c b/extra/curl/curl-8.14.1/lib/vquic/curl_msh3.c
similarity index 91%
rename from extra/curl/curl-8.12.1/lib/vquic/curl_msh3.c
rename to extra/curl/curl-8.14.1/lib/vquic/curl_msh3.c
index e0b5949cfd80..cbeb65051633 100644
--- a/extra/curl/curl-8.12.1/lib/vquic/curl_msh3.c
+++ b/extra/curl/curl-8.14.1/lib/vquic/curl_msh3.c
@@ -22,30 +22,32 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #ifdef USE_MSH3
 
-#include "urldata.h"
-#include "hash.h"
-#include "timeval.h"
-#include "multiif.h"
-#include "sendf.h"
-#include "curl_trc.h"
-#include "cfilters.h"
-#include "cf-socket.h"
-#include "connect.h"
-#include "progress.h"
-#include "http1.h"
+#include "../urldata.h"
+#include "../hash.h"
+#include "../uint-hash.h"
+#include "../curlx/timeval.h"
+#include "../multiif.h"
+#include "../sendf.h"
+#include "../curl_trc.h"
+#include "../cfilters.h"
+#include "../cf-socket.h"
+#include "../connect.h"
+#include "../progress.h"
+#include "../http1.h"
 #include "curl_msh3.h"
-#include "socketpair.h"
-#include "vtls/vtls.h"
-#include "vquic/vquic.h"
+#include "../socketpair.h"
+#include "../vtls/vtls.h"
+#include "vquic.h"
+#include "vquic_int.h"
 
 /* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_printf.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
 
 #ifdef CURL_DISABLE_SOCKETPAIR
 #error "MSH3 cannot be build with CURL_DISABLE_SOCKETPAIR set"
@@ -119,18 +121,18 @@ struct cf_msh3_ctx {
   struct cf_call_data call_data;
   struct curltime connect_started;   /* time the current attempt started */
   struct curltime handshake_at;      /* time connect handshake finished */
-  struct Curl_hash streams;          /* hash `data->mid` to `stream_ctx` */
+  struct uint_hash streams;          /* hash `data->mid` to `stream_ctx` */
   /* Flags written by msh3/msquic thread */
-  bool handshake_complete;
-  bool handshake_succeeded;
-  bool connected;
+  BIT(handshake_complete);
+  BIT(handshake_succeeded);
+  BIT(connected);
   BIT(initialized);
   /* Flags written by curl thread */
   BIT(verbose);
   BIT(active);
 };
 
-static void h3_stream_hash_free(void *stream);
+static void h3_stream_hash_free(unsigned int id, void *stream);
 
 static CURLcode cf_msh3_ctx_init(struct cf_msh3_ctx *ctx,
                                  const struct Curl_addrinfo *ai)
@@ -138,7 +140,7 @@ static CURLcode cf_msh3_ctx_init(struct cf_msh3_ctx *ctx,
   CURLcode result;
 
   DEBUGASSERT(!ctx->initialized);
-  Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free);
+  Curl_uint_hash_init(&ctx->streams, 63, h3_stream_hash_free);
 
   result = Curl_sock_assign_addr(&ctx->addr, ai, TRNSPRT_QUIC);
   if(result)
@@ -154,7 +156,7 @@ static CURLcode cf_msh3_ctx_init(struct cf_msh3_ctx *ctx,
 static void cf_msh3_ctx_free(struct cf_msh3_ctx *ctx)
 {
   if(ctx && ctx->initialized) {
-    Curl_hash_destroy(&ctx->streams);
+    Curl_uint_hash_destroy(&ctx->streams);
   }
   free(ctx);
 }
@@ -169,7 +171,7 @@ static struct cf_msh3_ctx *h3_get_msh3_ctx(struct Curl_easy *data);
 /**
  * All about the H3 internals of a stream
  */
-struct stream_ctx {
+struct h3_stream_ctx {
   struct MSH3_REQUEST *req;
   struct bufq recvbuf;   /* h3 response */
 #ifdef _WIN32
@@ -180,33 +182,31 @@ struct stream_ctx {
   uint64_t error3; /* HTTP/3 stream error code */
   int status_code; /* HTTP status code */
   CURLcode recv_error;
-  bool closed;
-  bool reset;
-  bool upload_done;
-  bool firstheader;  /* FALSE until headers arrive */
-  bool recv_header_complete;
+  BIT(closed);
+  BIT(reset);
+  BIT(upload_done);
+  BIT(firstheader);  /* FALSE until headers arrive */
+  BIT(recv_header_complete);
 };
 
-#define H3_STREAM_CTX(ctx,data)   ((struct stream_ctx *)((data && ctx)? \
-                Curl_hash_offt_get(&(ctx)->streams, (data)->mid) : NULL))
-
-static void h3_stream_ctx_free(struct stream_ctx *stream)
+static void h3_stream_ctx_free(struct h3_stream_ctx *stream)
 {
   Curl_bufq_free(&stream->recvbuf);
   free(stream);
 }
 
-static void h3_stream_hash_free(void *stream)
+static void h3_stream_hash_free(unsigned int id, void *stream)
 {
+  (void)id;
   DEBUGASSERT(stream);
-  h3_stream_ctx_free((struct stream_ctx *)stream);
+  h3_stream_ctx_free((struct h3_stream_ctx *)stream);
 }
 
 static CURLcode h3_data_setup(struct Curl_cfilter *cf,
                               struct Curl_easy *data)
 {
   struct cf_msh3_ctx *ctx = cf->ctx;
-  struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
 
   if(stream)
     return CURLE_OK;
@@ -221,7 +221,7 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf,
                   H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT);
   CURL_TRC_CF(data, cf, "data setup");
 
-  if(!Curl_hash_offt_set(&ctx->streams, data->mid, stream)) {
+  if(!Curl_uint_hash_set(&ctx->streams, data->mid, stream)) {
     h3_stream_ctx_free(stream);
     return CURLE_OUT_OF_MEMORY;
   }
@@ -232,17 +232,17 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf,
 static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   struct cf_msh3_ctx *ctx = cf->ctx;
-  struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
 
   (void)cf;
   if(stream) {
     CURL_TRC_CF(data, cf, "easy handle is done");
-    Curl_hash_offt_remove(&ctx->streams, data->mid);
+    Curl_uint_hash_remove(&ctx->streams, data->mid);
   }
 }
 
 static void drain_stream_from_other_thread(struct Curl_easy *data,
-                                           struct stream_ctx *stream)
+                                           struct h3_stream_ctx *stream)
 {
   unsigned char bits;
 
@@ -260,7 +260,7 @@ static void h3_drain_stream(struct Curl_cfilter *cf,
                             struct Curl_easy *data)
 {
   struct cf_msh3_ctx *ctx = cf->ctx;
-  struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
   unsigned char bits;
 
   (void)cf;
@@ -359,7 +359,7 @@ static CURLcode write_resp_raw(struct Curl_easy *data,
                                const void *mem, size_t memlen)
 {
   struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data);
-  struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
   CURLcode result = CURLE_OK;
   ssize_t nwritten;
 
@@ -386,7 +386,7 @@ static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
 {
   struct Curl_easy *data = userp;
   struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data);
-  struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
   CURLcode result;
   (void)Request;
 
@@ -398,7 +398,7 @@ static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
   msh3_lock_acquire(&stream->recv_lock);
 
   if((hd->NameLength == 7) &&
-     !strncmp(HTTP_PSEUDO_STATUS, (char *)hd->Name, 7)) {
+     !strncmp(HTTP_PSEUDO_STATUS, (const char *)hd->Name, 7)) {
     char line[14]; /* status line is always 13 characters long */
     size_t ncopy;
 
@@ -437,7 +437,7 @@ static bool MSH3_CALL msh3_data_received(MSH3_REQUEST *Request,
 {
   struct Curl_easy *data = IfContext;
   struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data);
-  struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
   CURLcode result;
   bool rv = FALSE;
 
@@ -477,7 +477,7 @@ static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext,
 {
   struct Curl_easy *data = IfContext;
   struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data);
-  struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
 
   (void)Request;
   if(!stream)
@@ -497,7 +497,7 @@ static void MSH3_CALL msh3_shutdown_complete(MSH3_REQUEST *Request,
 {
   struct Curl_easy *data = IfContext;
   struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data);
-  struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
 
   if(!stream)
     return;
@@ -510,7 +510,7 @@ static void MSH3_CALL msh3_data_sent(MSH3_REQUEST *Request,
 {
   struct Curl_easy *data = IfContext;
   struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data);
-  struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
   if(!stream)
     return;
   (void)Request;
@@ -523,7 +523,7 @@ static ssize_t recv_closed_stream(struct Curl_cfilter *cf,
                                   CURLcode *err)
 {
   struct cf_msh3_ctx *ctx = cf->ctx;
-  struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
   ssize_t nread = -1;
 
   if(!stream) {
@@ -557,7 +557,7 @@ static ssize_t recv_closed_stream(struct Curl_cfilter *cf,
 static void set_quic_expire(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   struct cf_msh3_ctx *ctx = cf->ctx;
-  struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
 
   /* we have no indication from msh3 when it would be a good time
    * to juggle the connection again. So, we compromise by calling
@@ -575,7 +575,7 @@ static ssize_t cf_msh3_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
                             char *buf, size_t len, CURLcode *err)
 {
   struct cf_msh3_ctx *ctx = cf->ctx;
-  struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
   ssize_t nread = -1;
   struct cf_call_data save;
 
@@ -627,7 +627,7 @@ static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data,
                             CURLcode *err)
 {
   struct cf_msh3_ctx *ctx = cf->ctx;
-  struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
   struct h1_req_parser h1;
   struct dynhds h2_headers;
   MSH3_HEADER *nva = NULL;
@@ -723,7 +723,7 @@ static void cf_msh3_adjust_pollset(struct Curl_cfilter *cf,
                                    struct easy_pollset *ps)
 {
   struct cf_msh3_ctx *ctx = cf->ctx;
-  struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
   struct cf_call_data save;
 
   CF_DATA_SAVE(save, cf, data);
@@ -743,7 +743,7 @@ static bool cf_msh3_data_pending(struct Curl_cfilter *cf,
                                  const struct Curl_easy *data)
 {
   struct cf_msh3_ctx *ctx = cf->ctx;
-  struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
   struct cf_call_data save;
   bool pending = FALSE;
 
@@ -752,12 +752,13 @@ static bool cf_msh3_data_pending(struct Curl_cfilter *cf,
   (void)cf;
   if(stream && stream->req) {
     msh3_lock_acquire(&stream->recv_lock);
-    CURL_TRC_CF((struct Curl_easy *)data, cf, "data pending = %zu",
+    CURL_TRC_CF((struct Curl_easy *)CURL_UNCONST(data), cf,
+                "data pending = %zu",
                 Curl_bufq_len(&stream->recvbuf));
     pending = !Curl_bufq_is_empty(&stream->recvbuf);
     msh3_lock_release(&stream->recv_lock);
     if(pending)
-      h3_drain_stream(cf, (struct Curl_easy *)data);
+      h3_drain_stream(cf, (struct Curl_easy *)CURL_UNCONST(data));
   }
 
   CF_DATA_RESTORE(cf, save);
@@ -780,7 +781,7 @@ static CURLcode cf_msh3_data_event(struct Curl_cfilter *cf,
                                    int event, int arg1, void *arg2)
 {
   struct cf_msh3_ctx *ctx = cf->ctx;
-  struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
   struct cf_call_data save;
   CURLcode result = CURLE_OK;
 
@@ -838,16 +839,10 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf,
   MSH3_SET_PORT(&addr, (uint16_t)cf->conn->remote_port);
 
   if(verify && (conn_config->CAfile || conn_config->CApath)) {
-    /* Need a way to provide trust anchors to MSH3 */
-#ifdef DEBUGBUILD
-    /* we need this for our test cases to run */
-    CURL_TRC_CF(data, cf, "non-standard CA not supported, "
-                "switching off verifypeer in DEBUG mode");
-    verify = 0;
-#else
+    /* Note there's currently no way to provide trust anchors to MSH3 and
+       that causes tests to fail. */
     CURL_TRC_CF(data, cf, "non-standard CA not supported, "
                 "attempting with built-in verification");
-#endif
   }
 
   CURL_TRC_CF(data, cf, "connecting to %s:%d (verify=%d)",
@@ -883,13 +878,12 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf,
 
 static CURLcode cf_msh3_connect(struct Curl_cfilter *cf,
                                 struct Curl_easy *data,
-                                bool blocking, bool *done)
+                                bool *done)
 {
   struct cf_msh3_ctx *ctx = cf->ctx;
   struct cf_call_data save;
   CURLcode result = CURLE_OK;
 
-  (void)blocking;
   if(cf->connected) {
     *done = TRUE;
     return CURLE_OK;
@@ -907,14 +901,14 @@ static CURLcode cf_msh3_connect(struct Curl_cfilter *cf,
 
   *done = FALSE;
   if(!ctx->qconn) {
-    ctx->connect_started = Curl_now();
+    ctx->connect_started = curlx_now();
     result = cf_connect_start(cf, data);
     if(result)
       goto out;
   }
 
   if(ctx->handshake_complete) {
-    ctx->handshake_at = Curl_now();
+    ctx->handshake_at = curlx_now();
     if(ctx->handshake_succeeded) {
       CURL_TRC_CF(data, cf, "handshake succeeded");
       cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
diff --git a/extra/curl/curl-8.12.1/lib/vquic/curl_msh3.h b/extra/curl/curl-8.14.1/lib/vquic/curl_msh3.h
similarity index 98%
rename from extra/curl/curl-8.12.1/lib/vquic/curl_msh3.h
rename to extra/curl/curl-8.14.1/lib/vquic/curl_msh3.h
index 33931f59bbcf..d2862c2d8964 100644
--- a/extra/curl/curl-8.12.1/lib/vquic/curl_msh3.h
+++ b/extra/curl/curl-8.14.1/lib/vquic/curl_msh3.h
@@ -24,7 +24,7 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #ifdef USE_MSH3
 
diff --git a/extra/curl/curl-8.12.1/lib/vquic/curl_ngtcp2.c b/extra/curl/curl-8.14.1/lib/vquic/curl_ngtcp2.c
similarity index 89%
rename from extra/curl/curl-8.12.1/lib/vquic/curl_ngtcp2.c
rename to extra/curl/curl-8.14.1/lib/vquic/curl_ngtcp2.c
index cc9d560d7023..f529f7e4fa85 100644
--- a/extra/curl/curl-8.12.1/lib/vquic/curl_ngtcp2.c
+++ b/extra/curl/curl-8.14.1/lib/vquic/curl_ngtcp2.c
@@ -22,7 +22,7 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #if defined(USE_NGTCP2) && defined(USE_NGHTTP3)
 #include <ngtcp2/ngtcp2.h>
@@ -32,49 +32,51 @@
 #include <openssl/err.h>
 #if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
 #include <ngtcp2/ngtcp2_crypto_boringssl.h>
+#elif defined(OPENSSL_QUIC_API2)
+#include <ngtcp2/ngtcp2_crypto_ossl.h>
 #else
 #include <ngtcp2/ngtcp2_crypto_quictls.h>
 #endif
-#include "vtls/openssl.h"
+#include "../vtls/openssl.h"
 #elif defined(USE_GNUTLS)
 #include <ngtcp2/ngtcp2_crypto_gnutls.h>
-#include "vtls/gtls.h"
+#include "../vtls/gtls.h"
 #elif defined(USE_WOLFSSL)
 #include <ngtcp2/ngtcp2_crypto_wolfssl.h>
-#include "vtls/wolfssl.h"
+#include "../vtls/wolfssl.h"
 #endif
 
-#include "urldata.h"
-#include "hash.h"
-#include "sendf.h"
-#include "strdup.h"
-#include "rand.h"
-#include "multiif.h"
-#include "strcase.h"
-#include "cfilters.h"
-#include "cf-socket.h"
-#include "connect.h"
-#include "progress.h"
-#include "strerror.h"
-#include "dynbuf.h"
-#include "http1.h"
-#include "select.h"
-#include "inet_pton.h"
-#include "transfer.h"
+#include "../urldata.h"
+#include "../uint-hash.h"
+#include "../sendf.h"
+#include "../strdup.h"
+#include "../rand.h"
+#include "../multiif.h"
+#include "../strcase.h"
+#include "../cfilters.h"
+#include "../cf-socket.h"
+#include "../connect.h"
+#include "../progress.h"
+#include "../strerror.h"
+#include "../curlx/dynbuf.h"
+#include "../http1.h"
+#include "../select.h"
+#include "../curlx/inet_pton.h"
+#include "../transfer.h"
 #include "vquic.h"
 #include "vquic_int.h"
 #include "vquic-tls.h"
-#include "vtls/keylog.h"
-#include "vtls/vtls.h"
-#include "vtls/vtls_scache.h"
+#include "../vtls/keylog.h"
+#include "../vtls/vtls.h"
+#include "../vtls/vtls_scache.h"
 #include "curl_ngtcp2.h"
 
-#include "warnless.h"
+#include "../curlx/warnless.h"
 
 /* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_printf.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
 
 
 #define QUIC_MAX_STREAMS (256*1024)
@@ -87,6 +89,10 @@
  * Chunk size is large enough to take a full DATA frame */
 #define H3_STREAM_WINDOW_SIZE (128 * 1024)
 #define H3_STREAM_CHUNK_SIZE   (16 * 1024)
+#if H3_STREAM_CHUNK_SIZE < NGTCP2_MAX_UDP_PAYLOAD_SIZE
+#error H3_STREAM_CHUNK_SIZE smaller than NGTCP2_MAX_UDP_PAYLOAD_SIZE
+#endif
+
 /* The pool keeps spares around and half of a full stream windows
  * seems good. More does not seem to improve performance.
  * The benefit of the pool is that stream buffer to not keep
@@ -117,6 +123,9 @@ struct cf_ngtcp2_ctx {
   struct cf_quic_ctx q;
   struct ssl_peer peer;
   struct curl_tls_ctx tls;
+#ifdef OPENSSL_QUIC_API2
+  ngtcp2_crypto_ossl_ctx *ossl_ctx;
+#endif
   ngtcp2_path connected_path;
   ngtcp2_conn *qconn;
   ngtcp2_cid dcid;
@@ -133,9 +142,8 @@ struct cf_ngtcp2_ctx {
   struct curltime handshake_at;      /* time connect handshake finished */
   struct bufc_pool stream_bufcp;     /* chunk pool for streams */
   struct dynbuf scratch;             /* temp buffer for header construction */
-  struct Curl_hash streams;          /* hash `data->mid` to `h3_stream_ctx` */
+  struct uint_hash streams;          /* hash `data->mid` to `h3_stream_ctx` */
   size_t max_stream_window;          /* max flow window for one stream */
-  uint64_t max_idle_ms;              /* max idle time for QUIC connection */
   uint64_t used_bidi_streams;        /* bidi streams we have opened */
   uint64_t max_bidi_streams;         /* max bidi streams we can open */
   size_t earlydata_max;              /* max amount of early data supported by
@@ -156,7 +164,7 @@ struct cf_ngtcp2_ctx {
 #define CF_CTX_CALL_DATA(cf)  \
   ((struct cf_ngtcp2_ctx *)(cf)->ctx)->call_data
 
-static void h3_stream_hash_free(void *stream);
+static void h3_stream_hash_free(unsigned int id, void *stream);
 
 static void cf_ngtcp2_ctx_init(struct cf_ngtcp2_ctx *ctx)
 {
@@ -164,11 +172,10 @@ static void cf_ngtcp2_ctx_init(struct cf_ngtcp2_ctx *ctx)
   ctx->qlogfd = -1;
   ctx->version = NGTCP2_PROTO_VER_MAX;
   ctx->max_stream_window = H3_STREAM_WINDOW_SIZE;
-  ctx->max_idle_ms = CURL_QUIC_MAX_IDLE_MS;
   Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE,
                   H3_STREAM_POOL_SPARES);
-  Curl_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER);
-  Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free);
+  curlx_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER);
+  Curl_uint_hash_init(&ctx->streams, 63, h3_stream_hash_free);
   ctx->initialized = TRUE;
 }
 
@@ -178,14 +185,51 @@ static void cf_ngtcp2_ctx_free(struct cf_ngtcp2_ctx *ctx)
     Curl_vquic_tls_cleanup(&ctx->tls);
     vquic_ctx_free(&ctx->q);
     Curl_bufcp_free(&ctx->stream_bufcp);
-    Curl_dyn_free(&ctx->scratch);
-    Curl_hash_clean(&ctx->streams);
-    Curl_hash_destroy(&ctx->streams);
+    curlx_dyn_free(&ctx->scratch);
+    Curl_uint_hash_destroy(&ctx->streams);
     Curl_ssl_peer_cleanup(&ctx->peer);
   }
   free(ctx);
 }
 
+static void cf_ngtcp2_setup_keep_alive(struct Curl_cfilter *cf,
+                                       struct Curl_easy *data)
+{
+  struct cf_ngtcp2_ctx *ctx = cf->ctx;
+  const ngtcp2_transport_params *rp;
+  /* Peer should have sent us its transport parameters. If it
+  * announces a positive `max_idle_timeout` it will close the
+  * connection when it does not hear from us for that time.
+  *
+  * Some servers use this as a keep-alive timer at a rather low
+  * value. We are doing HTTP/3 here and waiting for the response
+  * to a request may take a considerable amount of time. We need
+  * to prevent the peer's QUIC stack from closing in this case.
+  */
+  if(!ctx->qconn)
+    return;
+
+  rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn);
+  if(!rp || !rp->max_idle_timeout) {
+    ngtcp2_conn_set_keep_alive_timeout(ctx->qconn, UINT64_MAX);
+    CURL_TRC_CF(data, cf, "no peer idle timeout, unset keep-alive");
+  }
+  else if(!Curl_uint_hash_count(&ctx->streams)) {
+    ngtcp2_conn_set_keep_alive_timeout(ctx->qconn, UINT64_MAX);
+    CURL_TRC_CF(data, cf, "no active streams, unset keep-alive");
+  }
+  else {
+    ngtcp2_duration keep_ns;
+    keep_ns = (rp->max_idle_timeout > 1) ? (rp->max_idle_timeout / 2) : 1;
+    ngtcp2_conn_set_keep_alive_timeout(ctx->qconn, keep_ns);
+    CURL_TRC_CF(data, cf, "peer idle timeout is %" FMT_PRIu64 "ms, "
+                "set keep-alive to %" FMT_PRIu64 " ms.",
+                (curl_uint64_t)(rp->max_idle_timeout / NGTCP2_MILLISECONDS),
+                (curl_uint64_t)(keep_ns / NGTCP2_MILLISECONDS));
+  }
+}
+
+
 struct pkt_io_ctx;
 static CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
                                     struct Curl_easy *data,
@@ -206,18 +250,13 @@ struct h3_stream_ctx {
   curl_off_t upload_left; /* number of request bytes left to upload */
   int status_code; /* HTTP status code */
   CURLcode xfer_result; /* result from xfer_resp_write(_hd) */
-  bool resp_hds_complete; /* we have a complete, final response */
-  bool closed; /* TRUE on stream close */
-  bool reset;  /* TRUE on stream reset */
-  bool send_closed; /* stream is local closed */
+  BIT(resp_hds_complete); /* we have a complete, final response */
+  BIT(closed); /* TRUE on stream close */
+  BIT(reset);  /* TRUE on stream reset */
+  BIT(send_closed); /* stream is local closed */
   BIT(quic_flow_blocked); /* stream is blocked by QUIC flow control */
 };
 
-#define H3_STREAM_CTX(ctx,data)   ((struct h3_stream_ctx *)(\
-            data? Curl_hash_offt_get(&(ctx)->streams, (data)->mid) : NULL))
-#define H3_STREAM_CTX_ID(ctx,id)  ((struct h3_stream_ctx *)(\
-            Curl_hash_offt_get(&(ctx)->streams, (id))))
-
 static void h3_stream_ctx_free(struct h3_stream_ctx *stream)
 {
   Curl_bufq_free(&stream->sendbuf);
@@ -225,8 +264,9 @@ static void h3_stream_ctx_free(struct h3_stream_ctx *stream)
   free(stream);
 }
 
-static void h3_stream_hash_free(void *stream)
+static void h3_stream_hash_free(unsigned int id, void *stream)
 {
+  (void)id;
   DEBUGASSERT(stream);
   h3_stream_ctx_free((struct h3_stream_ctx *)stream);
 }
@@ -254,11 +294,14 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf,
   stream->sendbuf_len_in_flight = 0;
   Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
 
-  if(!Curl_hash_offt_set(&ctx->streams, data->mid, stream)) {
+  if(!Curl_uint_hash_set(&ctx->streams, data->mid, stream)) {
     h3_stream_ctx_free(stream);
     return CURLE_OUT_OF_MEMORY;
   }
 
+  if(Curl_uint_hash_count(&ctx->streams) == 1)
+    cf_ngtcp2_setup_keep_alive(cf, data);
+
   return CURLE_OK;
 }
 
@@ -293,40 +336,10 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
     CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] easy handle is done",
                 stream->id);
     cf_ngtcp2_stream_close(cf, data, stream);
-    Curl_hash_offt_remove(&ctx->streams, data->mid);
-  }
-}
-
-static struct Curl_easy *get_stream_easy(struct Curl_cfilter *cf,
-                                         struct Curl_easy *data,
-                                         int64_t stream_id,
-                                         struct h3_stream_ctx **pstream)
-{
-  struct cf_ngtcp2_ctx *ctx = cf->ctx;
-  struct h3_stream_ctx *stream;
-
-  (void)cf;
-  stream = H3_STREAM_CTX(ctx, data);
-  if(stream && stream->id == stream_id) {
-    *pstream = stream;
-    return data;
-  }
-  else {
-    struct Curl_llist_node *e;
-    DEBUGASSERT(data->multi);
-    for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) {
-      struct Curl_easy *sdata = Curl_node_elem(e);
-      if(sdata->conn != data->conn)
-        continue;
-      stream = H3_STREAM_CTX(ctx, sdata);
-      if(stream && stream->id == stream_id) {
-        *pstream = stream;
-        return sdata;
-      }
-    }
+    Curl_uint_hash_remove(&ctx->streams, data->mid);
+    if(!Curl_uint_hash_count(&ctx->streams))
+      cf_ngtcp2_setup_keep_alive(cf, data);
   }
-  *pstream = NULL;
-  return NULL;
 }
 
 static void h3_drain_stream(struct Curl_cfilter *cf,
@@ -447,7 +460,7 @@ static void quic_settings(struct cf_ngtcp2_ctx *ctx,
   t->initial_max_stream_data_uni = ctx->max_stream_window;
   t->initial_max_streams_bidi = QUIC_MAX_STREAMS;
   t->initial_max_streams_uni = QUIC_MAX_STREAMS;
-  t->max_idle_timeout = (ctx->max_idle_ms * NGTCP2_MILLISECONDS);
+  t->max_idle_timeout = 0; /* no idle timeout from our side */
   if(ctx->qlogfd != -1) {
     s->qlog_write = qlog_callback;
   }
@@ -469,29 +482,44 @@ static int cf_ngtcp2_handshake_completed(ngtcp2_conn *tconn, void *user_data)
   if(!ctx || !data)
     return NGHTTP3_ERR_CALLBACK_FAILURE;
 
-  ctx->handshake_at = Curl_now();
+  ctx->handshake_at = curlx_now();
   ctx->tls_handshake_complete = TRUE;
   cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
 
   ctx->tls_vrfy_result = Curl_vquic_tls_verify_peer(&ctx->tls, cf,
                                                     data, &ctx->peer);
   CURL_TRC_CF(data, cf, "handshake complete after %dms",
-             (int)Curl_timediff(ctx->handshake_at, ctx->started_at));
+             (int)curlx_timediff(ctx->handshake_at, ctx->started_at));
   /* In case of earlydata, where we simulate being connected, update
    * the handshake time when we really did connect */
   if(ctx->use_earlydata)
     Curl_pgrsTimeWas(data, TIMER_APPCONNECT, ctx->handshake_at);
-#ifdef USE_GNUTLS
   if(ctx->use_earlydata) {
+#if defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA)
+    ctx->earlydata_accepted =
+      (SSL_get_early_data_status(ctx->tls.ossl.ssl) !=
+       SSL_EARLY_DATA_REJECTED);
+#endif
+#ifdef USE_GNUTLS
     int flags = gnutls_session_get_flags(ctx->tls.gtls.session);
     ctx->earlydata_accepted = !!(flags & GNUTLS_SFLAGS_EARLY_DATA);
+#endif
+#ifdef USE_WOLFSSL
+#ifdef WOLFSSL_EARLY_DATA
+    ctx->earlydata_accepted =
+      (wolfSSL_get_early_data_status(ctx->tls.wssl.ssl) !=
+       WOLFSSL_EARLY_DATA_REJECTED);
+#else
+    DEBUGASSERT(0); /* should not come here if ED is disabled. */
+    ctx->earlydata_accepted = FALSE;
+#endif /* WOLFSSL_EARLY_DATA */
+#endif
     CURL_TRC_CF(data, cf, "server did%s accept %zu bytes of early data",
                 ctx->earlydata_accepted ? "" : " not", ctx->earlydata_skip);
     Curl_pgrsEarlyData(data, ctx->earlydata_accepted ?
                               (curl_off_t)ctx->earlydata_skip :
                              -(curl_off_t)ctx->earlydata_skip);
   }
-#endif
   return 0;
 }
 
@@ -563,7 +591,7 @@ static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags,
     CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] read_stream(len=%zu) -> %zd",
                 stream_id, buflen, nconsumed);
   if(nconsumed < 0) {
-    struct h3_stream_ctx *stream = H3_STREAM_CTX_ID(ctx, stream_id);
+    struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
     if(data && stream) {
       CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] error on known stream, "
                   "reset=%d, closed=%d",
@@ -695,28 +723,26 @@ static int cb_extend_max_local_streams_bidi(ngtcp2_conn *tconn,
   return 0;
 }
 
-static int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t sid,
+static int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t stream_id,
                                      uint64_t max_data, void *user_data,
                                      void *stream_user_data)
 {
   struct Curl_cfilter *cf = user_data;
   struct cf_ngtcp2_ctx *ctx = cf->ctx;
-  curl_int64_t stream_id = (curl_int64_t)sid;
-  struct Curl_easy *data = CF_DATA_CURRENT(cf);
-  struct Curl_easy *s_data;
+  struct Curl_easy *s_data = stream_user_data;
   struct h3_stream_ctx *stream;
   int rv;
   (void)tconn;
   (void)max_data;
-  (void)stream_user_data;
 
   rv = nghttp3_conn_unblock_stream(ctx->h3conn, stream_id);
   if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
     return NGTCP2_ERR_CALLBACK_FAILURE;
   }
-  s_data = get_stream_easy(cf, data, stream_id, &stream);
-  if(s_data && stream && stream->quic_flow_blocked) {
-    CURL_TRC_CF(s_data, cf, "[%" FMT_PRId64 "] unblock quic flow", stream_id);
+  stream = H3_STREAM_CTX(ctx, s_data);
+  if(stream && stream->quic_flow_blocked) {
+    CURL_TRC_CF(s_data, cf, "[%" FMT_PRId64 "] unblock quic flow",
+                (curl_int64_t)stream_id);
     stream->quic_flow_blocked = FALSE;
     h3_drain_stream(cf, s_data);
   }
@@ -996,7 +1022,7 @@ static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id,
   if(!stream)
     return NGHTTP3_ERR_CALLBACK_FAILURE;
 
-  h3_xfer_write_resp(cf, data, stream, (char *)buf, blen, FALSE);
+  h3_xfer_write_resp(cf, data, stream, (const char *)buf, blen, FALSE);
   if(blen) {
     CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] ACK %zu bytes of DATA",
                 stream->id, blen);
@@ -1079,18 +1105,18 @@ static int cb_h3_recv_header(nghttp3_conn *conn, int64_t sid,
                                      (const char *)h3val.base, h3val.len);
     if(result)
       return -1;
-    Curl_dyn_reset(&ctx->scratch);
-    result = Curl_dyn_addn(&ctx->scratch, STRCONST("HTTP/3 "));
+    curlx_dyn_reset(&ctx->scratch);
+    result = curlx_dyn_addn(&ctx->scratch, STRCONST("HTTP/3 "));
     if(!result)
-      result = Curl_dyn_addn(&ctx->scratch,
-                             (const char *)h3val.base, h3val.len);
+      result = curlx_dyn_addn(&ctx->scratch,
+                              (const char *)h3val.base, h3val.len);
     if(!result)
-      result = Curl_dyn_addn(&ctx->scratch, STRCONST(" \r\n"));
+      result = curlx_dyn_addn(&ctx->scratch, STRCONST(" \r\n"));
     if(!result)
-      h3_xfer_write_resp_hd(cf, data, stream, Curl_dyn_ptr(&ctx->scratch),
-                            Curl_dyn_len(&ctx->scratch), FALSE);
+      h3_xfer_write_resp_hd(cf, data, stream, curlx_dyn_ptr(&ctx->scratch),
+                            curlx_dyn_len(&ctx->scratch), FALSE);
     CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] status: %s",
-                stream_id, Curl_dyn_ptr(&ctx->scratch));
+                stream_id, curlx_dyn_ptr(&ctx->scratch));
     if(result) {
       return -1;
     }
@@ -1100,19 +1126,19 @@ static int cb_h3_recv_header(nghttp3_conn *conn, int64_t sid,
     CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] header: %.*s: %.*s",
                 stream_id, (int)h3name.len, h3name.base,
                 (int)h3val.len, h3val.base);
-    Curl_dyn_reset(&ctx->scratch);
-    result = Curl_dyn_addn(&ctx->scratch,
-                           (const char *)h3name.base, h3name.len);
+    curlx_dyn_reset(&ctx->scratch);
+    result = curlx_dyn_addn(&ctx->scratch,
+                            (const char *)h3name.base, h3name.len);
     if(!result)
-      result = Curl_dyn_addn(&ctx->scratch, STRCONST(": "));
+      result = curlx_dyn_addn(&ctx->scratch, STRCONST(": "));
     if(!result)
-      result = Curl_dyn_addn(&ctx->scratch,
-                             (const char *)h3val.base, h3val.len);
+      result = curlx_dyn_addn(&ctx->scratch,
+                              (const char *)h3val.base, h3val.len);
     if(!result)
-      result = Curl_dyn_addn(&ctx->scratch, STRCONST("\r\n"));
+      result = curlx_dyn_addn(&ctx->scratch, STRCONST("\r\n"));
     if(!result)
-      h3_xfer_write_resp_hd(cf, data, stream, Curl_dyn_ptr(&ctx->scratch),
-                            Curl_dyn_len(&ctx->scratch), FALSE);
+      h3_xfer_write_resp_hd(cf, data, stream, curlx_dyn_ptr(&ctx->scratch),
+                            curlx_dyn_len(&ctx->scratch), FALSE);
   }
   return 0;
 }
@@ -1400,7 +1426,7 @@ cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id,
     while(nvecs < veccnt &&
           Curl_bufq_peek_at(&stream->sendbuf,
                             stream->sendbuf_len_in_flight,
-                            (const unsigned char **)&vec[nvecs].base,
+                            CURL_UNCONST(&vec[nvecs].base),
                             &vec[nvecs].len)) {
       stream->sendbuf_len_in_flight += vec[nvecs].len;
       nwritten += vec[nvecs].len;
@@ -1759,7 +1785,7 @@ static ssize_t read_pkt_to_send(void *userp,
 
     if(ctx->h3conn && ngtcp2_conn_get_max_data_left(ctx->qconn)) {
       veccnt = nghttp3_conn_writev_stream(ctx->h3conn, &stream_id, &fin, vec,
-                                          sizeof(vec) / sizeof(vec[0]));
+                                          CURL_ARRAYSIZE(vec));
       if(veccnt < 0) {
         failf(x->data, "nghttp3_conn_writev_stream returned error: %s",
               nghttp3_strerror((int)veccnt));
@@ -2033,10 +2059,20 @@ static void cf_ngtcp2_ctx_close(struct cf_ngtcp2_ctx *ctx)
   ctx->qlogfd = -1;
   Curl_vquic_tls_cleanup(&ctx->tls);
   vquic_ctx_free(&ctx->q);
-  if(ctx->h3conn)
+  if(ctx->h3conn) {
     nghttp3_conn_del(ctx->h3conn);
-  if(ctx->qconn)
+    ctx->h3conn = NULL;
+  }
+  if(ctx->qconn) {
     ngtcp2_conn_del(ctx->qconn);
+    ctx->qconn = NULL;
+  }
+#ifdef OPENSSL_QUIC_API2
+  if(ctx->ossl_ctx) {
+    ngtcp2_crypto_ossl_ctx_del(ctx->ossl_ctx);
+    ctx->ossl_ctx = NULL;
+  }
+#endif
   ctx->call_data = save;
 }
 
@@ -2076,6 +2112,7 @@ static CURLcode cf_ngtcp2_shutdown(struct Curl_cfilter *cf,
       }
     }
 
+    DEBUGASSERT(Curl_bufq_is_empty(&ctx->q.sendbuf));
     ctx->shutdown_started = TRUE;
     nwritten = ngtcp2_conn_write_connection_close(
       ctx->qconn, NULL, /* path */
@@ -2085,14 +2122,21 @@ static CURLcode cf_ngtcp2_shutdown(struct Curl_cfilter *cf,
     CURL_TRC_CF(data, cf, "start shutdown(err_type=%d, err_code=%"
                 FMT_PRIu64 ") -> %d", ctx->last_error.type,
                 (curl_uint64_t)ctx->last_error.error_code, (int)nwritten);
+    /* there are cases listed in ngtcp2 documentation where this call
+     * may fail. Since we are doing a connection shutdown as graceful
+     * as we can, such an error is ignored here. */
     if(nwritten > 0) {
-      Curl_bufq_write(&ctx->q.sendbuf, (const unsigned char *)buffer,
-                      (size_t)nwritten, &result);
+      /* Ignore amount written. sendbuf was empty and has always room for
+       * NGTCP2_MAX_UDP_PAYLOAD_SIZE. It can only completely fail, in which
+       * case `result` is set non zero. */
+      (void)Curl_bufq_write(&ctx->q.sendbuf, (const unsigned char *)buffer,
+                            (size_t)nwritten, &result);
       if(result) {
         CURL_TRC_CF(data, cf, "error %d adding shutdown packets to sendbuf, "
                     "aborting shutdown", result);
         goto out;
       }
+
       ctx->q.no_gso = TRUE;
       ctx->q.gsolen = (size_t)nwritten;
       ctx->q.split_len = 0;
@@ -2152,6 +2196,7 @@ static void cf_ngtcp2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   CURL_TRC_CF(data, cf, "destroy");
   if(cf->ctx) {
+    cf_ngtcp2_close(cf, data);
     cf_ngtcp2_ctx_free(cf->ctx);
     cf->ctx = NULL;
   }
@@ -2173,8 +2218,24 @@ static int quic_ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
   ctx = cf ? cf->ctx : NULL;
   data = cf ? CF_DATA_CURRENT(cf) : NULL;
   if(cf && data && ctx) {
+    unsigned char *quic_tp = NULL;
+    size_t quic_tp_len = 0;
+#ifdef HAVE_OPENSSL_EARLYDATA
+    ngtcp2_ssize tplen;
+    uint8_t tpbuf[256];
+
+    tplen = ngtcp2_conn_encode_0rtt_transport_params(ctx->qconn, tpbuf,
+                                                     sizeof(tpbuf));
+    if(tplen < 0)
+      CURL_TRC_CF(data, cf, "error encoding 0RTT transport data: %s",
+                  ngtcp2_strerror((int)tplen));
+    else {
+      quic_tp = (unsigned char *)tpbuf;
+      quic_tp_len = (size_t)tplen;
+    }
+#endif
     Curl_ossl_add_session(cf, data, ctx->peer.scache_key, ssl_sessionid,
-                          SSL_version(ssl), "h3");
+                          SSL_version(ssl), "h3", quic_tp, quic_tp_len);
     return 1;
   }
   return 0;
@@ -2257,8 +2318,23 @@ static int wssl_quic_new_session_cb(WOLFSSL *ssl, WOLFSSL_SESSION *session)
     struct Curl_easy *data = CF_DATA_CURRENT(cf);
     DEBUGASSERT(data);
     if(data && ctx) {
+      ngtcp2_ssize tplen;
+      uint8_t tpbuf[256];
+      unsigned char *quic_tp = NULL;
+      size_t quic_tp_len = 0;
+
+      tplen = ngtcp2_conn_encode_0rtt_transport_params(ctx->qconn, tpbuf,
+                                                       sizeof(tpbuf));
+      if(tplen < 0)
+        CURL_TRC_CF(data, cf, "error encoding 0RTT transport data: %s",
+                    ngtcp2_strerror((int)tplen));
+      else {
+        quic_tp = (unsigned char *)tpbuf;
+        quic_tp_len = (size_t)tplen;
+      }
       (void)Curl_wssl_cache_session(cf, data, ctx->peer.scache_key,
-                                    session, wolfSSL_version(ssl), "h3");
+                                    session, wolfSSL_version(ssl),
+                                    "h3", quic_tp, quic_tp_len);
     }
   }
   return 0;
@@ -2279,6 +2355,8 @@ static CURLcode cf_ngtcp2_tls_ctx_setup(struct Curl_cfilter *cf,
     failf(data, "ngtcp2_crypto_boringssl_configure_client_context failed");
     return CURLE_FAILED_INIT;
   }
+#elif defined(OPENSSL_QUIC_API2)
+  /* nothing to do */
 #else
   if(ngtcp2_crypto_quictls_configure_client_context(ctx->ossl.ssl_ctx) != 0) {
     failf(data, "ngtcp2_crypto_quictls_configure_client_context failed");
@@ -2308,13 +2386,13 @@ static CURLcode cf_ngtcp2_tls_ctx_setup(struct Curl_cfilter *cf,
   }
 
 #elif defined(USE_WOLFSSL)
-  if(ngtcp2_crypto_wolfssl_configure_client_context(ctx->wssl.ctx) != 0) {
+  if(ngtcp2_crypto_wolfssl_configure_client_context(ctx->wssl.ssl_ctx) != 0) {
     failf(data, "ngtcp2_crypto_wolfssl_configure_client_context failed");
     return CURLE_FAILED_INIT;
   }
   if(ssl_config->primary.cache_session) {
     /* Register to get notified when a new session is received */
-    wolfSSL_CTX_sess_set_new_cb(ctx->wssl.ctx, wssl_quic_new_session_cb);
+    wolfSSL_CTX_sess_set_new_cb(ctx->wssl.ssl_ctx, wssl_quic_new_session_cb);
   }
 #endif
   return CURLE_OK;
@@ -2322,6 +2400,7 @@ static CURLcode cf_ngtcp2_tls_ctx_setup(struct Curl_cfilter *cf,
 
 static CURLcode cf_ngtcp2_on_session_reuse(struct Curl_cfilter *cf,
                                            struct Curl_easy *data,
+                                           struct alpn_spec *alpns,
                                            struct Curl_ssl_session *scs,
                                            bool *do_early_data)
 {
@@ -2329,13 +2408,26 @@ static CURLcode cf_ngtcp2_on_session_reuse(struct Curl_cfilter *cf,
   CURLcode result = CURLE_OK;
 
   *do_early_data = FALSE;
+#if defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA)
+  ctx->earlydata_max = scs->earlydata_max;
+#endif
 #ifdef USE_GNUTLS
   ctx->earlydata_max =
     gnutls_record_get_max_early_data_size(ctx->tls.gtls.session);
+#endif
+#ifdef USE_WOLFSSL
+#ifdef WOLFSSL_EARLY_DATA
+  ctx->earlydata_max = scs->earlydata_max;
+#else
+  ctx->earlydata_max = 0;
+#endif /* WOLFSSL_EARLY_DATA */
+#endif
+#if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || \
+    (defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA))
   if((!ctx->earlydata_max)) {
     CURL_TRC_CF(data, cf, "SSL session does not allow earlydata");
   }
-  else if(strcmp("h3", scs->alpn)) {
+  else if(!Curl_alpn_contains_proto(alpns, scs->alpn)) {
     CURL_TRC_CF(data, cf, "SSL session from different ALPN, no early data");
   }
   else if(!scs->quic_tp || !scs->quic_tp_len) {
@@ -2344,7 +2436,7 @@ static CURLcode cf_ngtcp2_on_session_reuse(struct Curl_cfilter *cf,
   else {
     int rv;
     rv = ngtcp2_conn_decode_and_set_0rtt_transport_params(
-      ctx->qconn, (uint8_t *)scs->quic_tp, scs->quic_tp_len);
+      ctx->qconn, (const uint8_t *)scs->quic_tp, scs->quic_tp_len);
     if(rv)
       CURL_TRC_CF(data, cf, "no early data, failed to set 0RTT transport "
                   "parameters: %s", ngtcp2_strerror(rv));
@@ -2359,10 +2451,11 @@ static CURLcode cf_ngtcp2_on_session_reuse(struct Curl_cfilter *cf,
       }
     }
   }
-#else /* USE_GNUTLS */
+#else /* not supported in the TLS backend */
   (void)data;
   (void)ctx;
   (void)scs;
+  (void)alpns;
 #endif
   return result;
 }
@@ -2380,6 +2473,9 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf,
   CURLcode result;
   const struct Curl_sockaddr_ex *sockaddr = NULL;
   int qfd;
+static const struct alpn_spec ALPN_SPEC_H3 = {
+  { "h3", "h3-29" }, 2
+};
 
   DEBUGASSERT(ctx->initialized);
   ctx->dcid.datalen = NGTCP2_MAX_CIDLEN;
@@ -2423,37 +2519,45 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf,
   if(rc)
     return CURLE_QUIC_CONNECT_ERROR;
 
-#define H3_ALPN "\x2h3\x5h3-29"
-  result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer,
-                               H3_ALPN, sizeof(H3_ALPN) - 1,
+  ctx->conn_ref.get_conn = get_conn;
+  ctx->conn_ref.user_data = cf;
+
+  result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer, &ALPN_SPEC_H3,
                                cf_ngtcp2_tls_ctx_setup, &ctx->tls,
                                &ctx->conn_ref,
                                cf_ngtcp2_on_session_reuse);
   if(result)
     return result;
 
-#ifdef USE_OPENSSL
+#if defined(USE_OPENSSL) && defined(OPENSSL_QUIC_API2)
+  if(ngtcp2_crypto_ossl_ctx_new(&ctx->ossl_ctx, ctx->tls.ossl.ssl) != 0) {
+    failf(data, "ngtcp2_crypto_ossl_ctx_new failed");
+    return CURLE_FAILED_INIT;
+  }
+  ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->ossl_ctx);
+  if(ngtcp2_crypto_ossl_configure_client_session(ctx->tls.ossl.ssl) != 0) {
+    failf(data, "ngtcp2_crypto_ossl_configure_client_session failed");
+    return CURLE_FAILED_INIT;
+  }
+#elif defined(USE_OPENSSL)
   SSL_set_quic_use_legacy_codepoint(ctx->tls.ossl.ssl, 0);
   ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.ossl.ssl);
 #elif defined(USE_GNUTLS)
   ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.gtls.session);
 #elif defined(USE_WOLFSSL)
-  ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.wssl.handle);
+  ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.wssl.ssl);
 #else
   #error "ngtcp2 TLS backend not defined"
 #endif
 
   ngtcp2_ccerr_default(&ctx->last_error);
 
-  ctx->conn_ref.get_conn = get_conn;
-  ctx->conn_ref.user_data = cf;
-
   return CURLE_OK;
 }
 
 static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf,
                                   struct Curl_easy *data,
-                                  bool blocking, bool *done)
+                                  bool *done)
 {
   struct cf_ngtcp2_ctx *ctx = cf->ctx;
   CURLcode result = CURLE_OK;
@@ -2468,13 +2572,13 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf,
 
   /* Connect the UDP filter first */
   if(!cf->next->connected) {
-    result = Curl_conn_cf_connect(cf->next, data, blocking, done);
+    result = Curl_conn_cf_connect(cf->next, data, done);
     if(result || !*done)
       return result;
   }
 
   *done = FALSE;
-  now = Curl_now();
+  now = curlx_now();
   pktx_init(&pktx, cf, data);
 
   CF_DATA_SAVE(save, cf, data);
@@ -2561,7 +2665,7 @@ static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf,
     }
     else if(ctx->max_bidi_streams) {
       uint64_t avail_bidi_streams = 0;
-      uint64_t max_streams = CONN_INUSE(cf->conn);
+      uint64_t max_streams = CONN_ATTACHED(cf->conn);
       if(ctx->max_bidi_streams > ctx->used_bidi_streams)
         avail_bidi_streams = ctx->max_bidi_streams - ctx->used_bidi_streams;
       max_streams += avail_bidi_streams;
@@ -2570,14 +2674,14 @@ static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf,
     else  /* transport params not arrived yet? take our default. */
       *pres1 = (int)Curl_multi_max_concurrent_streams(data->multi);
     CURL_TRC_CF(data, cf, "query conn[%" FMT_OFF_T "]: "
-                "MAX_CONCURRENT -> %d (%zu in use)",
-                cf->conn->connection_id, *pres1, CONN_INUSE(cf->conn));
+                "MAX_CONCURRENT -> %d (%u in use)",
+                cf->conn->connection_id, *pres1, CONN_ATTACHED(cf->conn));
     CF_DATA_RESTORE(cf, save);
     return CURLE_OK;
   }
   case CF_QUERY_CONNECT_REPLY_MS:
     if(ctx->q.got_first_byte) {
-      timediff_t ms = Curl_timediff(ctx->q.first_byte_at, ctx->started_at);
+      timediff_t ms = curlx_timediff(ctx->q.first_byte_at, ctx->started_at);
       *pres1 = (ms < INT_MAX) ? (int)ms : INT_MAX;
     }
     else
@@ -2620,21 +2724,12 @@ static bool cf_ngtcp2_conn_is_alive(struct Curl_cfilter *cf,
   if(!ctx->qconn || ctx->shutdown_started)
     goto out;
 
-  /* Both sides of the QUIC connection announce they max idle times in
-   * the transport parameters. Look at the minimum of both and if
-   * we exceed this, regard the connection as dead. The other side
-   * may have completely purged it and will no longer respond
-   * to any packets from us. */
+  /* We do not announce a max idle timeout, but when the peer does
+   * it will close the connection when it expires. */
   rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn);
-  if(rp) {
-    timediff_t idletime;
-    uint64_t idle_ms = ctx->max_idle_ms;
-
-    if(rp->max_idle_timeout &&
-      (rp->max_idle_timeout / NGTCP2_MILLISECONDS) < idle_ms)
-      idle_ms = (rp->max_idle_timeout / NGTCP2_MILLISECONDS);
-    idletime = Curl_timediff(Curl_now(), ctx->q.last_io);
-    if(idletime > 0 && (uint64_t)idletime > idle_ms)
+  if(rp && rp->max_idle_timeout) {
+    timediff_t idletime = curlx_timediff(curlx_now(), ctx->q.last_io);
+    if(idletime > 0 && (uint64_t)idletime > rp->max_idle_timeout)
       goto out;
   }
 
diff --git a/extra/curl/curl-8.12.1/lib/vquic/curl_ngtcp2.h b/extra/curl/curl-8.14.1/lib/vquic/curl_ngtcp2.h
similarity index 94%
rename from extra/curl/curl-8.12.1/lib/vquic/curl_ngtcp2.h
rename to extra/curl/curl-8.14.1/lib/vquic/curl_ngtcp2.h
index db3e611bd0c8..884662523f60 100644
--- a/extra/curl/curl-8.12.1/lib/vquic/curl_ngtcp2.h
+++ b/extra/curl/curl-8.14.1/lib/vquic/curl_ngtcp2.h
@@ -24,7 +24,7 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #if defined(USE_NGTCP2) && defined(USE_NGHTTP3)
 
@@ -33,6 +33,9 @@
 #endif
 
 #include <ngtcp2/ngtcp2_crypto.h>
+#ifdef OPENSSL_QUIC_API2
+#include <ngtcp2/ngtcp2_crypto_ossl.h>
+#endif
 #include <nghttp3/nghttp3.h>
 #ifdef USE_OPENSSL
 #include <openssl/ssl.h>
@@ -44,7 +47,7 @@
 
 struct Curl_cfilter;
 
-#include "urldata.h"
+#include "../urldata.h"
 
 void Curl_ngtcp2_ver(char *p, size_t len);
 
diff --git a/extra/curl/curl-8.12.1/lib/vquic/curl_osslq.c b/extra/curl/curl-8.14.1/lib/vquic/curl_osslq.c
similarity index 91%
rename from extra/curl/curl-8.12.1/lib/vquic/curl_osslq.c
rename to extra/curl/curl-8.14.1/lib/vquic/curl_osslq.c
index 4fd4fee11a6d..d5af10b06b28 100644
--- a/extra/curl/curl-8.12.1/lib/vquic/curl_osslq.c
+++ b/extra/curl/curl-8.14.1/lib/vquic/curl_osslq.c
@@ -22,7 +22,7 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #if defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3)
 
@@ -31,36 +31,36 @@
 #include <openssl/err.h>
 #include <nghttp3/nghttp3.h>
 
-#include "urldata.h"
-#include "hash.h"
-#include "sendf.h"
-#include "strdup.h"
-#include "rand.h"
-#include "multiif.h"
-#include "strcase.h"
-#include "cfilters.h"
-#include "cf-socket.h"
-#include "connect.h"
-#include "progress.h"
-#include "strerror.h"
-#include "dynbuf.h"
-#include "http1.h"
-#include "select.h"
-#include "inet_pton.h"
+#include "../urldata.h"
+#include "../hash.h"
+#include "../sendf.h"
+#include "../strdup.h"
+#include "../rand.h"
+#include "../multiif.h"
+#include "../strcase.h"
+#include "../cfilters.h"
+#include "../cf-socket.h"
+#include "../connect.h"
+#include "../progress.h"
+#include "../strerror.h"
+#include "../curlx/dynbuf.h"
+#include "../http1.h"
+#include "../select.h"
+#include "../curlx/inet_pton.h"
+#include "../uint-hash.h"
 #include "vquic.h"
 #include "vquic_int.h"
 #include "vquic-tls.h"
-#include "vtls/keylog.h"
-#include "vtls/vtls.h"
-#include "vtls/openssl.h"
+#include "../vtls/keylog.h"
+#include "../vtls/vtls.h"
+#include "../vtls/openssl.h"
 #include "curl_osslq.h"
-
-#include "warnless.h"
+#include "../curlx/warnless.h"
 
 /* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_printf.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
 
 /* A stream window is the maximum amount we need to buffer for
  * each active transfer. We use HTTP/3 flow control and only ACK
@@ -171,7 +171,7 @@ static CURLcode make_bio_addr(BIO_ADDR **pbio_addr,
   switch(addr->family) {
   case AF_INET: {
     struct sockaddr_in * const sin =
-      (struct sockaddr_in * const)(void *)&addr->curl_sa_addr;
+      (struct sockaddr_in * const)CURL_UNCONST(&addr->curl_sa_addr);
     if(!BIO_ADDR_rawmake(ba, AF_INET, &sin->sin_addr,
                          sizeof(sin->sin_addr), sin->sin_port)) {
       goto out;
@@ -182,7 +182,7 @@ static CURLcode make_bio_addr(BIO_ADDR **pbio_addr,
 #ifdef USE_IPV6
   case AF_INET6: {
     struct sockaddr_in6 * const sin =
-      (struct sockaddr_in6 * const)(void *)&addr->curl_sa_addr;
+      (struct sockaddr_in6 * const)CURL_UNCONST(&addr->curl_sa_addr);
     if(!BIO_ADDR_rawmake(ba, AF_INET6, &sin->sin6_addr,
                          sizeof(sin->sin6_addr), sin->sin6_port)) {
     }
@@ -285,12 +285,12 @@ struct cf_osslq_ctx {
   struct curltime handshake_at;      /* time connect handshake finished */
   struct curltime first_byte_at;     /* when first byte was recvd */
   struct bufc_pool stream_bufcp;     /* chunk pool for streams */
-  struct Curl_hash streams;          /* hash `data->mid` to `h3_stream_ctx` */
+  struct uint_hash streams;          /* hash `data->mid` to `h3_stream_ctx` */
   size_t max_stream_window;          /* max flow window for one stream */
   uint64_t max_idle_ms;              /* max idle time for QUIC connection */
   SSL_POLL_ITEM *poll_items;         /* Array for polling on writable state */
   struct Curl_easy **curl_items;     /* Array of easy objs */
-  size_t item_count;                 /* count of elements in poll/curl_items */
+  size_t items_max;                  /* max elements in poll/curl_items */
   BIT(initialized);
   BIT(got_first_byte);               /* if first byte was received */
   BIT(x509_store_setup);             /* if x509 store has been set up */
@@ -299,17 +299,17 @@ struct cf_osslq_ctx {
   BIT(need_send);                    /* QUIC connection needs to send */
 };
 
-static void h3_stream_hash_free(void *stream);
+static void h3_stream_hash_free(unsigned int id, void *stream);
 
 static void cf_osslq_ctx_init(struct cf_osslq_ctx *ctx)
 {
   DEBUGASSERT(!ctx->initialized);
   Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE,
                   H3_STREAM_POOL_SPARES);
-  Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free);
+  Curl_uint_hash_init(&ctx->streams, 63, h3_stream_hash_free);
   ctx->poll_items = NULL;
   ctx->curl_items = NULL;
-  ctx->item_count = 0;
+  ctx->items_max = 0;
   ctx->initialized = TRUE;
 }
 
@@ -317,8 +317,7 @@ static void cf_osslq_ctx_free(struct cf_osslq_ctx *ctx)
 {
   if(ctx && ctx->initialized) {
     Curl_bufcp_free(&ctx->stream_bufcp);
-    Curl_hash_clean(&ctx->streams);
-    Curl_hash_destroy(&ctx->streams);
+    Curl_uint_hash_destroy(&ctx->streams);
     Curl_ssl_peer_cleanup(&ctx->peer);
     free(ctx->poll_items);
     free(ctx->curl_items);
@@ -584,16 +583,13 @@ struct h3_stream_ctx {
   curl_off_t upload_left; /* number of request bytes left to upload */
   curl_off_t download_recvd; /* number of response DATA bytes received */
   int status_code; /* HTTP status code */
-  bool resp_hds_complete; /* we have a complete, final response */
-  bool closed; /* TRUE on stream close */
-  bool reset;  /* TRUE on stream reset */
-  bool send_closed; /* stream is local closed */
+  BIT(resp_hds_complete); /* we have a complete, final response */
+  BIT(closed); /* TRUE on stream close */
+  BIT(reset);  /* TRUE on stream reset */
+  BIT(send_closed); /* stream is local closed */
   BIT(quic_flow_blocked); /* stream is blocked by QUIC flow control */
 };
 
-#define H3_STREAM_CTX(ctx,data)   ((struct h3_stream_ctx *)(\
-            data? Curl_hash_offt_get(&(ctx)->streams, (data)->mid) : NULL))
-
 static void h3_stream_ctx_free(struct h3_stream_ctx *stream)
 {
   cf_osslq_stream_cleanup(&stream->s);
@@ -603,8 +599,9 @@ static void h3_stream_ctx_free(struct h3_stream_ctx *stream)
   free(stream);
 }
 
-static void h3_stream_hash_free(void *stream)
+static void h3_stream_hash_free(unsigned int id, void *stream)
 {
+  (void)id;
   DEBUGASSERT(stream);
   h3_stream_ctx_free((struct h3_stream_ctx *)stream);
 }
@@ -637,7 +634,7 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf,
   stream->recv_buf_nonflow = 0;
   Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
 
-  if(!Curl_hash_offt_set(&ctx->streams, data->mid, stream)) {
+  if(!Curl_uint_hash_set(&ctx->streams, data->mid, stream)) {
     h3_stream_ctx_free(stream);
     return CURLE_OUT_OF_MEMORY;
   }
@@ -654,7 +651,7 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
   if(stream) {
     CURL_TRC_CF(data, cf, "[%"FMT_PRId64"] easy handle is done",
                 stream->s.id);
-    if(ctx->h3.conn && !stream->closed) {
+    if(ctx->h3.conn && (stream->s.id >= 0) && !stream->closed) {
       nghttp3_conn_shutdown_stream_read(ctx->h3.conn, stream->s.id);
       nghttp3_conn_close_stream(ctx->h3.conn, stream->s.id,
                                 NGHTTP3_H3_REQUEST_CANCELLED);
@@ -662,10 +659,28 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
       stream->closed = TRUE;
     }
 
-    Curl_hash_offt_remove(&ctx->streams, data->mid);
+    Curl_uint_hash_remove(&ctx->streams, data->mid);
   }
 }
 
+struct cf_ossq_find_ctx {
+  curl_int64_t stream_id;
+  struct h3_stream_ctx *stream;
+};
+
+static bool cf_osslq_find_stream(unsigned int mid, void *val, void *user_data)
+{
+  struct h3_stream_ctx *stream = val;
+  struct cf_ossq_find_ctx *fctx = user_data;
+
+  (void)mid;
+  if(stream && stream->s.id == fctx->stream_id) {
+    fctx->stream = stream;
+    return FALSE; /* stop iterating */
+  }
+  return TRUE;
+}
+
 static struct cf_osslq_stream *cf_osslq_get_qstream(struct Curl_cfilter *cf,
                                                     struct Curl_easy *data,
                                                     int64_t stream_id)
@@ -686,17 +701,12 @@ static struct cf_osslq_stream *cf_osslq_get_qstream(struct Curl_cfilter *cf,
     return &ctx->h3.s_qpack_dec;
   }
   else {
-    struct Curl_llist_node *e;
-    DEBUGASSERT(data->multi);
-    for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) {
-      struct Curl_easy *sdata = Curl_node_elem(e);
-      if(sdata->conn != data->conn)
-        continue;
-      stream = H3_STREAM_CTX(ctx, sdata);
-      if(stream && stream->s.id == stream_id) {
-        return &stream->s;
-      }
-    }
+    struct cf_ossq_find_ctx fctx;
+    fctx.stream_id = stream_id;
+    fctx.stream = NULL;
+    Curl_uint_hash_visit(&ctx->streams, cf_osslq_find_stream, &fctx);
+    if(fctx.stream)
+      return &fctx.stream->s;
   }
   return NULL;
 }
@@ -819,7 +829,7 @@ static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id,
     return NGHTTP3_ERR_CALLBACK_FAILURE;
   }
   stream->download_recvd += (curl_off_t)buflen;
-  CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] DATA len=%zu, total=%zd",
+  CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] DATA len=%zu, total=%" FMT_OFF_T,
               stream->s.id, buflen, stream->download_recvd);
   h3_drain_stream(cf, data);
   return 0;
@@ -1012,7 +1022,7 @@ cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id,
     while(nvecs < veccnt &&
           Curl_bufq_peek_at(&stream->sendbuf,
                             stream->sendbuf_len_in_flight,
-                            (const unsigned char **)&vec[nvecs].base,
+                            CURL_UNCONST(&vec[nvecs].base),
                             &vec[nvecs].len)) {
       stream->sendbuf_len_in_flight += vec[nvecs].len;
       nwritten += vec[nvecs].len;
@@ -1163,13 +1173,15 @@ static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf,
   const struct Curl_sockaddr_ex *peer_addr = NULL;
   BIO *bio = NULL;
   BIO_ADDR *baddr = NULL;
+static const struct alpn_spec ALPN_SPEC_H3 = {
+  { "h3" }, 1
+};
 
   DEBUGASSERT(ctx->initialized);
 
 #define H3_ALPN "\x2h3"
   result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer,
-                               H3_ALPN, sizeof(H3_ALPN) - 1,
-                               NULL, NULL, NULL, NULL);
+                               &ALPN_SPEC_H3, NULL, NULL, NULL, NULL);
   if(result)
     goto out;
 
@@ -1224,17 +1236,6 @@ static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf,
     goto out;
   }
 
-#ifdef SSL_VALUE_QUIC_IDLE_TIMEOUT
-  /* Added in OpenSSL v3.3.x */
-  if(!SSL_set_feature_request_uint(ctx->tls.ossl.ssl,
-                                   SSL_VALUE_QUIC_IDLE_TIMEOUT,
-                                   CURL_QUIC_MAX_IDLE_MS)) {
-    CURL_TRC_CF(data, cf, "error setting idle timeout, ");
-    result = CURLE_FAILED_INIT;
-    goto out;
-  }
-#endif
-
   SSL_set_bio(ctx->tls.ossl.ssl, bio, bio);
   bio = NULL;
   SSL_set_connect_state(ctx->tls.ossl.ssl);
@@ -1399,6 +1400,29 @@ static CURLcode cf_osslq_stream_recv(struct cf_osslq_stream *s,
   return result;
 }
 
+struct cf_ossq_recv_ctx {
+  struct Curl_cfilter *cf;
+  struct Curl_multi *multi;
+  CURLcode result;
+};
+
+static bool cf_osslq_iter_recv(unsigned int mid, void *val, void *user_data)
+{
+  struct h3_stream_ctx *stream = val;
+  struct cf_ossq_recv_ctx *rctx = user_data;
+
+  (void)mid;
+  if(stream && !stream->closed && !Curl_bufq_is_full(&stream->recvbuf)) {
+    struct Curl_easy *sdata = Curl_multi_get_easy(rctx->multi, mid);
+    if(sdata) {
+      rctx->result = cf_osslq_stream_recv(&stream->s, rctx->cf, sdata);
+      if(rctx->result)
+        return FALSE; /* abort iteration */
+    }
+  }
+  return TRUE;
+}
+
 static CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
                                     struct Curl_easy *data)
 {
@@ -1435,22 +1459,14 @@ static CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
   }
 
   if(ctx->h3.conn) {
-    struct Curl_llist_node *e;
-    struct h3_stream_ctx *stream;
-    /* PULL all open streams */
+    struct cf_ossq_recv_ctx rctx;
+
     DEBUGASSERT(data->multi);
-    for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) {
-      struct Curl_easy *sdata = Curl_node_elem(e);
-      if(sdata->conn == data->conn && CURL_WANT_RECV(sdata)) {
-        stream = H3_STREAM_CTX(ctx, sdata);
-        if(stream && !stream->closed &&
-           !Curl_bufq_is_full(&stream->recvbuf)) {
-          result = cf_osslq_stream_recv(&stream->s, cf, sdata);
-          if(result)
-            goto out;
-        }
-      }
-    }
+    rctx.cf = cf;
+    rctx.multi = data->multi;
+    rctx.result = CURLE_OK;
+    Curl_uint_hash_visit(&ctx->streams, cf_osslq_iter_recv, &rctx);
+    result = rctx.result;
   }
 
 out:
@@ -1458,13 +1474,41 @@ static CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
   return result;
 }
 
+struct cf_ossq_fill_ctx {
+  struct cf_osslq_ctx *ctx;
+  struct Curl_multi *multi;
+  size_t n;
+};
+
+static bool cf_osslq_collect_block_send(unsigned int mid, void *val,
+                                        void *user_data)
+{
+  struct h3_stream_ctx *stream = val;
+  struct cf_ossq_fill_ctx *fctx = user_data;
+  struct cf_osslq_ctx *ctx = fctx->ctx;
+
+  if(fctx->n >= ctx->items_max)  /* should not happen, prevent mayhem */
+    return FALSE;
+
+  if(stream && stream->s.ssl && stream->s.send_blocked) {
+    struct Curl_easy *sdata = Curl_multi_get_easy(fctx->multi, mid);
+    if(sdata) {
+      ctx->poll_items[fctx->n].desc = SSL_as_poll_descriptor(stream->s.ssl);
+      ctx->poll_items[fctx->n].events = SSL_POLL_EVENT_W;
+      ctx->curl_items[fctx->n] = sdata;
+      fctx->n++;
+    }
+  }
+  return TRUE;
+}
+
 /* Iterate over all streams and check if blocked can be unblocked */
 static CURLcode cf_osslq_check_and_unblock(struct Curl_cfilter *cf,
                                            struct Curl_easy *data)
 {
   struct cf_osslq_ctx *ctx = cf->ctx;
   struct h3_stream_ctx *stream;
-  size_t poll_count = 0;
+  size_t poll_count;
   size_t result_count = 0;
   size_t idx_count = 0;
   CURLcode res = CURLE_OK;
@@ -1472,66 +1516,61 @@ static CURLcode cf_osslq_check_and_unblock(struct Curl_cfilter *cf,
   void *tmpptr;
 
   if(ctx->h3.conn) {
-    struct Curl_llist_node *e;
+    struct cf_ossq_fill_ctx fill_ctx;
 
-    res = CURLE_OUT_OF_MEMORY;
-
-    if(ctx->item_count < Curl_llist_count(&data->multi->process)) {
-      ctx->item_count = 0;
-      tmpptr = realloc(ctx->poll_items,
-                       Curl_llist_count(&data->multi->process) *
-                       sizeof(SSL_POLL_ITEM));
+    if(ctx->items_max < Curl_uint_hash_count(&ctx->streams)) {
+      size_t nmax = Curl_uint_hash_count(&ctx->streams);
+      ctx->items_max = 0;
+      tmpptr = realloc(ctx->poll_items, nmax * sizeof(SSL_POLL_ITEM));
       if(!tmpptr) {
         free(ctx->poll_items);
         ctx->poll_items = NULL;
+        res = CURLE_OUT_OF_MEMORY;
         goto out;
       }
       ctx->poll_items = tmpptr;
 
-      tmpptr = realloc(ctx->curl_items,
-                       Curl_llist_count(&data->multi->process) *
-                       sizeof(struct Curl_easy *));
+      tmpptr = realloc(ctx->curl_items, nmax * sizeof(struct Curl_easy *));
       if(!tmpptr) {
         free(ctx->curl_items);
         ctx->curl_items = NULL;
+        res = CURLE_OUT_OF_MEMORY;
         goto out;
       }
       ctx->curl_items = tmpptr;
-
-      ctx->item_count = Curl_llist_count(&data->multi->process);
-    }
-
-    for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) {
-      struct Curl_easy *sdata = Curl_node_elem(e);
-      if(sdata->conn == data->conn) {
-        stream = H3_STREAM_CTX(ctx, sdata);
-        if(stream && stream->s.ssl && stream->s.send_blocked) {
-          ctx->poll_items[poll_count].desc =
-            SSL_as_poll_descriptor(stream->s.ssl);
-          ctx->poll_items[poll_count].events = SSL_POLL_EVENT_W;
-          ctx->curl_items[poll_count] = sdata;
-          poll_count++;
-        }
-      }
+      ctx->items_max = nmax;
     }
 
-    memset(&timeout, 0, sizeof(struct timeval));
-    res = CURLE_UNRECOVERABLE_POLL;
-    if(!SSL_poll(ctx->poll_items, poll_count, sizeof(SSL_POLL_ITEM), &timeout,
-                 0, &result_count))
-        goto out;
+    fill_ctx.ctx = ctx;
+    fill_ctx.multi = data->multi;
+    fill_ctx.n = 0;
+    Curl_uint_hash_visit(&ctx->streams, cf_osslq_collect_block_send,
+                         &fill_ctx);
+    poll_count = fill_ctx.n;
+    if(poll_count) {
+      CURL_TRC_CF(data, cf, "polling %zu blocked streams", poll_count);
+
+      memset(&timeout, 0, sizeof(struct timeval));
+      res = CURLE_UNRECOVERABLE_POLL;
+      if(!SSL_poll(ctx->poll_items, poll_count, sizeof(SSL_POLL_ITEM),
+                   &timeout, 0, &result_count))
+          goto out;
 
-    res = CURLE_OK;
-
-    for(idx_count = 0; idx_count < poll_count && result_count > 0;
-        idx_count++) {
-      if(ctx->poll_items[idx_count].revents & SSL_POLL_EVENT_W) {
-        stream = H3_STREAM_CTX(ctx, ctx->curl_items[idx_count]);
-        nghttp3_conn_unblock_stream(ctx->h3.conn, stream->s.id);
-        stream->s.send_blocked = FALSE;
-        h3_drain_stream(cf, ctx->curl_items[idx_count]);
-        CURL_TRC_CF(ctx->curl_items[idx_count], cf, "unblocked");
-        result_count--;
+      res = CURLE_OK;
+
+      for(idx_count = 0; idx_count < poll_count && result_count > 0;
+          idx_count++) {
+        if(ctx->poll_items[idx_count].revents & SSL_POLL_EVENT_W) {
+          stream = H3_STREAM_CTX(ctx, ctx->curl_items[idx_count]);
+          DEBUGASSERT(stream);  /* should still exist */
+          if(stream) {
+            nghttp3_conn_unblock_stream(ctx->h3.conn, stream->s.id);
+            stream->s.send_blocked = FALSE;
+            h3_drain_stream(cf, ctx->curl_items[idx_count]);
+            CURL_TRC_CF(ctx->curl_items[idx_count], cf, "unblocked");
+          }
+          result_count--;
+        }
       }
     }
   }
@@ -1625,7 +1664,7 @@ static CURLcode h3_send_streams(struct Curl_cfilter *cf,
     if(acked_len > 0 || (eos && !s->send_blocked)) {
       /* Since QUIC buffers the data written internally, we can tell
        * nghttp3 that it can move forward on it */
-      ctx->q.last_io = Curl_now();
+      ctx->q.last_io = curlx_now();
       rv = nghttp3_conn_add_write_offset(ctx->h3.conn, s->id, acked_len);
       if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
         failf(data, "nghttp3_conn_add_write_offset returned error: %s\n",
@@ -1718,7 +1757,7 @@ static CURLcode check_and_set_expiry(struct Curl_cfilter *cf,
 
 static CURLcode cf_osslq_connect(struct Curl_cfilter *cf,
                                  struct Curl_easy *data,
-                                 bool blocking, bool *done)
+                                 bool *done)
 {
   struct cf_osslq_ctx *ctx = cf->ctx;
   CURLcode result = CURLE_OK;
@@ -1733,13 +1772,13 @@ static CURLcode cf_osslq_connect(struct Curl_cfilter *cf,
 
   /* Connect the UDP filter first */
   if(!cf->next->connected) {
-    result = Curl_conn_cf_connect(cf->next, data, blocking, done);
+    result = Curl_conn_cf_connect(cf->next, data, done);
     if(result || !*done)
       return result;
   }
 
   *done = FALSE;
-  now = Curl_now();
+  now = curlx_now();
   CF_DATA_SAVE(save, cf, data);
 
   if(!ctx->tls.ossl.ssl) {
@@ -1753,7 +1792,7 @@ static CURLcode cf_osslq_connect(struct Curl_cfilter *cf,
     int readable = SOCKET_READABLE(ctx->q.sockfd, 0);
     if(readable > 0 && (readable & CURL_CSELECT_IN)) {
       ctx->got_first_byte = TRUE;
-      ctx->first_byte_at = Curl_now();
+      ctx->first_byte_at = curlx_now();
     }
   }
 
@@ -1773,7 +1812,7 @@ static CURLcode cf_osslq_connect(struct Curl_cfilter *cf,
     ctx->handshake_at = now;
     ctx->q.last_io = now;
     CURL_TRC_CF(data, cf, "handshake complete after %dms",
-               (int)Curl_timediff(now, ctx->started_at));
+               (int)curlx_timediff(now, ctx->started_at));
     result = cf_osslq_verify_peer(cf, data);
     if(!result) {
       CURL_TRC_CF(data, cf, "peer verified");
@@ -2249,7 +2288,7 @@ static bool cf_osslq_conn_is_alive(struct Curl_cfilter *cf,
       goto out;
     }
     CURL_TRC_CF(data, cf, "negotiated idle timeout: %zums", (size_t)idle_ms);
-    idletime = Curl_timediff(Curl_now(), ctx->q.last_io);
+    idletime = curlx_timediff(curlx_now(), ctx->q.last_io);
     if(idletime > 0 && (uint64_t)idletime > idle_ms)
       goto out;
   }
@@ -2326,17 +2365,17 @@ static CURLcode cf_osslq_query(struct Curl_cfilter *cf,
       return CURLE_HTTP3;
     }
     /* we report avail + in_use */
-    v += CONN_INUSE(cf->conn);
+    v += CONN_ATTACHED(cf->conn);
     *pres1 = (v > INT_MAX) ? INT_MAX : (int)v;
 #else
     *pres1 = 100;
 #endif
-    CURL_TRC_CF(data, cf, "query max_conncurrent -> %d", *pres1);
+    CURL_TRC_CF(data, cf, "query max_concurrent -> %d", *pres1);
     return CURLE_OK;
   }
   case CF_QUERY_CONNECT_REPLY_MS:
     if(ctx->got_first_byte) {
-      timediff_t ms = Curl_timediff(ctx->first_byte_at, ctx->started_at);
+      timediff_t ms = curlx_timediff(ctx->first_byte_at, ctx->started_at);
       *pres1 = (ms < INT_MAX) ? (int)ms : INT_MAX;
     }
     else
diff --git a/extra/curl/curl-8.12.1/lib/vquic/curl_osslq.h b/extra/curl/curl-8.14.1/lib/vquic/curl_osslq.h
similarity index 97%
rename from extra/curl/curl-8.12.1/lib/vquic/curl_osslq.h
rename to extra/curl/curl-8.14.1/lib/vquic/curl_osslq.h
index 0e12d7023e0b..a2809c92bcc0 100644
--- a/extra/curl/curl-8.12.1/lib/vquic/curl_osslq.h
+++ b/extra/curl/curl-8.14.1/lib/vquic/curl_osslq.h
@@ -24,7 +24,7 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #if defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3)
 
@@ -34,7 +34,7 @@
 
 struct Curl_cfilter;
 
-#include "urldata.h"
+#include "../urldata.h"
 
 void Curl_osslq_ver(char *p, size_t len);
 
diff --git a/extra/curl/curl-8.12.1/lib/vquic/curl_quiche.c b/extra/curl/curl-8.14.1/lib/vquic/curl_quiche.c
similarity index 86%
rename from extra/curl/curl-8.12.1/lib/vquic/curl_quiche.c
rename to extra/curl/curl-8.14.1/lib/vquic/curl_quiche.c
index 679cba3641a5..88065328896c 100644
--- a/extra/curl/curl-8.12.1/lib/vquic/curl_quiche.c
+++ b/extra/curl/curl-8.14.1/lib/vquic/curl_quiche.c
@@ -22,40 +22,40 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #ifdef USE_QUICHE
 #include <quiche.h>
 #include <openssl/err.h>
 #include <openssl/ssl.h>
-#include "bufq.h"
-#include "hash.h"
-#include "urldata.h"
-#include "cfilters.h"
-#include "cf-socket.h"
-#include "sendf.h"
-#include "strdup.h"
-#include "rand.h"
-#include "strcase.h"
-#include "multiif.h"
-#include "connect.h"
-#include "progress.h"
-#include "strerror.h"
-#include "http1.h"
+#include "../bufq.h"
+#include "../uint-hash.h"
+#include "../urldata.h"
+#include "../cfilters.h"
+#include "../cf-socket.h"
+#include "../sendf.h"
+#include "../strdup.h"
+#include "../rand.h"
+#include "../strcase.h"
+#include "../multiif.h"
+#include "../connect.h"
+#include "../progress.h"
+#include "../strerror.h"
+#include "../http1.h"
 #include "vquic.h"
 #include "vquic_int.h"
 #include "vquic-tls.h"
 #include "curl_quiche.h"
-#include "transfer.h"
-#include "inet_pton.h"
-#include "vtls/openssl.h"
-#include "vtls/keylog.h"
-#include "vtls/vtls.h"
+#include "../transfer.h"
+#include "../curlx/inet_pton.h"
+#include "../vtls/openssl.h"
+#include "../vtls/keylog.h"
+#include "../vtls/vtls.h"
 
 /* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_printf.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
 
 /* HTTP/3 error values defined in RFC 9114, ch. 8.1 */
 #define CURL_H3_NO_ERROR  (0x0100)
@@ -97,7 +97,7 @@ struct cf_quiche_ctx {
   struct curltime started_at;        /* time the current attempt started */
   struct curltime handshake_at;      /* time connect handshake finished */
   struct bufc_pool stream_bufcp;     /* chunk pool for streams */
-  struct Curl_hash streams;          /* hash `data->mid` to `stream_ctx` */
+  struct uint_hash streams;          /* hash `data->mid` to `stream_ctx` */
   curl_off_t data_recvd;
   BIT(initialized);
   BIT(goaway);                       /* got GOAWAY from server */
@@ -115,7 +115,7 @@ static void quiche_debug_log(const char *line, void *argp)
 }
 #endif
 
-static void h3_stream_hash_free(void *stream);
+static void h3_stream_hash_free(unsigned int id, void *stream);
 
 static void cf_quiche_ctx_init(struct cf_quiche_ctx *ctx)
 {
@@ -128,7 +128,7 @@ static void cf_quiche_ctx_init(struct cf_quiche_ctx *ctx)
 #endif
   Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE,
                   H3_STREAM_POOL_SPARES);
-  Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free);
+  Curl_uint_hash_init(&ctx->streams, 63, h3_stream_hash_free);
   ctx->data_recvd = 0;
   ctx->initialized = TRUE;
 }
@@ -142,8 +142,7 @@ static void cf_quiche_ctx_free(struct cf_quiche_ctx *ctx)
     Curl_ssl_peer_cleanup(&ctx->peer);
     vquic_ctx_free(&ctx->q);
     Curl_bufcp_free(&ctx->stream_bufcp);
-    Curl_hash_clean(&ctx->streams);
-    Curl_hash_destroy(&ctx->streams);
+    Curl_uint_hash_destroy(&ctx->streams);
   }
   free(ctx);
 }
@@ -166,7 +165,7 @@ static CURLcode cf_flush_egress(struct Curl_cfilter *cf,
 /**
  * All about the H3 internals of a stream
  */
-struct stream_ctx {
+struct h3_stream_ctx {
   curl_uint64_t id; /* HTTP/3 protocol stream identifier */
   struct bufq recvbuf; /* h3 response */
   struct h1_req_parser h1; /* h1 request parsing */
@@ -180,47 +179,87 @@ struct stream_ctx {
   BIT(quic_flow_blocked); /* stream is blocked by QUIC flow control */
 };
 
-#define H3_STREAM_CTX(ctx,data)   ((struct stream_ctx *)(\
-            data? Curl_hash_offt_get(&(ctx)->streams, (data)->mid) : NULL))
-
-static void h3_stream_ctx_free(struct stream_ctx *stream)
+static void h3_stream_ctx_free(struct h3_stream_ctx *stream)
 {
   Curl_bufq_free(&stream->recvbuf);
   Curl_h1_req_parse_free(&stream->h1);
   free(stream);
 }
 
-static void h3_stream_hash_free(void *stream)
+static void h3_stream_hash_free(unsigned int id, void *stream)
 {
+  (void)id;
   DEBUGASSERT(stream);
-  h3_stream_ctx_free((struct stream_ctx *)stream);
+  h3_stream_ctx_free((struct h3_stream_ctx *)stream);
+}
+
+typedef bool cf_quiche_svisit(struct Curl_cfilter *cf,
+                              struct Curl_easy *sdata,
+                              struct h3_stream_ctx *stream,
+                              void *user_data);
+
+struct cf_quiche_visit_ctx {
+  struct Curl_cfilter *cf;
+  struct Curl_multi *multi;
+  cf_quiche_svisit *cb;
+  void *user_data;
+};
+
+static bool cf_quiche_stream_do(unsigned int mid, void *val, void *user_data)
+{
+  struct cf_quiche_visit_ctx *vctx = user_data;
+  struct h3_stream_ctx *stream = val;
+  struct Curl_easy *sdata = Curl_multi_get_easy(vctx->multi, mid);
+  if(sdata)
+    return vctx->cb(vctx->cf, sdata, stream, vctx->user_data);
+  return TRUE;
 }
 
-static void check_resumes(struct Curl_cfilter *cf,
-                          struct Curl_easy *data)
+static void cf_quiche_for_all_streams(struct Curl_cfilter *cf,
+                                      struct Curl_multi *multi,
+                                      cf_quiche_svisit *do_cb,
+                                      void *user_data)
 {
   struct cf_quiche_ctx *ctx = cf->ctx;
-  struct Curl_llist_node *e;
+  struct cf_quiche_visit_ctx vctx;
+  vctx.cf = cf;
+  vctx.multi = multi;
+  vctx.cb = do_cb;
+  vctx.user_data = user_data;
+  Curl_uint_hash_visit(&ctx->streams, cf_quiche_stream_do, &vctx);
+}
 
-  DEBUGASSERT(data->multi);
-  for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) {
-    struct Curl_easy *sdata = Curl_node_elem(e);
-    if(sdata->conn == data->conn) {
-      struct stream_ctx *stream = H3_STREAM_CTX(ctx, sdata);
-      if(stream && stream->quic_flow_blocked) {
-        stream->quic_flow_blocked = FALSE;
-        Curl_expire(data, 0, EXPIRE_RUN_NOW);
-        CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] unblock", stream->id);
-      }
-    }
+static bool cf_quiche_do_resume(struct Curl_cfilter *cf,
+                                struct Curl_easy *sdata,
+                                struct h3_stream_ctx *stream,
+                                void *user_data)
+{
+  (void)user_data;
+  if(stream->quic_flow_blocked) {
+    stream->quic_flow_blocked = FALSE;
+    Curl_expire(sdata, 0, EXPIRE_RUN_NOW);
+    CURL_TRC_CF(sdata, cf, "[%"FMT_PRIu64"] unblock", stream->id);
   }
+  return TRUE;
+}
+
+static bool cf_quiche_do_expire(struct Curl_cfilter *cf,
+                                struct Curl_easy *sdata,
+                                struct h3_stream_ctx *stream,
+                                void *user_data)
+{
+  (void)stream;
+  (void)user_data;
+  CURL_TRC_CF(sdata, cf, "conn closed, expire transfer");
+  Curl_expire(sdata, 0, EXPIRE_RUN_NOW);
+  return TRUE;
 }
 
 static CURLcode h3_data_setup(struct Curl_cfilter *cf,
                               struct Curl_easy *data)
 {
   struct cf_quiche_ctx *ctx = cf->ctx;
-  struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
 
   if(stream)
     return CURLE_OK;
@@ -234,7 +273,7 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf,
                   H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT);
   Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
 
-  if(!Curl_hash_offt_set(&ctx->streams, data->mid, stream)) {
+  if(!Curl_uint_hash_set(&ctx->streams, data->mid, stream)) {
     h3_stream_ctx_free(stream);
     return CURLE_OUT_OF_MEMORY;
   }
@@ -245,7 +284,7 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf,
 static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   struct cf_quiche_ctx *ctx = cf->ctx;
-  struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
   CURLcode result;
 
   (void)cf;
@@ -264,7 +303,7 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
       if(result)
         CURL_TRC_CF(data, cf, "data_done, flush egress -> %d", result);
     }
-    Curl_hash_offt_remove(&ctx->streams, data->mid);
+    Curl_uint_hash_remove(&ctx->streams, data->mid);
   }
 }
 
@@ -272,7 +311,7 @@ static void h3_drain_stream(struct Curl_cfilter *cf,
                             struct Curl_easy *data)
 {
   struct cf_quiche_ctx *ctx = cf->ctx;
-  struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
   unsigned char bits;
 
   (void)cf;
@@ -285,52 +324,12 @@ static void h3_drain_stream(struct Curl_cfilter *cf,
   }
 }
 
-static struct Curl_easy *get_stream_easy(struct Curl_cfilter *cf,
-                                         struct Curl_easy *data,
-                                         curl_uint64_t stream_id,
-                                         struct stream_ctx **pstream)
-{
-  struct cf_quiche_ctx *ctx = cf->ctx;
-  struct stream_ctx *stream;
-
-  (void)cf;
-  stream = H3_STREAM_CTX(ctx, data);
-  if(stream && stream->id == stream_id) {
-    *pstream = stream;
-    return data;
-  }
-  else {
-    struct Curl_llist_node *e;
-    DEBUGASSERT(data->multi);
-    for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) {
-      struct Curl_easy *sdata = Curl_node_elem(e);
-      if(sdata->conn != data->conn)
-        continue;
-      stream = H3_STREAM_CTX(ctx, sdata);
-      if(stream && stream->id == stream_id) {
-        *pstream = stream;
-        return sdata;
-      }
-    }
-  }
-  *pstream = NULL;
-  return NULL;
-}
-
 static void cf_quiche_expire_conn_closed(struct Curl_cfilter *cf,
                                          struct Curl_easy *data)
 {
-  struct Curl_llist_node *e;
-
   DEBUGASSERT(data->multi);
   CURL_TRC_CF(data, cf, "conn closed, expire all transfers");
-  for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) {
-    struct Curl_easy *sdata = Curl_node_elem(e);
-    if(sdata == data || sdata->conn != data->conn)
-      continue;
-    CURL_TRC_CF(sdata, cf, "conn closed, expire transfer");
-    Curl_expire(sdata, 0, EXPIRE_RUN_NOW);
-  }
+  cf_quiche_for_all_streams(cf, data->multi, cf_quiche_do_expire, NULL);
 }
 
 /*
@@ -343,7 +342,7 @@ static CURLcode write_resp_raw(struct Curl_cfilter *cf,
                                const void *mem, size_t memlen)
 {
   struct cf_quiche_ctx *ctx = cf->ctx;
-  struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
   CURLcode result = CURLE_OK;
   ssize_t nwritten;
 
@@ -374,7 +373,7 @@ static int cb_each_header(uint8_t *name, size_t name_len,
 {
   struct cb_ctx *x = argp;
   struct cf_quiche_ctx *ctx = x->cf->ctx;
-  struct stream_ctx *stream = H3_STREAM_CTX(ctx, x->data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, x->data);
   CURLcode result;
 
   if(!stream)
@@ -414,7 +413,7 @@ static ssize_t stream_resp_read(void *reader_ctx,
 {
   struct cb_ctx *x = reader_ctx;
   struct cf_quiche_ctx *ctx = x->cf->ctx;
-  struct stream_ctx *stream = H3_STREAM_CTX(ctx, x->data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, x->data);
   ssize_t nread;
 
   if(!stream) {
@@ -438,7 +437,7 @@ static CURLcode cf_recv_body(struct Curl_cfilter *cf,
                              struct Curl_easy *data)
 {
   struct cf_quiche_ctx *ctx = cf->ctx;
-  struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
   ssize_t nwritten;
   struct cb_ctx cb_ctx;
   CURLcode result = CURLE_OK;
@@ -496,7 +495,7 @@ static const char *cf_ev_name(quiche_h3_event *ev)
 
 static CURLcode h3_process_event(struct Curl_cfilter *cf,
                                  struct Curl_easy *data,
-                                 struct stream_ctx *stream,
+                                 struct h3_stream_ctx *stream,
                                  quiche_h3_event *ev)
 {
   struct cb_ctx cb_ctx;
@@ -557,14 +556,48 @@ static CURLcode h3_process_event(struct Curl_cfilter *cf,
   return result;
 }
 
+static CURLcode cf_quiche_ev_process(struct Curl_cfilter *cf,
+                                     struct Curl_easy *data,
+                                     struct h3_stream_ctx *stream,
+                                     quiche_h3_event *ev)
+{
+  CURLcode result = h3_process_event(cf, data, stream, ev);
+  h3_drain_stream(cf, data);
+  if(result)
+    CURL_TRC_CF(data, cf, "error processing event %s "
+                "for [%"FMT_PRIu64"] -> %d", cf_ev_name(ev),
+                stream->id, result);
+  return result;
+}
+
+struct cf_quich_disp_ctx {
+  curl_uint64_t stream_id;
+  struct Curl_cfilter *cf;
+  struct Curl_multi *multi;
+  quiche_h3_event *ev;
+  CURLcode result;
+};
+
+static bool cf_quiche_disp_event(unsigned int mid, void *val, void *user_data)
+{
+  struct cf_quich_disp_ctx *dctx = user_data;
+  struct h3_stream_ctx *stream = val;
+
+  if(stream->id == dctx->stream_id) {
+    struct Curl_easy *sdata = Curl_multi_get_easy(dctx->multi, mid);
+    if(sdata)
+      dctx->result = cf_quiche_ev_process(dctx->cf, sdata, stream, dctx->ev);
+    return FALSE; /* stop iterating */
+  }
+  return TRUE;
+}
+
 static CURLcode cf_poll_events(struct Curl_cfilter *cf,
                                struct Curl_easy *data)
 {
   struct cf_quiche_ctx *ctx = cf->ctx;
-  struct stream_ctx *stream = NULL;
-  struct Curl_easy *sdata;
+  struct h3_stream_ctx *stream = NULL;
   quiche_h3_event *ev;
-  CURLcode result;
 
   /* Take in the events and distribute them to the transfers. */
   while(ctx->h3c) {
@@ -576,28 +609,27 @@ static CURLcode cf_poll_events(struct Curl_cfilter *cf,
       CURL_TRC_CF(data, cf, "error poll: %"FMT_PRId64, stream3_id);
       return CURLE_HTTP3;
     }
-
-    sdata = get_stream_easy(cf, data, stream3_id, &stream);
-    if(!sdata || !stream) {
-      CURL_TRC_CF(data, cf, "discard event %s for unknown [%"FMT_PRId64"]",
-                  cf_ev_name(ev), stream3_id);
-    }
     else {
-      result = h3_process_event(cf, sdata, stream, ev);
-      h3_drain_stream(cf, sdata);
-      if(result) {
-        CURL_TRC_CF(data, cf, "error processing event %s "
-                    "for [%"FMT_PRIu64"] -> %d", cf_ev_name(ev),
-                    stream3_id, result);
-        if(data == sdata) {
-          /* Only report this error to the caller if it is about the
-           * transfer we were called with. Otherwise we fail a transfer
-           * due to a problem in another one. */
-          quiche_h3_event_free(ev);
+      struct cf_quich_disp_ctx dctx;
+      dctx.stream_id = (curl_uint64_t)stream3_id;
+      dctx.cf = cf;
+      dctx.multi = data->multi;
+      dctx.ev = ev;
+      dctx.result = CURLE_OK;
+      stream = H3_STREAM_CTX(ctx, data);
+      if(stream && stream->id == dctx.stream_id) {
+        /* event for calling transfer */
+        CURLcode result = cf_quiche_ev_process(cf, data, stream, ev);
+        quiche_h3_event_free(ev);
+        if(result)
           return result;
-        }
       }
-      quiche_h3_event_free(ev);
+      else {
+        /* another transfer, do not return errors, as they are not for
+         * the calling transfer */
+        Curl_uint_hash_visit(&ctx->streams, cf_quiche_disp_event, &dctx);
+        quiche_h3_event_free(ev);
+      }
     }
   }
   return CURLE_OK;
@@ -627,7 +659,8 @@ static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen,
   recv_info.from = (struct sockaddr *)remote_addr;
   recv_info.from_len = remote_addrlen;
 
-  nread = quiche_conn_recv(ctx->qconn, (unsigned char *)pkt, pktlen,
+  nread = quiche_conn_recv(ctx->qconn,
+                           (unsigned char *)CURL_UNCONST(pkt), pktlen,
                            &recv_info);
   if(nread < 0) {
     if(QUICHE_ERR_DONE == nread) {
@@ -686,7 +719,8 @@ static CURLcode cf_process_ingress(struct Curl_cfilter *cf,
   if(rctx.pkts > 0) {
     /* quiche digested ingress packets. It might have opened flow control
      * windows again. */
-    check_resumes(cf, data);
+    DEBUGASSERT(data->multi);
+    cf_quiche_for_all_streams(cf, data->multi, cf_quiche_do_resume, NULL);
   }
   return cf_poll_events(cf, data);
 }
@@ -811,7 +845,7 @@ static ssize_t recv_closed_stream(struct Curl_cfilter *cf,
                                   CURLcode *err)
 {
   struct cf_quiche_ctx *ctx = cf->ctx;
-  struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
   ssize_t nread = -1;
 
   DEBUGASSERT(stream);
@@ -843,7 +877,7 @@ static ssize_t cf_quiche_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
                               char *buf, size_t len, CURLcode *err)
 {
   struct cf_quiche_ctx *ctx = cf->ctx;
-  struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
   ssize_t nread = -1;
   CURLcode result;
 
@@ -916,7 +950,7 @@ static ssize_t cf_quiche_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
 
 static ssize_t cf_quiche_send_body(struct Curl_cfilter *cf,
                                    struct Curl_easy *data,
-                                   struct stream_ctx *stream,
+                                   struct h3_stream_ctx *stream,
                                    const void *buf, size_t len, bool eos,
                                    CURLcode *err)
 {
@@ -924,7 +958,7 @@ static ssize_t cf_quiche_send_body(struct Curl_cfilter *cf,
   ssize_t nwritten;
 
   nwritten = quiche_h3_send_body(ctx->h3c, ctx->qconn, stream->id,
-                                 (uint8_t *)buf, len, eos);
+                                 (uint8_t *)CURL_UNCONST(buf), len, eos);
   if(nwritten == QUICHE_H3_ERR_DONE || (nwritten == 0 && len > 0)) {
     /* Blocked on flow control and should HOLD sending. But when do we open
      * again? */
@@ -975,7 +1009,7 @@ static ssize_t h3_open_stream(struct Curl_cfilter *cf,
                               CURLcode *err)
 {
   struct cf_quiche_ctx *ctx = cf->ctx;
-  struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
   size_t nheader, i;
   curl_int64_t stream3_id;
   struct dynhds h2_headers;
@@ -1095,7 +1129,7 @@ static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data,
                               CURLcode *err)
 {
   struct cf_quiche_ctx *ctx = cf->ctx;
-  struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
   CURLcode result;
   ssize_t nwritten;
 
@@ -1154,7 +1188,7 @@ static bool stream_is_writeable(struct Curl_cfilter *cf,
                                 struct Curl_easy *data)
 {
   struct cf_quiche_ctx *ctx = cf->ctx;
-  struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
 
   return stream && (quiche_conn_stream_writable(
     ctx->qconn, (curl_uint64_t)stream->id, 1) > 0);
@@ -1172,7 +1206,7 @@ static void cf_quiche_adjust_pollset(struct Curl_cfilter *cf,
 
   Curl_pollset_check(data, ps, ctx->q.sockfd, &want_recv, &want_send);
   if(want_recv || want_send) {
-    struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+    struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
     bool c_exhaust, s_exhaust;
 
     c_exhaust = FALSE; /* Have not found any call in quiche that tells
@@ -1195,7 +1229,7 @@ static bool cf_quiche_data_pending(struct Curl_cfilter *cf,
                                    const struct Curl_easy *data)
 {
   struct cf_quiche_ctx *ctx = cf->ctx;
-  const struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+  const struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
   (void)cf;
   return stream && !Curl_bufq_is_empty(&stream->recvbuf);
 }
@@ -1232,7 +1266,7 @@ static CURLcode cf_quiche_data_event(struct Curl_cfilter *cf,
     h3_data_done(cf, data);
     break;
   case CF_CTRL_DATA_DONE_SEND: {
-    struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+    struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
     if(stream && !stream->send_closed) {
       unsigned char body[1];
       ssize_t sent;
@@ -1246,7 +1280,7 @@ static CURLcode cf_quiche_data_event(struct Curl_cfilter *cf,
     break;
   }
   case CF_CTRL_DATA_IDLE: {
-    struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+    struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
     if(stream && !stream->closed) {
       result = cf_flush_egress(cf, data);
       if(result)
@@ -1267,6 +1301,9 @@ static CURLcode cf_quiche_ctx_open(struct Curl_cfilter *cf,
   int rv;
   CURLcode result;
   const struct Curl_sockaddr_ex *sockaddr;
+static const struct alpn_spec ALPN_SPEC_H3 = {
+  { "h3" }, 1
+};
 
   DEBUGASSERT(ctx->q.sockfd != CURL_SOCKET_BAD);
   DEBUGASSERT(ctx->initialized);
@@ -1281,7 +1318,6 @@ static CURLcode cf_quiche_ctx_open(struct Curl_cfilter *cf,
     return CURLE_FAILED_INIT;
   }
   quiche_config_enable_pacing(ctx->cfg, FALSE);
-  quiche_config_set_max_idle_timeout(ctx->cfg, CURL_QUIC_MAX_IDLE_MS);
   quiche_config_set_initial_max_data(ctx->cfg, (1 * 1024 * 1024)
     /* (QUIC_MAX_STREAMS/2) * H3_STREAM_WINDOW_SIZE */);
   quiche_config_set_initial_max_streams_bidi(ctx->cfg, QUIC_MAX_STREAMS);
@@ -1298,15 +1334,12 @@ static CURLcode cf_quiche_ctx_open(struct Curl_cfilter *cf,
     10 * QUIC_MAX_STREAMS * H3_STREAM_WINDOW_SIZE);
   quiche_config_set_max_stream_window(ctx->cfg, 10 * H3_STREAM_WINDOW_SIZE);
   quiche_config_set_application_protos(ctx->cfg,
-                                       (uint8_t *)
-                                       QUICHE_H3_APPLICATION_PROTOCOL,
+                       (uint8_t *)CURL_UNCONST(QUICHE_H3_APPLICATION_PROTOCOL),
                                        sizeof(QUICHE_H3_APPLICATION_PROTOCOL)
                                        - 1);
 
   result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer,
-                               QUICHE_H3_APPLICATION_PROTOCOL,
-                               sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1,
-                               NULL, NULL, cf, NULL);
+                               &ALPN_SPEC_H3, NULL, NULL, cf, NULL);
   if(result)
     return result;
 
@@ -1378,7 +1411,7 @@ static CURLcode cf_quiche_verify_peer(struct Curl_cfilter *cf,
 
 static CURLcode cf_quiche_connect(struct Curl_cfilter *cf,
                                   struct Curl_easy *data,
-                                  bool blocking, bool *done)
+                                  bool *done)
 {
   struct cf_quiche_ctx *ctx = cf->ctx;
   CURLcode result = CURLE_OK;
@@ -1390,7 +1423,7 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf,
 
   /* Connect the UDP filter first */
   if(!cf->next->connected) {
-    result = Curl_conn_cf_connect(cf->next, data, blocking, done);
+    result = Curl_conn_cf_connect(cf->next, data, done);
     if(result || !*done)
       return result;
   }
@@ -1419,7 +1452,7 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf,
   if(quiche_conn_is_established(ctx->qconn)) {
     ctx->handshake_at = ctx->q.last_op;
     CURL_TRC_CF(data, cf, "handshake complete after %dms",
-                (int)Curl_timediff(ctx->handshake_at, ctx->started_at));
+                (int)curlx_timediff(ctx->handshake_at, ctx->started_at));
     result = cf_quiche_verify_peer(cf, data);
     if(!result) {
       CURL_TRC_CF(data, cf, "peer verified");
@@ -1535,19 +1568,19 @@ static CURLcode cf_quiche_query(struct Curl_cfilter *cf,
 
   switch(query) {
   case CF_QUERY_MAX_CONCURRENT: {
-    curl_uint64_t max_streams = CONN_INUSE(cf->conn);
+    curl_uint64_t max_streams = CONN_ATTACHED(cf->conn);
     if(!ctx->goaway) {
       max_streams += quiche_conn_peer_streams_left_bidi(ctx->qconn);
     }
     *pres1 = (max_streams > INT_MAX) ? INT_MAX : (int)max_streams;
     CURL_TRC_CF(data, cf, "query conn[%" FMT_OFF_T "]: "
-                "MAX_CONCURRENT -> %d (%zu in use)",
-                cf->conn->connection_id, *pres1, CONN_INUSE(cf->conn));
+                "MAX_CONCURRENT -> %d (%u in use)",
+                cf->conn->connection_id, *pres1, CONN_ATTACHED(cf->conn));
     return CURLE_OK;
   }
   case CF_QUERY_CONNECT_REPLY_MS:
     if(ctx->q.got_first_byte) {
-      timediff_t ms = Curl_timediff(ctx->q.first_byte_at, ctx->started_at);
+      timediff_t ms = curlx_timediff(ctx->q.first_byte_at, ctx->started_at);
       *pres1 = (ms < INT_MAX) ? (int)ms : INT_MAX;
     }
     else
diff --git a/extra/curl/curl-8.12.1/lib/vquic/curl_quiche.h b/extra/curl/curl-8.14.1/lib/vquic/curl_quiche.h
similarity index 98%
rename from extra/curl/curl-8.12.1/lib/vquic/curl_quiche.h
rename to extra/curl/curl-8.14.1/lib/vquic/curl_quiche.h
index bce781c1bccc..9832687bdc19 100644
--- a/extra/curl/curl-8.12.1/lib/vquic/curl_quiche.h
+++ b/extra/curl/curl-8.14.1/lib/vquic/curl_quiche.h
@@ -24,7 +24,7 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #ifdef USE_QUICHE
 
diff --git a/extra/curl/curl-8.14.1/lib/vquic/vquic-tls.c b/extra/curl/curl-8.14.1/lib/vquic/vquic-tls.c
new file mode 100644
index 000000000000..2a5be138fc68
--- /dev/null
+++ b/extra/curl/curl-8.14.1/lib/vquic/vquic-tls.c
@@ -0,0 +1,200 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "../curl_setup.h"
+
+#if defined(USE_HTTP3) && \
+  (defined(USE_OPENSSL) || defined(USE_GNUTLS) || defined(USE_WOLFSSL))
+
+#ifdef USE_OPENSSL
+#include <openssl/err.h>
+#include "../vtls/openssl.h"
+#elif defined(USE_GNUTLS)
+#include <gnutls/abstract.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#include <gnutls/crypto.h>
+#include <nettle/sha2.h>
+#include "../vtls/gtls.h"
+#elif defined(USE_WOLFSSL)
+#include <wolfssl/options.h>
+#include <wolfssl/ssl.h>
+#include <wolfssl/quic.h>
+#include "../vtls/wolfssl.h"
+#endif
+
+#include "../urldata.h"
+#include "../curl_trc.h"
+#include "../cfilters.h"
+#include "../multiif.h"
+#include "../vtls/keylog.h"
+#include "../vtls/vtls.h"
+#include "../vtls/vtls_scache.h"
+#include "vquic-tls.h"
+
+/* The last 3 #include files should be in this order */
+#include "../curl_printf.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
+
+CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx,
+                             struct Curl_cfilter *cf,
+                             struct Curl_easy *data,
+                             struct ssl_peer *peer,
+                             const struct alpn_spec *alpns,
+                             Curl_vquic_tls_ctx_setup *cb_setup,
+                             void *cb_user_data, void *ssl_user_data,
+                             Curl_vquic_session_reuse_cb *session_reuse_cb)
+{
+  char tls_id[80];
+  CURLcode result;
+
+#ifdef USE_OPENSSL
+  Curl_ossl_version(tls_id, sizeof(tls_id));
+#elif defined(USE_GNUTLS)
+  Curl_gtls_version(tls_id, sizeof(tls_id));
+#elif defined(USE_WOLFSSL)
+  Curl_wssl_version(tls_id, sizeof(tls_id));
+#else
+#error "no TLS lib in used, should not happen"
+  return CURLE_FAILED_INIT;
+#endif
+  (void)session_reuse_cb;
+  result = Curl_ssl_peer_init(peer, cf, tls_id, TRNSPRT_QUIC);
+  if(result)
+    return result;
+
+#ifdef USE_OPENSSL
+  (void)result;
+  return Curl_ossl_ctx_init(&ctx->ossl, cf, data, peer, alpns,
+                            cb_setup, cb_user_data, NULL, ssl_user_data,
+                            session_reuse_cb);
+#elif defined(USE_GNUTLS)
+  return Curl_gtls_ctx_init(&ctx->gtls, cf, data, peer, alpns,
+                            cb_setup, cb_user_data, ssl_user_data,
+                            session_reuse_cb);
+#elif defined(USE_WOLFSSL)
+  return Curl_wssl_ctx_init(&ctx->wssl, cf, data, peer, alpns,
+                            cb_setup, cb_user_data,
+                            ssl_user_data, session_reuse_cb);
+#else
+#error "no TLS lib in used, should not happen"
+  return CURLE_FAILED_INIT;
+#endif
+}
+
+void Curl_vquic_tls_cleanup(struct curl_tls_ctx *ctx)
+{
+#ifdef USE_OPENSSL
+  if(ctx->ossl.ssl)
+    SSL_free(ctx->ossl.ssl);
+  if(ctx->ossl.ssl_ctx)
+    SSL_CTX_free(ctx->ossl.ssl_ctx);
+#elif defined(USE_GNUTLS)
+  if(ctx->gtls.session)
+    gnutls_deinit(ctx->gtls.session);
+  Curl_gtls_shared_creds_free(&ctx->gtls.shared_creds);
+#elif defined(USE_WOLFSSL)
+  if(ctx->wssl.ssl)
+    wolfSSL_free(ctx->wssl.ssl);
+  if(ctx->wssl.ssl_ctx)
+    wolfSSL_CTX_free(ctx->wssl.ssl_ctx);
+#endif
+  memset(ctx, 0, sizeof(*ctx));
+}
+
+CURLcode Curl_vquic_tls_before_recv(struct curl_tls_ctx *ctx,
+                                    struct Curl_cfilter *cf,
+                                    struct Curl_easy *data)
+{
+#ifdef USE_OPENSSL
+  if(!ctx->ossl.x509_store_setup) {
+    CURLcode result = Curl_ssl_setup_x509_store(cf, data, ctx->ossl.ssl_ctx);
+    if(result)
+      return result;
+    ctx->ossl.x509_store_setup = TRUE;
+  }
+#elif defined(USE_WOLFSSL)
+  if(!ctx->wssl.x509_store_setup) {
+    CURLcode result = Curl_wssl_setup_x509_store(cf, data, &ctx->wssl);
+    if(result)
+      return result;
+  }
+#elif defined(USE_GNUTLS)
+  if(!ctx->gtls.shared_creds->trust_setup) {
+    CURLcode result = Curl_gtls_client_trust_setup(cf, data, &ctx->gtls);
+    if(result)
+      return result;
+  }
+#else
+  (void)ctx; (void)cf; (void)data;
+#endif
+  return CURLE_OK;
+}
+
+CURLcode Curl_vquic_tls_verify_peer(struct curl_tls_ctx *ctx,
+                                    struct Curl_cfilter *cf,
+                                    struct Curl_easy *data,
+                                    struct ssl_peer *peer)
+{
+  struct ssl_primary_config *conn_config;
+  CURLcode result = CURLE_OK;
+
+  conn_config = Curl_ssl_cf_get_primary_config(cf);
+  if(!conn_config)
+    return CURLE_FAILED_INIT;
+
+#ifdef USE_OPENSSL
+  (void)conn_config;
+  result = Curl_oss_check_peer_cert(cf, data, &ctx->ossl, peer);
+#elif defined(USE_GNUTLS)
+  if(conn_config->verifyhost) {
+    result = Curl_gtls_verifyserver(data, ctx->gtls.session,
+                                    conn_config, &data->set.ssl, peer,
+                                    data->set.str[STRING_SSL_PINNEDPUBLICKEY]);
+    if(result)
+      return result;
+  }
+#elif defined(USE_WOLFSSL)
+  (void)data;
+  if(conn_config->verifyhost) {
+    char *snihost = peer->sni ? peer->sni : peer->hostname;
+    WOLFSSL_X509* cert = wolfSSL_get_peer_certificate(ctx->wssl.ssl);
+    if(wolfSSL_X509_check_host(cert, snihost, strlen(snihost), 0, NULL)
+          == WOLFSSL_FAILURE) {
+      result = CURLE_PEER_FAILED_VERIFICATION;
+    }
+    wolfSSL_X509_free(cert);
+  }
+  if(!result)
+    result = Curl_wssl_verify_pinned(cf, data, &ctx->wssl);
+#endif
+  /* on error, remove any session we might have in the pool */
+  if(result)
+    Curl_ssl_scache_remove_all(cf, data, peer->scache_key);
+  return result;
+}
+
+
+#endif /* !USE_HTTP3 && (USE_OPENSSL || USE_GNUTLS || USE_WOLFSSL) */
diff --git a/extra/curl/curl-8.12.1/lib/vquic/vquic-tls.h b/extra/curl/curl-8.14.1/lib/vquic/vquic-tls.h
similarity index 91%
rename from extra/curl/curl-8.12.1/lib/vquic/vquic-tls.h
rename to extra/curl/curl-8.14.1/lib/vquic/vquic-tls.h
index c0706b0eb835..bf29eecc9169 100644
--- a/extra/curl/curl-8.12.1/lib/vquic/vquic-tls.h
+++ b/extra/curl/curl-8.14.1/lib/vquic/vquic-tls.h
@@ -24,15 +24,16 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
-#include "bufq.h"
-#include "vtls/vtls.h"
-#include "vtls/openssl.h"
+#include "../curl_setup.h"
+#include "../bufq.h"
+#include "../vtls/vtls.h"
+#include "../vtls/vtls_int.h"
+#include "../vtls/openssl.h"
 
 #if defined(USE_HTTP3) && \
   (defined(USE_OPENSSL) || defined(USE_GNUTLS) || defined(USE_WOLFSSL))
 
-#include "vtls/wolfssl.h"
+#include "../vtls/wolfssl.h"
 
 struct ssl_peer;
 struct Curl_ssl_session;
@@ -43,7 +44,7 @@ struct curl_tls_ctx {
 #elif defined(USE_GNUTLS)
   struct gtls_ctx gtls;
 #elif defined(USE_WOLFSSL)
-  struct wolfssl_ctx wssl;
+  struct wssl_ctx wssl;
 #endif
 };
 
@@ -60,6 +61,7 @@ typedef CURLcode Curl_vquic_tls_ctx_setup(struct Curl_cfilter *cf,
 
 typedef CURLcode Curl_vquic_session_reuse_cb(struct Curl_cfilter *cf,
                                              struct Curl_easy *data,
+                                             struct alpn_spec *alpns,
                                              struct Curl_ssl_session *scs,
                                              bool *do_early_data);
 
@@ -70,9 +72,7 @@ typedef CURLcode Curl_vquic_session_reuse_cb(struct Curl_cfilter *cf,
  * @param cf          the connection filter involved
  * @param data        the transfer involved
  * @param peer        the peer that will be connected to
- * @param alpn        the ALPN string in protocol format ((len+bytes+)+),
- *                    may be NULL
- * @param alpn_len    the overall number of bytes in `alpn`
+ * @param alpns       the ALPN specifications to negotiate, may be NULL
  * @param cb_setup    optional callback for early TLS config
  * @param cb_user_data user_data param for callback
  * @param ssl_user_data  optional pointer to set in TLS application context
@@ -82,7 +82,7 @@ CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx,
                              struct Curl_cfilter *cf,
                              struct Curl_easy *data,
                              struct ssl_peer *peer,
-                             const char *alpn, size_t alpn_len,
+                             const struct alpn_spec *alpns,
                              Curl_vquic_tls_ctx_setup *cb_setup,
                              void *cb_user_data,
                              void *ssl_user_data,
diff --git a/extra/curl/curl-8.12.1/lib/vquic/vquic.c b/extra/curl/curl-8.14.1/lib/vquic/vquic.c
similarity index 90%
rename from extra/curl/curl-8.12.1/lib/vquic/vquic.c
rename to extra/curl/curl-8.14.1/lib/vquic/vquic.c
index 69de5c1a8f58..b5dc44f8aa90 100644
--- a/extra/curl/curl-8.12.1/lib/vquic/vquic.c
+++ b/extra/curl/curl-8.14.1/lib/vquic/vquic.c
@@ -22,7 +22,7 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #ifdef HAVE_NETINET_UDP_H
 #include <netinet/udp.h>
@@ -30,25 +30,26 @@
 #ifdef HAVE_FCNTL_H
 #include <fcntl.h>
 #endif
-#include "urldata.h"
-#include "bufq.h"
-#include "dynbuf.h"
-#include "cfilters.h"
-#include "curl_trc.h"
+#include "../urldata.h"
+#include "../bufq.h"
+#include "../curlx/dynbuf.h"
+#include "../cfilters.h"
+#include "../curl_trc.h"
 #include "curl_msh3.h"
 #include "curl_ngtcp2.h"
 #include "curl_osslq.h"
 #include "curl_quiche.h"
-#include "multiif.h"
-#include "rand.h"
+#include "../multiif.h"
+#include "../rand.h"
 #include "vquic.h"
 #include "vquic_int.h"
-#include "strerror.h"
+#include "../strerror.h"
+#include "../curlx/strparse.h"
 
 /* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_printf.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
 
 
 #ifdef USE_HTTP3
@@ -57,6 +58,16 @@
 #define NW_SEND_CHUNKS    2
 
 
+int Curl_vquic_init(void)
+{
+#if defined(USE_NGTCP2) && defined(OPENSSL_QUIC_API2)
+  if(ngtcp2_crypto_ossl_init())
+    return 0;
+#endif
+
+  return 1;
+}
+
 void Curl_quic_ver(char *p, size_t len)
 {
 #if defined(USE_NGTCP2) && defined(USE_NGHTTP3)
@@ -81,10 +92,10 @@ CURLcode vquic_ctx_init(struct cf_quic_ctx *qctx)
 #endif
 #ifdef DEBUGBUILD
   {
-    char *p = getenv("CURL_DBG_QUIC_WBLOCK");
+    const char *p = getenv("CURL_DBG_QUIC_WBLOCK");
     if(p) {
-      long l = strtol(p, NULL, 10);
-      if(l >= 0 && l <= 100)
+      curl_off_t l;
+      if(!curlx_str_number(&p, &l, 100))
         qctx->wblock_percent = (int)l;
     }
   }
@@ -101,7 +112,7 @@ void vquic_ctx_free(struct cf_quic_ctx *qctx)
 
 void vquic_ctx_update_time(struct cf_quic_ctx *qctx)
 {
-  qctx->last_op = Curl_now();
+  qctx->last_op = curlx_now();
 }
 
 static CURLcode send_packet_no_gso(struct Curl_cfilter *cf,
@@ -126,7 +137,7 @@ static CURLcode do_sendmsg(struct Curl_cfilter *cf,
 #endif
 
   *psent = 0;
-  msg_iov.iov_base = (uint8_t *)pkt;
+  msg_iov.iov_base = (uint8_t *)CURL_UNCONST(pkt);
   msg_iov.iov_len = pktlen;
   msg.msg_iov = &msg_iov;
   msg.msg_iovlen = 1;
@@ -147,17 +158,18 @@ static CURLcode do_sendmsg(struct Curl_cfilter *cf,
 #endif
 
 
-  while((sent = sendmsg(qctx->sockfd, &msg, 0)) == -1 && SOCKERRNO == EINTR)
+  while((sent = sendmsg(qctx->sockfd, &msg, 0)) == -1 &&
+        SOCKERRNO == SOCKEINTR)
     ;
 
   if(sent == -1) {
     switch(SOCKERRNO) {
     case EAGAIN:
-#if EAGAIN != EWOULDBLOCK
-    case EWOULDBLOCK:
+#if EAGAIN != SOCKEWOULDBLOCK
+    case SOCKEWOULDBLOCK:
 #endif
       return CURLE_AGAIN;
-    case EMSGSIZE:
+    case SOCKEMSGSIZE:
       /* UDP datagram is too large; caused by PMTUD. Just let it be lost. */
       break;
     case EIO:
@@ -185,16 +197,16 @@ static CURLcode do_sendmsg(struct Curl_cfilter *cf,
 
   while((sent = send(qctx->sockfd,
                      (const char *)pkt, (SEND_TYPE_ARG3)pktlen, 0)) == -1 &&
-        SOCKERRNO == EINTR)
+        SOCKERRNO == SOCKEINTR)
     ;
 
   if(sent == -1) {
-    if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
+    if(SOCKERRNO == EAGAIN || SOCKERRNO == SOCKEWOULDBLOCK) {
       return CURLE_AGAIN;
     }
     else {
       failf(data, "send() returned %zd (errno %d)", sent, SOCKERRNO);
-      if(SOCKERRNO != EMSGSIZE) {
+      if(SOCKERRNO != SOCKEMSGSIZE) {
         return CURLE_SEND_ERROR;
       }
       /* UDP datagram is too large; caused by PMTUD. Just let it be
@@ -377,7 +389,7 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf,
 
   total_nread = 0;
   while(pkts < max_pkts) {
-    n = (int)CURLMIN(MMSG_NUM, max_pkts);
+    n = (int)CURLMIN(CURLMIN(MMSG_NUM, IOV_MAX), max_pkts);
     memset(&mmsg, 0, sizeof(mmsg));
     for(i = 0; i < n; ++i) {
       msg_iov[i].iov_base = bufs[i];
@@ -391,14 +403,14 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf,
     }
 
     while((mcount = recvmmsg(qctx->sockfd, mmsg, n, 0, NULL)) == -1 &&
-          SOCKERRNO == EINTR)
+          SOCKERRNO == SOCKEINTR)
       ;
     if(mcount == -1) {
-      if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
+      if(SOCKERRNO == EAGAIN || SOCKERRNO == SOCKEWOULDBLOCK) {
         CURL_TRC_CF(data, cf, "ingress, recvmmsg -> EAGAIN");
         goto out;
       }
-      if(!cf->connected && SOCKERRNO == ECONNREFUSED) {
+      if(!cf->connected && SOCKERRNO == SOCKECONNREFUSED) {
         struct ip_quadruple ip;
         Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip);
         failf(data, "QUIC: connection to %s port %u refused",
@@ -469,27 +481,28 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf,
   size_t pktlen;
   size_t offset, to;
 
-  msg_iov.iov_base = buf;
-  msg_iov.iov_len = (int)sizeof(buf);
-
-  memset(&msg, 0, sizeof(msg));
-  msg.msg_iov = &msg_iov;
-  msg.msg_iovlen = 1;
-  msg.msg_control = msg_ctrl;
-
   DEBUGASSERT(max_pkts > 0);
   for(pkts = 0, total_nread = 0; pkts < max_pkts;) {
+    /* fully initialise this on each call to `recvmsg()`. There seem to
+     * operating systems out there that mess with `msg_iov.iov_len`. */
+    memset(&msg, 0, sizeof(msg));
+    msg_iov.iov_base = buf;
+    msg_iov.iov_len = (int)sizeof(buf);
+    msg.msg_iov = &msg_iov;
+    msg.msg_iovlen = 1;
+    msg.msg_control = msg_ctrl;
     msg.msg_name = &remote_addr;
     msg.msg_namelen = sizeof(remote_addr);
     msg.msg_controllen = sizeof(msg_ctrl);
+
     while((nread = recvmsg(qctx->sockfd, &msg, 0)) == -1 &&
-          SOCKERRNO == EINTR)
+          SOCKERRNO == SOCKEINTR)
       ;
     if(nread == -1) {
-      if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
+      if(SOCKERRNO == EAGAIN || SOCKERRNO == SOCKEWOULDBLOCK) {
         goto out;
       }
-      if(!cf->connected && SOCKERRNO == ECONNREFUSED) {
+      if(!cf->connected && SOCKERRNO == SOCKECONNREFUSED) {
         struct ip_quadruple ip;
         Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip);
         failf(data, "QUIC: connection to %s port %u refused",
@@ -557,14 +570,14 @@ static CURLcode recvfrom_packets(struct Curl_cfilter *cf,
     while((nread = recvfrom(qctx->sockfd, (char *)buf, bufsize, 0,
                             (struct sockaddr *)&remote_addr,
                             &remote_addrlen)) == -1 &&
-          SOCKERRNO == EINTR)
+          SOCKERRNO == SOCKEINTR)
       ;
     if(nread == -1) {
-      if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
+      if(SOCKERRNO == EAGAIN || SOCKERRNO == SOCKEWOULDBLOCK) {
         CURL_TRC_CF(data, cf, "ingress, recvfrom -> EAGAIN");
         goto out;
       }
-      if(!cf->connected && SOCKERRNO == ECONNREFUSED) {
+      if(!cf->connected && SOCKERRNO == SOCKECONNREFUSED) {
         struct ip_quadruple ip;
         Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip);
         failf(data, "QUIC: connection to %s port %u refused",
@@ -638,25 +651,25 @@ CURLcode Curl_qlogdir(struct Curl_easy *data,
     struct dynbuf fname;
     CURLcode result;
     unsigned int i;
-    Curl_dyn_init(&fname, DYN_QLOG_NAME);
-    result = Curl_dyn_add(&fname, qlog_dir);
+    curlx_dyn_init(&fname, DYN_QLOG_NAME);
+    result = curlx_dyn_add(&fname, qlog_dir);
     if(!result)
-      result = Curl_dyn_add(&fname, "/");
+      result = curlx_dyn_add(&fname, "/");
     for(i = 0; (i < scidlen) && !result; i++) {
       char hex[3];
       msnprintf(hex, 3, "%02x", scid[i]);
-      result = Curl_dyn_add(&fname, hex);
+      result = curlx_dyn_add(&fname, hex);
     }
     if(!result)
-      result = Curl_dyn_add(&fname, ".sqlog");
+      result = curlx_dyn_add(&fname, ".sqlog");
 
     if(!result) {
-      int qlogfd = open(Curl_dyn_ptr(&fname), O_WRONLY|O_CREAT|CURL_O_BINARY,
+      int qlogfd = open(curlx_dyn_ptr(&fname), O_WRONLY|O_CREAT|CURL_O_BINARY,
                         data->set.new_file_perms);
       if(qlogfd != -1)
         *qlogfdp = qlogfd;
     }
-    Curl_dyn_free(&fname);
+    curlx_dyn_free(&fname);
     if(result)
       return result;
   }
diff --git a/extra/curl/curl-8.12.1/lib/vquic/vquic.h b/extra/curl/curl-8.14.1/lib/vquic/vquic.h
similarity index 95%
rename from extra/curl/curl-8.12.1/lib/vquic/vquic.h
rename to extra/curl/curl-8.14.1/lib/vquic/vquic.h
index 1cd3e258f42b..dbf63b1f6fe0 100644
--- a/extra/curl/curl-8.12.1/lib/vquic/vquic.h
+++ b/extra/curl/curl-8.14.1/lib/vquic/vquic.h
@@ -24,7 +24,7 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #ifdef USE_HTTP3
 struct Curl_cfilter;
@@ -33,6 +33,7 @@ struct connectdata;
 struct Curl_addrinfo;
 
 void Curl_quic_ver(char *p, size_t len);
+int Curl_vquic_init(void);
 
 CURLcode Curl_qlogdir(struct Curl_easy *data,
                       unsigned char *scid,
@@ -48,6 +49,8 @@ CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf,
 
 extern struct Curl_cftype Curl_cft_http3;
 
+#else
+#define Curl_vquic_init() 1
 #endif /* !USE_HTTP3 */
 
 CURLcode Curl_conn_may_http3(struct Curl_easy *data,
diff --git a/extra/curl/curl-8.12.1/lib/vquic/vquic_int.h b/extra/curl/curl-8.14.1/lib/vquic/vquic_int.h
similarity index 95%
rename from extra/curl/curl-8.12.1/lib/vquic/vquic_int.h
rename to extra/curl/curl-8.14.1/lib/vquic/vquic_int.h
index 754e1f5910fb..4641c3125b01 100644
--- a/extra/curl/curl-8.12.1/lib/vquic/vquic_int.h
+++ b/extra/curl/curl-8.14.1/lib/vquic/vquic_int.h
@@ -24,15 +24,13 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
-#include "bufq.h"
+#include "../curl_setup.h"
+#include "../bufq.h"
 
 #ifdef USE_HTTP3
 
 #define MAX_PKT_BURST 10
 #define MAX_UDP_PAYLOAD_SIZE  1452
-/* Default QUIC connection timeout we announce from our side */
-#define CURL_QUIC_MAX_IDLE_MS   (120 * 1000)
 
 struct cf_quic_ctx {
   curl_socket_t sockfd; /* connected UDP socket */
@@ -53,6 +51,9 @@ struct cf_quic_ctx {
   BIT(no_gso); /* do not use gso on sending */
 };
 
+#define H3_STREAM_CTX(ctx,data)                                         \
+  (data ? Curl_uint_hash_get(&(ctx)->streams, (data)->mid) : NULL)
+
 CURLcode vquic_ctx_init(struct cf_quic_ctx *qctx);
 void vquic_ctx_free(struct cf_quic_ctx *qctx);
 
diff --git a/extra/curl/curl-8.12.1/lib/vquic/.checksrc b/extra/curl/curl-8.14.1/lib/vssh/.checksrc
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/vquic/.checksrc
rename to extra/curl/curl-8.14.1/lib/vssh/.checksrc
diff --git a/extra/curl/curl-8.12.1/lib/vssh/curl_path.c b/extra/curl/curl-8.14.1/lib/vssh/curl_path.c
similarity index 86%
rename from extra/curl/curl-8.12.1/lib/vssh/curl_path.c
rename to extra/curl/curl-8.14.1/lib/vssh/curl_path.c
index 61452a42527c..117d2e6009f6 100644
--- a/extra/curl/curl-8.12.1/lib/vssh/curl_path.c
+++ b/extra/curl/curl-8.14.1/lib/vssh/curl_path.c
@@ -22,15 +22,15 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #if defined(USE_SSH)
 
 #include "curl_path.h"
 #include <curl/curl.h>
-#include "curl_memory.h"
-#include "escape.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../escape.h"
+#include "../memdebug.h"
 
 #define MAX_SSHPATH_LEN 100000 /* arbitrary */
 
@@ -50,13 +50,13 @@ CURLcode Curl_getworkingpath(struct Curl_easy *data,
     return result;
 
   /* new path to switch to in case we need to */
-  Curl_dyn_init(&npath, MAX_SSHPATH_LEN);
+  curlx_dyn_init(&npath, MAX_SSHPATH_LEN);
 
   /* Check for /~/, indicating relative to the user's home directory */
   if((data->conn->handler->protocol & CURLPROTO_SCP) &&
      (working_path_len > 3) && (!memcmp(working_path, "/~/", 3))) {
     /* It is referenced to the home directory, so strip the leading '/~/' */
-    if(Curl_dyn_addn(&npath, &working_path[3], working_path_len - 3)) {
+    if(curlx_dyn_addn(&npath, &working_path[3], working_path_len - 3)) {
       free(working_path);
       return CURLE_OUT_OF_MEMORY;
     }
@@ -64,7 +64,7 @@ CURLcode Curl_getworkingpath(struct Curl_easy *data,
   else if((data->conn->handler->protocol & CURLPROTO_SFTP) &&
           (!strcmp("/~", working_path) ||
            ((working_path_len > 2) && !memcmp(working_path, "/~/", 3)))) {
-    if(Curl_dyn_add(&npath, homedir)) {
+    if(curlx_dyn_add(&npath, homedir)) {
       free(working_path);
       return CURLE_OUT_OF_MEMORY;
     }
@@ -73,24 +73,24 @@ CURLcode Curl_getworkingpath(struct Curl_easy *data,
       const char *p;
       int copyfrom = 3;
       /* Copy a separating '/' if homedir does not end with one */
-      len = Curl_dyn_len(&npath);
-      p = Curl_dyn_ptr(&npath);
+      len = curlx_dyn_len(&npath);
+      p = curlx_dyn_ptr(&npath);
       if(len && (p[len-1] != '/'))
         copyfrom = 2;
 
-      if(Curl_dyn_addn(&npath,
-                       &working_path[copyfrom], working_path_len - copyfrom)) {
+      if(curlx_dyn_addn(&npath, &working_path[copyfrom],
+                        working_path_len - copyfrom)) {
         free(working_path);
         return CURLE_OUT_OF_MEMORY;
       }
     }
   }
 
-  if(Curl_dyn_len(&npath)) {
+  if(curlx_dyn_len(&npath)) {
     free(working_path);
 
     /* store the pointer for the caller to receive */
-    *path = Curl_dyn_ptr(&npath);
+    *path = curlx_dyn_ptr(&npath);
   }
   else
     *path = working_path;
@@ -133,7 +133,7 @@ CURLcode Curl_get_pathname(const char **cpp, char **path, const char *homedir)
   if(!*cp || !homedir)
     return CURLE_QUOTE_ERROR;
 
-  Curl_dyn_init(&out, MAX_PATHLENGTH);
+  curlx_dyn_init(&out, MAX_PATHLENGTH);
 
   /* Ignore leading whitespace */
   cp += strspn(cp, WHITESPACE);
@@ -158,12 +158,12 @@ CURLcode Curl_get_pathname(const char **cpp, char **path, const char *homedir)
           goto fail;
         }
       }
-      result = Curl_dyn_addn(&out, &cp[i], 1);
+      result = curlx_dyn_addn(&out, &cp[i], 1);
       if(result)
         return result;
     }
 
-    if(!Curl_dyn_len(&out))
+    if(!curlx_dyn_len(&out))
       goto fail;
 
     /* return pointer to second parameter if it exists */
@@ -180,23 +180,23 @@ CURLcode Curl_get_pathname(const char **cpp, char **path, const char *homedir)
 
     /* Handling for relative path - prepend home directory */
     if(cp[0] == '/' && cp[1] == '~' && cp[2] == '/') {
-      result = Curl_dyn_add(&out, homedir);
+      result = curlx_dyn_add(&out, homedir);
       if(!result)
-        result = Curl_dyn_addn(&out, "/", 1);
+        result = curlx_dyn_addn(&out, "/", 1);
       if(result)
         return result;
       cp += 3;
     }
     /* Copy path name up until first "whitespace" */
-    result = Curl_dyn_addn(&out, cp, (end - cp));
+    result = curlx_dyn_addn(&out, cp, (end - cp));
     if(result)
       return result;
   }
-  *path = Curl_dyn_ptr(&out);
+  *path = curlx_dyn_ptr(&out);
   return CURLE_OK;
 
 fail:
-  Curl_dyn_free(&out);
+  curlx_dyn_free(&out);
   return CURLE_QUOTE_ERROR;
 }
 
diff --git a/extra/curl/curl-8.12.1/lib/vssh/curl_path.h b/extra/curl/curl-8.14.1/lib/vssh/curl_path.h
similarity index 96%
rename from extra/curl/curl-8.12.1/lib/vssh/curl_path.h
rename to extra/curl/curl-8.14.1/lib/vssh/curl_path.h
index 8e984174d79c..1c167f96603a 100644
--- a/extra/curl/curl-8.12.1/lib/vssh/curl_path.h
+++ b/extra/curl/curl-8.14.1/lib/vssh/curl_path.h
@@ -24,9 +24,9 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 #include <curl/curl.h>
-#include "urldata.h"
+#include "../urldata.h"
 
 CURLcode Curl_getworkingpath(struct Curl_easy *data,
                              char *homedir,
diff --git a/extra/curl/curl-8.12.1/lib/vssh/libssh.c b/extra/curl/curl-8.14.1/lib/vssh/libssh.c
similarity index 66%
rename from extra/curl/curl-8.12.1/lib/vssh/libssh.c
rename to extra/curl/curl-8.14.1/lib/vssh/libssh.c
index 2390967d9121..86f1e1a568bf 100644
--- a/extra/curl/curl-8.12.1/lib/vssh/libssh.c
+++ b/extra/curl/curl-8.14.1/lib/vssh/libssh.c
@@ -25,7 +25,7 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #ifdef USE_LIBSSH
 
@@ -46,29 +46,29 @@
 #endif
 
 #include <curl/curl.h>
-#include "urldata.h"
-#include "sendf.h"
-#include "hostip.h"
-#include "progress.h"
-#include "transfer.h"
-#include "escape.h"
-#include "http.h"               /* for HTTP proxy tunnel stuff */
+#include "../urldata.h"
+#include "../sendf.h"
+#include "../hostip.h"
+#include "../progress.h"
+#include "../transfer.h"
+#include "../escape.h"
+#include "../http.h"               /* for HTTP proxy tunnel stuff */
 #include "ssh.h"
-#include "url.h"
-#include "speedcheck.h"
-#include "getinfo.h"
-#include "strdup.h"
-#include "strcase.h"
-#include "vtls/vtls.h"
-#include "cfilters.h"
-#include "connect.h"
-#include "inet_ntop.h"
-#include "parsedate.h"          /* for the week day and month names */
-#include "sockaddr.h"           /* required for Curl_sockaddr_storage */
-#include "strtoofft.h"
-#include "multiif.h"
-#include "select.h"
-#include "warnless.h"
+#include "../url.h"
+#include "../speedcheck.h"
+#include "../getinfo.h"
+#include "../strdup.h"
+#include "../strcase.h"
+#include "../vtls/vtls.h"
+#include "../cfilters.h"
+#include "../connect.h"
+#include "../inet_ntop.h"
+#include "../parsedate.h"          /* for the week day and month names */
+#include "../sockaddr.h"           /* required for Curl_sockaddr_storage */
+#include "../curlx/strparse.h"
+#include "../multiif.h"
+#include "../select.h"
+#include "../curlx/warnless.h"
 #include "curl_path.h"
 
 #ifdef HAVE_SYS_STAT_H
@@ -82,9 +82,9 @@
 #endif
 
 /* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_printf.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
 
 /* A recent macro provided by libssh. Or make our own. */
 #ifndef SSH_STRING_FREE_CHAR
@@ -130,14 +130,19 @@ CURLcode sftp_perform(struct Curl_easy *data,
                       bool *connected,
                       bool *dophase_done);
 
-static void sftp_quote(struct Curl_easy *data);
-static void sftp_quote_stat(struct Curl_easy *data);
+static void sftp_quote(struct Curl_easy *data,
+                       struct ssh_conn *sshc,
+                       struct SSHPROTO *sshp);
+static void sftp_quote_stat(struct Curl_easy *data, struct ssh_conn *sshc);
 static int myssh_getsock(struct Curl_easy *data,
                          struct connectdata *conn, curl_socket_t *sock);
-static void myssh_block2waitfor(struct connectdata *conn, bool block);
+static void myssh_block2waitfor(struct connectdata *conn,
+                                struct ssh_conn *sshc,
+                                bool block);
 
 static CURLcode myssh_setup_connection(struct Curl_easy *data,
                                        struct connectdata *conn);
+static void sshc_cleanup(struct ssh_conn *sshc);
 
 /*
  * SCP protocol handler.
@@ -223,23 +228,23 @@ static CURLcode sftp_error_to_CURLE(int err)
 }
 
 #ifndef DEBUGBUILD
-#define state(x,y) mystate(x,y)
+#define myssh_state(x,y,z) myssh_set_state(x,y,z)
 #else
-#define state(x,y) mystate(x,y, __LINE__)
+#define myssh_state(x,y,z) myssh_set_state(x,y,z, __LINE__)
 #endif
 
 /*
  * SSH State machine related code
  */
 /* This is the ONLY way to change SSH state! */
-static void mystate(struct Curl_easy *data, sshstate nowstate
+static void myssh_set_state(struct Curl_easy *data,
+                            struct ssh_conn *sshc,
+                            sshstate nowstate
 #ifdef DEBUGBUILD
-                    , int lineno
+                          , int lineno
 #endif
   )
 {
-  struct connectdata *conn = data->conn;
-  struct ssh_conn *sshc = &conn->proto.sshc;
 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
   /* for debug purposes */
   static const char *const names[] = {
@@ -312,7 +317,7 @@ static void mystate(struct Curl_easy *data, sshstate nowstate
           lineno);
   }
 #endif
-
+  (void)data;
   sshc->state = nowstate;
 }
 
@@ -326,11 +331,9 @@ static void mystate(struct Curl_easy *data, sshstate nowstate
  *
  * Returns SSH_OK or SSH_ERROR.
  */
-static int myssh_is_known(struct Curl_easy *data)
+static int myssh_is_known(struct Curl_easy *data, struct ssh_conn *sshc)
 {
   int rc;
-  struct connectdata *conn = data->conn;
-  struct ssh_conn *sshc = &conn->proto.sshc;
   ssh_key pubkey;
   size_t hlen;
   unsigned char *hash = NULL;
@@ -516,14 +519,14 @@ static int myssh_is_known(struct Curl_easy *data)
   return rc;
 }
 
-#define MOVE_TO_ERROR_STATE(_r) do {            \
-    state(data, SSH_SESSION_DISCONNECT);        \
-    sshc->actualcode = _r;                      \
-    rc = SSH_ERROR;                             \
+#define MOVE_TO_ERROR_STATE(_r) do {                      \
+    myssh_state(data, sshc, SSH_SESSION_DISCONNECT);      \
+    sshc->actualcode = _r;                                \
+    rc = SSH_ERROR;                                       \
   } while(0)
 
 #define MOVE_TO_SFTP_CLOSE_STATE() do {                         \
-    state(data, SSH_SFTP_CLOSE);                                \
+    myssh_state(data, sshc, SSH_SFTP_CLOSE);                    \
     sshc->actualcode =                                          \
       sftp_error_to_CURLE(sftp_get_error(sshc->sftp_session));  \
     rc = SSH_ERROR;                                             \
@@ -532,7 +535,7 @@ static int myssh_is_known(struct Curl_easy *data)
 #define MOVE_TO_PASSWD_AUTH do {                        \
     if(sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD) { \
       rc = SSH_OK;                                      \
-      state(data, SSH_AUTH_PASS_INIT);                  \
+      myssh_state(data, sshc, SSH_AUTH_PASS_INIT);      \
     }                                                   \
     else {                                              \
       MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);          \
@@ -542,7 +545,7 @@ static int myssh_is_known(struct Curl_easy *data)
 #define MOVE_TO_KEY_AUTH do {                                   \
     if(sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE) {      \
       rc = SSH_OK;                                              \
-      state(data, SSH_AUTH_KEY_INIT);                           \
+      myssh_state(data, sshc, SSH_AUTH_KEY_INIT);               \
     }                                                           \
     else {                                                      \
       MOVE_TO_PASSWD_AUTH;                                      \
@@ -552,7 +555,7 @@ static int myssh_is_known(struct Curl_easy *data)
 #define MOVE_TO_GSSAPI_AUTH do {                                \
     if(sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC) {       \
       rc = SSH_OK;                                              \
-      state(data, SSH_AUTH_GSSAPI);                             \
+      myssh_state(data, sshc, SSH_AUTH_GSSAPI);                 \
     }                                                           \
     else {                                                      \
       MOVE_TO_KEY_AUTH;                                         \
@@ -560,10 +563,10 @@ static int myssh_is_known(struct Curl_easy *data)
   } while(0)
 
 static
-int myssh_auth_interactive(struct connectdata *conn)
+int myssh_auth_interactive(struct connectdata *conn,
+                           struct ssh_conn *sshc)
 {
   int rc;
-  struct ssh_conn *sshc = &conn->proto.sshc;
   int nprompts;
 
 restart:
@@ -624,178 +627,493 @@ int myssh_auth_interactive(struct connectdata *conn)
   return rc;
 }
 
-/*
- * ssh_statemach_act() runs the SSH state machine as far as it can without
- * blocking and without reaching the end. The data the pointer 'block' points
- * to will be set to TRUE if the libssh function returns SSH_AGAIN
- * meaning it wants to be called again when the socket is ready
- */
-static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
+static void myssh_state_init(struct Curl_easy *data,
+                             struct ssh_conn *sshc)
 {
-  CURLcode result = CURLE_OK;
-  struct connectdata *conn = data->conn;
-  struct SSHPROTO *protop = data->req.p.ssh;
-  struct ssh_conn *sshc = &conn->proto.sshc;
-  curl_socket_t sock = conn->sock[FIRSTSOCKET];
-  int rc = SSH_NO_ERROR, err;
-  int seekerr = CURL_SEEKFUNC_OK;
-  const char *err_msg;
-  *block = 0;                   /* we are not blocking by default */
-
-  do {
-
-    switch(sshc->state) {
-    case SSH_INIT:
-      sshc->secondCreateDirs = 0;
-      sshc->nextstate = SSH_NO_STATE;
-      sshc->actualcode = CURLE_OK;
+  sshc->secondCreateDirs = 0;
+  sshc->nextstate = SSH_NO_STATE;
+  sshc->actualcode = CURLE_OK;
 
 #if 0
-      ssh_set_log_level(SSH_LOG_PROTOCOL);
+  ssh_set_log_level(SSH_LOG_PROTOCOL);
 #endif
 
-      /* Set libssh to non-blocking, since everything internally is
-         non-blocking */
-      ssh_set_blocking(sshc->ssh_session, 0);
+  /* Set libssh to non-blocking, since everything internally is
+     non-blocking */
+  ssh_set_blocking(sshc->ssh_session, 0);
 
-      state(data, SSH_S_STARTUP);
-      FALLTHROUGH();
+  myssh_state(data, sshc, SSH_S_STARTUP);
+}
 
-    case SSH_S_STARTUP:
-      rc = ssh_connect(sshc->ssh_session);
+static int myssh_state_startup(struct Curl_easy *data,
+                               struct ssh_conn *sshc)
+{
+  struct connectdata *conn = data->conn;
+  int rc = ssh_connect(sshc->ssh_session);
 
-      myssh_block2waitfor(conn, (rc == SSH_AGAIN));
-      if(rc == SSH_AGAIN) {
-        DEBUGF(infof(data, "ssh_connect -> EAGAIN"));
-        break;
-      }
+  myssh_block2waitfor(conn, sshc, (rc == SSH_AGAIN));
+  if(rc == SSH_AGAIN) {
+    DEBUGF(infof(data, "ssh_connect -> EAGAIN"));
+  }
+  else if(rc != SSH_OK) {
+    failf(data, "Failure establishing ssh session");
+    MOVE_TO_ERROR_STATE(CURLE_FAILED_INIT);
+  }
+  else
+    myssh_state(data, sshc, SSH_HOSTKEY);
 
-      if(rc != SSH_OK) {
-        failf(data, "Failure establishing ssh session");
-        MOVE_TO_ERROR_STATE(CURLE_FAILED_INIT);
-        break;
-      }
+  return rc;
+}
 
-      state(data, SSH_HOSTKEY);
+static int myssh_state_authlist(struct Curl_easy *data,
+                                struct ssh_conn *sshc)
+{
+  int rc;
+  sshc->authed = FALSE;
 
-      FALLTHROUGH();
-    case SSH_HOSTKEY:
+  rc = ssh_userauth_none(sshc->ssh_session, NULL);
+  if(rc == SSH_AUTH_AGAIN)
+    return SSH_AGAIN;
+
+  if(rc == SSH_AUTH_SUCCESS) {
+    sshc->authed = TRUE;
+    infof(data, "Authenticated with none");
+    myssh_state(data, sshc, SSH_AUTH_DONE);
+    return rc;
+  }
+  else if(rc == SSH_AUTH_ERROR) {
+    MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
+    return rc;
+  }
+
+  sshc->auth_methods =
+    (unsigned int)ssh_userauth_list(sshc->ssh_session, NULL);
+  if(sshc->auth_methods)
+    infof(data, "SSH authentication methods available: %s%s%s%s",
+          sshc->auth_methods & SSH_AUTH_METHOD_PUBLICKEY ?
+          "public key, ": "",
+          sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC ?
+          "GSSAPI, " : "",
+          sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE ?
+          "keyboard-interactive, " : "",
+          sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD ?
+          "password": "");
+  if(sshc->auth_methods & SSH_AUTH_METHOD_PUBLICKEY) {
+    myssh_state(data, sshc, SSH_AUTH_PKEY_INIT);
+    infof(data, "Authentication using SSH public key file");
+  }
+  else if(sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC) {
+    myssh_state(data, sshc, SSH_AUTH_GSSAPI);
+  }
+  else if(sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE) {
+    myssh_state(data, sshc, SSH_AUTH_KEY_INIT);
+  }
+  else if(sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD) {
+    myssh_state(data, sshc, SSH_AUTH_PASS_INIT);
+  }
+  else {                  /* unsupported authentication method */
+    MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
+  }
+  return rc;
+}
+
+static int myssh_state_auth_pkey_init(struct Curl_easy *data,
+                                      struct ssh_conn *sshc)
+{
+  int rc;
+  if(!(data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY)) {
+    MOVE_TO_GSSAPI_AUTH;
+    return 0;
+  }
+
+  /* Two choices, (1) private key was given on CMD,
+   * (2) use the "default" keys. */
+  if(data->set.str[STRING_SSH_PRIVATE_KEY]) {
+    if(sshc->pubkey && !data->set.ssl.key_passwd) {
+      rc = ssh_userauth_try_publickey(sshc->ssh_session, NULL,
+                                      sshc->pubkey);
+      if(rc == SSH_AUTH_AGAIN)
+        return SSH_AGAIN;
 
-      rc = myssh_is_known(data);
       if(rc != SSH_OK) {
-        MOVE_TO_ERROR_STATE(CURLE_PEER_FAILED_VERIFICATION);
-        break;
+        MOVE_TO_GSSAPI_AUTH;
+        return rc;
       }
+    }
 
-      state(data, SSH_AUTHLIST);
-      FALLTHROUGH();
-    case SSH_AUTHLIST:{
-        sshc->authed = FALSE;
+    rc = ssh_pki_import_privkey_file(data->
+                                     set.str[STRING_SSH_PRIVATE_KEY],
+                                     data->set.ssl.key_passwd, NULL,
+                                     NULL, &sshc->privkey);
+    if(rc != SSH_OK) {
+      failf(data, "Could not load private key file %s",
+            data->set.str[STRING_SSH_PRIVATE_KEY]);
+      MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
+      return rc;
+    }
 
-        rc = ssh_userauth_none(sshc->ssh_session, NULL);
-        if(rc == SSH_AUTH_AGAIN) {
-          rc = SSH_AGAIN;
-          break;
-        }
+    myssh_state(data, sshc, SSH_AUTH_PKEY);
+  }
+  else {
+    rc = ssh_userauth_publickey_auto(sshc->ssh_session, NULL,
+                                         data->set.ssl.key_passwd);
+    if(rc == SSH_AUTH_AGAIN)
+      return SSH_AGAIN;
+
+    if(rc == SSH_AUTH_SUCCESS) {
+      rc = SSH_OK;
+      sshc->authed = TRUE;
+      infof(data, "Completed public key authentication");
+      myssh_state(data, sshc, SSH_AUTH_DONE);
+      return rc;
+    }
 
-        if(rc == SSH_AUTH_SUCCESS) {
-          sshc->authed = TRUE;
-          infof(data, "Authenticated with none");
-          state(data, SSH_AUTH_DONE);
-          break;
-        }
-        else if(rc == SSH_AUTH_ERROR) {
-          MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
-          break;
-        }
+    MOVE_TO_GSSAPI_AUTH;
+  }
+  return rc;
+}
 
-        sshc->auth_methods =
-          (unsigned int)ssh_userauth_list(sshc->ssh_session, NULL);
-        if(sshc->auth_methods)
-          infof(data, "SSH authentication methods available: %s%s%s%s",
-                sshc->auth_methods & SSH_AUTH_METHOD_PUBLICKEY ?
-                "public key, ": "",
-                sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC ?
-                "GSSAPI, " : "",
-                sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE ?
-                "keyboard-interactive, " : "",
-                sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD ?
-                "password": "");
-        if(sshc->auth_methods & SSH_AUTH_METHOD_PUBLICKEY) {
-          state(data, SSH_AUTH_PKEY_INIT);
-          infof(data, "Authentication using SSH public key file");
-        }
-        else if(sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC) {
-          state(data, SSH_AUTH_GSSAPI);
-        }
-        else if(sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE) {
-          state(data, SSH_AUTH_KEY_INIT);
-        }
-        else if(sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD) {
-          state(data, SSH_AUTH_PASS_INIT);
-        }
-        else {                  /* unsupported authentication method */
-          MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
-          break;
+static int myssh_state_upload_init(struct Curl_easy *data,
+                                   struct ssh_conn *sshc,
+                                   struct SSHPROTO *sshp)
+{
+  int flags;
+  int rc = 0;
+
+  if(data->state.resume_from) {
+    sftp_attributes attrs;
+
+    if(data->state.resume_from < 0) {
+      attrs = sftp_stat(sshc->sftp_session, sshp->path);
+      if(attrs) {
+        curl_off_t size = attrs->size;
+        if(size < 0) {
+          failf(data, "Bad file size (%" FMT_OFF_T ")", size);
+          MOVE_TO_ERROR_STATE(CURLE_BAD_DOWNLOAD_RESUME);
+          return rc;
         }
+        data->state.resume_from = attrs->size;
 
-        break;
+        sftp_attributes_free(attrs);
       }
-    case SSH_AUTH_PKEY_INIT:
-      if(!(data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY)) {
-        MOVE_TO_GSSAPI_AUTH;
-        break;
+      else {
+        data->state.resume_from = 0;
       }
+    }
+  }
 
-      /* Two choices, (1) private key was given on CMD,
-       * (2) use the "default" keys. */
-      if(data->set.str[STRING_SSH_PRIVATE_KEY]) {
-        if(sshc->pubkey && !data->set.ssl.key_passwd) {
-          rc = ssh_userauth_try_publickey(sshc->ssh_session, NULL,
-                                          sshc->pubkey);
-          if(rc == SSH_AUTH_AGAIN) {
-            rc = SSH_AGAIN;
-            break;
-          }
+  if(data->set.remote_append)
+    /* Try to open for append, but create if nonexisting */
+    flags = O_WRONLY|O_CREAT|O_APPEND;
+  else if(data->state.resume_from > 0)
+    /* If we have restart position then open for append */
+    flags = O_WRONLY|O_APPEND;
+  else
+    /* Clear file before writing (normal behavior) */
+    flags = O_WRONLY|O_CREAT|O_TRUNC;
+
+  if(sshc->sftp_file)
+    sftp_close(sshc->sftp_file);
+  sshc->sftp_file =
+    sftp_open(sshc->sftp_session, sshp->path,
+              flags, (mode_t)data->set.new_file_perms);
+  if(!sshc->sftp_file) {
+    int err = sftp_get_error(sshc->sftp_session);
+
+    if(((err == SSH_FX_NO_SUCH_FILE || err == SSH_FX_FAILURE ||
+         err == SSH_FX_NO_SUCH_PATH)) &&
+       (data->set.ftp_create_missing_dirs &&
+        (strlen(sshp->path) > 1))) {
+      /* try to create the path remotely */
+      rc = 0;
+      sshc->secondCreateDirs = 1;
+      myssh_state(data, sshc, SSH_SFTP_CREATE_DIRS_INIT);
+      return rc;
+    }
+    else {
+      MOVE_TO_SFTP_CLOSE_STATE();
+      return rc;
+    }
+  }
 
-          if(rc != SSH_OK) {
-            MOVE_TO_GSSAPI_AUTH;
-            break;
-          }
-        }
+  /* If we have a restart point then we need to seek to the correct
+     position. */
+  if(data->state.resume_from > 0) {
+    int seekerr = CURL_SEEKFUNC_OK;
+    /* Let's read off the proper amount of bytes from the input. */
+    if(data->set.seek_func) {
+      Curl_set_in_callback(data, TRUE);
+      seekerr = data->set.seek_func(data->set.seek_client,
+                                    data->state.resume_from, SEEK_SET);
+      Curl_set_in_callback(data, FALSE);
+    }
 
-        rc = ssh_pki_import_privkey_file(data->
-                                         set.str[STRING_SSH_PRIVATE_KEY],
-                                         data->set.ssl.key_passwd, NULL,
-                                         NULL, &sshc->privkey);
-        if(rc != SSH_OK) {
-          failf(data, "Could not load private key file %s",
-                data->set.str[STRING_SSH_PRIVATE_KEY]);
-          MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
-          break;
+    if(seekerr != CURL_SEEKFUNC_OK) {
+      curl_off_t passed = 0;
+
+      if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
+        failf(data, "Could not seek stream");
+        MOVE_TO_ERROR_STATE(CURLE_FTP_COULDNT_USE_REST);
+        return rc;
+      }
+      /* seekerr == CURL_SEEKFUNC_CANTSEEK (cannot seek to offset) */
+      do {
+        char scratch[4*1024];
+        size_t readthisamountnow =
+          (data->state.resume_from - passed >
+           (curl_off_t)sizeof(scratch)) ?
+          sizeof(scratch) : curlx_sotouz(data->state.resume_from - passed);
+
+        size_t actuallyread =
+          data->state.fread_func(scratch, 1,
+                                 readthisamountnow, data->state.in);
+
+        passed += actuallyread;
+        if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
+          /* this checks for greater-than only to make sure that the
+             CURL_READFUNC_ABORT return code still aborts */
+          failf(data, "Failed to read data");
+          MOVE_TO_ERROR_STATE(CURLE_FTP_COULDNT_USE_REST);
+          return rc;
         }
+      } while(passed < data->state.resume_from);
+    }
 
-        state(data, SSH_AUTH_PKEY);
-        break;
+    /* now, decrease the size of the read */
+    if(data->state.infilesize > 0) {
+      data->state.infilesize -= data->state.resume_from;
+      data->req.size = data->state.infilesize;
+      Curl_pgrsSetUploadSize(data, data->state.infilesize);
+    }
+
+    rc = sftp_seek64(sshc->sftp_file, data->state.resume_from);
+    if(rc) {
+      MOVE_TO_SFTP_CLOSE_STATE();
+      return rc;
+    }
+  }
+  if(data->state.infilesize > 0) {
+    data->req.size = data->state.infilesize;
+    Curl_pgrsSetUploadSize(data, data->state.infilesize);
+  }
+  /* upload data */
+  Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE);
+
+  /* not set by Curl_xfer_setup to preserve keepon bits */
+  data->conn->sockfd = data->conn->writesockfd;
+
+  /* store this original bitmask setup to use later on if we cannot
+     figure out a "real" bitmask */
+  sshc->orig_waitfor = data->req.keepon;
 
+  /* we want to use the _sending_ function even when the socket turns
+     out readable as the underlying libssh sftp send function will deal
+     with both accordingly */
+  data->state.select_bits = CURL_CSELECT_OUT;
+
+  /* since we do not really wait for anything at this point, we want the
+     state machine to move on as soon as possible so we set a very short
+     timeout here */
+  Curl_expire(data, 0, EXPIRE_RUN_NOW);
+#if LIBSSH_VERSION_INT > SSH_VERSION_INT(0, 11, 0)
+  sshc->sftp_send_state = 0;
+#endif
+  myssh_state(data, sshc, SSH_STOP);
+  return rc;
+}
+
+static int myssh_state_sftp_download_stat(struct Curl_easy *data,
+                                          struct ssh_conn *sshc)
+{
+  curl_off_t size;
+  int rc = 0;
+  sftp_attributes attrs = sftp_fstat(sshc->sftp_file);
+  if(!attrs ||
+     !(attrs->flags & SSH_FILEXFER_ATTR_SIZE) ||
+     (attrs->size == 0)) {
+    /*
+     * sftp_fstat did not return an error, so maybe the server
+     * just does not support stat()
+     * OR the server does not return a file size with a stat()
+     * OR file size is 0
+     */
+    data->req.size = -1;
+    data->req.maxdownload = -1;
+    Curl_pgrsSetDownloadSize(data, -1);
+    size = 0;
+    if(attrs)
+      sftp_attributes_free(attrs);
+  }
+  else {
+    size = attrs->size;
+
+    sftp_attributes_free(attrs);
+
+    if(size < 0) {
+      failf(data, "Bad file size (%" FMT_OFF_T ")", size);
+      MOVE_TO_ERROR_STATE(CURLE_BAD_DOWNLOAD_RESUME);
+      return rc;
+    }
+    if(data->state.use_range) {
+      curl_off_t from, to;
+      const char *p = data->state.range;
+      int from_t, to_t;
+
+      from_t = curlx_str_number(&p, &from, CURL_OFF_T_MAX);
+      if(from_t == STRE_OVERFLOW) {
+        MOVE_TO_ERROR_STATE(CURLE_RANGE_ERROR);
+        return rc;
+      }
+      curlx_str_passblanks(&p);
+      (void)curlx_str_single(&p, '-');
+
+      to_t = curlx_str_numblanks(&p, &to);
+      if(to_t == STRE_OVERFLOW)
+        return CURLE_RANGE_ERROR;
+
+      if((to_t == STRE_NO_NUM) || (to >= size)) {
+        to = size - 1;
+        to_t = STRE_OK;
+      }
+
+      if(from_t == STRE_NO_NUM) {
+        /* from is relative to end of file */
+        from = size - to;
+        to = size - 1;
+        from_t = STRE_OK;
+      }
+      if(from > size) {
+        failf(data, "Offset (%" FMT_OFF_T ") was beyond file size (%"
+              FMT_OFF_T ")", from, size);
+        MOVE_TO_ERROR_STATE(CURLE_BAD_DOWNLOAD_RESUME);
+        return rc;
+      }
+      if(from > to) {
+        from = to;
+        size = 0;
       }
       else {
-        rc = ssh_userauth_publickey_auto(sshc->ssh_session, NULL,
-                                         data->set.ssl.key_passwd);
-        if(rc == SSH_AUTH_AGAIN) {
-          rc = SSH_AGAIN;
-          break;
-        }
-        if(rc == SSH_AUTH_SUCCESS) {
-          rc = SSH_OK;
-          sshc->authed = TRUE;
-          infof(data, "Completed public key authentication");
-          state(data, SSH_AUTH_DONE);
-          break;
+        if((to - from) == CURL_OFF_T_MAX) {
+          MOVE_TO_ERROR_STATE(CURLE_RANGE_ERROR);
+          return rc;
         }
+        size = to - from + 1;
+      }
 
-        MOVE_TO_GSSAPI_AUTH;
+      rc = sftp_seek64(sshc->sftp_file, from);
+      if(rc) {
+        MOVE_TO_SFTP_CLOSE_STATE();
+        return rc;
+      }
+    }
+    data->req.size = size;
+    data->req.maxdownload = size;
+    Curl_pgrsSetDownloadSize(data, size);
+  }
+
+  /* We can resume if we can seek to the resume position */
+  if(data->state.resume_from) {
+    if(data->state.resume_from < 0) {
+      /* We are supposed to download the last abs(from) bytes */
+      if((curl_off_t)size < -data->state.resume_from) {
+        failf(data, "Offset (%" FMT_OFF_T ") was beyond file size (%"
+              FMT_OFF_T ")", data->state.resume_from, size);
+        MOVE_TO_ERROR_STATE(CURLE_BAD_DOWNLOAD_RESUME);
+        return rc;
+      }
+      /* download from where? */
+      data->state.resume_from += size;
+    }
+    else {
+      if((curl_off_t)size < data->state.resume_from) {
+        failf(data, "Offset (%" FMT_OFF_T
+              ") was beyond file size (%" FMT_OFF_T ")",
+              data->state.resume_from, size);
+        MOVE_TO_ERROR_STATE(CURLE_BAD_DOWNLOAD_RESUME);
+        return rc;
+      }
+    }
+    /* Now store the number of bytes we are expected to download */
+    data->req.size = size - data->state.resume_from;
+    data->req.maxdownload = size - data->state.resume_from;
+    Curl_pgrsSetDownloadSize(data,
+                             size - data->state.resume_from);
+
+    rc = sftp_seek64(sshc->sftp_file, data->state.resume_from);
+    if(rc) {
+      MOVE_TO_SFTP_CLOSE_STATE();
+      return rc;
+    }
+  }
+
+  /* Setup the actual download */
+  if(data->req.size == 0) {
+    /* no data to transfer */
+    Curl_xfer_setup_nop(data);
+    infof(data, "File already completely downloaded");
+    myssh_state(data, sshc, SSH_STOP);
+    return rc;
+  }
+  Curl_xfer_setup1(data, CURL_XFER_RECV, data->req.size, FALSE);
+
+  /* not set by Curl_xfer_setup to preserve keepon bits */
+  data->conn->writesockfd = data->conn->sockfd;
+
+  /* we want to use the _receiving_ function even when the socket turns
+     out writableable as the underlying libssh recv function will deal
+     with both accordingly */
+  data->state.select_bits = CURL_CSELECT_IN;
+
+  sshc->sftp_recv_state = 0;
+  myssh_state(data, sshc, SSH_STOP);
+
+  return rc;
+}
+
+/*
+ * ssh_statemach_act() runs the SSH state machine as far as it can without
+ * blocking and without reaching the end. The data the pointer 'block' points
+ * to will be set to TRUE if the libssh function returns SSH_AGAIN
+ * meaning it wants to be called again when the socket is ready
+ */
+static CURLcode myssh_statemach_act(struct Curl_easy *data,
+                                    struct ssh_conn *sshc,
+                                    struct SSHPROTO *sshp,
+                                    bool *block)
+{
+  CURLcode result = CURLE_OK;
+  struct connectdata *conn = data->conn;
+  curl_socket_t sock = conn->sock[FIRSTSOCKET];
+  int rc = SSH_NO_ERROR, err;
+  const char *err_msg;
+  *block = 0;                   /* we are not blocking by default */
+
+  do {
+
+    switch(sshc->state) {
+    case SSH_INIT:
+      myssh_state_init(data, sshc);
+      FALLTHROUGH();
+
+    case SSH_S_STARTUP:
+      rc = myssh_state_startup(data, sshc);
+      if(rc)
+        break;
+      FALLTHROUGH();
+    case SSH_HOSTKEY:
+      rc = myssh_is_known(data, sshc);
+      if(rc != SSH_OK) {
+        MOVE_TO_ERROR_STATE(CURLE_PEER_FAILED_VERIFICATION);
+        break;
       }
+
+      myssh_state(data, sshc, SSH_AUTHLIST);
+      FALLTHROUGH();
+    case SSH_AUTHLIST:
+      rc = myssh_state_authlist(data, sshc);
+      break;
+    case SSH_AUTH_PKEY_INIT:
+      rc = myssh_state_auth_pkey_init(data, sshc);
       break;
     case SSH_AUTH_PKEY:
       rc = ssh_userauth_publickey(sshc->ssh_session, NULL, sshc->privkey);
@@ -807,7 +1125,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
       if(rc == SSH_AUTH_SUCCESS) {
         sshc->authed = TRUE;
         infof(data, "Completed public key authentication");
-        state(data, SSH_AUTH_DONE);
+        myssh_state(data, sshc, SSH_AUTH_DONE);
         break;
       }
       else {
@@ -832,7 +1150,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
         rc = SSH_OK;
         sshc->authed = TRUE;
         infof(data, "Completed gssapi authentication");
-        state(data, SSH_AUTH_DONE);
+        myssh_state(data, sshc, SSH_AUTH_DONE);
         break;
       }
 
@@ -841,7 +1159,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
 
     case SSH_AUTH_KEY_INIT:
       if(data->set.ssh_auth_types & CURLSSH_AUTH_KEYBOARD) {
-        state(data, SSH_AUTH_KEY);
+        myssh_state(data, sshc, SSH_AUTH_KEY);
       }
       else {
         MOVE_TO_PASSWD_AUTH;
@@ -850,14 +1168,14 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
 
     case SSH_AUTH_KEY:
       /* keyboard-interactive authentication */
-      rc = myssh_auth_interactive(conn);
+      rc = myssh_auth_interactive(conn, sshc);
       if(rc == SSH_AGAIN) {
         break;
       }
       if(rc == SSH_OK) {
         sshc->authed = TRUE;
         infof(data, "completed keyboard interactive authentication");
-        state(data, SSH_AUTH_DONE);
+        myssh_state(data, sshc, SSH_AUTH_DONE);
       }
       else {
         MOVE_TO_PASSWD_AUTH;
@@ -869,7 +1187,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
         MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
         break;
       }
-      state(data, SSH_AUTH_PASS);
+      myssh_state(data, sshc, SSH_AUTH_PASS);
       FALLTHROUGH();
 
     case SSH_AUTH_PASS:
@@ -882,7 +1200,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
       if(rc == SSH_AUTH_SUCCESS) {
         sshc->authed = TRUE;
         infof(data, "Completed password authentication");
-        state(data, SSH_AUTH_DONE);
+        myssh_state(data, sshc, SSH_AUTH_DONE);
       }
       else {
         MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
@@ -907,11 +1225,11 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
       conn->writesockfd = CURL_SOCKET_BAD;
 
       if(conn->handler->protocol == CURLPROTO_SFTP) {
-        state(data, SSH_SFTP_INIT);
+        myssh_state(data, sshc, SSH_SFTP_INIT);
         break;
       }
       infof(data, "SSH CONNECT phase done");
-      state(data, SSH_STOP);
+      myssh_state(data, sshc, SSH_STOP);
       break;
 
     case SSH_SFTP_INIT:
@@ -932,7 +1250,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
         MOVE_TO_ERROR_STATE(sftp_error_to_CURLE(SSH_FX_FAILURE));
         break;
       }
-      state(data, SSH_SFTP_REALPATH);
+      myssh_state(data, sshc, SSH_SFTP_REALPATH);
       FALLTHROUGH();
     case SSH_SFTP_REALPATH:
       /*
@@ -943,31 +1261,34 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
         MOVE_TO_ERROR_STATE(CURLE_COULDNT_CONNECT);
         break;
       }
-      data->state.most_recent_ftp_entrypath = sshc->homedir;
+      free(data->state.most_recent_ftp_entrypath);
+      data->state.most_recent_ftp_entrypath = strdup(sshc->homedir);
+      if(!data->state.most_recent_ftp_entrypath)
+        return CURLE_OUT_OF_MEMORY;
 
       /* This is the last step in the SFTP connect phase. Do note that while
          we get the homedir here, we get the "workingpath" in the DO action
          since the homedir will remain the same between request but the
          working path will not. */
       DEBUGF(infof(data, "SSH CONNECT phase done"));
-      state(data, SSH_STOP);
+      myssh_state(data, sshc, SSH_STOP);
       break;
 
     case SSH_SFTP_QUOTE_INIT:
-      result = Curl_getworkingpath(data, sshc->homedir, &protop->path);
+      result = Curl_getworkingpath(data, sshc->homedir, &sshp->path);
       if(result) {
         sshc->actualcode = result;
-        state(data, SSH_STOP);
+        myssh_state(data, sshc, SSH_STOP);
         break;
       }
 
       if(data->set.quote) {
         infof(data, "Sending quote commands");
         sshc->quote_item = data->set.quote;
-        state(data, SSH_SFTP_QUOTE);
+        myssh_state(data, sshc, SSH_SFTP_QUOTE);
       }
       else {
-        state(data, SSH_SFTP_GETINFO);
+        myssh_state(data, sshc, SSH_SFTP_GETINFO);
       }
       break;
 
@@ -975,16 +1296,16 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
       if(data->set.postquote) {
         infof(data, "Sending quote commands");
         sshc->quote_item = data->set.postquote;
-        state(data, SSH_SFTP_QUOTE);
+        myssh_state(data, sshc, SSH_SFTP_QUOTE);
       }
       else {
-        state(data, SSH_STOP);
+        myssh_state(data, sshc, SSH_STOP);
       }
       break;
 
     case SSH_SFTP_QUOTE:
       /* Send any quote commands */
-      sftp_quote(data);
+      sftp_quote(data, sshc, sshp);
       break;
 
     case SSH_SFTP_NEXT_QUOTE:
@@ -994,21 +1315,21 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
       sshc->quote_item = sshc->quote_item->next;
 
       if(sshc->quote_item) {
-        state(data, SSH_SFTP_QUOTE);
+        myssh_state(data, sshc, SSH_SFTP_QUOTE);
       }
       else {
         if(sshc->nextstate != SSH_NO_STATE) {
-          state(data, sshc->nextstate);
+          myssh_state(data, sshc, sshc->nextstate);
           sshc->nextstate = SSH_NO_STATE;
         }
         else {
-          state(data, SSH_SFTP_GETINFO);
+          myssh_state(data, sshc, SSH_SFTP_GETINFO);
         }
       }
       break;
 
     case SSH_SFTP_QUOTE_STAT:
-      sftp_quote_stat(data);
+      sftp_quote_stat(data, sshc);
       break;
 
     case SSH_SFTP_QUOTE_SETSTAT:
@@ -1019,7 +1340,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
         Curl_safefree(sshc->quote_path2);
         failf(data, "Attempt to set SFTP stats failed: %s",
               ssh_get_error(sshc->ssh_session));
-        state(data, SSH_SFTP_CLOSE);
+        myssh_state(data, sshc, SSH_SFTP_CLOSE);
         sshc->nextstate = SSH_NO_STATE;
         sshc->actualcode = CURLE_QUOTE_ERROR;
         /* sshc->actualcode = sftp_error_to_CURLE(err);
@@ -1027,7 +1348,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
          * the error the libssh2 backend is returning */
         break;
       }
-      state(data, SSH_SFTP_NEXT_QUOTE);
+      myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
       break;
 
     case SSH_SFTP_QUOTE_SYMLINK:
@@ -1038,12 +1359,12 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
         Curl_safefree(sshc->quote_path2);
         failf(data, "symlink command failed: %s",
               ssh_get_error(sshc->ssh_session));
-        state(data, SSH_SFTP_CLOSE);
+        myssh_state(data, sshc, SSH_SFTP_CLOSE);
         sshc->nextstate = SSH_NO_STATE;
         sshc->actualcode = CURLE_QUOTE_ERROR;
         break;
       }
-      state(data, SSH_SFTP_NEXT_QUOTE);
+      myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
       break;
 
     case SSH_SFTP_QUOTE_MKDIR:
@@ -1053,12 +1374,12 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
         Curl_safefree(sshc->quote_path1);
         failf(data, "mkdir command failed: %s",
               ssh_get_error(sshc->ssh_session));
-        state(data, SSH_SFTP_CLOSE);
+        myssh_state(data, sshc, SSH_SFTP_CLOSE);
         sshc->nextstate = SSH_NO_STATE;
         sshc->actualcode = CURLE_QUOTE_ERROR;
         break;
       }
-      state(data, SSH_SFTP_NEXT_QUOTE);
+      myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
       break;
 
     case SSH_SFTP_QUOTE_RENAME:
@@ -1069,12 +1390,12 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
         Curl_safefree(sshc->quote_path2);
         failf(data, "rename command failed: %s",
               ssh_get_error(sshc->ssh_session));
-        state(data, SSH_SFTP_CLOSE);
+        myssh_state(data, sshc, SSH_SFTP_CLOSE);
         sshc->nextstate = SSH_NO_STATE;
         sshc->actualcode = CURLE_QUOTE_ERROR;
         break;
       }
-      state(data, SSH_SFTP_NEXT_QUOTE);
+      myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
       break;
 
     case SSH_SFTP_QUOTE_RMDIR:
@@ -1083,12 +1404,12 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
         Curl_safefree(sshc->quote_path1);
         failf(data, "rmdir command failed: %s",
               ssh_get_error(sshc->ssh_session));
-        state(data, SSH_SFTP_CLOSE);
+        myssh_state(data, sshc, SSH_SFTP_CLOSE);
         sshc->nextstate = SSH_NO_STATE;
         sshc->actualcode = CURLE_QUOTE_ERROR;
         break;
       }
-      state(data, SSH_SFTP_NEXT_QUOTE);
+      myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
       break;
 
     case SSH_SFTP_QUOTE_UNLINK:
@@ -1097,12 +1418,12 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
         Curl_safefree(sshc->quote_path1);
         failf(data, "rm command failed: %s",
               ssh_get_error(sshc->ssh_session));
-        state(data, SSH_SFTP_CLOSE);
+        myssh_state(data, sshc, SSH_SFTP_CLOSE);
         sshc->nextstate = SSH_NO_STATE;
         sshc->actualcode = CURLE_QUOTE_ERROR;
         break;
       }
-      state(data, SSH_SFTP_NEXT_QUOTE);
+      myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
       break;
 
     case SSH_SFTP_QUOTE_STATVFS:
@@ -1114,7 +1435,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
         Curl_safefree(sshc->quote_path1);
         failf(data, "statvfs command failed: %s",
               ssh_get_error(sshc->ssh_session));
-        state(data, SSH_SFTP_CLOSE);
+        myssh_state(data, sshc, SSH_SFTP_CLOSE);
         sshc->nextstate = SSH_NO_STATE;
         sshc->actualcode = CURLE_QUOTE_ERROR;
         break;
@@ -1147,7 +1468,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
 
         if(!tmp) {
           result = CURLE_OUT_OF_MEMORY;
-          state(data, SSH_SFTP_CLOSE);
+          myssh_state(data, sshc, SSH_SFTP_CLOSE);
           sshc->nextstate = SSH_NO_STATE;
           break;
         }
@@ -1155,21 +1476,21 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
         result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp));
         free(tmp);
         if(result) {
-          state(data, SSH_SFTP_CLOSE);
+          myssh_state(data, sshc, SSH_SFTP_CLOSE);
           sshc->nextstate = SSH_NO_STATE;
           sshc->actualcode = result;
         }
       }
-      state(data, SSH_SFTP_NEXT_QUOTE);
+      myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
       break;
     }
 
     case SSH_SFTP_GETINFO:
       if(data->set.get_filetime) {
-        state(data, SSH_SFTP_FILETIME);
+        myssh_state(data, sshc, SSH_SFTP_FILETIME);
       }
       else {
-        state(data, SSH_SFTP_TRANS_INIT);
+        myssh_state(data, sshc, SSH_SFTP_TRANS_INIT);
       }
       break;
 
@@ -1177,180 +1498,38 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
     {
       sftp_attributes attrs;
 
-      attrs = sftp_stat(sshc->sftp_session, protop->path);
+      attrs = sftp_stat(sshc->sftp_session, sshp->path);
       if(attrs) {
         data->info.filetime = attrs->mtime;
         sftp_attributes_free(attrs);
       }
 
-      state(data, SSH_SFTP_TRANS_INIT);
+      myssh_state(data, sshc, SSH_SFTP_TRANS_INIT);
       break;
     }
 
     case SSH_SFTP_TRANS_INIT:
       if(data->state.upload)
-        state(data, SSH_SFTP_UPLOAD_INIT);
+        myssh_state(data, sshc, SSH_SFTP_UPLOAD_INIT);
       else {
-        if(protop->path[strlen(protop->path)-1] == '/')
-          state(data, SSH_SFTP_READDIR_INIT);
+        if(sshp->path[strlen(sshp->path)-1] == '/')
+          myssh_state(data, sshc, SSH_SFTP_READDIR_INIT);
         else
-          state(data, SSH_SFTP_DOWNLOAD_INIT);
+          myssh_state(data, sshc, SSH_SFTP_DOWNLOAD_INIT);
       }
       break;
 
     case SSH_SFTP_UPLOAD_INIT:
-    {
-      int flags;
-
-      if(data->state.resume_from) {
-        sftp_attributes attrs;
-
-        if(data->state.resume_from < 0) {
-          attrs = sftp_stat(sshc->sftp_session, protop->path);
-          if(attrs) {
-            curl_off_t size = attrs->size;
-            if(size < 0) {
-              failf(data, "Bad file size (%" FMT_OFF_T ")", size);
-              MOVE_TO_ERROR_STATE(CURLE_BAD_DOWNLOAD_RESUME);
-              break;
-            }
-            data->state.resume_from = attrs->size;
-
-            sftp_attributes_free(attrs);
-          }
-          else {
-            data->state.resume_from = 0;
-          }
-        }
-      }
-
-      if(data->set.remote_append)
-        /* Try to open for append, but create if nonexisting */
-        flags = O_WRONLY|O_CREAT|O_APPEND;
-      else if(data->state.resume_from > 0)
-        /* If we have restart position then open for append */
-        flags = O_WRONLY|O_APPEND;
-      else
-        /* Clear file before writing (normal behavior) */
-        flags = O_WRONLY|O_CREAT|O_TRUNC;
-
-      if(sshc->sftp_file)
-        sftp_close(sshc->sftp_file);
-      sshc->sftp_file =
-        sftp_open(sshc->sftp_session, protop->path,
-                  flags, (mode_t)data->set.new_file_perms);
-      if(!sshc->sftp_file) {
-        err = sftp_get_error(sshc->sftp_session);
-
-        if(((err == SSH_FX_NO_SUCH_FILE || err == SSH_FX_FAILURE ||
-             err == SSH_FX_NO_SUCH_PATH)) &&
-             (data->set.ftp_create_missing_dirs &&
-             (strlen(protop->path) > 1))) {
-               /* try to create the path remotely */
-               rc = 0;
-               sshc->secondCreateDirs = 1;
-               state(data, SSH_SFTP_CREATE_DIRS_INIT);
-               break;
-        }
-        else {
-          MOVE_TO_SFTP_CLOSE_STATE();
-          break;
-        }
-      }
-
-      /* If we have a restart point then we need to seek to the correct
-         position. */
-      if(data->state.resume_from > 0) {
-        /* Let's read off the proper amount of bytes from the input. */
-        if(data->set.seek_func) {
-          Curl_set_in_callback(data, TRUE);
-          seekerr = data->set.seek_func(data->set.seek_client,
-                                        data->state.resume_from, SEEK_SET);
-          Curl_set_in_callback(data, FALSE);
-        }
-
-        if(seekerr != CURL_SEEKFUNC_OK) {
-          curl_off_t passed = 0;
-
-          if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
-            failf(data, "Could not seek stream");
-            return CURLE_FTP_COULDNT_USE_REST;
-          }
-          /* seekerr == CURL_SEEKFUNC_CANTSEEK (cannot seek to offset) */
-          do {
-            char scratch[4*1024];
-            size_t readthisamountnow =
-              (data->state.resume_from - passed >
-                (curl_off_t)sizeof(scratch)) ?
-              sizeof(scratch) : curlx_sotouz(data->state.resume_from - passed);
-
-            size_t actuallyread =
-              data->state.fread_func(scratch, 1,
-                                     readthisamountnow, data->state.in);
-
-            passed += actuallyread;
-            if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
-              /* this checks for greater-than only to make sure that the
-                 CURL_READFUNC_ABORT return code still aborts */
-              failf(data, "Failed to read data");
-              MOVE_TO_ERROR_STATE(CURLE_FTP_COULDNT_USE_REST);
-              break;
-            }
-          } while(passed < data->state.resume_from);
-          if(rc)
-            break;
-        }
-
-        /* now, decrease the size of the read */
-        if(data->state.infilesize > 0) {
-          data->state.infilesize -= data->state.resume_from;
-          data->req.size = data->state.infilesize;
-          Curl_pgrsSetUploadSize(data, data->state.infilesize);
-        }
-
-        rc = sftp_seek64(sshc->sftp_file, data->state.resume_from);
-        if(rc) {
-          MOVE_TO_SFTP_CLOSE_STATE();
-          break;
-        }
-      }
-      if(data->state.infilesize > 0) {
-        data->req.size = data->state.infilesize;
-        Curl_pgrsSetUploadSize(data, data->state.infilesize);
-      }
-      /* upload data */
-      Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE);
-
-      /* not set by Curl_xfer_setup to preserve keepon bits */
-      conn->sockfd = conn->writesockfd;
-
-      /* store this original bitmask setup to use later on if we cannot
-         figure out a "real" bitmask */
-      sshc->orig_waitfor = data->req.keepon;
-
-      /* we want to use the _sending_ function even when the socket turns
-         out readable as the underlying libssh sftp send function will deal
-         with both accordingly */
-      data->state.select_bits = CURL_CSELECT_OUT;
-
-      /* since we do not really wait for anything at this point, we want the
-         state machine to move on as soon as possible so we set a very short
-         timeout here */
-      Curl_expire(data, 0, EXPIRE_RUN_NOW);
-#if LIBSSH_VERSION_INT > SSH_VERSION_INT(0, 11, 0)
-      sshc->sftp_send_state = 0;
-#endif
-      state(data, SSH_STOP);
+      rc = myssh_state_upload_init(data, sshc, sshp);
       break;
-    }
 
     case SSH_SFTP_CREATE_DIRS_INIT:
-      if(strlen(protop->path) > 1) {
-        sshc->slash_pos = protop->path + 1; /* ignore the leading '/' */
-        state(data, SSH_SFTP_CREATE_DIRS);
+      if(strlen(sshp->path) > 1) {
+        sshc->slash_pos = sshp->path + 1; /* ignore the leading '/' */
+        myssh_state(data, sshc, SSH_SFTP_CREATE_DIRS);
       }
       else {
-        state(data, SSH_SFTP_UPLOAD_INIT);
+        myssh_state(data, sshc, SSH_SFTP_UPLOAD_INIT);
       }
       break;
 
@@ -1359,16 +1538,16 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
       if(sshc->slash_pos) {
         *sshc->slash_pos = 0;
 
-        infof(data, "Creating directory '%s'", protop->path);
-        state(data, SSH_SFTP_CREATE_DIRS_MKDIR);
+        infof(data, "Creating directory '%s'", sshp->path);
+        myssh_state(data, sshc, SSH_SFTP_CREATE_DIRS_MKDIR);
         break;
       }
-      state(data, SSH_SFTP_UPLOAD_INIT);
+      myssh_state(data, sshc, SSH_SFTP_UPLOAD_INIT);
       break;
 
     case SSH_SFTP_CREATE_DIRS_MKDIR:
       /* 'mode' - parameter is preliminary - default to 0644 */
-      rc = sftp_mkdir(sshc->sftp_session, protop->path,
+      rc = sftp_mkdir(sshc->sftp_session, sshp->path,
                       (mode_t)data->set.new_directory_perms);
       *sshc->slash_pos = '/';
       ++sshc->slash_pos;
@@ -1387,13 +1566,13 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
         }
         rc = 0; /* clear rc and continue */
       }
-      state(data, SSH_SFTP_CREATE_DIRS);
+      myssh_state(data, sshc, SSH_SFTP_CREATE_DIRS);
       break;
 
     case SSH_SFTP_READDIR_INIT:
       Curl_pgrsSetDownloadSize(data, -1);
       if(data->req.no_body) {
-        state(data, SSH_STOP);
+        myssh_state(data, sshc, SSH_STOP);
         break;
       }
 
@@ -1402,18 +1581,18 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
        * listing
        */
       sshc->sftp_dir = sftp_opendir(sshc->sftp_session,
-                                    protop->path);
+                                    sshp->path);
       if(!sshc->sftp_dir) {
         failf(data, "Could not open directory for reading: %s",
               ssh_get_error(sshc->ssh_session));
         MOVE_TO_SFTP_CLOSE_STATE();
         break;
       }
-      state(data, SSH_SFTP_READDIR);
+      myssh_state(data, sshc, SSH_SFTP_READDIR);
       break;
 
     case SSH_SFTP_READDIR:
-      Curl_dyn_reset(&sshc->readdir_buf);
+      curlx_dyn_reset(&sshc->readdir_buf);
       if(sshc->readdir_attrs)
         sftp_attributes_free(sshc->readdir_attrs);
 
@@ -1428,7 +1607,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
 
           tmpLine = aprintf("%s\n", sshc->readdir_filename);
           if(!tmpLine) {
-            state(data, SSH_SFTP_CLOSE);
+            myssh_state(data, sshc, SSH_SFTP_CLOSE);
             sshc->actualcode = CURLE_OUT_OF_MEMORY;
             break;
           }
@@ -1437,39 +1616,39 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
           free(tmpLine);
 
           if(result) {
-            state(data, SSH_STOP);
+            myssh_state(data, sshc, SSH_STOP);
             break;
           }
 
         }
         else {
-          if(Curl_dyn_add(&sshc->readdir_buf, sshc->readdir_longentry)) {
+          if(curlx_dyn_add(&sshc->readdir_buf, sshc->readdir_longentry)) {
             sshc->actualcode = CURLE_OUT_OF_MEMORY;
-            state(data, SSH_STOP);
+            myssh_state(data, sshc, SSH_STOP);
             break;
           }
 
           if((sshc->readdir_attrs->flags & SSH_FILEXFER_ATTR_PERMISSIONS) &&
              ((sshc->readdir_attrs->permissions & SSH_S_IFMT) ==
               SSH_S_IFLNK)) {
-            sshc->readdir_linkPath = aprintf("%s%s", protop->path,
+            sshc->readdir_linkPath = aprintf("%s%s", sshp->path,
                                              sshc->readdir_filename);
 
             if(!sshc->readdir_linkPath) {
-              state(data, SSH_SFTP_CLOSE);
+              myssh_state(data, sshc, SSH_SFTP_CLOSE);
               sshc->actualcode = CURLE_OUT_OF_MEMORY;
               break;
             }
 
-            state(data, SSH_SFTP_READDIR_LINK);
+            myssh_state(data, sshc, SSH_SFTP_READDIR_LINK);
             break;
           }
-          state(data, SSH_SFTP_READDIR_BOTTOM);
+          myssh_state(data, sshc, SSH_SFTP_READDIR_BOTTOM);
           break;
         }
       }
       else if(sftp_dir_eof(sshc->sftp_dir)) {
-        state(data, SSH_SFTP_READDIR_DONE);
+        myssh_state(data, sshc, SSH_SFTP_READDIR_DONE);
         break;
       }
       else {
@@ -1511,8 +1690,8 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
 
       Curl_safefree(sshc->readdir_linkPath);
 
-      if(Curl_dyn_addf(&sshc->readdir_buf, " -> %s",
-                       sshc->readdir_filename)) {
+      if(curlx_dyn_addf(&sshc->readdir_buf, " -> %s",
+                        sshc->readdir_filename)) {
         sshc->actualcode = CURLE_OUT_OF_MEMORY;
         break;
       }
@@ -1522,24 +1701,24 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
       sshc->readdir_filename = NULL;
       sshc->readdir_longentry = NULL;
 
-      state(data, SSH_SFTP_READDIR_BOTTOM);
+      myssh_state(data, sshc, SSH_SFTP_READDIR_BOTTOM);
       FALLTHROUGH();
     case SSH_SFTP_READDIR_BOTTOM:
-      if(Curl_dyn_addn(&sshc->readdir_buf, "\n", 1))
+      if(curlx_dyn_addn(&sshc->readdir_buf, "\n", 1))
         result = CURLE_OUT_OF_MEMORY;
       else
         result = Curl_client_write(data, CLIENTWRITE_BODY,
-                                   Curl_dyn_ptr(&sshc->readdir_buf),
-                                   Curl_dyn_len(&sshc->readdir_buf));
+                                   curlx_dyn_ptr(&sshc->readdir_buf),
+                                   curlx_dyn_len(&sshc->readdir_buf));
 
       ssh_string_free_char(sshc->readdir_tmp);
       sshc->readdir_tmp = NULL;
 
       if(result) {
-        state(data, SSH_STOP);
+        myssh_state(data, sshc, SSH_STOP);
       }
       else
-        state(data, SSH_SFTP_READDIR);
+        myssh_state(data, sshc, SSH_SFTP_READDIR);
       break;
 
     case SSH_SFTP_READDIR_DONE:
@@ -1548,7 +1727,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
 
       /* no data to transfer */
       Curl_xfer_setup_nop(data);
-      state(data, SSH_STOP);
+      myssh_state(data, sshc, SSH_STOP);
       break;
 
     case SSH_SFTP_DOWNLOAD_INIT:
@@ -1558,7 +1737,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
       if(sshc->sftp_file)
         sftp_close(sshc->sftp_file);
 
-      sshc->sftp_file = sftp_open(sshc->sftp_session, protop->path,
+      sshc->sftp_file = sftp_open(sshc->sftp_session, sshp->path,
                                   O_RDONLY, (mode_t)data->set.new_file_perms);
       if(!sshc->sftp_file) {
         failf(data, "Could not open remote file for reading: %s",
@@ -1568,160 +1747,19 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
         break;
       }
       sftp_file_set_nonblocking(sshc->sftp_file);
-      state(data, SSH_SFTP_DOWNLOAD_STAT);
+      myssh_state(data, sshc, SSH_SFTP_DOWNLOAD_STAT);
       break;
 
     case SSH_SFTP_DOWNLOAD_STAT:
-    {
-      sftp_attributes attrs;
-      curl_off_t size;
-
-      attrs = sftp_fstat(sshc->sftp_file);
-      if(!attrs ||
-              !(attrs->flags & SSH_FILEXFER_ATTR_SIZE) ||
-              (attrs->size == 0)) {
-        /*
-         * sftp_fstat did not return an error, so maybe the server
-         * just does not support stat()
-         * OR the server does not return a file size with a stat()
-         * OR file size is 0
-         */
-        data->req.size = -1;
-        data->req.maxdownload = -1;
-        Curl_pgrsSetDownloadSize(data, -1);
-        size = 0;
-      }
-      else {
-        size = attrs->size;
-
-        sftp_attributes_free(attrs);
-
-        if(size < 0) {
-          failf(data, "Bad file size (%" FMT_OFF_T ")", size);
-          return CURLE_BAD_DOWNLOAD_RESUME;
-        }
-        if(data->state.use_range) {
-          curl_off_t from, to;
-          char *ptr;
-          char *ptr2;
-          CURLofft to_t;
-          CURLofft from_t;
-
-          from_t = curlx_strtoofft(data->state.range, &ptr, 10, &from);
-          if(from_t == CURL_OFFT_FLOW) {
-            return CURLE_RANGE_ERROR;
-          }
-          while(*ptr && (ISBLANK(*ptr) || (*ptr == '-')))
-            ptr++;
-          to_t = curlx_strtoofft(ptr, &ptr2, 10, &to);
-          if(to_t == CURL_OFFT_FLOW) {
-            return CURLE_RANGE_ERROR;
-          }
-          if((to_t == CURL_OFFT_INVAL) /* no "to" value given */
-             || (to >= size)) {
-            to = size - 1;
-          }
-          if(from_t) {
-            /* from is relative to end of file */
-            from = size - to;
-            to = size - 1;
-          }
-          if(from > size) {
-            failf(data, "Offset (%" FMT_OFF_T ") was beyond file size (%"
-                  FMT_OFF_T ")", from, size);
-            return CURLE_BAD_DOWNLOAD_RESUME;
-          }
-          if(from > to) {
-            from = to;
-            size = 0;
-          }
-          else {
-            if((to - from) == CURL_OFF_T_MAX)
-              return CURLE_RANGE_ERROR;
-            size = to - from + 1;
-          }
-
-          rc = sftp_seek64(sshc->sftp_file, from);
-          if(rc) {
-            MOVE_TO_SFTP_CLOSE_STATE();
-            break;
-          }
-        }
-        data->req.size = size;
-        data->req.maxdownload = size;
-        Curl_pgrsSetDownloadSize(data, size);
-      }
-
-      /* We can resume if we can seek to the resume position */
-      if(data->state.resume_from) {
-        if(data->state.resume_from < 0) {
-          /* We are supposed to download the last abs(from) bytes */
-          if((curl_off_t)size < -data->state.resume_from) {
-            failf(data, "Offset (%" FMT_OFF_T ") was beyond file size (%"
-                  FMT_OFF_T ")", data->state.resume_from, size);
-            return CURLE_BAD_DOWNLOAD_RESUME;
-          }
-          /* download from where? */
-          data->state.resume_from += size;
-        }
-        else {
-          if((curl_off_t)size < data->state.resume_from) {
-            failf(data, "Offset (%" FMT_OFF_T
-                  ") was beyond file size (%" FMT_OFF_T ")",
-                  data->state.resume_from, size);
-            return CURLE_BAD_DOWNLOAD_RESUME;
-          }
-        }
-        /* Now store the number of bytes we are expected to download */
-        data->req.size = size - data->state.resume_from;
-        data->req.maxdownload = size - data->state.resume_from;
-        Curl_pgrsSetDownloadSize(data,
-                                 size - data->state.resume_from);
-
-        rc = sftp_seek64(sshc->sftp_file, data->state.resume_from);
-        if(rc) {
-          MOVE_TO_SFTP_CLOSE_STATE();
-          break;
-        }
-      }
-    }
-
-    /* Setup the actual download */
-    if(data->req.size == 0) {
-      /* no data to transfer */
-      Curl_xfer_setup_nop(data);
-      infof(data, "File already completely downloaded");
-      state(data, SSH_STOP);
+      rc = myssh_state_sftp_download_stat(data, sshc);
       break;
-    }
-    Curl_xfer_setup1(data, CURL_XFER_RECV, data->req.size, FALSE);
-
-    /* not set by Curl_xfer_setup to preserve keepon bits */
-    conn->writesockfd = conn->sockfd;
-
-    /* we want to use the _receiving_ function even when the socket turns
-       out writableable as the underlying libssh recv function will deal
-       with both accordingly */
-    data->state.select_bits = CURL_CSELECT_IN;
-
-    if(result) {
-      /* this should never occur; the close state should be entered
-         at the time the error occurs */
-      state(data, SSH_SFTP_CLOSE);
-      sshc->actualcode = result;
-    }
-    else {
-      sshc->sftp_recv_state = 0;
-      state(data, SSH_STOP);
-    }
-    break;
 
     case SSH_SFTP_CLOSE:
       if(sshc->sftp_file) {
         sftp_close(sshc->sftp_file);
         sshc->sftp_file = NULL;
       }
-      Curl_safefree(protop->path);
+      Curl_safefree(sshp->path);
 
       DEBUGF(infof(data, "SFTP DONE done"));
 
@@ -1730,11 +1768,11 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
          SSH_SFTP_CLOSE to pass the correct result back  */
       if(sshc->nextstate != SSH_NO_STATE &&
          sshc->nextstate != SSH_SFTP_CLOSE) {
-        state(data, sshc->nextstate);
+        myssh_state(data, sshc, sshc->nextstate);
         sshc->nextstate = SSH_SFTP_CLOSE;
       }
       else {
-        state(data, SSH_STOP);
+        myssh_state(data, sshc, SSH_STOP);
         result = sshc->actualcode;
       }
       break;
@@ -1762,16 +1800,15 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
       }
 
       SSH_STRING_FREE_CHAR(sshc->homedir);
-      data->state.most_recent_ftp_entrypath = NULL;
 
-      state(data, SSH_SESSION_DISCONNECT);
+      myssh_state(data, sshc, SSH_SESSION_DISCONNECT);
       break;
 
     case SSH_SCP_TRANS_INIT:
-      result = Curl_getworkingpath(data, sshc->homedir, &protop->path);
+      result = Curl_getworkingpath(data, sshc->homedir, &sshp->path);
       if(result) {
         sshc->actualcode = result;
-        state(data, SSH_STOP);
+        myssh_state(data, sshc, SSH_STOP);
         break;
       }
 
@@ -1787,13 +1824,13 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
         }
 
         sshc->scp_session =
-          ssh_scp_new(sshc->ssh_session, SSH_SCP_WRITE, protop->path);
-        state(data, SSH_SCP_UPLOAD_INIT);
+          ssh_scp_new(sshc->ssh_session, SSH_SCP_WRITE, sshp->path);
+        myssh_state(data, sshc, SSH_SCP_UPLOAD_INIT);
       }
       else {
         sshc->scp_session =
-          ssh_scp_new(sshc->ssh_session, SSH_SCP_READ, protop->path);
-        state(data, SSH_SCP_DOWNLOAD_INIT);
+          ssh_scp_new(sshc->ssh_session, SSH_SCP_READ, sshp->path);
+        myssh_state(data, sshc, SSH_SCP_DOWNLOAD_INIT);
       }
 
       if(!sshc->scp_session) {
@@ -1814,9 +1851,10 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
         break;
       }
 
-      rc = ssh_scp_push_file(sshc->scp_session, protop->path,
-                             (size_t)data->state.infilesize,
-                             (int)data->set.new_file_perms);
+      rc = ssh_scp_push_file64(sshc->scp_session, sshp->path,
+                               (uint64_t)data->state.infilesize,
+                               (int)data->set.new_file_perms);
+
       if(rc != SSH_OK) {
         err_msg = ssh_get_error(sshc->ssh_session);
         failf(data, "%s", err_msg);
@@ -1839,7 +1877,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
          with both accordingly */
       data->state.select_bits = CURL_CSELECT_OUT;
 
-      state(data, SSH_STOP);
+      myssh_state(data, sshc, SSH_STOP);
 
       break;
 
@@ -1852,7 +1890,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
         MOVE_TO_ERROR_STATE(CURLE_COULDNT_CONNECT);
         break;
       }
-      state(data, SSH_SCP_DOWNLOAD);
+      myssh_state(data, sshc, SSH_SCP_DOWNLOAD);
       FALLTHROUGH();
 
     case SSH_SCP_DOWNLOAD:{
@@ -1879,14 +1917,14 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
            with both accordingly */
         data->state.select_bits = CURL_CSELECT_IN;
 
-        state(data, SSH_STOP);
+        myssh_state(data, sshc, SSH_STOP);
         break;
       }
     case SSH_SCP_DONE:
       if(data->state.upload)
-        state(data, SSH_SCP_SEND_EOF);
+        myssh_state(data, sshc, SSH_SCP_SEND_EOF);
       else
-        state(data, SSH_SCP_CHANNEL_FREE);
+        myssh_state(data, sshc, SSH_SCP_CHANNEL_FREE);
       break;
 
     case SSH_SCP_SEND_EOF:
@@ -1904,7 +1942,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
         }
       }
 
-      state(data, SSH_SCP_CHANNEL_FREE);
+      myssh_state(data, sshc, SSH_SCP_CHANNEL_FREE);
       break;
 
     case SSH_SCP_CHANNEL_FREE:
@@ -1916,7 +1954,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
 
       ssh_set_blocking(sshc->ssh_session, 0);
 
-      state(data, SSH_SESSION_DISCONNECT);
+      myssh_state(data, sshc, SSH_SESSION_DISCONNECT);
       FALLTHROUGH();
 
     case SSH_SESSION_DISCONNECT:
@@ -1928,6 +1966,15 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
         sshc->scp_session = NULL;
       }
 
+      if(sshc->sftp_file) {
+        sftp_close(sshc->sftp_file);
+        sshc->sftp_file = NULL;
+      }
+      if(sshc->sftp_session) {
+        sftp_free(sshc->sftp_session);
+        sshc->sftp_session = NULL;
+      }
+
       ssh_disconnect(sshc->ssh_session);
       if(!ssh_version(SSH_VERSION_INT(0, 10, 0))) {
         /* conn->sock[FIRSTSOCKET] is closed by ssh_disconnect behind our back,
@@ -1937,64 +1984,25 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
       }
 
       SSH_STRING_FREE_CHAR(sshc->homedir);
-      data->state.most_recent_ftp_entrypath = NULL;
 
-      state(data, SSH_SESSION_FREE);
+      myssh_state(data, sshc, SSH_SESSION_FREE);
       FALLTHROUGH();
     case SSH_SESSION_FREE:
-      if(sshc->ssh_session) {
-        ssh_free(sshc->ssh_session);
-        sshc->ssh_session = NULL;
-      }
-
-      /* worst-case scenario cleanup */
-
-      DEBUGASSERT(sshc->ssh_session == NULL);
-      DEBUGASSERT(sshc->scp_session == NULL);
-
-      if(sshc->readdir_tmp) {
-        ssh_string_free_char(sshc->readdir_tmp);
-        sshc->readdir_tmp = NULL;
-      }
-
-      if(sshc->quote_attrs)
-        sftp_attributes_free(sshc->quote_attrs);
-
-      if(sshc->readdir_attrs)
-        sftp_attributes_free(sshc->readdir_attrs);
-
-      if(sshc->readdir_link_attrs)
-        sftp_attributes_free(sshc->readdir_link_attrs);
-
-      if(sshc->privkey)
-        ssh_key_free(sshc->privkey);
-      if(sshc->pubkey)
-        ssh_key_free(sshc->pubkey);
-
-      Curl_safefree(sshc->rsa_pub);
-      Curl_safefree(sshc->rsa);
-      Curl_safefree(sshc->quote_path1);
-      Curl_safefree(sshc->quote_path2);
-      Curl_dyn_free(&sshc->readdir_buf);
-      Curl_safefree(sshc->readdir_linkPath);
-      SSH_STRING_FREE_CHAR(sshc->homedir);
-
+      sshc_cleanup(sshc);
       /* the code we are about to return */
       result = sshc->actualcode;
-
       memset(sshc, 0, sizeof(struct ssh_conn));
-
       connclose(conn, "SSH session free");
       sshc->state = SSH_SESSION_FREE;   /* current */
       sshc->nextstate = SSH_NO_STATE;
-      state(data, SSH_STOP);
+      myssh_state(data, sshc, SSH_STOP);
       break;
 
     case SSH_QUIT:
     default:
       /* internal error */
       sshc->nextstate = SSH_NO_STATE;
-      state(data, SSH_STOP);
+      myssh_state(data, sshc, SSH_STOP);
       break;
 
     }
@@ -2034,10 +2042,10 @@ static int myssh_getsock(struct Curl_easy *data,
   return bitmap;
 }
 
-static void myssh_block2waitfor(struct connectdata *conn, bool block)
+static void myssh_block2waitfor(struct connectdata *conn,
+                                struct ssh_conn *sshc,
+                                bool block)
 {
-  struct ssh_conn *sshc = &conn->proto.sshc;
-
   /* If it did not block, or nothing was returned by ssh_get_poll_flags
    * have the original set */
   conn->waitfor = sshc->orig_waitfor;
@@ -2058,30 +2066,35 @@ static CURLcode myssh_multi_statemach(struct Curl_easy *data,
                                       bool *done)
 {
   struct connectdata *conn = data->conn;
-  struct ssh_conn *sshc = &conn->proto.sshc;
+  struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
+  struct SSHPROTO *sshp = Curl_meta_get(data, CURL_META_SSH_EASY);
   bool block;    /* we store the status and use that to provide a ssh_getsock()
                     implementation */
-  CURLcode result = myssh_statemach_act(data, &block);
+  CURLcode result;
 
+  if(!sshc || !sshp)
+    return CURLE_FAILED_INIT;
+  result = myssh_statemach_act(data, sshc, sshp, &block);
   *done = (sshc->state == SSH_STOP);
-  myssh_block2waitfor(conn, block);
+  myssh_block2waitfor(conn, sshc, block);
 
   return result;
 }
 
 static CURLcode myssh_block_statemach(struct Curl_easy *data,
+                                      struct ssh_conn *sshc,
+                                      struct SSHPROTO *sshp,
                                       bool disconnect)
 {
   struct connectdata *conn = data->conn;
-  struct ssh_conn *sshc = &conn->proto.sshc;
   CURLcode result = CURLE_OK;
 
   while((sshc->state != SSH_STOP) && !result) {
     bool block;
     timediff_t left = 1000;
-    struct curltime now = Curl_now();
+    struct curltime now = curlx_now();
 
-    result = myssh_statemach_act(data, &block);
+    result = myssh_statemach_act(data, sshc, sshp, &block);
     if(result)
       break;
 
@@ -2112,19 +2125,46 @@ static CURLcode myssh_block_statemach(struct Curl_easy *data,
   return result;
 }
 
+static void myssh_easy_dtor(void *key, size_t klen, void *entry)
+{
+  struct SSHPROTO *sshp = entry;
+  (void)key;
+  (void)klen;
+  Curl_safefree(sshp->path);
+  free(sshp);
+}
+
+static void myssh_conn_dtor(void *key, size_t klen, void *entry)
+{
+  struct ssh_conn *sshc = entry;
+  (void)key;
+  (void)klen;
+  sshc_cleanup(sshc);
+  free(sshc);
+}
+
 /*
  * SSH setup connection
  */
 static CURLcode myssh_setup_connection(struct Curl_easy *data,
                                        struct connectdata *conn)
 {
-  struct SSHPROTO *ssh;
-  struct ssh_conn *sshc = &conn->proto.sshc;
+  struct SSHPROTO *sshp;
+  struct ssh_conn *sshc;
+
+  sshc = calloc(1, sizeof(*sshc));
+  if(!sshc)
+    return CURLE_OUT_OF_MEMORY;
 
-  data->req.p.ssh = ssh = calloc(1, sizeof(struct SSHPROTO));
-  if(!ssh)
+  curlx_dyn_init(&sshc->readdir_buf, CURL_PATH_MAX * 2);
+  sshc->initialised = TRUE;
+  if(Curl_conn_meta_set(conn, CURL_META_SSH_CONN, sshc, myssh_conn_dtor))
+    return CURLE_OUT_OF_MEMORY;
+
+  sshp = calloc(1, sizeof(*sshp));
+  if(!sshp ||
+     Curl_meta_set(data, CURL_META_SSH_EASY, sshp, myssh_easy_dtor))
     return CURLE_OUT_OF_MEMORY;
-  Curl_dyn_init(&sshc->readdir_buf, CURL_PATH_MAX * 2);
 
   return CURLE_OK;
 }
@@ -2138,15 +2178,15 @@ static Curl_send scp_send, sftp_send;
  */
 static CURLcode myssh_connect(struct Curl_easy *data, bool *done)
 {
-  struct ssh_conn *ssh;
   CURLcode result;
   struct connectdata *conn = data->conn;
+  struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
+  struct SSHPROTO *ssh = Curl_meta_get(data, CURL_META_SSH_EASY);
   curl_socket_t sock = conn->sock[FIRSTSOCKET];
   int rc;
 
-  /* initialize per-handle data if not already */
-  if(!data->req.p.ssh)
-    myssh_setup_connection(data, conn);
+  if(!sshc || !ssh)
+    return CURLE_FAILED_INIT;
 
   /* We default to persistent connections. We set this already in this connect
      function to make the reuse checks properly be able to check this bit. */
@@ -2161,10 +2201,8 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done)
     conn->send[FIRSTSOCKET] = sftp_send;
   }
 
-  ssh = &conn->proto.sshc;
-
-  ssh->ssh_session = ssh_new();
-  if(!ssh->ssh_session) {
+  sshc->ssh_session = ssh_new();
+  if(!sshc->ssh_session) {
     failf(data, "Failure initialising ssh session");
     return CURLE_FAILED_INIT;
   }
@@ -2172,23 +2210,23 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done)
   if(conn->bits.ipv6_ip) {
     char ipv6[MAX_IPADR_LEN];
     msnprintf(ipv6, sizeof(ipv6), "[%s]", conn->host.name);
-    rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_HOST, ipv6);
+    rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_HOST, ipv6);
   }
   else
-    rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_HOST, conn->host.name);
+    rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_HOST, conn->host.name);
 
   if(rc != SSH_OK) {
     failf(data, "Could not set remote host");
     return CURLE_FAILED_INIT;
   }
 
-  rc = ssh_options_parse_config(ssh->ssh_session, NULL);
+  rc = ssh_options_parse_config(sshc->ssh_session, NULL);
   if(rc != SSH_OK) {
     infof(data, "Could not parse SSH configuration files");
     /* ignore */
   }
 
-  rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_FD, &sock);
+  rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_FD, &sock);
   if(rc != SSH_OK) {
     failf(data, "Could not set socket");
     return CURLE_FAILED_INIT;
@@ -2196,7 +2234,7 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done)
 
   if(conn->user && conn->user[0] != '\0') {
     infof(data, "User: %s", conn->user);
-    rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_USER, conn->user);
+    rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_USER, conn->user);
     if(rc != SSH_OK) {
       failf(data, "Could not set user");
       return CURLE_FAILED_INIT;
@@ -2205,7 +2243,7 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done)
 
   if(data->set.str[STRING_SSH_KNOWNHOSTS]) {
     infof(data, "Known hosts: %s", data->set.str[STRING_SSH_KNOWNHOSTS]);
-    rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_KNOWNHOSTS,
+    rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_KNOWNHOSTS,
                          data->set.str[STRING_SSH_KNOWNHOSTS]);
     if(rc != SSH_OK) {
       failf(data, "Could not set known hosts file path");
@@ -2214,7 +2252,7 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done)
   }
 
   if(conn->remote_port) {
-    rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_PORT,
+    rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_PORT,
                          &conn->remote_port);
     if(rc != SSH_OK) {
       failf(data, "Could not set remote port");
@@ -2223,7 +2261,7 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done)
   }
 
   if(data->set.ssh_compression) {
-    rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_COMPRESSION,
+    rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_COMPRESSION,
                          "zlib,zlib@openssh.com,none");
     if(rc != SSH_OK) {
       failf(data, "Could not set compression");
@@ -2231,12 +2269,12 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done)
     }
   }
 
-  ssh->privkey = NULL;
-  ssh->pubkey = NULL;
+  sshc->privkey = NULL;
+  sshc->pubkey = NULL;
 
   if(data->set.str[STRING_SSH_PUBLIC_KEY]) {
     rc = ssh_pki_import_pubkey_file(data->set.str[STRING_SSH_PUBLIC_KEY],
-                                    &ssh->pubkey);
+                                    &sshc->pubkey);
     if(rc != SSH_OK) {
       failf(data, "Could not load public key file");
       return CURLE_FAILED_INIT;
@@ -2246,7 +2284,7 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done)
   /* we do not verify here, we do it at the state machine,
    * after connection */
 
-  state(data, SSH_INIT);
+  myssh_state(data, sshc, SSH_INIT);
 
   result = myssh_multi_statemach(data, done);
 
@@ -2280,13 +2318,16 @@ CURLcode scp_perform(struct Curl_easy *data,
                      bool *connected, bool *dophase_done)
 {
   CURLcode result = CURLE_OK;
+  struct ssh_conn *sshc = Curl_conn_meta_get(data->conn, CURL_META_SSH_CONN);
 
   DEBUGF(infof(data, "DO phase starts"));
 
   *dophase_done = FALSE;        /* not done yet */
+  if(!sshc)
+    return CURLE_FAILED_INIT;
 
   /* start the first command in the DO phase */
-  state(data, SSH_SCP_TRANS_INIT);
+  myssh_state(data, sshc, SSH_SCP_TRANS_INIT);
 
   result = myssh_multi_statemach(data, dophase_done);
 
@@ -2304,9 +2345,11 @@ static CURLcode myssh_do_it(struct Curl_easy *data, bool *done)
   CURLcode result;
   bool connected = FALSE;
   struct connectdata *conn = data->conn;
-  struct ssh_conn *sshc = &conn->proto.sshc;
+  struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
 
   *done = FALSE;                /* default to false */
+  if(!sshc)
+    return CURLE_FAILED_INIT;
 
   data->req.size = -1;          /* make sure this is unknown at this point */
 
@@ -2327,6 +2370,62 @@ static CURLcode myssh_do_it(struct Curl_easy *data, bool *done)
   return result;
 }
 
+static void sshc_cleanup(struct ssh_conn *sshc)
+{
+  if(sshc->initialised) {
+    if(sshc->sftp_file) {
+      sftp_close(sshc->sftp_file);
+      sshc->sftp_file = NULL;
+    }
+    if(sshc->sftp_session) {
+      sftp_free(sshc->sftp_session);
+      sshc->sftp_session = NULL;
+    }
+    if(sshc->ssh_session) {
+      ssh_free(sshc->ssh_session);
+      sshc->ssh_session = NULL;
+    }
+
+    /* worst-case scenario cleanup */
+    DEBUGASSERT(sshc->ssh_session == NULL);
+    DEBUGASSERT(sshc->scp_session == NULL);
+
+    if(sshc->readdir_tmp) {
+      ssh_string_free_char(sshc->readdir_tmp);
+      sshc->readdir_tmp = NULL;
+    }
+    if(sshc->quote_attrs) {
+      sftp_attributes_free(sshc->quote_attrs);
+      sshc->quote_attrs = NULL;
+    }
+    if(sshc->readdir_attrs) {
+      sftp_attributes_free(sshc->readdir_attrs);
+      sshc->readdir_attrs = NULL;
+    }
+    if(sshc->readdir_link_attrs) {
+      sftp_attributes_free(sshc->readdir_link_attrs);
+      sshc->readdir_link_attrs = NULL;
+    }
+    if(sshc->privkey) {
+      ssh_key_free(sshc->privkey);
+      sshc->privkey = NULL;
+    }
+    if(sshc->pubkey) {
+      ssh_key_free(sshc->pubkey);
+      sshc->pubkey = NULL;
+    }
+
+    Curl_safefree(sshc->rsa_pub);
+    Curl_safefree(sshc->rsa);
+    Curl_safefree(sshc->quote_path1);
+    Curl_safefree(sshc->quote_path2);
+    curlx_dyn_free(&sshc->readdir_buf);
+    Curl_safefree(sshc->readdir_linkPath);
+    SSH_STRING_FREE_CHAR(sshc->homedir);
+    sshc->initialised = FALSE;
+  }
+}
+
 /* BLOCKING, but the function is using the state machine so the only reason
    this is still blocking is that the multi interface code has no support for
    disconnecting operations that takes a while */
@@ -2335,15 +2434,16 @@ static CURLcode scp_disconnect(struct Curl_easy *data,
                                bool dead_connection)
 {
   CURLcode result = CURLE_OK;
-  struct ssh_conn *ssh = &conn->proto.sshc;
+  struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
+  struct SSHPROTO *sshp = Curl_meta_get(data, CURL_META_SSH_EASY);
   (void) dead_connection;
 
-  if(ssh->ssh_session) {
+  if(sshc && sshc->ssh_session && sshp) {
     /* only if there is a session still around to use! */
 
-    state(data, SSH_SESSION_DISCONNECT);
+    myssh_state(data, sshc, SSH_SESSION_DISCONNECT);
 
-    result = myssh_block_statemach(data, TRUE);
+    result = myssh_block_statemach(data, sshc, sshp, TRUE);
   }
 
   return result;
@@ -2351,20 +2451,20 @@ static CURLcode scp_disconnect(struct Curl_easy *data,
 
 /* generic done function for both SCP and SFTP called from their specific
    done functions */
-static CURLcode myssh_done(struct Curl_easy *data, CURLcode status)
+static CURLcode myssh_done(struct Curl_easy *data,
+                           struct ssh_conn *sshc,
+                           CURLcode status)
 {
   CURLcode result = CURLE_OK;
-  struct SSHPROTO *protop = data->req.p.ssh;
+  struct SSHPROTO *sshp = Curl_meta_get(data, CURL_META_SSH_EASY);
 
-  if(!status) {
+  if(!status && sshp) {
     /* run the state-machine */
-    result = myssh_block_statemach(data, FALSE);
+    result = myssh_block_statemach(data, sshc, sshp, FALSE);
   }
   else
     result = status;
 
-  if(protop)
-    Curl_safefree(protop->path);
   if(Curl_pgrsDone(data))
     return CURLE_ABORTED_BY_CALLBACK;
 
@@ -2376,13 +2476,15 @@ static CURLcode myssh_done(struct Curl_easy *data, CURLcode status)
 static CURLcode scp_done(struct Curl_easy *data, CURLcode status,
                          bool premature)
 {
+  struct ssh_conn *sshc = Curl_conn_meta_get(data->conn, CURL_META_SSH_CONN);
   (void) premature;             /* not used */
 
+  if(!sshc)
+    return CURLE_FAILED_INIT;
   if(!status)
-    state(data, SSH_SCP_DONE);
-
-  return myssh_done(data, status);
+    myssh_state(data, sshc, SSH_SCP_DONE);
 
+  return myssh_done(data, sshc, status);
 }
 
 static ssize_t scp_send(struct Curl_easy *data, int sockindex,
@@ -2390,17 +2492,22 @@ static ssize_t scp_send(struct Curl_easy *data, int sockindex,
 {
   int rc;
   struct connectdata *conn = data->conn;
+  struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
   (void) sockindex; /* we only support SCP on the fixed known primary socket */
-  (void) err;
   (void)eos;
 
-  rc = ssh_scp_write(conn->proto.sshc.scp_session, mem, len);
+  if(!sshc) {
+    *err = CURLE_FAILED_INIT;
+    return -1;
+  }
+
+  rc = ssh_scp_write(sshc->scp_session, mem, len);
 
 #if 0
   /* The following code is misleading, mostly added as wishful thinking
    * that libssh at some point will implement non-blocking ssh_scp_write/read.
    * Currently rc can only be number of bytes read or SSH_ERROR. */
-  myssh_block2waitfor(conn, (rc == SSH_AGAIN));
+  myssh_block2waitfor(conn, sshc, (rc == SSH_AGAIN));
 
   if(rc == SSH_AGAIN) {
     *err = CURLE_AGAIN;
@@ -2421,18 +2528,22 @@ static ssize_t scp_recv(struct Curl_easy *data, int sockindex,
 {
   ssize_t nread;
   struct connectdata *conn = data->conn;
-  (void) err;
+  struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
   (void) sockindex; /* we only support SCP on the fixed known primary socket */
 
+  if(!sshc) {
+    *err = CURLE_FAILED_INIT;
+    return -1;
+  }
   /* libssh returns int */
-  nread = ssh_scp_read(conn->proto.sshc.scp_session, mem, len);
+  nread = ssh_scp_read(sshc->scp_session, mem, len);
 
 #if 0
   /* The following code is misleading, mostly added as wishful thinking
    * that libssh at some point will implement non-blocking ssh_scp_write/read.
    * Currently rc can only be SSH_OK or SSH_ERROR. */
 
-  myssh_block2waitfor(conn, (nread == SSH_AGAIN));
+  myssh_block2waitfor(conn, sshc, (nread == SSH_AGAIN));
   if(nread == SSH_AGAIN) {
     *err = CURLE_AGAIN;
     nread = -1;
@@ -2460,14 +2571,17 @@ CURLcode sftp_perform(struct Curl_easy *data,
                       bool *connected,
                       bool *dophase_done)
 {
+  struct ssh_conn *sshc = Curl_conn_meta_get(data->conn, CURL_META_SSH_CONN);
   CURLcode result = CURLE_OK;
 
   DEBUGF(infof(data, "DO phase starts"));
 
   *dophase_done = FALSE; /* not done yet */
+  if(!sshc)
+    return CURLE_FAILED_INIT;
 
   /* start the first command in the DO phase */
-  state(data, SSH_SFTP_QUOTE_INIT);
+  myssh_state(data, sshc, SSH_SFTP_QUOTE_INIT);
 
   /* run the state-machine */
   result = myssh_multi_statemach(data, dophase_done);
@@ -2499,38 +2613,40 @@ static CURLcode sftp_disconnect(struct Curl_easy *data,
                                 struct connectdata *conn,
                                 bool dead_connection)
 {
+  struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
+  struct SSHPROTO *sshp = Curl_meta_get(data, CURL_META_SSH_EASY);
   CURLcode result = CURLE_OK;
   (void) dead_connection;
 
   DEBUGF(infof(data, "SSH DISCONNECT starts now"));
 
-  if(conn->proto.sshc.ssh_session) {
+  if(sshc && sshc->ssh_session && sshp) {
     /* only if there is a session still around to use! */
-    state(data, SSH_SFTP_SHUTDOWN);
-    result = myssh_block_statemach(data, TRUE);
+    myssh_state(data, sshc, SSH_SFTP_SHUTDOWN);
+    result = myssh_block_statemach(data, sshc, sshp, TRUE);
   }
 
   DEBUGF(infof(data, "SSH DISCONNECT is done"));
-
   return result;
-
 }
 
 static CURLcode sftp_done(struct Curl_easy *data, CURLcode status,
                           bool premature)
 {
   struct connectdata *conn = data->conn;
-  struct ssh_conn *sshc = &conn->proto.sshc;
+  struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
 
+  if(!sshc)
+    return CURLE_FAILED_INIT;
   if(!status) {
     /* Post quote commands are executed after the SFTP_CLOSE state to avoid
        errors that could happen due to open file handles during POSTQUOTE
        operation */
     if(!premature && data->set.postquote && !conn->bits.retry)
       sshc->nextstate = SSH_SFTP_POSTQUOTE_INIT;
-    state(data, SSH_SFTP_CLOSE);
+    myssh_state(data, sshc, SSH_SFTP_CLOSE);
   }
-  return myssh_done(data, status);
+  return myssh_done(data, sshc, status);
 }
 
 /* return number of sent bytes */
@@ -2540,28 +2656,33 @@ static ssize_t sftp_send(struct Curl_easy *data, int sockindex,
 {
   ssize_t nwrite;
   struct connectdata *conn = data->conn;
+  struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
   (void)sockindex;
   (void)eos;
 
+  if(!sshc) {
+    *err = CURLE_FAILED_INIT;
+    return -1;
+  }
   /* limit the writes to the maximum specified in Section 3 of
    * https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-02
    */
   if(len > 32768)
     len = 32768;
 #if LIBSSH_VERSION_INT > SSH_VERSION_INT(0, 11, 0)
-  switch(conn->proto.sshc.sftp_send_state) {
+  switch(sshc->sftp_send_state) {
     case 0:
-      sftp_file_set_nonblocking(conn->proto.sshc.sftp_file);
-      if(sftp_aio_begin_write(conn->proto.sshc.sftp_file, mem, len,
-                              &conn->proto.sshc.sftp_aio) == SSH_ERROR) {
+      sftp_file_set_nonblocking(sshc->sftp_file);
+      if(sftp_aio_begin_write(sshc->sftp_file, mem, len,
+                              &sshc->sftp_aio) == SSH_ERROR) {
         *err = CURLE_SEND_ERROR;
         return -1;
       }
-      conn->proto.sshc.sftp_send_state = 1;
+      sshc->sftp_send_state = 1;
       FALLTHROUGH();
     case 1:
-      nwrite = sftp_aio_wait_write(&conn->proto.sshc.sftp_aio);
-      myssh_block2waitfor(conn, (nwrite == SSH_AGAIN) ? TRUE : FALSE);
+      nwrite = sftp_aio_wait_write(&sshc->sftp_aio);
+      myssh_block2waitfor(conn, sshc, (nwrite == SSH_AGAIN) ? TRUE : FALSE);
       if(nwrite == SSH_AGAIN) {
         *err = CURLE_AGAIN;
         return 0;
@@ -2570,20 +2691,20 @@ static ssize_t sftp_send(struct Curl_easy *data, int sockindex,
         *err = CURLE_SEND_ERROR;
         return -1;
       }
-      if(conn->proto.sshc.sftp_aio) {
-        sftp_aio_free(conn->proto.sshc.sftp_aio);
-        conn->proto.sshc.sftp_aio = NULL;
+      if(sshc->sftp_aio) {
+        sftp_aio_free(sshc->sftp_aio);
+        sshc->sftp_aio = NULL;
       }
-      conn->proto.sshc.sftp_send_state = 0;
+      sshc->sftp_send_state = 0;
       return nwrite;
     default:
       /* we never reach here */
       return -1;
   }
 #else
-  nwrite = sftp_write(conn->proto.sshc.sftp_file, mem, len);
+  nwrite = sftp_write(sshc->sftp_file, mem, len);
 
-  myssh_block2waitfor(conn, FALSE);
+  myssh_block2waitfor(conn, sshc, FALSE);
 
 #if 0 /* not returned by libssh on write */
   if(nwrite == SSH_AGAIN) {
@@ -2610,29 +2731,31 @@ static ssize_t sftp_recv(struct Curl_easy *data, int sockindex,
 {
   ssize_t nread;
   struct connectdata *conn = data->conn;
+  struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
   (void)sockindex;
 
   DEBUGASSERT(len < CURL_MAX_READ_SIZE);
+  if(!sshc) {
+    *err = CURLE_FAILED_INIT;
+    return -1;
+  }
 
-  switch(conn->proto.sshc.sftp_recv_state) {
+  switch(sshc->sftp_recv_state) {
     case 0:
-      conn->proto.sshc.sftp_file_index =
-        sftp_async_read_begin(conn->proto.sshc.sftp_file,
-                              (uint32_t)len);
-      if(conn->proto.sshc.sftp_file_index < 0) {
+      sshc->sftp_file_index =
+        sftp_async_read_begin(sshc->sftp_file, (uint32_t)len);
+      if(sshc->sftp_file_index < 0) {
         *err = CURLE_RECV_ERROR;
         return -1;
       }
 
       FALLTHROUGH();
     case 1:
-      conn->proto.sshc.sftp_recv_state = 1;
-
-      nread = sftp_async_read(conn->proto.sshc.sftp_file,
-                              mem, (uint32_t)len,
-                              (uint32_t)conn->proto.sshc.sftp_file_index);
+      sshc->sftp_recv_state = 1;
+      nread = sftp_async_read(sshc->sftp_file, mem, (uint32_t)len,
+                              (uint32_t)sshc->sftp_file_index);
 
-      myssh_block2waitfor(conn, (nread == SSH_AGAIN));
+      myssh_block2waitfor(conn, sshc, (nread == SSH_AGAIN));
 
       if(nread == SSH_AGAIN) {
         *err = CURLE_AGAIN;
@@ -2643,7 +2766,7 @@ static ssize_t sftp_recv(struct Curl_easy *data, int sockindex,
         return -1;
       }
 
-      conn->proto.sshc.sftp_recv_state = 0;
+      sshc->sftp_recv_state = 0;
       return nread;
 
     default:
@@ -2652,12 +2775,11 @@ static ssize_t sftp_recv(struct Curl_easy *data, int sockindex,
   }
 }
 
-static void sftp_quote(struct Curl_easy *data)
+static void sftp_quote(struct Curl_easy *data,
+                       struct ssh_conn *sshc,
+                       struct SSHPROTO *sshp)
 {
   const char *cp;
-  struct connectdata *conn = data->conn;
-  struct SSHPROTO *protop = data->req.p.ssh;
-  struct ssh_conn *sshc = &conn->proto.sshc;
   CURLcode result;
 
   /*
@@ -2678,15 +2800,14 @@ static void sftp_quote(struct Curl_easy *data)
 
   if(strcasecompare("pwd", cmd)) {
     /* output debug output if that is requested */
-    char *tmp = aprintf("257 \"%s\" is current directory.\n",
-                        protop->path);
+    char *tmp = aprintf("257 \"%s\" is current directory.\n", sshp->path);
     if(!tmp) {
       sshc->actualcode = CURLE_OUT_OF_MEMORY;
-      state(data, SSH_SFTP_CLOSE);
+      myssh_state(data, sshc, SSH_SFTP_CLOSE);
       sshc->nextstate = SSH_NO_STATE;
       return;
     }
-    Curl_debug(data, CURLINFO_HEADER_OUT, (char *) "PWD\n", 4);
+    Curl_debug(data, CURLINFO_HEADER_OUT, "PWD\n", 4);
     Curl_debug(data, CURLINFO_HEADER_IN, tmp, strlen(tmp));
 
     /* this sends an FTP-like "header" to the header callback so that the
@@ -2695,12 +2816,12 @@ static void sftp_quote(struct Curl_easy *data)
     result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp));
     free(tmp);
     if(result) {
-      state(data, SSH_SFTP_CLOSE);
+      myssh_state(data, sshc, SSH_SFTP_CLOSE);
       sshc->nextstate = SSH_NO_STATE;
       sshc->actualcode = result;
     }
     else
-      state(data, SSH_SFTP_NEXT_QUOTE);
+      myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
     return;
   }
 
@@ -2711,7 +2832,7 @@ static void sftp_quote(struct Curl_easy *data)
   cp = strchr(cmd, ' ');
   if(!cp) {
     failf(data, "Syntax error in SFTP command. Supply parameter(s)");
-    state(data, SSH_SFTP_CLOSE);
+    myssh_state(data, sshc, SSH_SFTP_CLOSE);
     sshc->nextstate = SSH_NO_STATE;
     sshc->actualcode = CURLE_QUOTE_ERROR;
     return;
@@ -2727,7 +2848,7 @@ static void sftp_quote(struct Curl_easy *data)
       failf(data, "Out of memory");
     else
       failf(data, "Syntax error: Bad first parameter");
-    state(data, SSH_SFTP_CLOSE);
+    myssh_state(data, sshc, SSH_SFTP_CLOSE);
     sshc->nextstate = SSH_NO_STATE;
     sshc->actualcode = result;
     return;
@@ -2739,11 +2860,11 @@ static void sftp_quote(struct Curl_easy *data)
    * OpenSSH's sftp program and call the appropriate libssh
    * functions.
    */
-  if(strncasecompare(cmd, "chgrp ", 6) ||
-     strncasecompare(cmd, "chmod ", 6) ||
-     strncasecompare(cmd, "chown ", 6) ||
-     strncasecompare(cmd, "atime ", 6) ||
-     strncasecompare(cmd, "mtime ", 6)) {
+  if(!strncmp(cmd, "chgrp ", 6) ||
+     !strncmp(cmd, "chmod ", 6) ||
+     !strncmp(cmd, "chown ", 6) ||
+     !strncmp(cmd, "atime ", 6) ||
+     !strncmp(cmd, "mtime ", 6)) {
     /* attribute change */
 
     /* sshc->quote_path1 contains the mode to set */
@@ -2756,17 +2877,17 @@ static void sftp_quote(struct Curl_easy *data)
         failf(data, "Syntax error in chgrp/chmod/chown/atime/mtime: "
               "Bad second parameter");
       Curl_safefree(sshc->quote_path1);
-      state(data, SSH_SFTP_CLOSE);
+      myssh_state(data, sshc, SSH_SFTP_CLOSE);
       sshc->nextstate = SSH_NO_STATE;
       sshc->actualcode = result;
       return;
     }
     sshc->quote_attrs = NULL;
-    state(data, SSH_SFTP_QUOTE_STAT);
+    myssh_state(data, sshc, SSH_SFTP_QUOTE_STAT);
     return;
   }
-  if(strncasecompare(cmd, "ln ", 3) ||
-     strncasecompare(cmd, "symlink ", 8)) {
+  if(!strncmp(cmd, "ln ", 3) ||
+     !strncmp(cmd, "symlink ", 8)) {
     /* symbolic linking */
     /* sshc->quote_path1 is the source */
     /* get the destination */
@@ -2777,20 +2898,20 @@ static void sftp_quote(struct Curl_easy *data)
       else
         failf(data, "Syntax error in ln/symlink: Bad second parameter");
       Curl_safefree(sshc->quote_path1);
-      state(data, SSH_SFTP_CLOSE);
+      myssh_state(data, sshc, SSH_SFTP_CLOSE);
       sshc->nextstate = SSH_NO_STATE;
       sshc->actualcode = result;
       return;
     }
-    state(data, SSH_SFTP_QUOTE_SYMLINK);
+    myssh_state(data, sshc, SSH_SFTP_QUOTE_SYMLINK);
     return;
   }
-  else if(strncasecompare(cmd, "mkdir ", 6)) {
+  else if(!strncmp(cmd, "mkdir ", 6)) {
     /* create dir */
-    state(data, SSH_SFTP_QUOTE_MKDIR);
+    myssh_state(data, sshc, SSH_SFTP_QUOTE_MKDIR);
     return;
   }
-  else if(strncasecompare(cmd, "rename ", 7)) {
+  else if(!strncmp(cmd, "rename ", 7)) {
     /* rename file */
     /* first param is the source path */
     /* second param is the dest. path */
@@ -2801,26 +2922,26 @@ static void sftp_quote(struct Curl_easy *data)
       else
         failf(data, "Syntax error in rename: Bad second parameter");
       Curl_safefree(sshc->quote_path1);
-      state(data, SSH_SFTP_CLOSE);
+      myssh_state(data, sshc, SSH_SFTP_CLOSE);
       sshc->nextstate = SSH_NO_STATE;
       sshc->actualcode = result;
       return;
     }
-    state(data, SSH_SFTP_QUOTE_RENAME);
+    myssh_state(data, sshc, SSH_SFTP_QUOTE_RENAME);
     return;
   }
-  else if(strncasecompare(cmd, "rmdir ", 6)) {
+  else if(!strncmp(cmd, "rmdir ", 6)) {
     /* delete dir */
-    state(data, SSH_SFTP_QUOTE_RMDIR);
+    myssh_state(data, sshc, SSH_SFTP_QUOTE_RMDIR);
     return;
   }
-  else if(strncasecompare(cmd, "rm ", 3)) {
-    state(data, SSH_SFTP_QUOTE_UNLINK);
+  else if(!strncmp(cmd, "rm ", 3)) {
+    myssh_state(data, sshc, SSH_SFTP_QUOTE_UNLINK);
     return;
   }
 #ifdef HAS_STATVFS_SUPPORT
-  else if(strncasecompare(cmd, "statvfs ", 8)) {
-    state(data, SSH_SFTP_QUOTE_STATVFS);
+  else if(!strncmp(cmd, "statvfs ", 8)) {
+    myssh_state(data, sshc, SSH_SFTP_QUOTE_STATVFS);
     return;
   }
 #endif
@@ -2828,15 +2949,14 @@ static void sftp_quote(struct Curl_easy *data)
   failf(data, "Unknown SFTP command");
   Curl_safefree(sshc->quote_path1);
   Curl_safefree(sshc->quote_path2);
-  state(data, SSH_SFTP_CLOSE);
+  myssh_state(data, sshc, SSH_SFTP_CLOSE);
   sshc->nextstate = SSH_NO_STATE;
   sshc->actualcode = CURLE_QUOTE_ERROR;
 }
 
-static void sftp_quote_stat(struct Curl_easy *data)
+static void sftp_quote_stat(struct Curl_easy *data,
+                            struct ssh_conn *sshc)
 {
-  struct connectdata *conn = data->conn;
-  struct ssh_conn *sshc = &conn->proto.sshc;
   char *cmd = sshc->quote_item->data;
   sshc->acceptfail = FALSE;
 
@@ -2863,59 +2983,63 @@ static void sftp_quote_stat(struct Curl_easy *data)
     Curl_safefree(sshc->quote_path2);
     failf(data, "Attempt to get SFTP stats failed: %d",
           sftp_get_error(sshc->sftp_session));
-    state(data, SSH_SFTP_CLOSE);
+    myssh_state(data, sshc, SSH_SFTP_CLOSE);
     sshc->nextstate = SSH_NO_STATE;
     sshc->actualcode = CURLE_QUOTE_ERROR;
     return;
   }
 
   /* Now set the new attributes... */
-  if(strncasecompare(cmd, "chgrp", 5)) {
-    sshc->quote_attrs->gid = (uint32_t)strtoul(sshc->quote_path1, NULL, 10);
+  if(!strncmp(cmd, "chgrp", 5)) {
+    const char *p = sshc->quote_path1;
+    curl_off_t gid;
+    (void)curlx_str_number(&p, &gid, UINT_MAX);
+    sshc->quote_attrs->gid = (uint32_t)gid;
     if(sshc->quote_attrs->gid == 0 && !ISDIGIT(sshc->quote_path1[0]) &&
-        !sshc->acceptfail) {
+       !sshc->acceptfail) {
       Curl_safefree(sshc->quote_path1);
       Curl_safefree(sshc->quote_path2);
       failf(data, "Syntax error: chgrp gid not a number");
-      state(data, SSH_SFTP_CLOSE);
+      myssh_state(data, sshc, SSH_SFTP_CLOSE);
       sshc->nextstate = SSH_NO_STATE;
       sshc->actualcode = CURLE_QUOTE_ERROR;
       return;
     }
     sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_UIDGID;
   }
-  else if(strncasecompare(cmd, "chmod", 5)) {
-    mode_t perms;
-    perms = (mode_t)strtoul(sshc->quote_path1, NULL, 8);
-    /* permissions are octal */
-    if(perms == 0 && !ISDIGIT(sshc->quote_path1[0])) {
+  else if(!strncmp(cmd, "chmod", 5)) {
+    curl_off_t perms;
+    const char *p = sshc->quote_path1;
+    if(curlx_str_octal(&p, &perms, 07777)) {
       Curl_safefree(sshc->quote_path1);
       Curl_safefree(sshc->quote_path2);
       failf(data, "Syntax error: chmod permissions not a number");
-      state(data, SSH_SFTP_CLOSE);
+      myssh_state(data, sshc, SSH_SFTP_CLOSE);
       sshc->nextstate = SSH_NO_STATE;
       sshc->actualcode = CURLE_QUOTE_ERROR;
       return;
     }
-    sshc->quote_attrs->permissions = perms;
+    sshc->quote_attrs->permissions = (mode_t)perms;
     sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_PERMISSIONS;
   }
-  else if(strncasecompare(cmd, "chown", 5)) {
-    sshc->quote_attrs->uid = (uint32_t)strtoul(sshc->quote_path1, NULL, 10);
+  else if(!strncmp(cmd, "chown", 5)) {
+    const char *p = sshc->quote_path1;
+    curl_off_t uid;
+    (void)curlx_str_number(&p, &uid, UINT_MAX);
     if(sshc->quote_attrs->uid == 0 && !ISDIGIT(sshc->quote_path1[0]) &&
-        !sshc->acceptfail) {
+       !sshc->acceptfail) {
       Curl_safefree(sshc->quote_path1);
       Curl_safefree(sshc->quote_path2);
       failf(data, "Syntax error: chown uid not a number");
-      state(data, SSH_SFTP_CLOSE);
+      myssh_state(data, sshc, SSH_SFTP_CLOSE);
       sshc->nextstate = SSH_NO_STATE;
       sshc->actualcode = CURLE_QUOTE_ERROR;
       return;
     }
     sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_UIDGID;
   }
-  else if(strncasecompare(cmd, "atime", 5) ||
-          strncasecompare(cmd, "mtime", 5)) {
+  else if(!strncmp(cmd, "atime", 5) ||
+          !strncmp(cmd, "mtime", 5)) {
     time_t date = Curl_getdate_capped(sshc->quote_path1);
     bool fail = FALSE;
     if(date == -1) {
@@ -2931,12 +3055,12 @@ static void sftp_quote_stat(struct Curl_easy *data)
     if(fail) {
       Curl_safefree(sshc->quote_path1);
       Curl_safefree(sshc->quote_path2);
-      state(data, SSH_SFTP_CLOSE);
+      myssh_state(data, sshc, SSH_SFTP_CLOSE);
       sshc->nextstate = SSH_NO_STATE;
       sshc->actualcode = CURLE_QUOTE_ERROR;
       return;
     }
-    if(strncasecompare(cmd, "atime", 5))
+    if(!strncmp(cmd, "atime", 5))
       sshc->quote_attrs->atime = (uint32_t)date;
     else /* mtime */
       sshc->quote_attrs->mtime = (uint32_t)date;
@@ -2945,7 +3069,7 @@ static void sftp_quote_stat(struct Curl_easy *data)
   }
 
   /* Now send the completed structure... */
-  state(data, SSH_SFTP_QUOTE_SETSTAT);
+  myssh_state(data, sshc, SSH_SFTP_QUOTE_SETSTAT);
   return;
 }
 
diff --git a/extra/curl/curl-8.12.1/lib/vssh/libssh2.c b/extra/curl/curl-8.14.1/lib/vssh/libssh2.c
similarity index 58%
rename from extra/curl/curl-8.12.1/lib/vssh/libssh2.c
rename to extra/curl/curl-8.14.1/lib/vssh/libssh2.c
index 429abac31474..c16b3ac38865 100644
--- a/extra/curl/curl-8.12.1/lib/vssh/libssh2.c
+++ b/extra/curl/curl-8.14.1/lib/vssh/libssh2.c
@@ -24,7 +24,7 @@
 
 /* #define CURL_LIBSSH2_DEBUG */
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #ifdef USE_LIBSSH2
 
@@ -49,46 +49,45 @@
 #endif
 
 #include <curl/curl.h>
-#include "urldata.h"
-#include "sendf.h"
-#include "hostip.h"
-#include "progress.h"
-#include "transfer.h"
-#include "escape.h"
-#include "http.h" /* for HTTP proxy tunnel stuff */
+#include "../urldata.h"
+#include "../sendf.h"
+#include "../hostip.h"
+#include "../progress.h"
+#include "../transfer.h"
+#include "../escape.h"
+#include "../http.h" /* for HTTP proxy tunnel stuff */
 #include "ssh.h"
-#include "url.h"
-#include "speedcheck.h"
-#include "getinfo.h"
-#include "strdup.h"
-#include "strcase.h"
-#include "vtls/vtls.h"
-#include "cfilters.h"
-#include "connect.h"
-#include "inet_ntop.h"
-#include "parsedate.h" /* for the week day and month names */
-#include "sockaddr.h" /* required for Curl_sockaddr_storage */
-#include "strtoofft.h"
-#include "multiif.h"
-#include "select.h"
-#include "warnless.h"
+#include "../url.h"
+#include "../speedcheck.h"
+#include "../getinfo.h"
+#include "../strdup.h"
+#include "../strcase.h"
+#include "../vtls/vtls.h"
+#include "../cfilters.h"
+#include "../connect.h"
+#include "../inet_ntop.h"
+#include "../parsedate.h" /* for the week day and month names */
+#include "../sockaddr.h" /* required for Curl_sockaddr_storage */
+#include "../multiif.h"
+#include "../select.h"
+#include "../curlx/warnless.h"
 #include "curl_path.h"
-
-#include <curl_base64.h> /* for base64 encoding/decoding */
-#include <curl_sha256.h>
-
+#include "../curlx/strparse.h"
+#include "../curlx/base64.h" /* for base64 encoding/decoding */
+#include "../curl_sha256.h"
 
 /* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_printf.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
 
 /* Local functions: */
 static const char *sftp_libssh2_strerror(unsigned long err);
 static LIBSSH2_ALLOC_FUNC(my_libssh2_malloc);
 static LIBSSH2_REALLOC_FUNC(my_libssh2_realloc);
 static LIBSSH2_FREE_FUNC(my_libssh2_free);
-static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data);
+static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data,
+                                             struct ssh_conn *sshc);
 static CURLcode ssh_connect(struct Curl_easy *data, bool *done);
 static CURLcode ssh_multi_statemach(struct Curl_easy *data, bool *done);
 static CURLcode ssh_do(struct Curl_easy *data, bool *done);
@@ -107,7 +106,8 @@ static int ssh_getsock(struct Curl_easy *data, struct connectdata *conn,
 static CURLcode ssh_setup_connection(struct Curl_easy *data,
                                      struct connectdata *conn);
 static void ssh_attach(struct Curl_easy *data, struct connectdata *conn);
-
+static CURLcode sshc_cleanup(struct ssh_conn *sshc, struct Curl_easy *data,
+                             bool block);
 /*
  * SCP protocol handler.
  */
@@ -299,10 +299,10 @@ static LIBSSH2_FREE_FUNC(my_libssh2_free)
  * SSH State machine related code
  */
 /* This is the ONLY way to change SSH state! */
-static void state(struct Curl_easy *data, sshstate nowstate)
+static void myssh_state(struct Curl_easy *data,
+                        struct ssh_conn *sshc,
+                        sshstate nowstate)
 {
-  struct connectdata *conn = data->conn;
-  struct ssh_conn *sshc = &conn->proto.sshc;
 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
   /* for debug purposes */
   static const char * const names[] = {
@@ -369,14 +369,14 @@ static void state(struct Curl_easy *data, sshstate nowstate)
   };
 
   /* a precaution to make sure the lists are in sync */
-  DEBUGASSERT(sizeof(names)/sizeof(names[0]) == SSH_LAST);
+  DEBUGASSERT(CURL_ARRAYSIZE(names) == SSH_LAST);
 
   if(sshc->state != nowstate) {
     infof(data, "SFTP %p state change from %s to %s",
           (void *)sshc, names[sshc->state], names[nowstate]);
   }
 #endif
-
+  (void)data;
   sshc->state = nowstate;
 }
 
@@ -429,7 +429,8 @@ static enum curl_khtype convert_ssh2_keytype(int sshkeytype)
   return keytype;
 }
 
-static CURLcode ssh_knownhost(struct Curl_easy *data)
+static CURLcode ssh_knownhost(struct Curl_easy *data,
+                              struct ssh_conn *sshc)
 {
   int sshkeytype = 0;
   size_t keylen = 0;
@@ -439,7 +440,6 @@ static CURLcode ssh_knownhost(struct Curl_easy *data)
   if(data->set.str[STRING_SSH_KNOWNHOSTS]) {
     /* we are asked to verify the host against a file */
     struct connectdata *conn = data->conn;
-    struct ssh_conn *sshc = &conn->proto.sshc;
     struct libssh2_knownhost *host = NULL;
     const char *remotekey = libssh2_session_hostkey(sshc->ssh_session,
                                                     &keylen, &sshkeytype);
@@ -544,11 +544,11 @@ static CURLcode ssh_knownhost(struct Curl_easy *data)
     switch(rc) {
     default: /* unknown return codes will equal reject */
     case CURLKHSTAT_REJECT:
-      state(data, SSH_SESSION_FREE);
+      myssh_state(data, sshc, SSH_SESSION_FREE);
       FALLTHROUGH();
     case CURLKHSTAT_DEFER:
       /* DEFER means bail out but keep the SSH_HOSTKEY state */
-      result = sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
+      result = CURLE_PEER_FAILED_VERIFICATION;
       break;
     case CURLKHSTAT_FINE_REPLACE:
       /* remove old host+key that does not match */
@@ -590,17 +590,16 @@ static CURLcode ssh_knownhost(struct Curl_easy *data)
   return result;
 }
 
-static CURLcode ssh_check_fingerprint(struct Curl_easy *data)
+static CURLcode ssh_check_fingerprint(struct Curl_easy *data,
+                                      struct ssh_conn *sshc)
 {
-  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];
   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");
+        pubkey_md5 != NULL ? pubkey_md5 : "NULL");
   infof(data, "SSH SHA256 public key: %s",
-      pubkey_sha256 != NULL ? pubkey_sha256 : "NULL");
+        pubkey_sha256 != NULL ? pubkey_sha256 : "NULL");
 
   if(pubkey_sha256) {
     const char *fingerprint = NULL;
@@ -629,25 +628,22 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data)
       failf(data,
             "Denied establishing ssh session: sha256 fingerprint "
             "not available");
-      state(data, SSH_SESSION_FREE);
-      sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
-      return sshc->actualcode;
+      myssh_state(data, sshc, SSH_SESSION_FREE);
+      return CURLE_PEER_FAILED_VERIFICATION;
     }
 
     /* The length of fingerprint is 32 bytes for SHA256.
      * See libssh2_hostkey_hash documentation. */
-    if(Curl_base64_encode(fingerprint, 32, &fingerprint_b64,
-                          &fingerprint_b64_len) != CURLE_OK) {
-      state(data, SSH_SESSION_FREE);
-      sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
-      return sshc->actualcode;
+    if(curlx_base64_encode(fingerprint, 32, &fingerprint_b64,
+                           &fingerprint_b64_len) != CURLE_OK) {
+      myssh_state(data, sshc, SSH_SESSION_FREE);
+      return CURLE_PEER_FAILED_VERIFICATION;
     }
 
     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;
+      myssh_state(data, sshc, SSH_SESSION_FREE);
+      return CURLE_PEER_FAILED_VERIFICATION;
     }
 
     infof(data, "SSH SHA256 fingerprint: %s", fingerprint_b64);
@@ -672,9 +668,8 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data)
             "Denied establishing ssh session: mismatch sha256 fingerprint. "
             "Remote %s is not equal to %s", fingerprint_b64, pubkey_sha256);
       free(fingerprint_b64);
-      state(data, SSH_SESSION_FREE);
-      sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
-      return sshc->actualcode;
+      myssh_state(data, sshc, SSH_SESSION_FREE);
+      return CURLE_PEER_FAILED_VERIFICATION;
     }
 
     free(fingerprint_b64);
@@ -684,7 +679,7 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data)
 
   if(pubkey_md5) {
     char md5buffer[33];
-    const char *fingerprint = NULL;
+    const char *fingerprint;
 
     fingerprint = libssh2_hostkey_hash(sshc->ssh_session,
                                        LIBSSH2_HOSTKEY_HASH_MD5);
@@ -712,9 +707,8 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data)
               "Denied establishing ssh session: md5 fingerprint "
               "not available");
       }
-      state(data, SSH_SESSION_FREE);
-      sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
-      return sshc->actualcode;
+      myssh_state(data, sshc, SSH_SESSION_FREE);
+      return CURLE_PEER_FAILED_VERIFICATION;
     }
     infof(data, "MD5 checksum match");
   }
@@ -734,20 +728,18 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data)
                                        (int)keytype, remotekey, keylen);
         Curl_set_in_callback(data, FALSE);
         if(rc!= CURLKHMATCH_OK) {
-          state(data, SSH_SESSION_FREE);
-          sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
-          return sshc->actualcode;
+          myssh_state(data, sshc, SSH_SESSION_FREE);
+          return CURLE_PEER_FAILED_VERIFICATION;
         }
       }
       else {
-        state(data, SSH_SESSION_FREE);
-        sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
-        return sshc->actualcode;
+        myssh_state(data, sshc, SSH_SESSION_FREE);
+        return CURLE_PEER_FAILED_VERIFICATION;
       }
       return CURLE_OK;
     }
     else {
-      return ssh_knownhost(data);
+      return ssh_knownhost(data, sshc);
     }
   }
   else {
@@ -760,7 +752,8 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data)
  * ssh_force_knownhost_key_type() will check the known hosts file and try to
  * force a specific public key type from the server if an entry is found.
  */
-static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data)
+static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data,
+                                             struct ssh_conn *sshc)
 {
   CURLcode result = CURLE_OK;
 
@@ -789,14 +782,15 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data)
 
   const char *hostkey_method = NULL;
   struct connectdata *conn = data->conn;
-  struct ssh_conn *sshc = &conn->proto.sshc;
   struct libssh2_knownhost* store = NULL;
   const char *kh_name_end = NULL;
   size_t kh_name_size = 0;
   int port = 0;
   bool found = FALSE;
 
-  if(sshc->kh && !data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]) {
+  if(sshc->kh &&
+     !data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5] &&
+     !data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256]) {
     /* lets try to find our host in the known hosts file */
     while(!libssh2_knownhost_get(sshc->kh, &store, store)) {
       /* For non-standard ports, the name will be enclosed in */
@@ -931,7 +925,7 @@ static CURLcode sftp_quote(struct Curl_easy *data,
     char *tmp = aprintf("257 \"%s\" is current directory.\n", sshp->path);
     if(!tmp)
       return CURLE_OUT_OF_MEMORY;
-    Curl_debug(data, CURLINFO_HEADER_OUT, (char *)"PWD\n", 4);
+    Curl_debug(data, CURLINFO_HEADER_OUT, "PWD\n", 4);
     Curl_debug(data, CURLINFO_HEADER_IN, tmp, strlen(tmp));
 
     /* this sends an FTP-like "header" to the header callback so that the
@@ -940,7 +934,7 @@ static CURLcode sftp_quote(struct Curl_easy *data,
     result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp));
     free(tmp);
     if(!result)
-      state(data, SSH_SFTP_NEXT_QUOTE);
+      myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
     return result;
   }
 
@@ -970,11 +964,11 @@ static CURLcode sftp_quote(struct Curl_easy *data,
    * Instead, we scan for commands used by OpenSSH's sftp program and call the
    * appropriate libssh2 functions.
    */
-  if(strncasecompare(cmd, "chgrp ", 6) ||
-     strncasecompare(cmd, "chmod ", 6) ||
-     strncasecompare(cmd, "chown ", 6) ||
-     strncasecompare(cmd, "atime ", 6) ||
-     strncasecompare(cmd, "mtime ", 6)) {
+  if(!strncmp(cmd, "chgrp ", 6) ||
+     !strncmp(cmd, "chmod ", 6) ||
+     !strncmp(cmd, "chown ", 6) ||
+     !strncmp(cmd, "atime ", 6) ||
+     !strncmp(cmd, "mtime ", 6)) {
     /* attribute change */
 
     /* sshc->quote_path1 contains the mode to set */
@@ -987,11 +981,11 @@ static CURLcode sftp_quote(struct Curl_easy *data,
       return result;
     }
     memset(&sshp->quote_attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES));
-    state(data, SSH_SFTP_QUOTE_STAT);
+    myssh_state(data, sshc, SSH_SFTP_QUOTE_STAT);
     return result;
   }
-  if(strncasecompare(cmd, "ln ", 3) ||
-     strncasecompare(cmd, "symlink ", 8)) {
+  if(!strncmp(cmd, "ln ", 3) ||
+     !strncmp(cmd, "symlink ", 8)) {
     /* symbolic linking */
     /* sshc->quote_path1 is the source */
     /* get the destination */
@@ -1002,15 +996,15 @@ static CURLcode sftp_quote(struct Curl_easy *data,
       Curl_safefree(sshc->quote_path1);
       return result;
     }
-    state(data, SSH_SFTP_QUOTE_SYMLINK);
+    myssh_state(data, sshc, SSH_SFTP_QUOTE_SYMLINK);
     return result;
   }
-  else if(strncasecompare(cmd, "mkdir ", 6)) {
+  else if(!strncmp(cmd, "mkdir ", 6)) {
     /* create dir */
-    state(data, SSH_SFTP_QUOTE_MKDIR);
+    myssh_state(data, sshc, SSH_SFTP_QUOTE_MKDIR);
     return result;
   }
-  else if(strncasecompare(cmd, "rename ", 7)) {
+  else if(!strncmp(cmd, "rename ", 7)) {
     /* rename file */
     /* first param is the source path */
     /* second param is the dest. path */
@@ -1021,20 +1015,20 @@ static CURLcode sftp_quote(struct Curl_easy *data,
       Curl_safefree(sshc->quote_path1);
       return result;
     }
-    state(data, SSH_SFTP_QUOTE_RENAME);
+    myssh_state(data, sshc, SSH_SFTP_QUOTE_RENAME);
     return result;
   }
-  else if(strncasecompare(cmd, "rmdir ", 6)) {
+  else if(!strncmp(cmd, "rmdir ", 6)) {
     /* delete dir */
-    state(data, SSH_SFTP_QUOTE_RMDIR);
+    myssh_state(data, sshc, SSH_SFTP_QUOTE_RMDIR);
     return result;
   }
-  else if(strncasecompare(cmd, "rm ", 3)) {
-    state(data, SSH_SFTP_QUOTE_UNLINK);
+  else if(!strncmp(cmd, "rm ", 3)) {
+    myssh_state(data, sshc, SSH_SFTP_QUOTE_UNLINK);
     return result;
   }
-  else if(strncasecompare(cmd, "statvfs ", 8)) {
-    state(data, SSH_SFTP_QUOTE_STATVFS);
+  else if(!strncmp(cmd, "statvfs ", 8)) {
+    myssh_state(data, sshc, SSH_SFTP_QUOTE_STATVFS);
     return result;
   }
 
@@ -1100,6 +1094,7 @@ sftp_upload_init(struct Curl_easy *data,
                          LIBSSH2_SFTP_OPENFILE);
 
   if(!sshc->sftp_handle) {
+    CURLcode result;
     unsigned long sftperr;
     int rc = libssh2_session_last_errno(sshc->ssh_session);
 
@@ -1116,12 +1111,10 @@ sftp_upload_init(struct Curl_easy *data,
       sftperr = LIBSSH2_FX_OK; /* not an sftp error at all */
 
     if(sshc->secondCreateDirs) {
-      state(data, SSH_SFTP_CLOSE);
-      sshc->actualcode = sftperr != LIBSSH2_FX_OK ?
-        sftp_libssh2_error_to_CURLE(sftperr) : CURLE_SSH;
+      myssh_state(data, sshc, SSH_SFTP_CLOSE);
       failf(data, "Creating the dir/file failed: %s",
             sftp_libssh2_strerror(sftperr));
-      return CURLE_OK;
+      return sftp_libssh2_error_to_CURLE(sftperr);
     }
     if(((sftperr == LIBSSH2_FX_NO_SUCH_FILE) ||
         (sftperr == LIBSSH2_FX_FAILURE) ||
@@ -1130,24 +1123,23 @@ sftp_upload_init(struct Curl_easy *data,
         (strlen(sshp->path) > 1))) {
       /* try to create the path remotely */
       sshc->secondCreateDirs = 1;
-      state(data, SSH_SFTP_CREATE_DIRS_INIT);
+      myssh_state(data, sshc, SSH_SFTP_CREATE_DIRS_INIT);
       return CURLE_OK;
     }
-    state(data, SSH_SFTP_CLOSE);
-    sshc->actualcode = sftperr != LIBSSH2_FX_OK ?
-      sftp_libssh2_error_to_CURLE(sftperr) : CURLE_SSH;
-    if(!sshc->actualcode) {
+    myssh_state(data, sshc, SSH_SFTP_CLOSE);
+    result = sftp_libssh2_error_to_CURLE(sftperr);
+    if(!result) {
       /* Sometimes, for some reason libssh2_sftp_last_error() returns zero
          even though libssh2_sftp_open() failed previously! We need to
          work around that! */
-      sshc->actualcode = CURLE_SSH;
+      result = CURLE_SSH;
       sftperr = LIBSSH2_FX_OK;
     }
     failf(data, "Upload failed: %s (%lu/%d)",
           sftperr != LIBSSH2_FX_OK ?
           sftp_libssh2_strerror(sftperr) : "ssh error",
           sftperr, rc);
-    return sshc->actualcode;
+    return result;
   }
 
   /* If we have a restart point then we need to seek to the correct
@@ -1228,13 +1220,15 @@ sftp_upload_init(struct Curl_easy *data,
      timeout here */
   Curl_expire(data, 0, EXPIRE_RUN_NOW);
 
-  state(data, SSH_STOP);
+  myssh_state(data, sshc, SSH_STOP);
   return CURLE_OK;
 }
 
-static CURLcode
-sftp_pkey_init(struct Curl_easy *data,
-               struct ssh_conn *sshc)
+/* make sure that this does not collide with an actual libssh2 error code */
+#define ERROR_LIBBSH2 1
+
+static CURLcode ssh_state_pkey_init(struct Curl_easy *data,
+                                    struct ssh_conn *sshc)
 {
   /*
    * Check the supported auth types in the order I feel is most secure
@@ -1263,7 +1257,7 @@ sftp_pkey_init(struct Curl_easy *data,
         if(!sshc->rsa)
           out_of_memory = TRUE;
         else if(stat(sshc->rsa, &sbuf)) {
-          Curl_safefree(sshc->rsa);
+          free(sshc->rsa);
           sshc->rsa = aprintf("%s/.ssh/id_dsa", home);
           if(!sshc->rsa)
             out_of_memory = TRUE;
@@ -1277,10 +1271,10 @@ sftp_pkey_init(struct Curl_easy *data,
         /* Nothing found; try the current dir. */
         sshc->rsa = strdup("id_rsa");
         if(sshc->rsa && stat(sshc->rsa, &sbuf)) {
-          Curl_safefree(sshc->rsa);
+          free(sshc->rsa);
           sshc->rsa = strdup("id_dsa");
           if(sshc->rsa && stat(sshc->rsa, &sbuf)) {
-            Curl_safefree(sshc->rsa);
+            free(sshc->rsa);
             /* Out of guesses. Set to the empty string to avoid
              * surprising info messages. */
             sshc->rsa = strdup("");
@@ -1305,8 +1299,7 @@ sftp_pkey_init(struct Curl_easy *data,
     if(out_of_memory || !sshc->rsa) {
       Curl_safefree(sshc->rsa);
       Curl_safefree(sshc->rsa_pub);
-      state(data, SSH_SESSION_FREE);
-      sshc->actualcode = CURLE_OUT_OF_MEMORY;
+      myssh_state(data, sshc, SSH_SESSION_FREE);
       return CURLE_OUT_OF_MEMORY;
     }
 
@@ -1318,12 +1311,12 @@ sftp_pkey_init(struct Curl_easy *data,
       infof(data, "Using SSH public key file '%s'", sshc->rsa_pub);
     infof(data, "Using SSH private key file '%s'", sshc->rsa);
 
-    state(data, SSH_AUTH_PKEY);
+    myssh_state(data, sshc, SSH_AUTH_PKEY);
   }
   else {
-    state(data, SSH_AUTH_PASS_INIT);
+    myssh_state(data, sshc, SSH_AUTH_PASS_INIT);
   }
-  return CURLE_OK;
+  return 0;
 }
 
 static CURLcode
@@ -1345,7 +1338,7 @@ sftp_quote_stat(struct Curl_easy *data,
     sshc->acceptfail = TRUE;
   }
 
-  if(!strncasecompare(cmd, "chmod", 5)) {
+  if(!!strncmp(cmd, "chmod", 5)) {
     /* Since chown and chgrp only set owner OR group but libssh2 wants to set
      * them both at once, we need to obtain the current ownership first. This
      * takes an extra protocol round trip.
@@ -1367,8 +1360,11 @@ sftp_quote_stat(struct Curl_easy *data,
   }
 
   /* Now set the new attributes... */
-  if(strncasecompare(cmd, "chgrp", 5)) {
-    sshp->quote_attrs.gid = strtoul(sshc->quote_path1, NULL, 10);
+  if(!strncmp(cmd, "chgrp", 5)) {
+    const char *p = sshc->quote_path1;
+    curl_off_t gid;
+    (void)curlx_str_number(&p, &gid, ULONG_MAX);
+    sshp->quote_attrs.gid = (unsigned long)gid;
     sshp->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID;
     if(sshp->quote_attrs.gid == 0 && !ISDIGIT(sshc->quote_path1[0]) &&
        !sshc->acceptfail) {
@@ -1376,18 +1372,23 @@ sftp_quote_stat(struct Curl_easy *data,
       goto fail;
     }
   }
-  else if(strncasecompare(cmd, "chmod", 5)) {
-    sshp->quote_attrs.permissions = strtoul(sshc->quote_path1, NULL, 8);
-    sshp->quote_attrs.flags = LIBSSH2_SFTP_ATTR_PERMISSIONS;
+  else if(!strncmp(cmd, "chmod", 5)) {
+    curl_off_t perms;
+    const char *p = sshc->quote_path1;
     /* permissions are octal */
-    if(sshp->quote_attrs.permissions == 0 &&
-       !ISDIGIT(sshc->quote_path1[0])) {
+    if(curlx_str_octal(&p, &perms, 07777)) {
       failf(data, "Syntax error: chmod permissions not a number");
       goto fail;
     }
+
+    sshp->quote_attrs.permissions = (unsigned long)perms;
+    sshp->quote_attrs.flags = LIBSSH2_SFTP_ATTR_PERMISSIONS;
   }
-  else if(strncasecompare(cmd, "chown", 5)) {
-    sshp->quote_attrs.uid = strtoul(sshc->quote_path1, NULL, 10);
+  else if(!strncmp(cmd, "chown", 5)) {
+    const char *p = sshc->quote_path1;
+    curl_off_t uid;
+    (void)curlx_str_number(&p, &uid, ULONG_MAX);
+    sshp->quote_attrs.uid = (unsigned long)uid;
     sshp->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID;
     if(sshp->quote_attrs.uid == 0 && !ISDIGIT(sshc->quote_path1[0]) &&
        !sshc->acceptfail) {
@@ -1395,8 +1396,8 @@ sftp_quote_stat(struct Curl_easy *data,
       goto fail;
     }
   }
-  else if(strncasecompare(cmd, "atime", 5) ||
-          strncasecompare(cmd, "mtime", 5)) {
+  else if(!strncmp(cmd, "atime", 5) ||
+          !strncmp(cmd, "mtime", 5)) {
     time_t date = Curl_getdate_capped(sshc->quote_path1);
     bool fail = FALSE;
 
@@ -1413,7 +1414,7 @@ sftp_quote_stat(struct Curl_easy *data,
 #endif
     if(fail)
       goto fail;
-    if(strncasecompare(cmd, "atime", 5))
+    if(!strncmp(cmd, "atime", 5))
       sshp->quote_attrs.atime = (unsigned long)date;
     else /* mtime */
       sshp->quote_attrs.mtime = (unsigned long)date;
@@ -1422,7 +1423,7 @@ sftp_quote_stat(struct Curl_easy *data,
   }
 
   /* Now send the completed structure... */
-  state(data, SSH_SFTP_QUOTE_SETSTAT);
+  myssh_state(data, sshc, SSH_SFTP_QUOTE_SETSTAT);
   return CURLE_OK;
 fail:
   Curl_safefree(sshc->quote_path1);
@@ -1466,20 +1467,19 @@ sftp_download_stat(struct Curl_easy *data,
     }
     if(data->state.use_range) {
       curl_off_t from, to;
-      char *ptr;
-      char *ptr2;
-      CURLofft to_t;
-      CURLofft from_t;
+      const char *p = data->state.range;
+      int to_t, from_t;
 
-      from_t = curlx_strtoofft(data->state.range, &ptr, 10, &from);
-      if(from_t == CURL_OFFT_FLOW)
+      from_t = curlx_str_number(&p, &from, CURL_OFF_T_MAX);
+      if(from_t == STRE_OVERFLOW)
         return CURLE_RANGE_ERROR;
-      while(*ptr && (ISBLANK(*ptr) || (*ptr == '-')))
-        ptr++;
-      to_t = curlx_strtoofft(ptr, &ptr2, 10, &to);
-      if(to_t == CURL_OFFT_FLOW)
+      curlx_str_passblanks(&p);
+      (void)curlx_str_single(&p, '-');
+
+      to_t = curlx_str_numblanks(&p, &to);
+      if(to_t == STRE_OVERFLOW)
         return CURLE_RANGE_ERROR;
-      if((to_t == CURL_OFFT_INVAL) /* no "to" value given */
+      if((to_t == STRE_NO_NUM) /* no "to" value given */
          || (to >= size)) {
         to = size - 1;
       }
@@ -1545,7 +1545,7 @@ sftp_download_stat(struct Curl_easy *data,
     /* no data to transfer */
     Curl_xfer_setup_nop(data);
     infof(data, "File already completely downloaded");
-    state(data, SSH_STOP);
+    myssh_state(data, sshc, SSH_STOP);
     return CURLE_OK;
   }
   Curl_xfer_setup1(data, CURL_XFER_RECV, data->req.size, FALSE);
@@ -1557,7 +1557,7 @@ sftp_download_stat(struct Curl_easy *data,
      out writableable as the underlying libssh2 recv function will deal
      with both accordingly */
   data->state.select_bits = CURL_CSELECT_IN;
-  state(data, SSH_STOP);
+  myssh_state(data, sshc, SSH_STOP);
 
   return CURLE_OK;
 }
@@ -1585,764 +1585,1303 @@ static CURLcode sftp_readdir(struct Curl_easy *data,
                                  sshp->readdir_filename,
                                  readdir_len);
       if(!result)
-        result = Curl_client_write(data, CLIENTWRITE_BODY,
-                                   (char *)"\n", 1);
+        result = Curl_client_write(data, CLIENTWRITE_BODY, "\n", 1);
       if(result)
         return result;
     }
     else {
-      result = Curl_dyn_add(&sshp->readdir, sshp->readdir_longentry);
+      result = curlx_dyn_add(&sshp->readdir, sshp->readdir_longentry);
 
       if(!result) {
         if((sshp->readdir_attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) &&
            ((sshp->readdir_attrs.permissions & LIBSSH2_SFTP_S_IFMT) ==
             LIBSSH2_SFTP_S_IFLNK)) {
-          Curl_dyn_init(&sshp->readdir_link, CURL_PATH_MAX);
-          result = Curl_dyn_addf(&sshp->readdir_link, "%s%s", sshp->path,
-                                 sshp->readdir_filename);
-          state(data, SSH_SFTP_READDIR_LINK);
+          result = curlx_dyn_addf(&sshp->readdir_link, "%s%s", sshp->path,
+                                  sshp->readdir_filename);
+          myssh_state(data, sshc, SSH_SFTP_READDIR_LINK);
         }
         else {
-          state(data, SSH_SFTP_READDIR_BOTTOM);
+          myssh_state(data, sshc, SSH_SFTP_READDIR_BOTTOM);
         }
       }
       return result;
     }
   }
   else if(!rc) {
-    state(data, SSH_SFTP_READDIR_DONE);
+    myssh_state(data, sshc, SSH_SFTP_READDIR_DONE);
   }
   else {
     unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
-    result = sftp_libssh2_error_to_CURLE(sftperr);
-    sshc->actualcode = result ? result : CURLE_SSH;
+    result = sftperr ? sftp_libssh2_error_to_CURLE(sftperr) : CURLE_SSH;
     failf(data, "Could not open remote file for reading: %s :: %d",
           sftp_libssh2_strerror(sftperr),
           libssh2_session_last_errno(sshc->ssh_session));
-    state(data, SSH_SFTP_CLOSE);
+    myssh_state(data, sshc, SSH_SFTP_CLOSE);
   }
   return result;
 }
-/*
- * ssh_statemachine() runs the SSH state machine as far as it can without
- * blocking and without reaching the end. The data the pointer 'block' points
- * to will be set to TRUE if the libssh2 function returns LIBSSH2_ERROR_EAGAIN
- * meaning it wants to be called again when the socket is ready
- */
 
-static CURLcode ssh_statemachine(struct Curl_easy *data, bool *block)
+static CURLcode ssh_state_init(struct Curl_easy *data,
+                               struct ssh_conn *sshc)
 {
-  CURLcode result = CURLE_OK;
-  struct connectdata *conn = data->conn;
-  struct SSHPROTO *sshp = data->req.p.ssh;
-  struct ssh_conn *sshc = &conn->proto.sshc;
+  CURLcode result;
+  sshc->secondCreateDirs = 0;
+  sshc->nextstate = SSH_NO_STATE;
 
-  int rc = LIBSSH2_ERROR_NONE;
-  *block = 0; /* we are not blocking by default */
+  /* Set libssh2 to non-blocking, since everything internally is
+     non-blocking */
+  libssh2_session_set_blocking(sshc->ssh_session, 0);
 
-  do {
-    switch(sshc->state) {
-    case SSH_INIT:
-      sshc->secondCreateDirs = 0;
-      sshc->nextstate = SSH_NO_STATE;
-      sshc->actualcode = CURLE_OK;
+  result = ssh_force_knownhost_key_type(data, sshc);
+  if(result)
+    myssh_state(data, sshc, SSH_SESSION_FREE);
+  else
+    myssh_state(data, sshc, SSH_S_STARTUP);
+  return result;
+}
 
-      /* Set libssh2 to non-blocking, since everything internally is
-         non-blocking */
-      libssh2_session_set_blocking(sshc->ssh_session, 0);
+static CURLcode ssh_state_startup(struct Curl_easy *data,
+                                  struct ssh_conn *sshc)
+{
+  struct connectdata *conn = data->conn;
+  int rc = libssh2_session_handshake(sshc->ssh_session,
+                                     conn->sock[FIRSTSOCKET]);
+  if(rc == LIBSSH2_ERROR_EAGAIN)
+    return CURLE_AGAIN;
 
-      result = ssh_force_knownhost_key_type(data);
-      if(result) {
-        state(data, SSH_SESSION_FREE);
-        sshc->actualcode = result;
-        break;
-      }
+  if(rc) {
+    char *err_msg = NULL;
+    (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0);
+    failf(data, "Failure establishing ssh session: %d, %s", rc, err_msg);
 
-      state(data, SSH_S_STARTUP);
-      FALLTHROUGH();
+    myssh_state(data, sshc, SSH_SESSION_FREE);
+    return CURLE_FAILED_INIT;
+  }
 
-    case SSH_S_STARTUP:
-      rc = libssh2_session_handshake(sshc->ssh_session,
-                                     conn->sock[FIRSTSOCKET]);
-      if(rc == LIBSSH2_ERROR_EAGAIN) {
-        break;
-      }
-      if(rc) {
-        char *err_msg = NULL;
-        (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0);
-        failf(data, "Failure establishing ssh session: %d, %s", rc, err_msg);
+  myssh_state(data, sshc, SSH_HOSTKEY);
+  return CURLE_OK;
+}
 
-        state(data, SSH_SESSION_FREE);
-        sshc->actualcode = CURLE_FAILED_INIT;
-        break;
-      }
+static CURLcode ssh_state_hostkey(struct Curl_easy *data,
+                                  struct ssh_conn *sshc)
+{
+  /*
+   * Before we authenticate we should check the hostkey's fingerprint
+   * against our known hosts. How that is handled (reading from file,
+   * whatever) is up to us.
+   */
+  CURLcode result = ssh_check_fingerprint(data, sshc);
+  if(!result)
+    myssh_state(data, sshc, SSH_AUTHLIST);
+  return result;
+}
 
-      state(data, SSH_HOSTKEY);
+static CURLcode ssh_state_authlist(struct Curl_easy *data,
+                                   struct ssh_conn *sshc)
+{
+  /*
+   * Figure out authentication methods
+   * NB: As soon as we have provided a username to an openssh server we
+   * must never change it later. Thus, always specify the correct username
+   * here, even though the libssh2 docs kind of indicate that it should be
+   * possible to get a 'generic' list (not user-specific) of authentication
+   * methods, presumably with a blank username. That will not work in my
+   * experience.
+   * So always specify it here.
+   */
+  struct connectdata *conn = data->conn;
+  sshc->authlist = libssh2_userauth_list(sshc->ssh_session,
+                                         conn->user,
+                                         curlx_uztoui(strlen(conn->user)));
 
-      FALLTHROUGH();
-    case SSH_HOSTKEY:
-      /*
-       * Before we authenticate we should check the hostkey's fingerprint
-       * against our known hosts. How that is handled (reading from file,
-       * whatever) is up to us.
-       */
-      result = ssh_check_fingerprint(data);
-      if(!result)
-        state(data, SSH_AUTHLIST);
-      /* ssh_check_fingerprint sets state appropriately on error */
-      break;
+  if(!sshc->authlist) {
+    int rc;
+    if(libssh2_userauth_authenticated(sshc->ssh_session)) {
+      sshc->authed = TRUE;
+      infof(data, "SSH user accepted with no authentication");
+      myssh_state(data, sshc, SSH_AUTH_DONE);
+      return CURLE_OK;
+    }
+    rc = libssh2_session_last_errno(sshc->ssh_session);
+    if(rc == LIBSSH2_ERROR_EAGAIN)
+      return CURLE_AGAIN;
 
-    case SSH_AUTHLIST:
-      /*
-       * Figure out authentication methods
-       * NB: As soon as we have provided a username to an openssh server we
-       * must never change it later. Thus, always specify the correct username
-       * here, even though the libssh2 docs kind of indicate that it should be
-       * possible to get a 'generic' list (not user-specific) of authentication
-       * methods, presumably with a blank username. That will not work in my
-       * experience.
-       * So always specify it here.
-       */
-      sshc->authlist = libssh2_userauth_list(sshc->ssh_session,
-                                             conn->user,
-                                             curlx_uztoui(strlen(conn->user)));
+    myssh_state(data, sshc, SSH_SESSION_FREE);
+    return libssh2_session_error_to_CURLE(rc);
+  }
+  infof(data, "SSH authentication methods available: %s",
+        sshc->authlist);
 
-      if(!sshc->authlist) {
-        if(libssh2_userauth_authenticated(sshc->ssh_session)) {
-          sshc->authed = TRUE;
-          infof(data, "SSH user accepted with no authentication");
-          state(data, SSH_AUTH_DONE);
-          break;
-        }
-        rc = libssh2_session_last_errno(sshc->ssh_session);
-        if(rc == LIBSSH2_ERROR_EAGAIN)
-          rc = LIBSSH2_ERROR_EAGAIN;
-        else {
-          state(data, SSH_SESSION_FREE);
-          sshc->actualcode = libssh2_session_error_to_CURLE(rc);
-        }
-        break;
-      }
-      infof(data, "SSH authentication methods available: %s",
-            sshc->authlist);
+  myssh_state(data, sshc, SSH_AUTH_PKEY_INIT);
+  return CURLE_OK;
+}
 
-      state(data, SSH_AUTH_PKEY_INIT);
-      break;
+static CURLcode ssh_state_auth_pkey(struct Curl_easy *data,
+                                    struct ssh_conn *sshc)
+{
+  /* The function below checks if the files exists, no need to stat() here.
+   */
+  struct connectdata *conn = data->conn;
+  int rc =
+    libssh2_userauth_publickey_fromfile_ex(sshc->ssh_session,
+                                           conn->user,
+                                           curlx_uztoui(
+                                             strlen(conn->user)),
+                                           sshc->rsa_pub,
+                                           sshc->rsa, sshc->passphrase);
+  if(rc == LIBSSH2_ERROR_EAGAIN)
+    return CURLE_AGAIN;
+
+  Curl_safefree(sshc->rsa_pub);
+  Curl_safefree(sshc->rsa);
+
+  if(rc == 0) {
+    sshc->authed = TRUE;
+    infof(data, "Initialized SSH public key authentication");
+    myssh_state(data, sshc, SSH_AUTH_DONE);
+  }
+  else {
+    char *err_msg = NULL;
+    char unknown[] = "Reason unknown (-1)";
+    if(rc == -1) {
+      /* No error message has been set and the last set error message, if
+         any, is from a previous error so ignore it. #11837 */
+      err_msg = unknown;
+    }
+    else {
+      (void)libssh2_session_last_error(sshc->ssh_session,
+                                       &err_msg, NULL, 0);
+    }
+    infof(data, "SSH public key authentication failed: %s", err_msg);
+    myssh_state(data, sshc, SSH_AUTH_PASS_INIT);
+  }
+  return CURLE_OK;
+}
 
-    case SSH_AUTH_PKEY_INIT:
-      result = sftp_pkey_init(data, sshc);
-      break;
+static CURLcode ssh_state_auth_pass_init(struct Curl_easy *data,
+                                         struct ssh_conn *sshc)
+{
+  if((data->set.ssh_auth_types & CURLSSH_AUTH_PASSWORD) &&
+     (strstr(sshc->authlist, "password") != NULL)) {
+    myssh_state(data, sshc, SSH_AUTH_PASS);
+  }
+  else {
+    myssh_state(data, sshc, SSH_AUTH_HOST_INIT);
+  }
+  return CURLE_OK;
+}
 
-    case SSH_AUTH_PKEY:
-      /* The function below checks if the files exists, no need to stat() here.
-       */
-      rc = libssh2_userauth_publickey_fromfile_ex(sshc->ssh_session,
-                                                  conn->user,
-                                                  curlx_uztoui(
-                                                    strlen(conn->user)),
-                                                  sshc->rsa_pub,
-                                                  sshc->rsa, sshc->passphrase);
-      if(rc == LIBSSH2_ERROR_EAGAIN) {
-        break;
-      }
+static CURLcode ssh_state_auth_pass(struct Curl_easy *data,
+                                    struct ssh_conn *sshc)
+{
+  struct connectdata *conn = data->conn;
+  int rc =
+    libssh2_userauth_password_ex(sshc->ssh_session, conn->user,
+                                 curlx_uztoui(strlen(conn->user)),
+                                 conn->passwd,
+                                 curlx_uztoui(strlen(conn->passwd)),
+                                 NULL);
+  if(rc == LIBSSH2_ERROR_EAGAIN) {
+    return CURLE_AGAIN;
+  }
+  if(rc == 0) {
+    sshc->authed = TRUE;
+    infof(data, "Initialized password authentication");
+    myssh_state(data, sshc, SSH_AUTH_DONE);
+  }
+  else {
+    myssh_state(data, sshc, SSH_AUTH_HOST_INIT);
+  }
+  return CURLE_OK;
+}
 
-      Curl_safefree(sshc->rsa_pub);
-      Curl_safefree(sshc->rsa);
+static CURLcode ssh_state_auth_host_init(struct Curl_easy *data,
+                                         struct ssh_conn *sshc)
+{
+  if((data->set.ssh_auth_types & CURLSSH_AUTH_HOST) &&
+     (strstr(sshc->authlist, "hostbased") != NULL)) {
+    myssh_state(data, sshc, SSH_AUTH_HOST);
+  }
+  else {
+    myssh_state(data, sshc, SSH_AUTH_AGENT_INIT);
+  }
+  return CURLE_OK;
+}
 
-      if(rc == 0) {
-        sshc->authed = TRUE;
-        infof(data, "Initialized SSH public key authentication");
-        state(data, SSH_AUTH_DONE);
-      }
-      else {
-        char *err_msg = NULL;
-        char unknown[] = "Reason unknown (-1)";
-        if(rc == -1) {
-          /* No error message has been set and the last set error message, if
-             any, is from a previous error so ignore it. #11837 */
-          err_msg = unknown;
-        }
-        else {
-          (void)libssh2_session_last_error(sshc->ssh_session,
-                                           &err_msg, NULL, 0);
-        }
-        infof(data, "SSH public key authentication failed: %s", err_msg);
-        state(data, SSH_AUTH_PASS_INIT);
-        rc = 0; /* clear rc and continue */
+static CURLcode ssh_state_auth_agent_init(struct Curl_easy *data,
+                                     struct ssh_conn *sshc)
+{
+  int rc = 0;
+  if((data->set.ssh_auth_types & CURLSSH_AUTH_AGENT)
+     && (strstr(sshc->authlist, "publickey") != NULL)) {
+
+    /* Connect to the ssh-agent */
+    /* The agent could be shared by a curl thread i believe
+       but nothing obvious as keys can be added/removed at any time */
+    if(!sshc->ssh_agent) {
+      sshc->ssh_agent = libssh2_agent_init(sshc->ssh_session);
+      if(!sshc->ssh_agent) {
+        infof(data, "Could not create agent object");
+
+        myssh_state(data, sshc, SSH_AUTH_KEY_INIT);
+        return CURLE_OK;
       }
-      break;
+    }
 
-    case SSH_AUTH_PASS_INIT:
-      if((data->set.ssh_auth_types & CURLSSH_AUTH_PASSWORD) &&
-         (strstr(sshc->authlist, "password") != NULL)) {
-        state(data, SSH_AUTH_PASS);
-      }
-      else {
-        state(data, SSH_AUTH_HOST_INIT);
-        rc = 0; /* clear rc and continue */
-      }
-      break;
+    rc = libssh2_agent_connect(sshc->ssh_agent);
+    if(rc == LIBSSH2_ERROR_EAGAIN)
+      return CURLE_AGAIN;
+    if(rc < 0) {
+      infof(data, "Failure connecting to agent");
+      myssh_state(data, sshc, SSH_AUTH_KEY_INIT);
+    }
+    else {
+      myssh_state(data, sshc, SSH_AUTH_AGENT_LIST);
+    }
+  }
+  else
+    myssh_state(data, sshc, SSH_AUTH_KEY_INIT);
+  return CURLE_OK;
+}
 
-    case SSH_AUTH_PASS:
-      rc = libssh2_userauth_password_ex(sshc->ssh_session, conn->user,
-                                        curlx_uztoui(strlen(conn->user)),
-                                        conn->passwd,
-                                        curlx_uztoui(strlen(conn->passwd)),
-                                        NULL);
-      if(rc == LIBSSH2_ERROR_EAGAIN) {
-        break;
-      }
-      if(rc == 0) {
-        sshc->authed = TRUE;
-        infof(data, "Initialized password authentication");
-        state(data, SSH_AUTH_DONE);
-      }
-      else {
-        state(data, SSH_AUTH_HOST_INIT);
-        rc = 0; /* clear rc and continue */
-      }
-      break;
+static CURLcode ssh_state_auth_agent_list(struct Curl_easy *data,
+                                          struct ssh_conn *sshc)
+{
+  int rc = libssh2_agent_list_identities(sshc->ssh_agent);
 
-    case SSH_AUTH_HOST_INIT:
-      if((data->set.ssh_auth_types & CURLSSH_AUTH_HOST) &&
-         (strstr(sshc->authlist, "hostbased") != NULL)) {
-        state(data, SSH_AUTH_HOST);
-      }
-      else {
-        state(data, SSH_AUTH_AGENT_INIT);
-      }
-      break;
+  if(rc == LIBSSH2_ERROR_EAGAIN)
+    return CURLE_AGAIN;
+  if(rc < 0) {
+    infof(data, "Failure requesting identities to agent");
+    myssh_state(data, sshc, SSH_AUTH_KEY_INIT);
+  }
+  else {
+    myssh_state(data, sshc, SSH_AUTH_AGENT);
+    sshc->sshagent_prev_identity = NULL;
+  }
+  return CURLE_OK;
+}
 
-    case SSH_AUTH_HOST:
-      state(data, SSH_AUTH_AGENT_INIT);
-      break;
+static CURLcode ssh_state_auth_agent(struct Curl_easy *data,
+                                     struct ssh_conn *sshc)
+{
+  /* as prev_identity evolves only after an identity user auth finished we
+     can safely request it again as long as EAGAIN is returned here or by
+     libssh2_agent_userauth */
+  int rc = libssh2_agent_get_identity(sshc->ssh_agent,
+                                      &sshc->sshagent_identity,
+                                      sshc->sshagent_prev_identity);
+  if(rc == LIBSSH2_ERROR_EAGAIN)
+    return CURLE_AGAIN;
 
-    case SSH_AUTH_AGENT_INIT:
-      if((data->set.ssh_auth_types & CURLSSH_AUTH_AGENT)
-         && (strstr(sshc->authlist, "publickey") != NULL)) {
-
-        /* Connect to the ssh-agent */
-        /* The agent could be shared by a curl thread i believe
-           but nothing obvious as keys can be added/removed at any time */
-        if(!sshc->ssh_agent) {
-          sshc->ssh_agent = libssh2_agent_init(sshc->ssh_session);
-          if(!sshc->ssh_agent) {
-            infof(data, "Could not create agent object");
-
-            state(data, SSH_AUTH_KEY_INIT);
-            break;
-          }
-        }
+  if(rc == 0) {
+    struct connectdata *conn = data->conn;
+    rc = libssh2_agent_userauth(sshc->ssh_agent, conn->user,
+                                sshc->sshagent_identity);
 
-        rc = libssh2_agent_connect(sshc->ssh_agent);
-        if(rc == LIBSSH2_ERROR_EAGAIN)
-          break;
-        if(rc < 0) {
-          infof(data, "Failure connecting to agent");
-          state(data, SSH_AUTH_KEY_INIT);
-          rc = 0; /* clear rc and continue */
-        }
-        else {
-          state(data, SSH_AUTH_AGENT_LIST);
-        }
+    if(rc < 0) {
+      if(rc != LIBSSH2_ERROR_EAGAIN) {
+        /* tried and failed? go to next identity */
+        sshc->sshagent_prev_identity = sshc->sshagent_identity;
       }
-      else
-        state(data, SSH_AUTH_KEY_INIT);
-      break;
+      return CURLE_OK;
+    }
+  }
 
-    case SSH_AUTH_AGENT_LIST:
-      rc = libssh2_agent_list_identities(sshc->ssh_agent);
+  if(rc < 0)
+    infof(data, "Failure requesting identities to agent");
+  else if(rc == 1)
+    infof(data, "No identity would match");
 
-      if(rc == LIBSSH2_ERROR_EAGAIN)
-        break;
-      if(rc < 0) {
-        infof(data, "Failure requesting identities to agent");
-        state(data, SSH_AUTH_KEY_INIT);
-        rc = 0; /* clear rc and continue */
-      }
-      else {
-        state(data, SSH_AUTH_AGENT);
-        sshc->sshagent_prev_identity = NULL;
-      }
-      break;
+  if(rc == LIBSSH2_ERROR_NONE) {
+    sshc->authed = TRUE;
+    infof(data, "Agent based authentication successful");
+    myssh_state(data, sshc, SSH_AUTH_DONE);
+  }
+  else {
+    myssh_state(data, sshc, SSH_AUTH_KEY_INIT);
+  }
+  return CURLE_OK;
+}
 
-    case SSH_AUTH_AGENT:
-      /* as prev_identity evolves only after an identity user auth finished we
-         can safely request it again as long as EAGAIN is returned here or by
-         libssh2_agent_userauth */
-      rc = libssh2_agent_get_identity(sshc->ssh_agent,
-                                      &sshc->sshagent_identity,
-                                      sshc->sshagent_prev_identity);
-      if(rc == LIBSSH2_ERROR_EAGAIN)
-        break;
+static CURLcode ssh_state_auth_key_init(struct Curl_easy *data,
+                                        struct ssh_conn *sshc)
+{
+  if((data->set.ssh_auth_types & CURLSSH_AUTH_KEYBOARD)
+     && (strstr(sshc->authlist, "keyboard-interactive") != NULL)) {
+    myssh_state(data, sshc, SSH_AUTH_KEY);
+  }
+  else {
+    myssh_state(data, sshc, SSH_AUTH_DONE);
+  }
+  return CURLE_OK;
+}
 
-      if(rc == 0) {
-        rc = libssh2_agent_userauth(sshc->ssh_agent, conn->user,
-                                    sshc->sshagent_identity);
+static CURLcode ssh_state_auth_key(struct Curl_easy *data,
+                              struct ssh_conn *sshc)
+{
+  /* Authentication failed. Continue with keyboard-interactive now. */
+  struct connectdata *conn = data->conn;
+  int rc =
+    libssh2_userauth_keyboard_interactive_ex(sshc->ssh_session,
+                                             conn->user,
+                                             curlx_uztoui(
+                                               strlen(conn->user)),
+                                             &kbd_callback);
+  if(rc == LIBSSH2_ERROR_EAGAIN)
+    return CURLE_AGAIN;
+
+  if(rc == 0) {
+    sshc->authed = TRUE;
+    infof(data, "Initialized keyboard interactive authentication");
+    myssh_state(data, sshc, SSH_AUTH_DONE);
+    return CURLE_OK;
+  }
+  return CURLE_LOGIN_DENIED;
+}
 
-        if(rc < 0) {
-          if(rc != LIBSSH2_ERROR_EAGAIN) {
-            /* tried and failed? go to next identity */
-            sshc->sshagent_prev_identity = sshc->sshagent_identity;
-          }
-          break;
-        }
-      }
+static CURLcode ssh_state_auth_done(struct Curl_easy *data,
+                                    struct ssh_conn *sshc)
+{
+  struct connectdata *conn = data->conn;
+  if(!sshc->authed) {
+    failf(data, "Authentication failure");
+    myssh_state(data, sshc, SSH_SESSION_FREE);
+    return CURLE_LOGIN_DENIED;
+  }
 
-      if(rc < 0)
-        infof(data, "Failure requesting identities to agent");
-      else if(rc == 1)
-        infof(data, "No identity would match");
+  /*
+   * At this point we have an authenticated ssh session.
+   */
+  infof(data, "Authentication complete");
 
-      if(rc == LIBSSH2_ERROR_NONE) {
-        sshc->authed = TRUE;
-        infof(data, "Agent based authentication successful");
-        state(data, SSH_AUTH_DONE);
-      }
-      else {
-        state(data, SSH_AUTH_KEY_INIT);
-        rc = 0; /* clear rc and continue */
-      }
-      break;
+  Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSH is connected */
 
-    case SSH_AUTH_KEY_INIT:
-      if((data->set.ssh_auth_types & CURLSSH_AUTH_KEYBOARD)
-         && (strstr(sshc->authlist, "keyboard-interactive") != NULL)) {
-        state(data, SSH_AUTH_KEY);
-      }
-      else {
-        state(data, SSH_AUTH_DONE);
-      }
-      break;
+  conn->sockfd = conn->sock[FIRSTSOCKET];
+  conn->writesockfd = CURL_SOCKET_BAD;
 
-    case SSH_AUTH_KEY:
-      /* Authentication failed. Continue with keyboard-interactive now. */
-      rc = libssh2_userauth_keyboard_interactive_ex(sshc->ssh_session,
-                                                    conn->user,
-                                                    curlx_uztoui(
-                                                      strlen(conn->user)),
-                                                    &kbd_callback);
-      if(rc == LIBSSH2_ERROR_EAGAIN) {
-        break;
-      }
-      if(rc == 0) {
-        sshc->authed = TRUE;
-        infof(data, "Initialized keyboard interactive authentication");
-      }
-      state(data, SSH_AUTH_DONE);
-      break;
+  if(conn->handler->protocol == CURLPROTO_SFTP) {
+    myssh_state(data, sshc, SSH_SFTP_INIT);
+    return CURLE_OK;
+  }
+  infof(data, "SSH CONNECT phase done");
+  myssh_state(data, sshc, SSH_STOP);
+  return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_init(struct Curl_easy *data,
+                                    struct ssh_conn *sshc)
+{
+  /*
+   * Start the libssh2 sftp session
+   */
+  sshc->sftp_session = libssh2_sftp_init(sshc->ssh_session);
+  if(!sshc->sftp_session) {
+    char *err_msg = NULL;
+    if(libssh2_session_last_errno(sshc->ssh_session) ==
+       LIBSSH2_ERROR_EAGAIN)
+      return CURLE_AGAIN;
+
+    (void)libssh2_session_last_error(sshc->ssh_session,
+                                     &err_msg, NULL, 0);
+    failf(data, "Failure initializing sftp session: %s", err_msg);
+    myssh_state(data, sshc, SSH_SESSION_FREE);
+    return CURLE_FAILED_INIT;
+  }
+  myssh_state(data, sshc, SSH_SFTP_REALPATH);
+  return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_realpath(struct Curl_easy *data,
+                                        struct ssh_conn *sshc,
+                                        struct SSHPROTO *sshp)
+{
+  /*
+   * Get the "home" directory
+   */
+  int rc = libssh2_sftp_symlink_ex(sshc->sftp_session,
+                                   ".", curlx_uztoui(strlen(".")),
+                                   sshp->readdir_filename, CURL_PATH_MAX,
+                                   LIBSSH2_SFTP_REALPATH);
+  if(rc == LIBSSH2_ERROR_EAGAIN)
+    return CURLE_AGAIN;
+
+  if(rc > 0) {
+    /* It seems that this string is not always null-terminated */
+    sshp->readdir_filename[rc] = '\0';
+    free(sshc->homedir);
+    sshc->homedir = strdup(sshp->readdir_filename);
+    if(!sshc->homedir) {
+      myssh_state(data, sshc, SSH_SFTP_CLOSE);
+      return CURLE_OUT_OF_MEMORY;
+    }
+    free(data->state.most_recent_ftp_entrypath);
+    data->state.most_recent_ftp_entrypath = strdup(sshc->homedir);
+    if(!data->state.most_recent_ftp_entrypath)
+      return CURLE_OUT_OF_MEMORY;
+  }
+  else {
+    /* Return the error type */
+    unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+    CURLcode result;
+    if(sftperr)
+      result = sftp_libssh2_error_to_CURLE(sftperr);
+    else
+      /* in this case, the error was not in the SFTP level but for example
+         a time-out or similar */
+      result = CURLE_SSH;
+    DEBUGF(infof(data, "error = %lu makes libcurl = %d",
+                 sftperr, (int)result));
+    myssh_state(data, sshc, SSH_STOP);
+    return result;
+  }
+
+  /* This is the last step in the SFTP connect phase. Do note that while
+     we get the homedir here, we get the "workingpath" in the DO action
+     since the homedir will remain the same between request but the
+     working path will not. */
+  DEBUGF(infof(data, "SSH CONNECT phase done"));
+  myssh_state(data, sshc, SSH_STOP);
+  return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_quote_init(struct Curl_easy *data,
+                                          struct ssh_conn *sshc,
+                                          struct SSHPROTO *sshp)
+{
+  CURLcode result = Curl_getworkingpath(data, sshc->homedir, &sshp->path);
+  if(result) {
+    myssh_state(data, sshc, SSH_STOP);
+    return result;
+  }
+
+  if(data->set.quote) {
+    infof(data, "Sending quote commands");
+    sshc->quote_item = data->set.quote;
+    myssh_state(data, sshc, SSH_SFTP_QUOTE);
+  }
+  else {
+    myssh_state(data, sshc, SSH_SFTP_GETINFO);
+  }
+  return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_postquote_init(struct Curl_easy *data,
+                                              struct ssh_conn *sshc)
+{
+  if(data->set.postquote) {
+    infof(data, "Sending quote commands");
+    sshc->quote_item = data->set.postquote;
+    myssh_state(data, sshc, SSH_SFTP_QUOTE);
+  }
+  else {
+    myssh_state(data, sshc, SSH_STOP);
+  }
+  return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_quote(struct Curl_easy *data,
+                                     struct ssh_conn *sshc,
+                                     struct SSHPROTO *sshp)
+{
+  /* Send quote commands */
+  CURLcode result = sftp_quote(data, sshc, sshp);
+  if(result) {
+    myssh_state(data, sshc, SSH_SFTP_CLOSE);
+    sshc->nextstate = SSH_NO_STATE;
+  }
+  return result;
+}
+
+static CURLcode ssh_state_sftp_next_quote(struct Curl_easy *data,
+                                          struct ssh_conn *sshc)
+{
+  Curl_safefree(sshc->quote_path1);
+  Curl_safefree(sshc->quote_path2);
+
+  sshc->quote_item = sshc->quote_item->next;
+
+  if(sshc->quote_item) {
+    myssh_state(data, sshc, SSH_SFTP_QUOTE);
+  }
+  else {
+    if(sshc->nextstate != SSH_NO_STATE) {
+      myssh_state(data, sshc, sshc->nextstate);
+      sshc->nextstate = SSH_NO_STATE;
+    }
+    else {
+      myssh_state(data, sshc, SSH_SFTP_GETINFO);
+    }
+  }
+  return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_quote_stat(struct Curl_easy *data,
+                                          struct ssh_conn *sshc,
+                                          struct SSHPROTO *sshp,
+                                          bool *blockp)
+{
+  CURLcode result = sftp_quote_stat(data, sshc, sshp, blockp);
+  if(result) {
+    myssh_state(data, sshc, SSH_SFTP_CLOSE);
+    sshc->nextstate = SSH_NO_STATE;
+  }
+  return result;
+}
+
+static CURLcode ssh_state_sftp_quote_setstat(struct Curl_easy *data,
+                                             struct ssh_conn *sshc,
+                                             struct SSHPROTO *sshp)
+{
+  int rc =
+    libssh2_sftp_stat_ex(sshc->sftp_session, sshc->quote_path2,
+                         curlx_uztoui(strlen(sshc->quote_path2)),
+                         LIBSSH2_SFTP_SETSTAT,
+                         &sshp->quote_attrs);
+  if(rc == LIBSSH2_ERROR_EAGAIN)
+    return CURLE_AGAIN;
+
+  if(rc && !sshc->acceptfail) {
+    unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+    Curl_safefree(sshc->quote_path1);
+    Curl_safefree(sshc->quote_path2);
+    failf(data, "Attempt to set SFTP stats failed: %s",
+          sftp_libssh2_strerror(sftperr));
+    myssh_state(data, sshc, SSH_SFTP_CLOSE);
+    sshc->nextstate = SSH_NO_STATE;
+    return CURLE_QUOTE_ERROR;
+  }
+  myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
+  return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_quote_symlink(struct Curl_easy *data,
+                                             struct ssh_conn *sshc)
+{
+  int rc =
+    libssh2_sftp_symlink_ex(sshc->sftp_session, sshc->quote_path1,
+                            curlx_uztoui(strlen(sshc->quote_path1)),
+                            sshc->quote_path2,
+                            curlx_uztoui(strlen(sshc->quote_path2)),
+                            LIBSSH2_SFTP_SYMLINK);
+  if(rc == LIBSSH2_ERROR_EAGAIN)
+    return CURLE_AGAIN;
+
+  if(rc && !sshc->acceptfail) {
+    unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+    Curl_safefree(sshc->quote_path1);
+    Curl_safefree(sshc->quote_path2);
+    failf(data, "symlink command failed: %s",
+          sftp_libssh2_strerror(sftperr));
+    myssh_state(data, sshc, SSH_SFTP_CLOSE);
+    sshc->nextstate = SSH_NO_STATE;
+    return CURLE_QUOTE_ERROR;
+  }
+  myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
+  return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_quote_mkdir(struct Curl_easy *data,
+                                           struct ssh_conn *sshc)
+{
+  int rc = libssh2_sftp_mkdir_ex(sshc->sftp_session, sshc->quote_path1,
+                                 curlx_uztoui(strlen(sshc->quote_path1)),
+                                 (long)data->set.new_directory_perms);
+  if(rc == LIBSSH2_ERROR_EAGAIN)
+    return CURLE_AGAIN;
+
+  if(rc && !sshc->acceptfail) {
+    unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+    Curl_safefree(sshc->quote_path1);
+    failf(data, "mkdir command failed: %s",
+          sftp_libssh2_strerror(sftperr));
+    myssh_state(data, sshc, SSH_SFTP_CLOSE);
+    sshc->nextstate = SSH_NO_STATE;
+    return CURLE_QUOTE_ERROR;
+  }
+  myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
+  return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_quote_rename(struct Curl_easy *data,
+                                            struct ssh_conn *sshc)
+{
+  int rc =
+    libssh2_sftp_rename_ex(sshc->sftp_session, sshc->quote_path1,
+                           curlx_uztoui(strlen(sshc->quote_path1)),
+                           sshc->quote_path2,
+                           curlx_uztoui(strlen(sshc->quote_path2)),
+                           LIBSSH2_SFTP_RENAME_OVERWRITE |
+                           LIBSSH2_SFTP_RENAME_ATOMIC |
+                           LIBSSH2_SFTP_RENAME_NATIVE);
+
+  if(rc == LIBSSH2_ERROR_EAGAIN)
+    return CURLE_AGAIN;
+
+  if(rc && !sshc->acceptfail) {
+    unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+    Curl_safefree(sshc->quote_path1);
+    Curl_safefree(sshc->quote_path2);
+    failf(data, "rename command failed: %s",
+          sftp_libssh2_strerror(sftperr));
+    myssh_state(data, sshc, SSH_SFTP_CLOSE);
+    sshc->nextstate = SSH_NO_STATE;
+    return CURLE_QUOTE_ERROR;
+  }
+  myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
+  return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_quote_rmdir(struct Curl_easy *data,
+                                           struct ssh_conn *sshc)
+{
+  int rc = libssh2_sftp_rmdir_ex(sshc->sftp_session, sshc->quote_path1,
+                                 curlx_uztoui(strlen(sshc->quote_path1)));
+  if(rc == LIBSSH2_ERROR_EAGAIN)
+    return CURLE_AGAIN;
+
+  if(rc && !sshc->acceptfail) {
+    unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+    Curl_safefree(sshc->quote_path1);
+    failf(data, "rmdir command failed: %s",
+          sftp_libssh2_strerror(sftperr));
+    myssh_state(data, sshc, SSH_SFTP_CLOSE);
+    sshc->nextstate = SSH_NO_STATE;
+    return CURLE_QUOTE_ERROR;
+  }
+  myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
+  return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_quote_unlink(struct Curl_easy *data,
+                                            struct ssh_conn *sshc)
+{
+  int rc = libssh2_sftp_unlink_ex(sshc->sftp_session, sshc->quote_path1,
+                                  curlx_uztoui(strlen(sshc->quote_path1)));
+  if(rc == LIBSSH2_ERROR_EAGAIN)
+    return CURLE_AGAIN;
+
+  if(rc && !sshc->acceptfail) {
+    unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+    Curl_safefree(sshc->quote_path1);
+    failf(data, "rm command failed: %s", sftp_libssh2_strerror(sftperr));
+    myssh_state(data, sshc, SSH_SFTP_CLOSE);
+    sshc->nextstate = SSH_NO_STATE;
+    return CURLE_QUOTE_ERROR;
+  }
+  myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
+  return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_quote_statvfs(struct Curl_easy *data,
+                                        struct ssh_conn *sshc)
+{
+  LIBSSH2_SFTP_STATVFS statvfs;
+  int rc = libssh2_sftp_statvfs(sshc->sftp_session, sshc->quote_path1,
+                                curlx_uztoui(strlen(sshc->quote_path1)),
+                                &statvfs);
+
+  if(rc == LIBSSH2_ERROR_EAGAIN)
+    return CURLE_AGAIN;
+
+  if(rc && !sshc->acceptfail) {
+    unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+    Curl_safefree(sshc->quote_path1);
+    failf(data, "statvfs command failed: %s",
+          sftp_libssh2_strerror(sftperr));
+    myssh_state(data, sshc, SSH_SFTP_CLOSE);
+    sshc->nextstate = SSH_NO_STATE;
+    return CURLE_QUOTE_ERROR;
+  }
+  else if(rc == 0) {
+#ifdef _MSC_VER
+#define CURL_LIBSSH2_VFS_SIZE_MASK "I64u"
+#else
+#define CURL_LIBSSH2_VFS_SIZE_MASK "llu"
+#endif
+    CURLcode result;
+    char *tmp = aprintf("statvfs:\n"
+                        "f_bsize: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+                        "f_frsize: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+                        "f_blocks: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+                        "f_bfree: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+                        "f_bavail: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+                        "f_files: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+                        "f_ffree: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+                        "f_favail: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+                        "f_fsid: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+                        "f_flag: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+                        "f_namemax: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n",
+                        statvfs.f_bsize, statvfs.f_frsize,
+                        statvfs.f_blocks, statvfs.f_bfree,
+                        statvfs.f_bavail, statvfs.f_files,
+                        statvfs.f_ffree, statvfs.f_favail,
+                        statvfs.f_fsid, statvfs.f_flag,
+                        statvfs.f_namemax);
+    if(!tmp) {
+      myssh_state(data, sshc, SSH_SFTP_CLOSE);
+      sshc->nextstate = SSH_NO_STATE;
+      return CURLE_OUT_OF_MEMORY;
+    }
+
+    result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp));
+    free(tmp);
+    if(result) {
+      myssh_state(data, sshc, SSH_SFTP_CLOSE);
+      sshc->nextstate = SSH_NO_STATE;
+      return result;
+    }
+  }
+  myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
+  return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_create_dirs_mkdir(struct Curl_easy *data,
+                                                 struct ssh_conn *sshc,
+                                                 struct SSHPROTO *sshp)
+{
+  /* 'mode' - parameter is preliminary - default to 0644 */
+  int rc = libssh2_sftp_mkdir_ex(sshc->sftp_session, sshp->path,
+                                 curlx_uztoui(strlen(sshp->path)),
+                                 (long)data->set.new_directory_perms);
+  if(rc == LIBSSH2_ERROR_EAGAIN)
+    return CURLE_AGAIN;
+
+  *sshc->slash_pos = '/';
+  ++sshc->slash_pos;
+  if(rc < 0) {
+    /*
+     * Abort if failure was not that the dir already exists or the
+     * permission was denied (creation might succeed further down the
+     * path) - retry on unspecific FAILURE also
+     */
+    unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+    if((sftperr != LIBSSH2_FX_FILE_ALREADY_EXISTS) &&
+       (sftperr != LIBSSH2_FX_FAILURE) &&
+       (sftperr != LIBSSH2_FX_PERMISSION_DENIED)) {
+      myssh_state(data, sshc, SSH_SFTP_CLOSE);
+      return sftp_libssh2_error_to_CURLE(sftperr);
+    }
+  }
+  myssh_state(data, sshc, SSH_SFTP_CREATE_DIRS);
+  return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_readdir_init(struct Curl_easy *data,
+                                            struct ssh_conn *sshc,
+                                            struct SSHPROTO *sshp)
+{
+  Curl_pgrsSetDownloadSize(data, -1);
+  if(data->req.no_body) {
+    myssh_state(data, sshc, SSH_STOP);
+    return CURLE_OK;
+  }
+
+  /*
+   * This is a directory that we are trying to get, so produce a directory
+   * listing
+   */
+  sshc->sftp_handle =
+    libssh2_sftp_open_ex(sshc->sftp_session, sshp->path,
+                         curlx_uztoui(strlen(sshp->path)),
+                         0, 0, LIBSSH2_SFTP_OPENDIR);
+  if(!sshc->sftp_handle) {
+    unsigned long sftperr;
+    if(libssh2_session_last_errno(sshc->ssh_session) == LIBSSH2_ERROR_EAGAIN)
+      return CURLE_AGAIN;
+
+    sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+    failf(data, "Could not open directory for reading: %s",
+          sftp_libssh2_strerror(sftperr));
+    myssh_state(data, sshc, SSH_SFTP_CLOSE);
+    return sftp_libssh2_error_to_CURLE(sftperr);
+  }
+  myssh_state(data, sshc, SSH_SFTP_READDIR);
+  return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_readdir_link(struct Curl_easy *data,
+                                            struct ssh_conn *sshc,
+                                            struct SSHPROTO *sshp)
+{
+  CURLcode result;
+  int rc =
+    libssh2_sftp_symlink_ex(sshc->sftp_session,
+                            curlx_dyn_ptr(&sshp->readdir_link),
+                            (unsigned int)
+                            curlx_dyn_len(&sshp->readdir_link),
+                            sshp->readdir_filename,
+                            CURL_PATH_MAX, LIBSSH2_SFTP_READLINK);
+  if(rc == LIBSSH2_ERROR_EAGAIN)
+    return CURLE_AGAIN;
+
+  curlx_dyn_free(&sshp->readdir_link);
+
+  /* append filename and extra output */
+  result = curlx_dyn_addf(&sshp->readdir, " -> %s", sshp->readdir_filename);
+  if(result)
+    myssh_state(data, sshc, SSH_SFTP_CLOSE);
+  else
+    myssh_state(data, sshc, SSH_SFTP_READDIR_BOTTOM);
+  return result;
+}
+
+static CURLcode ssh_state_scp_download_init(struct Curl_easy *data,
+                                            struct ssh_conn *sshc,
+                                            struct SSHPROTO *sshp)
+{
+  curl_off_t bytecount;
+
+  /*
+   * We must check the remote file; if it is a directory no values will
+   * be set in sb
+   */
+
+  /*
+   * If support for >2GB files exists, use it.
+   */
+
+  /* get a fresh new channel from the ssh layer */
+#if LIBSSH2_VERSION_NUM < 0x010700
+  struct stat sb;
+  memset(&sb, 0, sizeof(struct stat));
+  sshc->ssh_channel = libssh2_scp_recv(sshc->ssh_session,
+                                       sshp->path, &sb);
+#else
+  libssh2_struct_stat sb;
+  memset(&sb, 0, sizeof(libssh2_struct_stat));
+  sshc->ssh_channel = libssh2_scp_recv2(sshc->ssh_session,
+                                        sshp->path, &sb);
+#endif
+
+  if(!sshc->ssh_channel) {
+    int ssh_err;
+    char *err_msg = NULL;
+
+    if(libssh2_session_last_errno(sshc->ssh_session) ==
+       LIBSSH2_ERROR_EAGAIN)
+      return CURLE_AGAIN;
+
+    ssh_err = (int)(libssh2_session_last_error(sshc->ssh_session,
+                                               &err_msg, NULL, 0));
+    failf(data, "%s", err_msg);
+    myssh_state(data, sshc, SSH_SCP_CHANNEL_FREE);
+    return libssh2_session_error_to_CURLE(ssh_err);
+  }
+
+  /* download data */
+  bytecount = (curl_off_t)sb.st_size;
+  data->req.maxdownload = (curl_off_t)sb.st_size;
+  Curl_xfer_setup1(data, CURL_XFER_RECV, bytecount, FALSE);
+
+  /* not set by Curl_xfer_setup to preserve keepon bits */
+  data->conn->writesockfd = data->conn->sockfd;
+
+  /* we want to use the _receiving_ function even when the socket turns
+     out writableable as the underlying libssh2 recv function will deal
+     with both accordingly */
+  data->state.select_bits = CURL_CSELECT_IN;
+
+  myssh_state(data, sshc, SSH_STOP);
+  return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_close(struct Curl_easy *data,
+                                     struct ssh_conn *sshc,
+                                     struct SSHPROTO *sshp)
+{
+  int rc = 0;
+  if(sshc->sftp_handle) {
+    rc = libssh2_sftp_close(sshc->sftp_handle);
+    if(rc == LIBSSH2_ERROR_EAGAIN)
+      return CURLE_AGAIN;
+
+    if(rc < 0) {
+      char *err_msg = NULL;
+      (void)libssh2_session_last_error(sshc->ssh_session,
+                                       &err_msg, NULL, 0);
+      infof(data, "Failed to close libssh2 file: %d %s", rc, err_msg);
+    }
+    sshc->sftp_handle = NULL;
+  }
+
+  Curl_safefree(sshp->path);
+
+  DEBUGF(infof(data, "SFTP DONE done"));
+
+  /* Check if nextstate is set and move .nextstate could be POSTQUOTE_INIT
+     After nextstate is executed, the control should come back to
+     SSH_SFTP_CLOSE to pass the correct result back  */
+  if(sshc->nextstate != SSH_NO_STATE &&
+     sshc->nextstate != SSH_SFTP_CLOSE) {
+    myssh_state(data, sshc, sshc->nextstate);
+    sshc->nextstate = SSH_SFTP_CLOSE;
+  }
+  else
+    myssh_state(data, sshc, SSH_STOP);
+
+  return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_shutdown(struct Curl_easy *data,
+                                   struct ssh_conn *sshc)
+{
+  /* during times we get here due to a broken transfer and then the
+     sftp_handle might not have been taken down so make sure that is done
+     before we proceed */
+  int rc = 0;
+  if(sshc->sftp_handle) {
+    rc = libssh2_sftp_close(sshc->sftp_handle);
+    if(rc == LIBSSH2_ERROR_EAGAIN)
+      return CURLE_AGAIN;
+
+    if(rc < 0) {
+      char *err_msg = NULL;
+      (void)libssh2_session_last_error(sshc->ssh_session, &err_msg,
+                                       NULL, 0);
+      infof(data, "Failed to close libssh2 file: %d %s", rc, err_msg);
+    }
+    sshc->sftp_handle = NULL;
+  }
+  if(sshc->sftp_session) {
+    rc = libssh2_sftp_shutdown(sshc->sftp_session);
+    if(rc == LIBSSH2_ERROR_EAGAIN)
+      return CURLE_AGAIN;
+
+    if(rc < 0) {
+      infof(data, "Failed to stop libssh2 sftp subsystem");
+    }
+    sshc->sftp_session = NULL;
+  }
+
+  Curl_safefree(sshc->homedir);
+
+  myssh_state(data, sshc, SSH_SESSION_DISCONNECT);
+  return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_download_init(struct Curl_easy *data,
+                                             struct ssh_conn *sshc,
+                                             struct SSHPROTO *sshp)
+{
+  /*
+   * Work on getting the specified file
+   */
+  sshc->sftp_handle =
+    libssh2_sftp_open_ex(sshc->sftp_session, sshp->path,
+                         curlx_uztoui(strlen(sshp->path)),
+                         LIBSSH2_FXF_READ, (long)data->set.new_file_perms,
+                         LIBSSH2_SFTP_OPENFILE);
+  if(!sshc->sftp_handle) {
+    unsigned long sftperr;
+    if(libssh2_session_last_errno(sshc->ssh_session) ==
+       LIBSSH2_ERROR_EAGAIN) {
+      return CURLE_AGAIN;
+    }
+    sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+    failf(data, "Could not open remote file for reading: %s",
+          sftp_libssh2_strerror(sftperr));
+    myssh_state(data, sshc, SSH_SFTP_CLOSE);
+    return sftp_libssh2_error_to_CURLE(sftperr);
+  }
+  myssh_state(data, sshc, SSH_SFTP_DOWNLOAD_STAT);
+  return CURLE_OK;
+}
+
+static CURLcode ssh_state_scp_upload_init(struct Curl_easy *data,
+                                          struct ssh_conn *sshc,
+                                          struct SSHPROTO *sshp)
+{
+  /*
+   * libssh2 requires that the destination path is a full path that
+   * includes the destination file and name OR ends in a "/" . If this is
+   * not done the destination file will be named the same name as the last
+   * directory in the path.
+   */
+  sshc->ssh_channel =
+    libssh2_scp_send64(sshc->ssh_session, sshp->path,
+                       (int)data->set.new_file_perms,
+                       (libssh2_int64_t)data->state.infilesize, 0, 0);
+  if(!sshc->ssh_channel) {
+    int ssh_err;
+    char *err_msg = NULL;
+    CURLcode result;
+    if(libssh2_session_last_errno(sshc->ssh_session) ==
+       LIBSSH2_ERROR_EAGAIN)
+      return CURLE_AGAIN;
+
+    ssh_err = (int)(libssh2_session_last_error(sshc->ssh_session,
+                                               &err_msg, NULL, 0));
+    failf(data, "%s", err_msg);
+    myssh_state(data, sshc, SSH_SCP_CHANNEL_FREE);
+    result = libssh2_session_error_to_CURLE(ssh_err);
+
+    /* Map generic errors to upload failed */
+    if(result == CURLE_SSH ||
+       result == CURLE_REMOTE_FILE_NOT_FOUND)
+      result = CURLE_UPLOAD_FAILED;
+    return result;
+  }
 
-    case SSH_AUTH_DONE:
-      if(!sshc->authed) {
-        failf(data, "Authentication failure");
-        state(data, SSH_SESSION_FREE);
-        sshc->actualcode = CURLE_LOGIN_DENIED;
-        break;
-      }
+  /* upload data */
+  data->req.size = data->state.infilesize;
+  Curl_pgrsSetUploadSize(data, data->state.infilesize);
+  Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE);
 
-      /*
-       * At this point we have an authenticated ssh session.
-       */
-      infof(data, "Authentication complete");
+  /* not set by Curl_xfer_setup to preserve keepon bits */
+  data->conn->sockfd = data->conn->writesockfd;
+
+  /* store this original bitmask setup to use later on if we cannot
+     figure out a "real" bitmask */
+  sshc->orig_waitfor = data->req.keepon;
+
+  /* we want to use the _sending_ function even when the socket turns
+     out readable as the underlying libssh2 scp send function will deal
+     with both accordingly */
+  data->state.select_bits = CURL_CSELECT_OUT;
+
+  myssh_state(data, sshc, SSH_STOP);
+
+  return CURLE_OK;
+}
+
+static CURLcode ssh_state_session_disconnect(struct Curl_easy *data,
+                                             struct ssh_conn *sshc)
+{
+  /* during weird times when we have been prematurely aborted, the channel
+     is still alive when we reach this state and we MUST kill the channel
+     properly first */
+  int rc = 0;
+  if(sshc->ssh_channel) {
+    rc = libssh2_channel_free(sshc->ssh_channel);
+    if(rc == LIBSSH2_ERROR_EAGAIN)
+      return CURLE_OK;
+
+    if(rc < 0) {
+      char *err_msg = NULL;
+      (void)libssh2_session_last_error(sshc->ssh_session,
+                                       &err_msg, NULL, 0);
+      infof(data, "Failed to free libssh2 scp subsystem: %d %s",
+            rc, err_msg);
+    }
+    sshc->ssh_channel = NULL;
+  }
+
+  if(sshc->ssh_session) {
+    rc = libssh2_session_disconnect(sshc->ssh_session, "Shutdown");
+    if(rc == LIBSSH2_ERROR_EAGAIN)
+      return CURLE_AGAIN;
+
+    if(rc < 0) {
+      char *err_msg = NULL;
+      (void)libssh2_session_last_error(sshc->ssh_session,
+                                       &err_msg, NULL, 0);
+      infof(data, "Failed to disconnect libssh2 session: %d %s",
+            rc, err_msg);
+    }
+  }
+
+  Curl_safefree(sshc->homedir);
+
+  myssh_state(data, sshc, SSH_SESSION_FREE);
+  return CURLE_OK;
+}
+/*
+ * ssh_statemachine() runs the SSH state machine as far as it can without
+ * blocking and without reaching the end. The data the pointer 'block' points
+ * to will be set to TRUE if the libssh2 function returns LIBSSH2_ERROR_EAGAIN
+ * meaning it wants to be called again when the socket is ready
+ */
 
-      Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSH is connected */
+static CURLcode ssh_statemachine(struct Curl_easy *data,
+                                 struct ssh_conn *sshc,
+                                 struct SSHPROTO *sshp,
+                                 bool *block)
+{
+  CURLcode result = CURLE_OK;
+  struct connectdata *conn = data->conn;
+  *block = 0; /* we are not blocking by default */
 
-      conn->sockfd = conn->sock[FIRSTSOCKET];
-      conn->writesockfd = CURL_SOCKET_BAD;
+  do {
+    switch(sshc->state) {
+    case SSH_INIT:
+      result = ssh_state_init(data, sshc);
+      if(result)
+        break;
+      FALLTHROUGH();
 
-      if(conn->handler->protocol == CURLPROTO_SFTP) {
-        state(data, SSH_SFTP_INIT);
+    case SSH_S_STARTUP:
+      result = ssh_state_startup(data, sshc);
+      if(result)
         break;
-      }
-      infof(data, "SSH CONNECT phase done");
-      state(data, SSH_STOP);
+      FALLTHROUGH();
+
+    case SSH_HOSTKEY:
+      result = ssh_state_hostkey(data, sshc);
       break;
 
-    case SSH_SFTP_INIT:
-      /*
-       * Start the libssh2 sftp session
-       */
-      sshc->sftp_session = libssh2_sftp_init(sshc->ssh_session);
-      if(!sshc->sftp_session) {
-        char *err_msg = NULL;
-        if(libssh2_session_last_errno(sshc->ssh_session) ==
-           LIBSSH2_ERROR_EAGAIN) {
-          rc = LIBSSH2_ERROR_EAGAIN;
-          break;
-        }
+    case SSH_AUTHLIST:
+      result = ssh_state_authlist(data, sshc);
+      break;
 
-        (void)libssh2_session_last_error(sshc->ssh_session,
-                                         &err_msg, NULL, 0);
-        failf(data, "Failure initializing sftp session: %s", err_msg);
-        state(data, SSH_SESSION_FREE);
-        sshc->actualcode = CURLE_FAILED_INIT;
-        break;
-      }
-      state(data, SSH_SFTP_REALPATH);
+    case SSH_AUTH_PKEY_INIT:
+      result = ssh_state_pkey_init(data, sshc);
       break;
 
-    case SSH_SFTP_REALPATH:
-      /*
-       * Get the "home" directory
-       */
-      rc = libssh2_sftp_symlink_ex(sshc->sftp_session,
-                                   ".", curlx_uztoui(strlen(".")),
-                                   sshp->readdir_filename, CURL_PATH_MAX,
-                                   LIBSSH2_SFTP_REALPATH);
-      if(rc == LIBSSH2_ERROR_EAGAIN) {
-        break;
-      }
-      if(rc > 0) {
-        /* It seems that this string is not always NULL terminated */
-        sshp->readdir_filename[rc] = '\0';
-        sshc->homedir = strdup(sshp->readdir_filename);
-        if(!sshc->homedir) {
-          state(data, SSH_SFTP_CLOSE);
-          sshc->actualcode = CURLE_OUT_OF_MEMORY;
-          break;
-        }
-        data->state.most_recent_ftp_entrypath = sshc->homedir;
-      }
-      else {
-        /* Return the error type */
-        unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
-        if(sftperr)
-          result = sftp_libssh2_error_to_CURLE(sftperr);
-        else
-          /* in this case, the error was not in the SFTP level but for example
-             a time-out or similar */
-          result = CURLE_SSH;
-        sshc->actualcode = result;
-        DEBUGF(infof(data, "error = %lu makes libcurl = %d",
-                     sftperr, (int)result));
-        state(data, SSH_STOP);
-        break;
-      }
+    case SSH_AUTH_PKEY:
+      result = ssh_state_auth_pkey(data, sshc);
+      break;
 
-    /* This is the last step in the SFTP connect phase. Do note that while
-       we get the homedir here, we get the "workingpath" in the DO action
-       since the homedir will remain the same between request but the
-       working path will not. */
-    DEBUGF(infof(data, "SSH CONNECT phase done"));
-    state(data, SSH_STOP);
-    break;
+    case SSH_AUTH_PASS_INIT:
+      result = ssh_state_auth_pass_init(data, sshc);
+      break;
 
-    case SSH_SFTP_QUOTE_INIT:
+    case SSH_AUTH_PASS:
+      result = ssh_state_auth_pass(data, sshc);
+      break;
 
-      result = Curl_getworkingpath(data, sshc->homedir, &sshp->path);
-      if(result) {
-        sshc->actualcode = result;
-        state(data, SSH_STOP);
-        break;
-      }
+    case SSH_AUTH_HOST_INIT:
+      result = ssh_state_auth_host_init(data, sshc);
+      break;
 
-      if(data->set.quote) {
-        infof(data, "Sending quote commands");
-        sshc->quote_item = data->set.quote;
-        state(data, SSH_SFTP_QUOTE);
-      }
-      else {
-        state(data, SSH_SFTP_GETINFO);
-      }
+    case SSH_AUTH_HOST:
+      myssh_state(data, sshc, SSH_AUTH_AGENT_INIT);
+      break;
+
+    case SSH_AUTH_AGENT_INIT:
+      result = ssh_state_auth_agent_init(data, sshc);
+      break;
+
+    case SSH_AUTH_AGENT_LIST:
+      result = ssh_state_auth_agent_list(data, sshc);
+      break;
+
+    case SSH_AUTH_AGENT:
+      result = ssh_state_auth_agent(data, sshc);
+      break;
+
+    case SSH_AUTH_KEY_INIT:
+      result = ssh_state_auth_key_init(data, sshc);
+      break;
+
+    case SSH_AUTH_KEY:
+      result = ssh_state_auth_key(data, sshc);
+      break;
+
+    case SSH_AUTH_DONE:
+      result = ssh_state_auth_done(data, sshc);
+      break;
+
+    case SSH_SFTP_INIT:
+      result = ssh_state_sftp_init(data, sshc);
+      break;
+
+    case SSH_SFTP_REALPATH:
+      result = ssh_state_sftp_realpath(data, sshc, sshp);
+      break;
+
+    case SSH_SFTP_QUOTE_INIT:
+      result = ssh_state_sftp_quote_init(data, sshc, sshp);
       break;
 
     case SSH_SFTP_POSTQUOTE_INIT:
-      if(data->set.postquote) {
-        infof(data, "Sending quote commands");
-        sshc->quote_item = data->set.postquote;
-        state(data, SSH_SFTP_QUOTE);
-      }
-      else {
-        state(data, SSH_STOP);
-      }
+      result = ssh_state_sftp_postquote_init(data, sshc);
       break;
 
     case SSH_SFTP_QUOTE:
-      /* Send quote commands */
-      result = sftp_quote(data, sshc, sshp);
-      if(result) {
-        state(data, SSH_SFTP_CLOSE);
-        sshc->nextstate = SSH_NO_STATE;
-        sshc->actualcode = result;
-      }
+      result = ssh_state_sftp_quote(data, sshc, sshp);
       break;
 
     case SSH_SFTP_NEXT_QUOTE:
-      Curl_safefree(sshc->quote_path1);
-      Curl_safefree(sshc->quote_path2);
-
-      sshc->quote_item = sshc->quote_item->next;
-
-      if(sshc->quote_item) {
-        state(data, SSH_SFTP_QUOTE);
-      }
-      else {
-        if(sshc->nextstate != SSH_NO_STATE) {
-          state(data, sshc->nextstate);
-          sshc->nextstate = SSH_NO_STATE;
-        }
-        else {
-          state(data, SSH_SFTP_GETINFO);
-        }
-      }
+      result = ssh_state_sftp_next_quote(data, sshc);
       break;
 
     case SSH_SFTP_QUOTE_STAT:
-      result = sftp_quote_stat(data, sshc, sshp, block);
-      if(result) {
-        state(data, SSH_SFTP_CLOSE);
-        sshc->nextstate = SSH_NO_STATE;
-        sshc->actualcode = result;
-      }
+      result = ssh_state_sftp_quote_stat(data, sshc, sshp, block);
       break;
 
     case SSH_SFTP_QUOTE_SETSTAT:
-      rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshc->quote_path2,
-                                curlx_uztoui(strlen(sshc->quote_path2)),
-                                LIBSSH2_SFTP_SETSTAT,
-                                &sshp->quote_attrs);
-      if(rc == LIBSSH2_ERROR_EAGAIN) {
-        break;
-      }
-      if(rc && !sshc->acceptfail) {
-        unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
-        Curl_safefree(sshc->quote_path1);
-        Curl_safefree(sshc->quote_path2);
-        failf(data, "Attempt to set SFTP stats failed: %s",
-              sftp_libssh2_strerror(sftperr));
-        state(data, SSH_SFTP_CLOSE);
-        sshc->nextstate = SSH_NO_STATE;
-        sshc->actualcode = CURLE_QUOTE_ERROR;
-        break;
-      }
-      state(data, SSH_SFTP_NEXT_QUOTE);
+      result = ssh_state_sftp_quote_setstat(data, sshc, sshp);
       break;
 
     case SSH_SFTP_QUOTE_SYMLINK:
-      rc = libssh2_sftp_symlink_ex(sshc->sftp_session, sshc->quote_path1,
-                                   curlx_uztoui(strlen(sshc->quote_path1)),
-                                   sshc->quote_path2,
-                                   curlx_uztoui(strlen(sshc->quote_path2)),
-                                   LIBSSH2_SFTP_SYMLINK);
-      if(rc == LIBSSH2_ERROR_EAGAIN) {
-        break;
-      }
-      if(rc && !sshc->acceptfail) {
-        unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
-        Curl_safefree(sshc->quote_path1);
-        Curl_safefree(sshc->quote_path2);
-        failf(data, "symlink command failed: %s",
-              sftp_libssh2_strerror(sftperr));
-        state(data, SSH_SFTP_CLOSE);
-        sshc->nextstate = SSH_NO_STATE;
-        sshc->actualcode = CURLE_QUOTE_ERROR;
-        break;
-      }
-      state(data, SSH_SFTP_NEXT_QUOTE);
+      result = ssh_state_sftp_quote_symlink(data, sshc);
       break;
 
     case SSH_SFTP_QUOTE_MKDIR:
-      rc = libssh2_sftp_mkdir_ex(sshc->sftp_session, sshc->quote_path1,
-                                 curlx_uztoui(strlen(sshc->quote_path1)),
-                                 (long)data->set.new_directory_perms);
-      if(rc == LIBSSH2_ERROR_EAGAIN) {
-        break;
-      }
-      if(rc && !sshc->acceptfail) {
-        unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
-        Curl_safefree(sshc->quote_path1);
-        failf(data, "mkdir command failed: %s",
-              sftp_libssh2_strerror(sftperr));
-        state(data, SSH_SFTP_CLOSE);
-        sshc->nextstate = SSH_NO_STATE;
-        sshc->actualcode = CURLE_QUOTE_ERROR;
-        break;
-      }
-      state(data, SSH_SFTP_NEXT_QUOTE);
+      result = ssh_state_sftp_quote_mkdir(data, sshc);
       break;
 
     case SSH_SFTP_QUOTE_RENAME:
-      rc = libssh2_sftp_rename_ex(sshc->sftp_session, sshc->quote_path1,
-                                  curlx_uztoui(strlen(sshc->quote_path1)),
-                                  sshc->quote_path2,
-                                  curlx_uztoui(strlen(sshc->quote_path2)),
-                                  LIBSSH2_SFTP_RENAME_OVERWRITE |
-                                  LIBSSH2_SFTP_RENAME_ATOMIC |
-                                  LIBSSH2_SFTP_RENAME_NATIVE);
-
-      if(rc == LIBSSH2_ERROR_EAGAIN) {
-        break;
-      }
-      if(rc && !sshc->acceptfail) {
-        unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
-        Curl_safefree(sshc->quote_path1);
-        Curl_safefree(sshc->quote_path2);
-        failf(data, "rename command failed: %s",
-              sftp_libssh2_strerror(sftperr));
-        state(data, SSH_SFTP_CLOSE);
-        sshc->nextstate = SSH_NO_STATE;
-        sshc->actualcode = CURLE_QUOTE_ERROR;
-        break;
-      }
-      state(data, SSH_SFTP_NEXT_QUOTE);
+      result = ssh_state_sftp_quote_rename(data, sshc);
       break;
 
     case SSH_SFTP_QUOTE_RMDIR:
-      rc = libssh2_sftp_rmdir_ex(sshc->sftp_session, sshc->quote_path1,
-                                 curlx_uztoui(strlen(sshc->quote_path1)));
-      if(rc == LIBSSH2_ERROR_EAGAIN) {
-        break;
-      }
-      if(rc && !sshc->acceptfail) {
-        unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
-        Curl_safefree(sshc->quote_path1);
-        failf(data, "rmdir command failed: %s",
-              sftp_libssh2_strerror(sftperr));
-        state(data, SSH_SFTP_CLOSE);
-        sshc->nextstate = SSH_NO_STATE;
-        sshc->actualcode = CURLE_QUOTE_ERROR;
-        break;
-      }
-      state(data, SSH_SFTP_NEXT_QUOTE);
+      result = ssh_state_sftp_quote_rmdir(data, sshc);
       break;
 
     case SSH_SFTP_QUOTE_UNLINK:
-      rc = libssh2_sftp_unlink_ex(sshc->sftp_session, sshc->quote_path1,
-                                  curlx_uztoui(strlen(sshc->quote_path1)));
-      if(rc == LIBSSH2_ERROR_EAGAIN) {
-        break;
-      }
-      if(rc && !sshc->acceptfail) {
-        unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
-        Curl_safefree(sshc->quote_path1);
-        failf(data, "rm command failed: %s", sftp_libssh2_strerror(sftperr));
-        state(data, SSH_SFTP_CLOSE);
-        sshc->nextstate = SSH_NO_STATE;
-        sshc->actualcode = CURLE_QUOTE_ERROR;
-        break;
-      }
-      state(data, SSH_SFTP_NEXT_QUOTE);
+      result = ssh_state_sftp_quote_unlink(data, sshc);
       break;
 
     case SSH_SFTP_QUOTE_STATVFS:
-    {
-      LIBSSH2_SFTP_STATVFS statvfs;
-      rc = libssh2_sftp_statvfs(sshc->sftp_session, sshc->quote_path1,
-                                curlx_uztoui(strlen(sshc->quote_path1)),
-                                &statvfs);
-
-      if(rc == LIBSSH2_ERROR_EAGAIN) {
-        break;
-      }
-      if(rc && !sshc->acceptfail) {
-        unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
-        Curl_safefree(sshc->quote_path1);
-        failf(data, "statvfs command failed: %s",
-              sftp_libssh2_strerror(sftperr));
-        state(data, SSH_SFTP_CLOSE);
-        sshc->nextstate = SSH_NO_STATE;
-        sshc->actualcode = CURLE_QUOTE_ERROR;
-        break;
-      }
-      else if(rc == 0) {
-#ifdef _MSC_VER
-#define CURL_LIBSSH2_VFS_SIZE_MASK "I64u"
-#else
-#define CURL_LIBSSH2_VFS_SIZE_MASK "llu"
-#endif
-        char *tmp = aprintf("statvfs:\n"
-                            "f_bsize: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
-                            "f_frsize: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
-                            "f_blocks: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
-                            "f_bfree: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
-                            "f_bavail: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
-                            "f_files: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
-                            "f_ffree: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
-                            "f_favail: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
-                            "f_fsid: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
-                            "f_flag: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
-                            "f_namemax: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n",
-                            statvfs.f_bsize, statvfs.f_frsize,
-                            statvfs.f_blocks, statvfs.f_bfree,
-                            statvfs.f_bavail, statvfs.f_files,
-                            statvfs.f_ffree, statvfs.f_favail,
-                            statvfs.f_fsid, statvfs.f_flag,
-                            statvfs.f_namemax);
-        if(!tmp) {
-          result = CURLE_OUT_OF_MEMORY;
-          state(data, SSH_SFTP_CLOSE);
-          sshc->nextstate = SSH_NO_STATE;
-          break;
-        }
-
-        result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp));
-        free(tmp);
-        if(result) {
-          state(data, SSH_SFTP_CLOSE);
-          sshc->nextstate = SSH_NO_STATE;
-          sshc->actualcode = result;
-        }
-      }
-      state(data, SSH_SFTP_NEXT_QUOTE);
+      result = ssh_state_sftp_quote_statvfs(data, sshc);
       break;
-    }
 
     case SSH_SFTP_GETINFO:
-    {
       if(data->set.get_filetime) {
-        state(data, SSH_SFTP_FILETIME);
+        myssh_state(data, sshc, SSH_SFTP_FILETIME);
       }
       else {
-        state(data, SSH_SFTP_TRANS_INIT);
+        myssh_state(data, sshc, SSH_SFTP_TRANS_INIT);
       }
       break;
-    }
 
     case SSH_SFTP_FILETIME:
     {
       LIBSSH2_SFTP_ATTRIBUTES attrs;
 
-      rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshp->path,
-                                curlx_uztoui(strlen(sshp->path)),
-                                LIBSSH2_SFTP_STAT, &attrs);
+      int rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshp->path,
+                                    curlx_uztoui(strlen(sshp->path)),
+                                    LIBSSH2_SFTP_STAT, &attrs);
       if(rc == LIBSSH2_ERROR_EAGAIN) {
+        result = CURLE_AGAIN;
         break;
       }
       if(rc == 0) {
         data->info.filetime = (time_t)attrs.mtime;
       }
 
-      state(data, SSH_SFTP_TRANS_INIT);
+      myssh_state(data, sshc, SSH_SFTP_TRANS_INIT);
       break;
     }
 
     case SSH_SFTP_TRANS_INIT:
       if(data->state.upload)
-        state(data, SSH_SFTP_UPLOAD_INIT);
+        myssh_state(data, sshc, SSH_SFTP_UPLOAD_INIT);
       else {
         if(sshp->path[strlen(sshp->path)-1] == '/')
-          state(data, SSH_SFTP_READDIR_INIT);
+          myssh_state(data, sshc, SSH_SFTP_READDIR_INIT);
         else
-          state(data, SSH_SFTP_DOWNLOAD_INIT);
+          myssh_state(data, sshc, SSH_SFTP_DOWNLOAD_INIT);
       }
       break;
 
     case SSH_SFTP_UPLOAD_INIT:
       result = sftp_upload_init(data, sshc, sshp, block);
       if(result) {
-        state(data, SSH_SFTP_CLOSE);
+        myssh_state(data, sshc, SSH_SFTP_CLOSE);
         sshc->nextstate = SSH_NO_STATE;
-        sshc->actualcode = result;
       }
       break;
 
     case SSH_SFTP_CREATE_DIRS_INIT:
       if(strlen(sshp->path) > 1) {
         sshc->slash_pos = sshp->path + 1; /* ignore the leading '/' */
-        state(data, SSH_SFTP_CREATE_DIRS);
+        myssh_state(data, sshc, SSH_SFTP_CREATE_DIRS);
       }
       else {
-        state(data, SSH_SFTP_UPLOAD_INIT);
+        myssh_state(data, sshc, SSH_SFTP_UPLOAD_INIT);
       }
       break;
 
@@ -2352,402 +2891,121 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, bool *block)
         *sshc->slash_pos = 0;
 
         infof(data, "Creating directory '%s'", sshp->path);
-        state(data, SSH_SFTP_CREATE_DIRS_MKDIR);
+        myssh_state(data, sshc, SSH_SFTP_CREATE_DIRS_MKDIR);
         break;
       }
-      state(data, SSH_SFTP_UPLOAD_INIT);
+      myssh_state(data, sshc, SSH_SFTP_UPLOAD_INIT);
       break;
 
     case SSH_SFTP_CREATE_DIRS_MKDIR:
-      /* 'mode' - parameter is preliminary - default to 0644 */
-      rc = libssh2_sftp_mkdir_ex(sshc->sftp_session, sshp->path,
-                                 curlx_uztoui(strlen(sshp->path)),
-                                 (long)data->set.new_directory_perms);
-      if(rc == LIBSSH2_ERROR_EAGAIN) {
-        break;
-      }
-      *sshc->slash_pos = '/';
-      ++sshc->slash_pos;
-      if(rc < 0) {
-        /*
-         * Abort if failure was not that the dir already exists or the
-         * permission was denied (creation might succeed further down the
-         * path) - retry on unspecific FAILURE also
-         */
-        unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
-        if((sftperr != LIBSSH2_FX_FILE_ALREADY_EXISTS) &&
-           (sftperr != LIBSSH2_FX_FAILURE) &&
-           (sftperr != LIBSSH2_FX_PERMISSION_DENIED)) {
-          result = sftp_libssh2_error_to_CURLE(sftperr);
-          state(data, SSH_SFTP_CLOSE);
-          sshc->actualcode = result ? result : CURLE_SSH;
-          break;
-        }
-        rc = 0; /* clear rc and continue */
-      }
-      state(data, SSH_SFTP_CREATE_DIRS);
+      result = ssh_state_sftp_create_dirs_mkdir(data, sshc, sshp);
       break;
 
     case SSH_SFTP_READDIR_INIT:
-      Curl_pgrsSetDownloadSize(data, -1);
-      if(data->req.no_body) {
-        state(data, SSH_STOP);
-        break;
-      }
-
-      /*
-       * This is a directory that we are trying to get, so produce a directory
-       * listing
-       */
-      sshc->sftp_handle =
-        libssh2_sftp_open_ex(sshc->sftp_session, sshp->path,
-                             curlx_uztoui(strlen(sshp->path)),
-                             0, 0, LIBSSH2_SFTP_OPENDIR);
-      if(!sshc->sftp_handle) {
-        unsigned long sftperr;
-        if(libssh2_session_last_errno(sshc->ssh_session) ==
-           LIBSSH2_ERROR_EAGAIN) {
-          rc = LIBSSH2_ERROR_EAGAIN;
-          break;
-        }
-        sftperr = libssh2_sftp_last_error(sshc->sftp_session);
-        failf(data, "Could not open directory for reading: %s",
-              sftp_libssh2_strerror(sftperr));
-        state(data, SSH_SFTP_CLOSE);
-        result = sftp_libssh2_error_to_CURLE(sftperr);
-        sshc->actualcode = result ? result : CURLE_SSH;
-        break;
-      }
-      Curl_dyn_init(&sshp->readdir, CURL_PATH_MAX * 2);
-      state(data, SSH_SFTP_READDIR);
+      result = ssh_state_sftp_readdir_init(data, sshc, sshp);
       break;
 
     case SSH_SFTP_READDIR:
       result = sftp_readdir(data, sshc, sshp, block);
       if(result) {
-        sshc->actualcode = result;
-        state(data, SSH_SFTP_CLOSE);
+        myssh_state(data, sshc, SSH_SFTP_CLOSE);
       }
       break;
 
     case SSH_SFTP_READDIR_LINK:
-      rc =
-        libssh2_sftp_symlink_ex(sshc->sftp_session,
-                                Curl_dyn_ptr(&sshp->readdir_link),
-                                (unsigned int)
-                                  Curl_dyn_len(&sshp->readdir_link),
-                                sshp->readdir_filename,
-                                CURL_PATH_MAX, LIBSSH2_SFTP_READLINK);
-      if(rc == LIBSSH2_ERROR_EAGAIN) {
-        break;
-      }
-      Curl_dyn_free(&sshp->readdir_link);
-
-      /* append filename and extra output */
-      result = Curl_dyn_addf(&sshp->readdir, " -> %s", sshp->readdir_filename);
-
-      if(result) {
-        state(data, SSH_SFTP_CLOSE);
-        sshc->actualcode = result;
-        break;
-      }
-
-      state(data, SSH_SFTP_READDIR_BOTTOM);
+      result = ssh_state_sftp_readdir_link(data, sshc, sshp);
       break;
 
     case SSH_SFTP_READDIR_BOTTOM:
-      result = Curl_dyn_addn(&sshp->readdir, "\n", 1);
+      result = curlx_dyn_addn(&sshp->readdir, "\n", 1);
       if(!result)
         result = Curl_client_write(data, CLIENTWRITE_BODY,
-                                   Curl_dyn_ptr(&sshp->readdir),
-                                   Curl_dyn_len(&sshp->readdir));
+                                   curlx_dyn_ptr(&sshp->readdir),
+                                   curlx_dyn_len(&sshp->readdir));
 
       if(result) {
-        Curl_dyn_free(&sshp->readdir);
-        state(data, SSH_STOP);
+        curlx_dyn_free(&sshp->readdir);
+        myssh_state(data, sshc, SSH_STOP);
       }
       else {
-        Curl_dyn_reset(&sshp->readdir);
-        state(data, SSH_SFTP_READDIR);
+        curlx_dyn_reset(&sshp->readdir);
+        myssh_state(data, sshc, SSH_SFTP_READDIR);
       }
       break;
 
-    case SSH_SFTP_READDIR_DONE:
-      if(libssh2_sftp_closedir(sshc->sftp_handle) ==
-         LIBSSH2_ERROR_EAGAIN) {
-        rc = LIBSSH2_ERROR_EAGAIN;
-        break;
+    case SSH_SFTP_READDIR_DONE:
+      if(libssh2_sftp_closedir(sshc->sftp_handle) == LIBSSH2_ERROR_EAGAIN)
+        result = CURLE_AGAIN;
+      else {
+        sshc->sftp_handle = NULL;
+
+        /* no data to transfer */
+        Curl_xfer_setup_nop(data);
+        myssh_state(data, sshc, SSH_STOP);
       }
-      sshc->sftp_handle = NULL;
-
-      /* no data to transfer */
-      Curl_xfer_setup_nop(data);
-      state(data, SSH_STOP);
       break;
 
     case SSH_SFTP_DOWNLOAD_INIT:
-      /*
-       * Work on getting the specified file
-       */
-      sshc->sftp_handle =
-        libssh2_sftp_open_ex(sshc->sftp_session, sshp->path,
-                             curlx_uztoui(strlen(sshp->path)),
-                             LIBSSH2_FXF_READ, (long)data->set.new_file_perms,
-                             LIBSSH2_SFTP_OPENFILE);
-      if(!sshc->sftp_handle) {
-        unsigned long sftperr;
-        if(libssh2_session_last_errno(sshc->ssh_session) ==
-           LIBSSH2_ERROR_EAGAIN) {
-          rc = LIBSSH2_ERROR_EAGAIN;
-          break;
-        }
-        sftperr = libssh2_sftp_last_error(sshc->sftp_session);
-        failf(data, "Could not open remote file for reading: %s",
-              sftp_libssh2_strerror(sftperr));
-        state(data, SSH_SFTP_CLOSE);
-        result = sftp_libssh2_error_to_CURLE(sftperr);
-        sshc->actualcode = result ? result : CURLE_SSH;
-        break;
-      }
-      state(data, SSH_SFTP_DOWNLOAD_STAT);
+      result = ssh_state_sftp_download_init(data, sshc, sshp);
       break;
 
     case SSH_SFTP_DOWNLOAD_STAT:
       result = sftp_download_stat(data, sshc, sshp, block);
       if(result) {
-        state(data, SSH_SFTP_CLOSE);
+        myssh_state(data, sshc, SSH_SFTP_CLOSE);
         sshc->nextstate = SSH_NO_STATE;
-        sshc->actualcode = result;
       }
       break;
 
     case SSH_SFTP_CLOSE:
-      if(sshc->sftp_handle) {
-        rc = libssh2_sftp_close(sshc->sftp_handle);
-        if(rc == LIBSSH2_ERROR_EAGAIN) {
-          break;
-        }
-        if(rc < 0) {
-          char *err_msg = NULL;
-          (void)libssh2_session_last_error(sshc->ssh_session,
-                                           &err_msg, NULL, 0);
-          infof(data, "Failed to close libssh2 file: %d %s", rc, err_msg);
-        }
-        sshc->sftp_handle = NULL;
-      }
-
-      Curl_safefree(sshp->path);
-
-      DEBUGF(infof(data, "SFTP DONE done"));
-
-      /* Check if nextstate is set and move .nextstate could be POSTQUOTE_INIT
-         After nextstate is executed, the control should come back to
-         SSH_SFTP_CLOSE to pass the correct result back  */
-      if(sshc->nextstate != SSH_NO_STATE &&
-         sshc->nextstate != SSH_SFTP_CLOSE) {
-        state(data, sshc->nextstate);
-        sshc->nextstate = SSH_SFTP_CLOSE;
-      }
-      else {
-        state(data, SSH_STOP);
-        result = sshc->actualcode;
-      }
+      result = ssh_state_sftp_close(data, sshc, sshp);
       break;
 
     case SSH_SFTP_SHUTDOWN:
-      /* during times we get here due to a broken transfer and then the
-         sftp_handle might not have been taken down so make sure that is done
-         before we proceed */
-
-      if(sshc->sftp_handle) {
-        rc = libssh2_sftp_close(sshc->sftp_handle);
-        if(rc == LIBSSH2_ERROR_EAGAIN) {
-          break;
-        }
-        if(rc < 0) {
-          char *err_msg = NULL;
-          (void)libssh2_session_last_error(sshc->ssh_session, &err_msg,
-                                           NULL, 0);
-          infof(data, "Failed to close libssh2 file: %d %s", rc, err_msg);
-        }
-        sshc->sftp_handle = NULL;
-      }
-      if(sshc->sftp_session) {
-        rc = libssh2_sftp_shutdown(sshc->sftp_session);
-        if(rc == LIBSSH2_ERROR_EAGAIN) {
-          break;
-        }
-        if(rc < 0) {
-          infof(data, "Failed to stop libssh2 sftp subsystem");
-        }
-        sshc->sftp_session = NULL;
-      }
-
-      Curl_safefree(sshc->homedir);
-      data->state.most_recent_ftp_entrypath = NULL;
-
-      state(data, SSH_SESSION_DISCONNECT);
+      result = ssh_state_sftp_shutdown(data, sshc);
       break;
 
     case SSH_SCP_TRANS_INIT:
       result = Curl_getworkingpath(data, sshc->homedir, &sshp->path);
       if(result) {
-        sshc->actualcode = result;
-        state(data, SSH_STOP);
+        myssh_state(data, sshc, SSH_STOP);
         break;
       }
 
       if(data->state.upload) {
         if(data->state.infilesize < 0) {
           failf(data, "SCP requires a known file size for upload");
-          sshc->actualcode = CURLE_UPLOAD_FAILED;
-          state(data, SSH_SCP_CHANNEL_FREE);
+          result = CURLE_UPLOAD_FAILED;
+          myssh_state(data, sshc, SSH_SCP_CHANNEL_FREE);
           break;
         }
-        state(data, SSH_SCP_UPLOAD_INIT);
+        myssh_state(data, sshc, SSH_SCP_UPLOAD_INIT);
       }
       else {
-        state(data, SSH_SCP_DOWNLOAD_INIT);
+        myssh_state(data, sshc, SSH_SCP_DOWNLOAD_INIT);
       }
       break;
 
     case SSH_SCP_UPLOAD_INIT:
-      /*
-       * libssh2 requires that the destination path is a full path that
-       * includes the destination file and name OR ends in a "/" . If this is
-       * not done the destination file will be named the same name as the last
-       * directory in the path.
-       */
-      sshc->ssh_channel =
-        libssh2_scp_send64(sshc->ssh_session, sshp->path,
-                           (int)data->set.new_file_perms,
-                           (libssh2_int64_t)data->state.infilesize, 0, 0);
-      if(!sshc->ssh_channel) {
-        int ssh_err;
-        char *err_msg = NULL;
-
-        if(libssh2_session_last_errno(sshc->ssh_session) ==
-           LIBSSH2_ERROR_EAGAIN) {
-          rc = LIBSSH2_ERROR_EAGAIN;
-          break;
-        }
-
-        ssh_err = (int)(libssh2_session_last_error(sshc->ssh_session,
-                                                   &err_msg, NULL, 0));
-        failf(data, "%s", err_msg);
-        state(data, SSH_SCP_CHANNEL_FREE);
-        sshc->actualcode = libssh2_session_error_to_CURLE(ssh_err);
-        /* Map generic errors to upload failed */
-        if(sshc->actualcode == CURLE_SSH ||
-           sshc->actualcode == CURLE_REMOTE_FILE_NOT_FOUND)
-          sshc->actualcode = CURLE_UPLOAD_FAILED;
-        break;
-      }
-
-      /* upload data */
-      data->req.size = data->state.infilesize;
-      Curl_pgrsSetUploadSize(data, data->state.infilesize);
-      Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE);
-
-      /* not set by Curl_xfer_setup to preserve keepon bits */
-      conn->sockfd = conn->writesockfd;
-
-      if(result) {
-        state(data, SSH_SCP_CHANNEL_FREE);
-        sshc->actualcode = result;
-      }
-      else {
-        /* store this original bitmask setup to use later on if we cannot
-           figure out a "real" bitmask */
-        sshc->orig_waitfor = data->req.keepon;
-
-        /* we want to use the _sending_ function even when the socket turns
-           out readable as the underlying libssh2 scp send function will deal
-           with both accordingly */
-        data->state.select_bits = CURL_CSELECT_OUT;
-
-        state(data, SSH_STOP);
-      }
+      result = ssh_state_scp_upload_init(data, sshc, sshp);
       break;
 
     case SSH_SCP_DOWNLOAD_INIT:
-    {
-      curl_off_t bytecount;
-
-      /*
-       * We must check the remote file; if it is a directory no values will
-       * be set in sb
-       */
-
-      /*
-       * If support for >2GB files exists, use it.
-       */
-
-      /* get a fresh new channel from the ssh layer */
-#if LIBSSH2_VERSION_NUM < 0x010700
-      struct stat sb;
-      memset(&sb, 0, sizeof(struct stat));
-      sshc->ssh_channel = libssh2_scp_recv(sshc->ssh_session,
-                                           sshp->path, &sb);
-#else
-      libssh2_struct_stat sb;
-      memset(&sb, 0, sizeof(libssh2_struct_stat));
-      sshc->ssh_channel = libssh2_scp_recv2(sshc->ssh_session,
-                                            sshp->path, &sb);
-#endif
-
-      if(!sshc->ssh_channel) {
-        int ssh_err;
-        char *err_msg = NULL;
-
-        if(libssh2_session_last_errno(sshc->ssh_session) ==
-           LIBSSH2_ERROR_EAGAIN) {
-          rc = LIBSSH2_ERROR_EAGAIN;
-          break;
-        }
-
-
-        ssh_err = (int)(libssh2_session_last_error(sshc->ssh_session,
-                                                   &err_msg, NULL, 0));
-        failf(data, "%s", err_msg);
-        state(data, SSH_SCP_CHANNEL_FREE);
-        sshc->actualcode = libssh2_session_error_to_CURLE(ssh_err);
-        break;
-      }
-
-      /* download data */
-      bytecount = (curl_off_t)sb.st_size;
-      data->req.maxdownload = (curl_off_t)sb.st_size;
-      Curl_xfer_setup1(data, CURL_XFER_RECV, bytecount, FALSE);
-
-      /* not set by Curl_xfer_setup to preserve keepon bits */
-      conn->writesockfd = conn->sockfd;
-
-      /* we want to use the _receiving_ function even when the socket turns
-         out writableable as the underlying libssh2 recv function will deal
-         with both accordingly */
-      data->state.select_bits = CURL_CSELECT_IN;
-
-      if(result) {
-        state(data, SSH_SCP_CHANNEL_FREE);
-        sshc->actualcode = result;
-      }
-      else
-        state(data, SSH_STOP);
-    }
-    break;
+      result = ssh_state_scp_download_init(data, sshc, sshp);
+      break;
 
     case SSH_SCP_DONE:
       if(data->state.upload)
-        state(data, SSH_SCP_SEND_EOF);
+        myssh_state(data, sshc, SSH_SCP_SEND_EOF);
       else
-        state(data, SSH_SCP_CHANNEL_FREE);
+        myssh_state(data, sshc, SSH_SCP_CHANNEL_FREE);
       break;
 
     case SSH_SCP_SEND_EOF:
       if(sshc->ssh_channel) {
-        rc = libssh2_channel_send_eof(sshc->ssh_channel);
+        int rc = libssh2_channel_send_eof(sshc->ssh_channel);
         if(rc == LIBSSH2_ERROR_EAGAIN) {
+          result = CURLE_AGAIN;
           break;
         }
         if(rc) {
@@ -2758,13 +3016,14 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, bool *block)
                 rc, err_msg);
         }
       }
-      state(data, SSH_SCP_WAIT_EOF);
+      myssh_state(data, sshc, SSH_SCP_WAIT_EOF);
       break;
 
     case SSH_SCP_WAIT_EOF:
       if(sshc->ssh_channel) {
-        rc = libssh2_channel_wait_eof(sshc->ssh_channel);
+        int rc = libssh2_channel_wait_eof(sshc->ssh_channel);
         if(rc == LIBSSH2_ERROR_EAGAIN) {
+          result = CURLE_AGAIN;
           break;
         }
         if(rc) {
@@ -2774,13 +3033,14 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, bool *block)
           infof(data, "Failed to get channel EOF: %d %s", rc, err_msg);
         }
       }
-      state(data, SSH_SCP_WAIT_CLOSE);
+      myssh_state(data, sshc, SSH_SCP_WAIT_CLOSE);
       break;
 
     case SSH_SCP_WAIT_CLOSE:
       if(sshc->ssh_channel) {
-        rc = libssh2_channel_wait_closed(sshc->ssh_channel);
+        int rc = libssh2_channel_wait_closed(sshc->ssh_channel);
         if(rc == LIBSSH2_ERROR_EAGAIN) {
+          result = CURLE_AGAIN;
           break;
         }
         if(rc) {
@@ -2790,13 +3050,14 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, bool *block)
           infof(data, "Channel failed to close: %d %s", rc, err_msg);
         }
       }
-      state(data, SSH_SCP_CHANNEL_FREE);
+      myssh_state(data, sshc, SSH_SCP_CHANNEL_FREE);
       break;
 
     case SSH_SCP_CHANNEL_FREE:
       if(sshc->ssh_channel) {
-        rc = libssh2_channel_free(sshc->ssh_channel);
+        int rc = libssh2_channel_free(sshc->ssh_channel);
         if(rc == LIBSSH2_ERROR_EAGAIN) {
+          result = CURLE_AGAIN;
           break;
         }
         if(rc < 0) {
@@ -2809,133 +3070,38 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, bool *block)
         sshc->ssh_channel = NULL;
       }
       DEBUGF(infof(data, "SCP DONE phase complete"));
-#if 0 /* PREV */
-      state(data, SSH_SESSION_DISCONNECT);
-#endif
-      state(data, SSH_STOP);
-      result = sshc->actualcode;
+      myssh_state(data, sshc, SSH_STOP);
       break;
 
     case SSH_SESSION_DISCONNECT:
-      /* during weird times when we have been prematurely aborted, the channel
-         is still alive when we reach this state and we MUST kill the channel
-         properly first */
-      if(sshc->ssh_channel) {
-        rc = libssh2_channel_free(sshc->ssh_channel);
-        if(rc == LIBSSH2_ERROR_EAGAIN) {
-          break;
-        }
-        if(rc < 0) {
-          char *err_msg = NULL;
-          (void)libssh2_session_last_error(sshc->ssh_session,
-                                           &err_msg, NULL, 0);
-          infof(data, "Failed to free libssh2 scp subsystem: %d %s",
-                rc, err_msg);
-        }
-        sshc->ssh_channel = NULL;
-      }
-
-      if(sshc->ssh_session) {
-        rc = libssh2_session_disconnect(sshc->ssh_session, "Shutdown");
-        if(rc == LIBSSH2_ERROR_EAGAIN) {
-          break;
-        }
-        if(rc < 0) {
-          char *err_msg = NULL;
-          (void)libssh2_session_last_error(sshc->ssh_session,
-                                           &err_msg, NULL, 0);
-          infof(data, "Failed to disconnect libssh2 session: %d %s",
-                rc, err_msg);
-        }
-      }
-
-      Curl_safefree(sshc->homedir);
-      data->state.most_recent_ftp_entrypath = NULL;
-
-      state(data, SSH_SESSION_FREE);
+      result = ssh_state_session_disconnect(data, sshc);
       break;
 
     case SSH_SESSION_FREE:
-      if(sshc->kh) {
-        libssh2_knownhost_free(sshc->kh);
-        sshc->kh = NULL;
-      }
-
-      if(sshc->ssh_agent) {
-        rc = libssh2_agent_disconnect(sshc->ssh_agent);
-        if(rc == LIBSSH2_ERROR_EAGAIN) {
-          break;
-        }
-        if(rc < 0) {
-          char *err_msg = NULL;
-          (void)libssh2_session_last_error(sshc->ssh_session,
-                                           &err_msg, NULL, 0);
-          infof(data, "Failed to disconnect from libssh2 agent: %d %s",
-                rc, err_msg);
-        }
-        libssh2_agent_free(sshc->ssh_agent);
-        sshc->ssh_agent = NULL;
-
-        /* NB: there is no need to free identities, they are part of internal
-           agent stuff */
-        sshc->sshagent_identity = NULL;
-        sshc->sshagent_prev_identity = NULL;
-      }
-
-      if(sshc->ssh_session) {
-        rc = libssh2_session_free(sshc->ssh_session);
-        if(rc == LIBSSH2_ERROR_EAGAIN) {
-          break;
-        }
-        if(rc < 0) {
-          char *err_msg = NULL;
-          (void)libssh2_session_last_error(sshc->ssh_session,
-                                           &err_msg, NULL, 0);
-          infof(data, "Failed to free libssh2 session: %d %s", rc, err_msg);
-        }
-        sshc->ssh_session = NULL;
-      }
-
-      /* worst-case scenario cleanup */
-
-      DEBUGASSERT(sshc->ssh_session == NULL);
-      DEBUGASSERT(sshc->ssh_channel == NULL);
-      DEBUGASSERT(sshc->sftp_session == NULL);
-      DEBUGASSERT(sshc->sftp_handle == NULL);
-      DEBUGASSERT(sshc->kh == NULL);
-      DEBUGASSERT(sshc->ssh_agent == NULL);
-
-      Curl_safefree(sshc->rsa_pub);
-      Curl_safefree(sshc->rsa);
-      Curl_safefree(sshc->quote_path1);
-      Curl_safefree(sshc->quote_path2);
-      Curl_safefree(sshc->homedir);
-
+      result = sshc_cleanup(sshc, data, FALSE);
+      if(result)
+        break;
       /* the code we are about to return */
-      result = sshc->actualcode;
-
       memset(sshc, 0, sizeof(struct ssh_conn));
-
       connclose(conn, "SSH session free");
       sshc->state = SSH_SESSION_FREE; /* current */
-      sshc->nextstate = SSH_NO_STATE;
-      state(data, SSH_STOP);
+      myssh_state(data, sshc, SSH_STOP);
       break;
 
     case SSH_QUIT:
     default:
       /* internal error */
-      sshc->nextstate = SSH_NO_STATE;
-      state(data, SSH_STOP);
+      myssh_state(data, sshc, SSH_STOP);
       break;
     }
 
-  } while(!rc && (sshc->state != SSH_STOP));
+  } while(!result && (sshc->state != SSH_STOP));
 
-  if(rc == LIBSSH2_ERROR_EAGAIN) {
+  if(result == CURLE_AGAIN) {
     /* we would block, we need to wait for the socket to be ready (in the
        right direction too)! */
     *block = TRUE;
+    result = CURLE_OK;
   }
 
   return result;
@@ -2968,10 +3134,11 @@ static int ssh_getsock(struct Curl_easy *data,
  * function in all cases so that when it _does not_ return EAGAIN we can
  * restore the default wait bits.
  */
-static void ssh_block2waitfor(struct Curl_easy *data, bool block)
+static void ssh_block2waitfor(struct Curl_easy *data,
+                              struct ssh_conn *sshc,
+                              bool block)
 {
   struct connectdata *conn = data->conn;
-  struct ssh_conn *sshc = &conn->proto.sshc;
   int dir = 0;
   if(block) {
     dir = libssh2_session_block_directions(sshc->ssh_session);
@@ -2991,35 +3158,39 @@ static void ssh_block2waitfor(struct Curl_easy *data, bool block)
 static CURLcode ssh_multi_statemach(struct Curl_easy *data, bool *done)
 {
   struct connectdata *conn = data->conn;
-  struct ssh_conn *sshc = &conn->proto.sshc;
+  struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
+  struct SSHPROTO *sshp = Curl_meta_get(data, CURL_META_SSH_EASY);
   CURLcode result = CURLE_OK;
   bool block; /* we store the status and use that to provide a ssh_getsock()
                  implementation */
+  if(!sshc || !sshp)
+    return CURLE_FAILED_INIT;
+
   do {
-    result = ssh_statemachine(data, &block);
+    result = ssh_statemachine(data, sshc, sshp, &block);
     *done = (sshc->state == SSH_STOP);
     /* if there is no error, it is not done and it did not EWOULDBLOCK, then
        try again */
   } while(!result && !*done && !block);
-  ssh_block2waitfor(data, block);
+  ssh_block2waitfor(data, sshc, block);
 
   return result;
 }
 
 static CURLcode ssh_block_statemach(struct Curl_easy *data,
-                                    struct connectdata *conn,
+                                    struct ssh_conn *sshc,
+                                    struct SSHPROTO *sshp,
                                     bool disconnect)
 {
-  struct ssh_conn *sshc = &conn->proto.sshc;
   CURLcode result = CURLE_OK;
-  struct curltime dis = Curl_now();
+  struct curltime dis = curlx_now();
 
   while((sshc->state != SSH_STOP) && !result) {
     bool block;
     timediff_t left = 1000;
-    struct curltime now = Curl_now();
+    struct curltime now = curlx_now();
 
-    result = ssh_statemachine(data, &block);
+    result = ssh_statemachine(data, sshc, sshp, &block);
     if(result)
       break;
 
@@ -3037,7 +3208,7 @@ static CURLcode ssh_block_statemach(struct Curl_easy *data,
         return CURLE_OPERATION_TIMEDOUT;
       }
     }
-    else if(Curl_timediff(now, dis) > 1000) {
+    else if(curlx_timediff(now, dis) > 1000) {
       /* disconnect timeout */
       failf(data, "Disconnect timed out");
       result = CURLE_OK;
@@ -3046,7 +3217,7 @@ static CURLcode ssh_block_statemach(struct Curl_easy *data,
 
     if(block) {
       int dir = libssh2_session_block_directions(sshc->ssh_session);
-      curl_socket_t sock = conn->sock[FIRSTSOCKET];
+      curl_socket_t sock = data->conn->sock[FIRSTSOCKET];
       curl_socket_t fd_read = CURL_SOCKET_BAD;
       curl_socket_t fd_write = CURL_SOCKET_BAD;
       if(LIBSSH2_SESSION_BLOCK_INBOUND & dir)
@@ -3062,17 +3233,51 @@ static CURLcode ssh_block_statemach(struct Curl_easy *data,
   return result;
 }
 
+static void myssh_easy_dtor(void *key, size_t klen, void *entry)
+{
+  struct SSHPROTO *sshp = entry;
+  (void)key;
+  (void)klen;
+  Curl_safefree(sshp->path);
+  curlx_dyn_free(&sshp->readdir);
+  curlx_dyn_free(&sshp->readdir_link);
+  free(sshp);
+}
+
+static void myssh_conn_dtor(void *key, size_t klen, void *entry)
+{
+  struct ssh_conn *sshc = entry;
+  (void)key;
+  (void)klen;
+  sshc_cleanup(sshc, NULL, TRUE);
+  free(sshc);
+}
+
 /*
  * SSH setup and connection
  */
 static CURLcode ssh_setup_connection(struct Curl_easy *data,
                                      struct connectdata *conn)
 {
-  struct SSHPROTO *ssh;
+  struct ssh_conn *sshc;
+  struct SSHPROTO *sshp;
   (void)conn;
 
-  data->req.p.ssh = ssh = calloc(1, sizeof(struct SSHPROTO));
-  if(!ssh)
+  sshc = calloc(1, sizeof(*sshc));
+  if(!sshc)
+    return CURLE_OUT_OF_MEMORY;
+
+  sshc->initialised = TRUE;
+  if(Curl_conn_meta_set(conn, CURL_META_SSH_CONN, sshc, myssh_conn_dtor))
+    return CURLE_OUT_OF_MEMORY;
+
+  sshp = calloc(1, sizeof(*sshp));
+  if(!sshp)
+    return CURLE_OUT_OF_MEMORY;
+
+  curlx_dyn_init(&sshp->readdir, CURL_PATH_MAX * 2);
+  curlx_dyn_init(&sshp->readdir_link, CURL_PATH_MAX);
+  if(Curl_meta_set(data, CURL_META_SSH_EASY, sshp, myssh_easy_dtor))
     return CURLE_OUT_OF_MEMORY;
 
   return CURLE_OK;
@@ -3090,20 +3295,23 @@ static ssize_t ssh_tls_recv(libssh2_socket_t sock, void *buffer,
   CURLcode result;
   struct connectdata *conn = data->conn;
   Curl_recv *backup = conn->recv[0];
-  struct ssh_conn *ssh = &conn->proto.sshc;
+  struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
   int socknum = Curl_conn_sockindex(data, sock);
   (void)flags;
 
+  if(!sshc)
+    return -1;
+
   /* swap in the TLS reader function for this call only, and then swap back
      the SSH one again */
-  conn->recv[0] = ssh->tls_recv;
+  conn->recv[0] = sshc->tls_recv;
   result = Curl_conn_recv(data, socknum, buffer, length, &nread);
   conn->recv[0] = backup;
   if(result == CURLE_AGAIN)
     return -EAGAIN; /* magic return code for libssh2 */
   else if(result)
     return -1; /* generic error */
-  Curl_debug(data, CURLINFO_DATA_IN, (char *)buffer, (size_t)nread);
+  Curl_debug(data, CURLINFO_DATA_IN, (const char *)buffer, (size_t)nread);
   return nread;
 }
 
@@ -3115,20 +3323,23 @@ static ssize_t ssh_tls_send(libssh2_socket_t sock, const void *buffer,
   CURLcode result;
   struct connectdata *conn = data->conn;
   Curl_send *backup = conn->send[0];
-  struct ssh_conn *ssh = &conn->proto.sshc;
+  struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
   int socknum = Curl_conn_sockindex(data, sock);
   (void)flags;
 
+  if(!sshc)
+    return -1;
+
   /* swap in the TLS writer function for this call only, and then swap back
      the SSH one again */
-  conn->send[0] = ssh->tls_send;
+  conn->send[0] = sshc->tls_send;
   result = Curl_conn_send(data, socknum, buffer, length, FALSE, &nwrite);
   conn->send[0] = backup;
   if(result == CURLE_AGAIN)
     return -EAGAIN; /* magic return code for libssh2 */
   else if(result)
     return -1; /* error */
-  Curl_debug(data, CURLINFO_DATA_OUT, (char *)buffer, nwrite);
+  Curl_debug(data, CURLINFO_DATA_OUT, (const char *)buffer, nwrite);
   return (ssize_t)nwrite;
 }
 #endif
@@ -3142,27 +3353,50 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done)
 #ifdef CURL_LIBSSH2_DEBUG
   curl_socket_t sock;
 #endif
-  struct ssh_conn *sshc;
-  CURLcode result;
   struct connectdata *conn = data->conn;
+  struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
+  CURLcode result;
 
-  /* initialize per-handle data if not already */
-  if(!data->req.p.ssh) {
-    result = ssh_setup_connection(data, conn);
-    if(result)
-      return result;
+#if LIBSSH2_VERSION_NUM >= 0x010b00
+  {
+    const char *crypto_str;
+    switch(libssh2_crypto_engine()) {
+      case libssh2_gcrypt:
+        crypto_str = "libgcrypt";
+        break;
+      case libssh2_mbedtls:
+        crypto_str = "mbedTLS";
+        break;
+      case libssh2_openssl:
+        crypto_str = "openssl compatible";
+        break;
+      case libssh2_os400qc3:
+        crypto_str = "OS400QC3";
+        break;
+      case libssh2_wincng:
+        crypto_str = "WinCNG";
+        break;
+      default:
+        crypto_str = NULL;
+        break;
+    }
+    if(crypto_str)
+      infof(data, "libssh2 cryptography backend: %s", crypto_str);
   }
+#endif
+
+  if(!sshc)
+    return CURLE_FAILED_INIT;
 
   /* We default to persistent connections. We set this already in this connect
      function to make the reuse checks properly be able to check this bit. */
   connkeep(conn, "SSH default");
 
-  sshc = &conn->proto.sshc;
-
+  if(conn->user)
+    infof(data, "User: '%s'", conn->user);
+  else
+    infof(data, "User: NULL");
 #ifdef CURL_LIBSSH2_DEBUG
-  if(conn->user) {
-    infof(data, "User: %s", conn->user);
-  }
   if(conn->passwd) {
     infof(data, "Password: %s", conn->passwd);
   }
@@ -3281,7 +3515,7 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done)
   infof(data, "SSH socket: %d", (int)sock);
 #endif /* CURL_LIBSSH2_DEBUG */
 
-  state(data, SSH_INIT);
+  myssh_state(data, sshc, SSH_INIT);
 
   result = ssh_multi_statemach(data, done);
 
@@ -3302,14 +3536,17 @@ CURLcode scp_perform(struct Curl_easy *data,
                      bool *connected,
                      bool *dophase_done)
 {
+  struct ssh_conn *sshc = Curl_conn_meta_get(data->conn, CURL_META_SSH_CONN);
   CURLcode result = CURLE_OK;
 
   DEBUGF(infof(data, "DO phase starts"));
 
   *dophase_done = FALSE; /* not done yet */
+  if(!sshc)
+    return CURLE_FAILED_INIT;
 
   /* start the first command in the DO phase */
-  state(data, SSH_SCP_TRANS_INIT);
+  myssh_state(data, sshc, SSH_SCP_TRANS_INIT);
 
   /* run the state-machine */
   result = ssh_multi_statemach(data, dophase_done);
@@ -3346,13 +3583,13 @@ static CURLcode ssh_do(struct Curl_easy *data, bool *done)
   CURLcode result;
   bool connected = FALSE;
   struct connectdata *conn = data->conn;
-  struct ssh_conn *sshc = &conn->proto.sshc;
+  struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
 
   *done = FALSE; /* default to false */
+  if(!sshc)
+    return CURLE_FAILED_INIT;
 
   data->req.size = -1; /* make sure this is unknown at this point */
-
-  sshc->actualcode = CURLE_OK; /* reset error code */
   sshc->secondCreateDirs = 0;   /* reset the create dir attempt state
                                    variable */
 
@@ -3369,6 +3606,110 @@ static CURLcode ssh_do(struct Curl_easy *data, bool *done)
   return result;
 }
 
+static CURLcode sshc_cleanup(struct ssh_conn *sshc, struct Curl_easy *data,
+                             bool block)
+{
+  int rc;
+
+  if(sshc->initialised) {
+    if(sshc->kh) {
+      libssh2_knownhost_free(sshc->kh);
+      sshc->kh = NULL;
+    }
+
+    if(sshc->ssh_agent) {
+      rc = libssh2_agent_disconnect(sshc->ssh_agent);
+      if(!block && (rc == LIBSSH2_ERROR_EAGAIN))
+        return CURLE_AGAIN;
+
+      if((rc < 0) && data) {
+        char *err_msg = NULL;
+        (void)libssh2_session_last_error(sshc->ssh_session,
+                                         &err_msg, NULL, 0);
+        infof(data, "Failed to disconnect from libssh2 agent: %d %s",
+              rc, err_msg);
+      }
+      libssh2_agent_free(sshc->ssh_agent);
+      sshc->ssh_agent = NULL;
+
+      /* NB: there is no need to free identities, they are part of internal
+         agent stuff */
+      sshc->sshagent_identity = NULL;
+      sshc->sshagent_prev_identity = NULL;
+    }
+
+    if(sshc->sftp_handle) {
+      rc = libssh2_sftp_close(sshc->sftp_handle);
+      if(!block && (rc == LIBSSH2_ERROR_EAGAIN))
+        return CURLE_AGAIN;
+
+      if((rc < 0) && data) {
+        char *err_msg = NULL;
+        (void)libssh2_session_last_error(sshc->ssh_session, &err_msg,
+                                         NULL, 0);
+        infof(data, "Failed to close libssh2 file: %d %s", rc, err_msg);
+      }
+      sshc->sftp_handle = NULL;
+    }
+
+    if(sshc->ssh_channel) {
+      rc = libssh2_channel_free(sshc->ssh_channel);
+      if(!block && (rc == LIBSSH2_ERROR_EAGAIN))
+        return CURLE_AGAIN;
+
+      if((rc < 0) && data) {
+        char *err_msg = NULL;
+        (void)libssh2_session_last_error(sshc->ssh_session,
+                                         &err_msg, NULL, 0);
+        infof(data, "Failed to free libssh2 scp subsystem: %d %s",
+              rc, err_msg);
+      }
+      sshc->ssh_channel = NULL;
+    }
+
+    if(sshc->sftp_session) {
+      rc = libssh2_sftp_shutdown(sshc->sftp_session);
+      if(!block && (rc == LIBSSH2_ERROR_EAGAIN))
+        return CURLE_AGAIN;
+
+      if((rc < 0) && data)
+        infof(data, "Failed to stop libssh2 sftp subsystem");
+      sshc->sftp_session = NULL;
+    }
+
+    if(sshc->ssh_session) {
+      rc = libssh2_session_free(sshc->ssh_session);
+      if(!block && (rc == LIBSSH2_ERROR_EAGAIN))
+        return CURLE_AGAIN;
+
+      if((rc < 0) && data) {
+        char *err_msg = NULL;
+        (void)libssh2_session_last_error(sshc->ssh_session,
+                                         &err_msg, NULL, 0);
+        infof(data, "Failed to free libssh2 session: %d %s", rc, err_msg);
+      }
+      sshc->ssh_session = NULL;
+    }
+
+    /* worst-case scenario cleanup */
+    DEBUGASSERT(sshc->ssh_session == NULL);
+    DEBUGASSERT(sshc->ssh_channel == NULL);
+    DEBUGASSERT(sshc->sftp_session == NULL);
+    DEBUGASSERT(sshc->sftp_handle == NULL);
+    DEBUGASSERT(sshc->kh == NULL);
+    DEBUGASSERT(sshc->ssh_agent == NULL);
+
+    Curl_safefree(sshc->rsa_pub);
+    Curl_safefree(sshc->rsa);
+    Curl_safefree(sshc->quote_path1);
+    Curl_safefree(sshc->quote_path2);
+    Curl_safefree(sshc->homedir);
+    sshc->initialised = FALSE;
+  }
+  return CURLE_OK;
+}
+
+
 /* BLOCKING, but the function is using the state machine so the only reason
    this is still blocking is that the multi interface code has no support for
    disconnecting operations that takes a while */
@@ -3377,15 +3718,18 @@ static CURLcode scp_disconnect(struct Curl_easy *data,
                                bool dead_connection)
 {
   CURLcode result = CURLE_OK;
-  struct ssh_conn *sshc = &conn->proto.sshc;
+  struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
+  struct SSHPROTO *sshp = Curl_meta_get(data, CURL_META_SSH_EASY);
   (void) dead_connection;
 
-  if(sshc->ssh_session) {
+  if(sshc && sshc->ssh_session && sshp) {
     /* only if there is a session still around to use! */
-    state(data, SSH_SESSION_DISCONNECT);
-    result = ssh_block_statemach(data, conn, TRUE);
+    myssh_state(data, sshc, SSH_SESSION_DISCONNECT);
+    result = ssh_block_statemach(data, sshc, sshp, TRUE);
   }
 
+  if(sshc)
+    return sshc_cleanup(sshc, data, TRUE);
   return result;
 }
 
@@ -3393,19 +3737,19 @@ static CURLcode scp_disconnect(struct Curl_easy *data,
    done functions */
 static CURLcode ssh_done(struct Curl_easy *data, CURLcode status)
 {
+  struct ssh_conn *sshc = Curl_conn_meta_get(data->conn, CURL_META_SSH_CONN);
+  struct SSHPROTO *sshp = Curl_meta_get(data, CURL_META_SSH_EASY);
   CURLcode result = CURLE_OK;
-  struct SSHPROTO *sshp = data->req.p.ssh;
-  struct connectdata *conn = data->conn;
+
+  if(!sshc || !sshp)
+    return CURLE_FAILED_INIT;
 
   if(!status)
     /* run the state-machine */
-    result = ssh_block_statemach(data, conn, FALSE);
+    result = ssh_block_statemach(data, sshc, sshp, FALSE);
   else
     result = status;
 
-  Curl_safefree(sshp->path);
-  Curl_dyn_free(&sshp->readdir);
-
   if(Curl_pgrsDone(data))
     return CURLE_ABORTED_BY_CALLBACK;
 
@@ -3417,13 +3761,13 @@ static CURLcode ssh_done(struct Curl_easy *data, CURLcode status)
 static CURLcode scp_done(struct Curl_easy *data, CURLcode status,
                          bool premature)
 {
+  struct ssh_conn *sshc = Curl_conn_meta_get(data->conn, CURL_META_SSH_CONN);
   (void)premature; /* not used */
 
-  if(!status)
-    state(data, SSH_SCP_DONE);
+  if(sshc && !status)
+    myssh_state(data, sshc, SSH_SCP_DONE);
 
   return ssh_done(data, status);
-
 }
 
 static ssize_t scp_send(struct Curl_easy *data, int sockindex,
@@ -3431,14 +3775,18 @@ static ssize_t scp_send(struct Curl_easy *data, int sockindex,
 {
   ssize_t nwrite;
   struct connectdata *conn = data->conn;
-  struct ssh_conn *sshc = &conn->proto.sshc;
+  struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
   (void)sockindex; /* we only support SCP on the fixed known primary socket */
   (void)eos;
 
+  if(!sshc) {
+    *err = CURLE_FAILED_INIT;
+    return -1;
+  }
   /* libssh2_channel_write() returns int! */
   nwrite = (ssize_t) libssh2_channel_write(sshc->ssh_channel, mem, len);
 
-  ssh_block2waitfor(data, (nwrite == LIBSSH2_ERROR_EAGAIN));
+  ssh_block2waitfor(data, sshc, (nwrite == LIBSSH2_ERROR_EAGAIN));
 
   if(nwrite == LIBSSH2_ERROR_EAGAIN) {
     *err = CURLE_AGAIN;
@@ -3457,13 +3805,17 @@ static ssize_t scp_recv(struct Curl_easy *data, int sockindex,
 {
   ssize_t nread;
   struct connectdata *conn = data->conn;
-  struct ssh_conn *sshc = &conn->proto.sshc;
+  struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
   (void)sockindex; /* we only support SCP on the fixed known primary socket */
 
+  if(!sshc) {
+    *err = CURLE_FAILED_INIT;
+    return -1;
+  }
   /* libssh2_channel_read() returns int */
   nread = (ssize_t) libssh2_channel_read(sshc->ssh_channel, mem, len);
 
-  ssh_block2waitfor(data, (nread == LIBSSH2_ERROR_EAGAIN));
+  ssh_block2waitfor(data, sshc, (nread == LIBSSH2_ERROR_EAGAIN));
   if(nread == LIBSSH2_ERROR_EAGAIN) {
     *err = CURLE_AGAIN;
     nread = -1;
@@ -3490,14 +3842,17 @@ CURLcode sftp_perform(struct Curl_easy *data,
                       bool *connected,
                       bool *dophase_done)
 {
+  struct ssh_conn *sshc = Curl_conn_meta_get(data->conn, CURL_META_SSH_CONN);
   CURLcode result = CURLE_OK;
 
   DEBUGF(infof(data, "DO phase starts"));
 
   *dophase_done = FALSE; /* not done yet */
+  if(!sshc)
+    return CURLE_FAILED_INIT;
 
   /* start the first command in the DO phase */
-  state(data, SSH_SFTP_QUOTE_INIT);
+  myssh_state(data, sshc, SSH_SFTP_QUOTE_INIT);
 
   /* run the state-machine */
   result = ssh_multi_statemach(data, dophase_done);
@@ -3530,18 +3885,21 @@ static CURLcode sftp_disconnect(struct Curl_easy *data,
                                 struct connectdata *conn, bool dead_connection)
 {
   CURLcode result = CURLE_OK;
-  struct ssh_conn *sshc = &conn->proto.sshc;
+  struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
+  struct SSHPROTO *sshp = Curl_meta_get(data, CURL_META_SSH_EASY);
   (void) dead_connection;
 
   DEBUGF(infof(data, "SSH DISCONNECT starts now"));
 
-  if(sshc->ssh_session) {
+  if(sshc && sshc->ssh_session && sshp) {
     /* only if there is a session still around to use! */
-    state(data, SSH_SFTP_SHUTDOWN);
-    result = ssh_block_statemach(data, conn, TRUE);
+    myssh_state(data, sshc, SSH_SFTP_SHUTDOWN);
+    result = ssh_block_statemach(data, sshc, sshp, TRUE);
   }
 
   DEBUGF(infof(data, "SSH DISCONNECT is done"));
+  if(sshc)
+    sshc_cleanup(sshc, data, TRUE);
 
   return result;
 
@@ -3551,7 +3909,10 @@ static CURLcode sftp_done(struct Curl_easy *data, CURLcode status,
                                bool premature)
 {
   struct connectdata *conn = data->conn;
-  struct ssh_conn *sshc = &conn->proto.sshc;
+  struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
+
+  if(!sshc)
+    return CURLE_FAILED_INIT;
 
   if(!status) {
     /* Post quote commands are executed after the SFTP_CLOSE state to avoid
@@ -3559,7 +3920,7 @@ static CURLcode sftp_done(struct Curl_easy *data, CURLcode status,
        operation */
     if(!premature && data->set.postquote && !conn->bits.retry)
       sshc->nextstate = SSH_SFTP_POSTQUOTE_INIT;
-    state(data, SSH_SFTP_CLOSE);
+    myssh_state(data, sshc, SSH_SFTP_CLOSE);
   }
   return ssh_done(data, status);
 }
@@ -3570,13 +3931,17 @@ static ssize_t sftp_send(struct Curl_easy *data, int sockindex,
 {
   ssize_t nwrite;
   struct connectdata *conn = data->conn;
-  struct ssh_conn *sshc = &conn->proto.sshc;
+  struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
   (void)sockindex;
   (void)eos;
 
+  if(!sshc) {
+    *err = CURLE_FAILED_INIT;
+    return -1;
+  }
   nwrite = libssh2_sftp_write(sshc->sftp_handle, mem, len);
 
-  ssh_block2waitfor(data, (nwrite == LIBSSH2_ERROR_EAGAIN));
+  ssh_block2waitfor(data, sshc, (nwrite == LIBSSH2_ERROR_EAGAIN));
 
   if(nwrite == LIBSSH2_ERROR_EAGAIN) {
     *err = CURLE_AGAIN;
@@ -3599,12 +3964,16 @@ static ssize_t sftp_recv(struct Curl_easy *data, int sockindex,
 {
   ssize_t nread;
   struct connectdata *conn = data->conn;
-  struct ssh_conn *sshc = &conn->proto.sshc;
+  struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
   (void)sockindex;
 
+  if(!sshc) {
+    *err = CURLE_FAILED_INIT;
+    return -1;
+  }
   nread = libssh2_sftp_read(sshc->sftp_handle, mem, len);
 
-  ssh_block2waitfor(data, (nread == LIBSSH2_ERROR_EAGAIN));
+  ssh_block2waitfor(data, sshc, (nread == LIBSSH2_ERROR_EAGAIN));
 
   if(nread == LIBSSH2_ERROR_EAGAIN) {
     *err = CURLE_AGAIN;
@@ -3711,8 +4080,8 @@ static void ssh_attach(struct Curl_easy *data, struct connectdata *conn)
   DEBUGASSERT(data);
   DEBUGASSERT(conn);
   if(conn->handler->protocol & PROTO_FAMILY_SSH) {
-    struct ssh_conn *sshc = &conn->proto.sshc;
-    if(sshc->ssh_session) {
+    struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
+    if(sshc && sshc->ssh_session) {
       /* only re-attach if the session already exists */
       void **abstract = libssh2_session_abstract(sshc->ssh_session);
       *abstract = data;
diff --git a/extra/curl/curl-8.12.1/lib/vssh/ssh.h b/extra/curl/curl-8.14.1/lib/vssh/ssh.h
similarity index 94%
rename from extra/curl/curl-8.12.1/lib/vssh/ssh.h
rename to extra/curl/curl-8.14.1/lib/vssh/ssh.h
index bbbe95d7dec4..feee886562e6 100644
--- a/extra/curl/curl-8.12.1/lib/vssh/ssh.h
+++ b/extra/curl/curl-8.14.1/lib/vssh/ssh.h
@@ -24,7 +24,7 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #if defined(USE_LIBSSH2)
 #include <libssh2.h>
@@ -41,6 +41,11 @@
 
 #include "curl_path.h"
 
+/* meta key for storing protocol meta at easy handle */
+#define CURL_META_SSH_EASY   "meta:proto:ssh:easy"
+/* meta key for storing protocol meta at connection */
+#define CURL_META_SSH_CONN   "meta:proto:ssh:conn"
+
 /****************************************************************************
  * SSH unique setup
  ***************************************************************************/
@@ -141,12 +146,8 @@ struct ssh_conn {
   const char *passphrase;     /* pass-phrase to use */
   char *rsa_pub;              /* strdup'ed public key file */
   char *rsa;                  /* strdup'ed private key file */
-  bool authed;                /* the connection has been authenticated fine */
-  bool acceptfail;            /* used by the SFTP_QUOTE (continue if
-                                 quote command fails) */
   sshstate state;             /* always use ssh.c:state() to change state! */
   sshstate nextstate;         /* the state to goto after stopping */
-  CURLcode actualcode;        /* the actual error code */
   struct curl_slist *quote_item; /* for the quote option */
   char *quote_path1;          /* two generic pointers for the QUOTE stuff */
   char *quote_path2;
@@ -162,6 +163,7 @@ struct ssh_conn {
   char *slash_pos;              /* used by the SFTP_CREATE_DIRS state */
 
 #if defined(USE_LIBSSH)
+  CURLcode actualcode;        /* the actual error code */
   char *readdir_linkPath;
   size_t readdir_len;
   struct dynbuf readdir_buf;
@@ -206,12 +208,17 @@ struct ssh_conn {
   struct libssh2_agent_publickey *sshagent_prev_identity;
   LIBSSH2_KNOWNHOSTS *kh;
 #elif defined(USE_WOLFSSH)
+  CURLcode actualcode;        /* the actual error code */
   WOLFSSH *ssh_session;
   WOLFSSH_CTX *ctx;
   word32 handleSz;
   byte handle[WOLFSSH_MAX_HANDLE];
   curl_off_t offset;
 #endif /* USE_LIBSSH */
+  BIT(initialised);
+  BIT(authed);                /* the connection has been authenticated fine */
+  BIT(acceptfail);            /* used by the SFTP_QUOTE (continue if
+                                 quote command fails) */
 };
 
 #ifdef USE_LIBSSH
diff --git a/extra/curl/curl-8.12.1/lib/vssh/wolfssh.c b/extra/curl/curl-8.14.1/lib/vssh/wolfssh.c
similarity index 85%
rename from extra/curl/curl-8.12.1/lib/vssh/wolfssh.c
rename to extra/curl/curl-8.14.1/lib/vssh/wolfssh.c
index e78a18e71a57..5097ca02c515 100644
--- a/extra/curl/curl-8.12.1/lib/vssh/wolfssh.c
+++ b/extra/curl/curl-8.14.1/lib/vssh/wolfssh.c
@@ -22,30 +22,30 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #ifdef USE_WOLFSSH
 
 #include <limits.h>
 
-#include "urldata.h"
-#include "cfilters.h"
-#include "connect.h"
-#include "sendf.h"
-#include "progress.h"
+#include "../urldata.h"
+#include "../url.h"
+#include "../cfilters.h"
+#include "../connect.h"
+#include "../sendf.h"
+#include "../progress.h"
 #include "curl_path.h"
-#include "strtoofft.h"
-#include "transfer.h"
-#include "speedcheck.h"
-#include "select.h"
-#include "multiif.h"
-#include "warnless.h"
-#include "strdup.h"
+#include "../transfer.h"
+#include "../speedcheck.h"
+#include "../select.h"
+#include "../multiif.h"
+#include "../curlx/warnless.h"
+#include "../strdup.h"
 
 /* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_printf.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
 
 static CURLcode wssh_connect(struct Curl_easy *data, bool *done);
 static CURLcode wssh_multi_statemach(struct Curl_easy *data, bool *done);
@@ -71,6 +71,7 @@ static int wssh_getsock(struct Curl_easy *data,
                         curl_socket_t *sock);
 static CURLcode wssh_setup_connection(struct Curl_easy *data,
                                       struct connectdata *conn);
+static void wssh_sshc_cleanup(struct ssh_conn *sshc);
 
 #if 0
 /*
@@ -138,10 +139,10 @@ const struct Curl_handler Curl_handler_sftp = {
  * SSH State machine related code
  */
 /* This is the ONLY way to change SSH state! */
-static void state(struct Curl_easy *data, sshstate nowstate)
+static void wssh_state(struct Curl_easy *data,
+                       struct ssh_conn *sshc,
+                       sshstate nowstate)
 {
-  struct connectdata *conn = data->conn;
-  struct ssh_conn *sshc = &conn->proto.sshc;
 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
   /* for debug purposes */
   static const char * const names[] = {
@@ -208,14 +209,14 @@ static void state(struct Curl_easy *data, sshstate nowstate)
   };
 
   /* a precaution to make sure the lists are in sync */
-  DEBUGASSERT(sizeof(names)/sizeof(names[0]) == SSH_LAST);
+  DEBUGASSERT(CURL_ARRAYSIZE(names) == SSH_LAST);
 
   if(sshc->state != nowstate) {
     infof(data, "wolfssh %p state change from %s to %s",
           (void *)sshc, names[sshc->state], names[nowstate]);
   }
 #endif
-
+  (void)data;
   sshc->state = nowstate;
 }
 
@@ -252,19 +253,23 @@ static ssize_t wsftp_send(struct Curl_easy *data, int sockindex,
                           const void *mem, size_t len, bool eos, CURLcode *err)
 {
   struct connectdata *conn = data->conn;
-  struct ssh_conn *sshc = &conn->proto.sshc;
+  struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
   word32 offset[2];
   int rc;
   (void)sockindex;
   (void)eos;
 
+  if(!sshc) {
+    *err = CURLE_FAILED_INIT;
+    return -1;
+  }
   offset[0] = (word32)sshc->offset & 0xFFFFFFFF;
   offset[1] = (word32)(sshc->offset >> 32) & 0xFFFFFFFF;
 
   rc = wolfSSH_SFTP_SendWritePacket(sshc->ssh_session, sshc->handle,
                                     sshc->handleSz,
                                     &offset[0],
-                                    (byte *)mem, (word32)len);
+                                    (byte *)CURL_UNCONST(mem), (word32)len);
 
   if(rc == WS_FATAL_ERROR)
     rc = wolfSSH_get_error(sshc->ssh_session);
@@ -298,10 +303,14 @@ static ssize_t wsftp_recv(struct Curl_easy *data, int sockindex,
 {
   int rc;
   struct connectdata *conn = data->conn;
-  struct ssh_conn *sshc = &conn->proto.sshc;
+  struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
   word32 offset[2];
   (void)sockindex;
 
+  if(!sshc) {
+    *err = CURLE_FAILED_INIT;
+    return -1;
+  }
   offset[0] = (word32)sshc->offset & 0xFFFFFFFF;
   offset[1] = (word32)(sshc->offset >> 32) & 0xFFFFFFFF;
 
@@ -333,17 +342,45 @@ static ssize_t wsftp_recv(struct Curl_easy *data, int sockindex,
   return (ssize_t)rc;
 }
 
+static void wssh_easy_dtor(void *key, size_t klen, void *entry)
+{
+  struct SSHPROTO *sshp = entry;
+  (void)key;
+  (void)klen;
+  Curl_safefree(sshp->path);
+  free(sshp);
+}
+
+static void wssh_conn_dtor(void *key, size_t klen, void *entry)
+{
+  struct ssh_conn *sshc = entry;
+  (void)key;
+  (void)klen;
+  wssh_sshc_cleanup(sshc);
+  free(sshc);
+}
+
 /*
  * SSH setup and connection
  */
 static CURLcode wssh_setup_connection(struct Curl_easy *data,
                                       struct connectdata *conn)
 {
-  struct SSHPROTO *ssh;
+  struct ssh_conn *sshc;
+  struct SSHPROTO *sshp;
   (void)conn;
 
-  data->req.p.ssh = ssh = calloc(1, sizeof(struct SSHPROTO));
-  if(!ssh)
+  sshc = calloc(1, sizeof(*sshc));
+  if(!sshc)
+    return CURLE_OUT_OF_MEMORY;
+
+  sshc->initialised = TRUE;
+  if(Curl_conn_meta_set(conn, CURL_META_SSH_CONN, sshc, wssh_conn_dtor))
+    return CURLE_OUT_OF_MEMORY;
+
+  sshp = calloc(1, sizeof(*sshp));
+  if(!sshp ||
+     Curl_meta_set(data, CURL_META_SSH_EASY, sshp, wssh_easy_dtor))
     return CURLE_OUT_OF_MEMORY;
 
   return CURLE_OK;
@@ -368,13 +405,13 @@ static int userauth(byte authtype,
 static CURLcode wssh_connect(struct Curl_easy *data, bool *done)
 {
   struct connectdata *conn = data->conn;
-  struct ssh_conn *sshc;
+  struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
+  struct SSHPROTO *sshp = Curl_meta_get(data, CURL_META_SSH_EASY);
   curl_socket_t sock = conn->sock[FIRSTSOCKET];
   int rc;
 
-  /* initialize per-handle data if not already */
-  if(!data->req.p.ssh)
-    wssh_setup_connection(data, conn);
+  if(!sshc || !sshp)
+    return CURLE_FAILED_INIT;
 
   /* We default to persistent connections. We set this already in this connect
      function to make the reuse checks properly be able to check this bit. */
@@ -388,7 +425,6 @@ static CURLcode wssh_connect(struct Curl_easy *data, bool *done)
     conn->recv[FIRSTSOCKET] = wsftp_recv;
     conn->send[FIRSTSOCKET] = wsftp_send;
   }
-  sshc = &conn->proto.sshc;
   sshc->ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, NULL);
   if(!sshc->ctx) {
     failf(data, "No wolfSSH context");
@@ -423,14 +459,13 @@ static CURLcode wssh_connect(struct Curl_easy *data, bool *done)
 
   *done = TRUE;
   if(conn->handler->protocol & CURLPROTO_SCP)
-    state(data, SSH_INIT);
+    wssh_state(data, sshc, SSH_INIT);
   else
-    state(data, SSH_SFTP_INIT);
+    wssh_state(data, sshc, SSH_SFTP_INIT);
 
   return wssh_multi_statemach(data, done);
 error:
-  wolfSSH_free(sshc->ssh_session);
-  wolfSSH_CTX_free(sshc->ctx);
+  wssh_sshc_cleanup(sshc);
   return CURLE_FAILED_INIT;
 }
 
@@ -441,20 +476,24 @@ static CURLcode wssh_connect(struct Curl_easy *data, bool *done)
  * wants to be called again when the socket is ready
  */
 
-static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
+static CURLcode wssh_statemach_act(struct Curl_easy *data,
+                                   struct ssh_conn *sshc,
+                                   bool *block)
 {
   CURLcode result = CURLE_OK;
   struct connectdata *conn = data->conn;
-  struct ssh_conn *sshc = &conn->proto.sshc;
-  struct SSHPROTO *sftp_scp = data->req.p.ssh;
+  struct SSHPROTO *sftp_scp = Curl_meta_get(data, CURL_META_SSH_EASY);
   WS_SFTPNAME *name;
   int rc = 0;
   *block = FALSE; /* we are not blocking by default */
 
+  if(!sftp_scp)
+    return CURLE_FAILED_INIT;
+
   do {
     switch(sshc->state) {
     case SSH_INIT:
-      state(data, SSH_S_STARTUP);
+      wssh_state(data, sshc, SSH_S_STARTUP);
       break;
 
     case SSH_S_STARTUP:
@@ -472,11 +511,11 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
         return CURLE_OK;
       }
       else if(rc != WS_SUCCESS) {
-        state(data, SSH_STOP);
+        wssh_state(data, sshc, SSH_STOP);
         return CURLE_SSH;
       }
       infof(data, "wolfssh connected");
-      state(data, SSH_STOP);
+      wssh_state(data, sshc, SSH_STOP);
       break;
     case SSH_STOP:
       break;
@@ -497,7 +536,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
       }
       else if(rc == WS_SUCCESS) {
         infof(data, "wolfssh SFTP connected");
-        state(data, SSH_SFTP_REALPATH);
+        wssh_state(data, sshc, SSH_SFTP_REALPATH);
       }
       else {
         failf(data, "wolfssh SFTP connect error %d", rc);
@@ -505,7 +544,8 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
       }
       break;
     case SSH_SFTP_REALPATH:
-      name = wolfSSH_SFTP_RealPath(sshc->ssh_session, (char *)".");
+      name = wolfSSH_SFTP_RealPath(sshc->ssh_session,
+                                   (char *)CURL_UNCONST("."));
       rc = wolfSSH_get_error(sshc->ssh_session);
       if(rc == WS_WANT_READ) {
         *block = TRUE;
@@ -522,7 +562,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
         if(!sshc->homedir)
           sshc->actualcode = CURLE_OUT_OF_MEMORY;
         wolfSSH_SFTPNAME_list_free(name);
-        state(data, SSH_STOP);
+        wssh_state(data, sshc, SSH_STOP);
         return CURLE_OK;
       }
       failf(data, "wolfssh SFTP realpath %d", rc);
@@ -532,35 +572,35 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
       result = Curl_getworkingpath(data, sshc->homedir, &sftp_scp->path);
       if(result) {
         sshc->actualcode = result;
-        state(data, SSH_STOP);
+        wssh_state(data, sshc, SSH_STOP);
         break;
       }
 
       if(data->set.quote) {
         infof(data, "Sending quote commands");
         sshc->quote_item = data->set.quote;
-        state(data, SSH_SFTP_QUOTE);
+        wssh_state(data, sshc, SSH_SFTP_QUOTE);
       }
       else {
-        state(data, SSH_SFTP_GETINFO);
+        wssh_state(data, sshc, SSH_SFTP_GETINFO);
       }
       break;
     case SSH_SFTP_GETINFO:
       if(data->set.get_filetime) {
-        state(data, SSH_SFTP_FILETIME);
+        wssh_state(data, sshc, SSH_SFTP_FILETIME);
       }
       else {
-        state(data, SSH_SFTP_TRANS_INIT);
+        wssh_state(data, sshc, SSH_SFTP_TRANS_INIT);
       }
       break;
     case SSH_SFTP_TRANS_INIT:
       if(data->state.upload)
-        state(data, SSH_SFTP_UPLOAD_INIT);
+        wssh_state(data, sshc, SSH_SFTP_UPLOAD_INIT);
       else {
         if(sftp_scp->path[strlen(sftp_scp->path)-1] == '/')
-          state(data, SSH_SFTP_READDIR_INIT);
+          wssh_state(data, sshc, SSH_SFTP_READDIR_INIT);
         else
-          state(data, SSH_SFTP_DOWNLOAD_INIT);
+          wssh_state(data, sshc, SSH_SFTP_DOWNLOAD_INIT);
       }
       break;
     case SSH_SFTP_UPLOAD_INIT: {
@@ -623,7 +663,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
         failf(data, "wolfssh SFTP upload open failed: %d", rc);
         return CURLE_SSH;
       }
-      state(data, SSH_SFTP_DOWNLOAD_STAT);
+      wssh_state(data, sshc, SSH_SFTP_DOWNLOAD_STAT);
 
       /* If we have a restart point then we need to seek to the correct
          position. */
@@ -689,7 +729,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
       conn->sockfd = conn->writesockfd;
 
       if(result) {
-        state(data, SSH_SFTP_CLOSE);
+        wssh_state(data, sshc, SSH_SFTP_CLOSE);
         sshc->actualcode = result;
       }
       else {
@@ -707,7 +747,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
            timeout here */
         Curl_expire(data, 0, EXPIRE_RUN_NOW);
 
-        state(data, SSH_STOP);
+        wssh_state(data, sshc, SSH_STOP);
       }
       break;
     }
@@ -730,7 +770,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
       }
       else if(rc == WS_SUCCESS) {
         infof(data, "wolfssh SFTP open succeeded");
-        state(data, SSH_SFTP_DOWNLOAD_STAT);
+        wssh_state(data, sshc, SSH_SFTP_DOWNLOAD_STAT);
         return CURLE_OK;
       }
 
@@ -785,7 +825,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
         /* no data to transfer */
         Curl_xfer_setup_nop(data);
         infof(data, "File already completely downloaded");
-        state(data, SSH_STOP);
+        wssh_state(data, sshc, SSH_STOP);
         break;
       }
       Curl_xfer_setup1(data, CURL_XFER_RECV, data->req.size, FALSE);
@@ -801,20 +841,24 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
       if(result) {
         /* this should never occur; the close state should be entered
            at the time the error occurs */
-        state(data, SSH_SFTP_CLOSE);
+        wssh_state(data, sshc, SSH_SFTP_CLOSE);
         sshc->actualcode = result;
       }
       else {
-        state(data, SSH_STOP);
+        wssh_state(data, sshc, SSH_STOP);
       }
       break;
     }
     case SSH_SFTP_CLOSE:
-      if(sshc->handleSz)
+      if(sshc->handleSz) {
         rc = wolfSSH_SFTP_Close(sshc->ssh_session, sshc->handle,
                                 sshc->handleSz);
-      else
+        if(rc != WS_SUCCESS)
+          rc = wolfSSH_get_error(sshc->ssh_session);
+      }
+      else {
         rc = WS_SUCCESS; /* directory listing */
+      }
       if(rc == WS_WANT_READ) {
         *block = TRUE;
         conn->waitfor = KEEP_RECV;
@@ -826,7 +870,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
         return CURLE_OK;
       }
       else if(rc == WS_SUCCESS) {
-        state(data, SSH_STOP);
+        wssh_state(data, sshc, SSH_STOP);
         return CURLE_OK;
       }
 
@@ -836,10 +880,10 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
     case SSH_SFTP_READDIR_INIT:
       Curl_pgrsSetDownloadSize(data, -1);
       if(data->req.no_body) {
-        state(data, SSH_STOP);
+        wssh_state(data, sshc, SSH_STOP);
         break;
       }
-      state(data, SSH_SFTP_READDIR);
+      wssh_state(data, sshc, SSH_SFTP_READDIR);
       break;
 
     case SSH_SFTP_READDIR:
@@ -867,7 +911,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
                                data->set.list_only ?
                                name->fName : name->lName);
           if(!line) {
-            state(data, SSH_SFTP_CLOSE);
+            wssh_state(data, sshc, SSH_SFTP_CLOSE);
             sshc->actualcode = CURLE_OUT_OF_MEMORY;
             break;
           }
@@ -881,17 +925,15 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
           name = name->next;
         }
         wolfSSH_SFTPNAME_list_free(origname);
-        state(data, SSH_STOP);
+        wssh_state(data, sshc, SSH_STOP);
         return result;
       }
       failf(data, "wolfssh SFTP ls failed: %d", rc);
       return CURLE_SSH;
 
     case SSH_SFTP_SHUTDOWN:
-      Curl_safefree(sshc->homedir);
-      wolfSSH_free(sshc->ssh_session);
-      wolfSSH_CTX_free(sshc->ctx);
-      state(data, SSH_STOP);
+      wssh_sshc_cleanup(sshc);
+      wssh_state(data, sshc, SSH_STOP);
       return CURLE_OK;
     default:
       break;
@@ -904,12 +946,15 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
 static CURLcode wssh_multi_statemach(struct Curl_easy *data, bool *done)
 {
   struct connectdata *conn = data->conn;
-  struct ssh_conn *sshc = &conn->proto.sshc;
+  struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
   CURLcode result = CURLE_OK;
   bool block; /* we store the status and use that to provide a ssh_getsock()
                  implementation */
+  if(!sshc)
+    return CURLE_FAILED_INIT;
+
   do {
-    result = wssh_statemach_act(data, &block);
+    result = wssh_statemach_act(data, sshc, &block);
     *done = (sshc->state == SSH_STOP);
     /* if there is no error, it is not done and it did not EWOULDBLOCK, then
        try again */
@@ -934,6 +979,7 @@ CURLcode wscp_perform(struct Curl_easy *data,
 
 static
 CURLcode wsftp_perform(struct Curl_easy *data,
+                       struct ssh_conn *sshc,
                        bool *connected,
                        bool *dophase_done)
 {
@@ -944,7 +990,7 @@ CURLcode wsftp_perform(struct Curl_easy *data,
   *dophase_done = FALSE; /* not done yet */
 
   /* start the first command in the DO phase */
-  state(data, SSH_SFTP_QUOTE_INIT);
+  wssh_state(data, sshc, SSH_SFTP_QUOTE_INIT);
 
   /* run the state-machine */
   result = wssh_multi_statemach(data, dophase_done);
@@ -966,9 +1012,12 @@ static CURLcode wssh_do(struct Curl_easy *data, bool *done)
   CURLcode result;
   bool connected = FALSE;
   struct connectdata *conn = data->conn;
-  struct ssh_conn *sshc = &conn->proto.sshc;
+  struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
 
   *done = FALSE; /* default to false */
+  if(!sshc)
+    return CURLE_FAILED_INIT;
+
   data->req.size = -1; /* make sure this is unknown at this point */
   sshc->actualcode = CURLE_OK; /* reset error code */
   sshc->secondCreateDirs = 0;   /* reset the create dir attempt state
@@ -982,24 +1031,24 @@ static CURLcode wssh_do(struct Curl_easy *data, bool *done)
   if(conn->handler->protocol & CURLPROTO_SCP)
     result = wscp_perform(data, &connected,  done);
   else
-    result = wsftp_perform(data, &connected,  done);
+    result = wsftp_perform(data, sshc, &connected,  done);
 
   return result;
 }
 
 static CURLcode wssh_block_statemach(struct Curl_easy *data,
-                                    bool disconnect)
+                                     struct ssh_conn *sshc,
+                                     bool disconnect)
 {
   struct connectdata *conn = data->conn;
-  struct ssh_conn *sshc = &conn->proto.sshc;
   CURLcode result = CURLE_OK;
 
   while((sshc->state != SSH_STOP) && !result) {
     bool block;
     timediff_t left = 1000;
-    struct curltime now = Curl_now();
+    struct curltime now = curlx_now();
 
-    result = wssh_statemach_act(data, &block);
+    result = wssh_statemach_act(data, sshc, &block);
     if(result)
       break;
 
@@ -1039,20 +1088,19 @@ static CURLcode wssh_block_statemach(struct Curl_easy *data,
 
 /* generic done function for both SCP and SFTP called from their specific
    done functions */
-static CURLcode wssh_done(struct Curl_easy *data, CURLcode status)
+static CURLcode wssh_done(struct Curl_easy *data,
+                          struct ssh_conn *sshc,
+                          CURLcode status)
 {
   CURLcode result = CURLE_OK;
-  struct SSHPROTO *sftp_scp = data->req.p.ssh;
 
   if(!status) {
     /* run the state-machine */
-    result = wssh_block_statemach(data, FALSE);
+    result = wssh_block_statemach(data, sshc, FALSE);
   }
   else
     result = status;
 
-  if(sftp_scp)
-    Curl_safefree(sftp_scp->path);
   if(Curl_pgrsDone(data))
     return CURLE_ABORTED_BY_CALLBACK;
 
@@ -1060,6 +1108,19 @@ static CURLcode wssh_done(struct Curl_easy *data, CURLcode status)
   return result;
 }
 
+static void wssh_sshc_cleanup(struct ssh_conn *sshc)
+{
+  if(sshc->ssh_session) {
+    wolfSSH_free(sshc->ssh_session);
+    sshc->ssh_session = NULL;
+  }
+  if(sshc->ctx) {
+    wolfSSH_CTX_free(sshc->ctx);
+    sshc->ctx = NULL;
+  }
+  Curl_safefree(sshc->homedir);
+}
+
 #if 0
 static CURLcode wscp_done(struct Curl_easy *data,
                          CURLcode code, bool premature)
@@ -1085,11 +1146,11 @@ static CURLcode wscp_doing(struct Curl_easy *data,
 static CURLcode wscp_disconnect(struct Curl_easy *data,
                                 struct connectdata *conn, bool dead_connection)
 {
+  struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
   CURLcode result = CURLE_OK;
-  (void)data;
-  (void)conn;
   (void)dead_connection;
-
+  if(sshc)
+    wssh_sshc_cleanup(sshc);
   return result;
 }
 #endif
@@ -1097,10 +1158,14 @@ static CURLcode wscp_disconnect(struct Curl_easy *data,
 static CURLcode wsftp_done(struct Curl_easy *data,
                           CURLcode code, bool premature)
 {
+  struct ssh_conn *sshc = Curl_conn_meta_get(data->conn, CURL_META_SSH_CONN);
   (void)premature;
-  state(data, SSH_SFTP_CLOSE);
+  if(!sshc)
+    return CURLE_FAILED_INIT;
+
+  wssh_state(data, sshc, SSH_SFTP_CLOSE);
 
-  return wssh_done(data, code);
+  return wssh_done(data, sshc, code);
 }
 
 static CURLcode wsftp_doing(struct Curl_easy *data,
@@ -1118,17 +1183,20 @@ static CURLcode wsftp_disconnect(struct Curl_easy *data,
                                  struct connectdata *conn,
                                  bool dead)
 {
+  struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
   CURLcode result = CURLE_OK;
   (void)dead;
 
   DEBUGF(infof(data, "SSH DISCONNECT starts now"));
 
-  if(conn->proto.sshc.ssh_session) {
+  if(sshc && sshc->ssh_session) {
     /* only if there is a session still around to use! */
-    state(data, SSH_SFTP_SHUTDOWN);
-    result = wssh_block_statemach(data, TRUE);
+    wssh_state(data, sshc, SSH_SFTP_SHUTDOWN);
+    result = wssh_block_statemach(data, sshc, TRUE);
   }
 
+  if(sshc)
+    wssh_sshc_cleanup(sshc);
   DEBUGF(infof(data, "SSH DISCONNECT is done"));
   return result;
 }
diff --git a/extra/curl/curl-8.12.1/lib/vtls/.checksrc b/extra/curl/curl-8.14.1/lib/vtls/.checksrc
similarity index 60%
rename from extra/curl/curl-8.12.1/lib/vtls/.checksrc
rename to extra/curl/curl-8.14.1/lib/vtls/.checksrc
index eff4bc0a3f72..770fad0dfcea 100644
--- a/extra/curl/curl-8.12.1/lib/vtls/.checksrc
+++ b/extra/curl/curl-8.14.1/lib/vtls/.checksrc
@@ -1,3 +1,5 @@
 banfunc strerror
 banfunc strncpy
 banfunc sscanf
+banfunc strtoul
+banfunc strtol
diff --git a/extra/curl/curl-8.12.1/lib/vtls/bearssl.c b/extra/curl/curl-8.14.1/lib/vtls/bearssl.c
similarity index 90%
rename from extra/curl/curl-8.12.1/lib/vtls/bearssl.c
rename to extra/curl/curl-8.14.1/lib/vtls/bearssl.c
index c52f28d748c1..4b652444510f 100644
--- a/extra/curl/curl-8.12.1/lib/vtls/bearssl.c
+++ b/extra/curl/curl-8.14.1/lib/vtls/bearssl.c
@@ -21,7 +21,7 @@
  * SPDX-License-Identifier: curl
  *
  ***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #ifdef USE_BEARSSL
 
@@ -29,20 +29,20 @@
 
 #include "bearssl.h"
 #include "cipher_suite.h"
-#include "urldata.h"
-#include "sendf.h"
-#include "inet_pton.h"
+#include "../urldata.h"
+#include "../sendf.h"
+#include "../curlx/inet_pton.h"
 #include "vtls.h"
 #include "vtls_int.h"
 #include "vtls_scache.h"
-#include "connect.h"
-#include "select.h"
-#include "multiif.h"
-#include "curl_printf.h"
+#include "../connect.h"
+#include "../select.h"
+#include "../multiif.h"
+#include "../curl_printf.h"
 
 /* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
 
 struct x509_context {
   const br_x509_class *vtable;
@@ -153,7 +153,7 @@ static CURLcode load_cafile(struct cafile_source *source,
     }
     else if(source->type == CAFILE_SOURCE_BLOB) {
       n = source->len;
-      p = (unsigned char *) source->data;
+      p = (const unsigned char *) source->data;
     }
     while(n) {
       pushed = br_pem_decoder_push(&pc, p, n);
@@ -338,7 +338,7 @@ static unsigned x509_end_chain(const br_x509_class **ctx)
 static const br_x509_pkey *x509_get_pkey(const br_x509_class *const *ctx,
                                          unsigned *usages)
 {
-  struct x509_context *x509 = (struct x509_context *)ctx;
+  struct x509_context *x509 = (struct x509_context *)CURL_UNCONST(ctx);
 
   if(!x509->verifypeer) {
     /* Nothing in the chain is verified, just return the public key of the
@@ -484,7 +484,7 @@ static const uint16_t ciphertable[] = {
   BR_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,        /* 0xCCA9 */
 };
 
-#define NUM_OF_CIPHERS (sizeof(ciphertable) / sizeof(ciphertable[0]))
+#define NUM_OF_CIPHERS CURL_ARRAYSIZE(ciphertable)
 
 static CURLcode bearssl_set_selected_ciphers(struct Curl_easy *data,
                                              br_ssl_engine_context *ssl_eng,
@@ -611,12 +611,12 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf,
 
   if(ssl_config->primary.cache_session) {
     struct Curl_ssl_session *sc_session = NULL;
-    const br_ssl_session_parameters *session;
 
     ret = Curl_ssl_scache_take(cf, data, connssl->peer.scache_key,
                                &sc_session);
     if(!ret && sc_session && sc_session->sdata && sc_session->sdata_len) {
-      session = (br_ssl_session_parameters *)(void *)sc_session->sdata;
+      const br_ssl_session_parameters *session;
+      session = (const br_ssl_session_parameters *)sc_session->sdata;
       br_ssl_engine_set_session_parameters(&backend->ctx.eng, session);
       session_set = 1;
       infof(data, "BearSSL: reusing session ID");
@@ -729,7 +729,7 @@ static CURLcode bearssl_run_until(struct Curl_cfilter *cf,
       return CURLE_OK;
     if(state & BR_SSL_SENDREC) {
       buf = br_ssl_engine_sendrec_buf(&backend->ctx.eng, &len);
-      ret = Curl_conn_cf_send(cf->next, data, (char *)buf, len, FALSE,
+      ret = Curl_conn_cf_send(cf->next, data, (const char *)buf, len, FALSE,
                               &result);
       CURL_TRC_CF(data, cf, "ssl_send(len=%zu) -> %zd, %d", len, ret, result);
       if(ret <= 0) {
@@ -911,18 +911,14 @@ static ssize_t bearssl_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
   return applen;
 }
 
-static CURLcode bearssl_connect_common(struct Curl_cfilter *cf,
-                                       struct Curl_easy *data,
-                                       bool nonblocking,
-                                       bool *done)
+static CURLcode bearssl_connect(struct Curl_cfilter *cf,
+                                struct Curl_easy *data,
+                                bool *done)
 {
   CURLcode ret;
   struct ssl_connect_data *connssl = cf->ctx;
-  curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
-  timediff_t timeout_ms;
-  int what;
 
-  CURL_TRC_CF(data, cf, "connect_common(blocking=%d)", !nonblocking);
+  CURL_TRC_CF(data, cf, "connect()");
   /* check if the connection has already been established */
   if(ssl_connection_complete == connssl->state) {
     CURL_TRC_CF(data, cf, "connect_common, connected");
@@ -930,61 +926,18 @@ static CURLcode bearssl_connect_common(struct Curl_cfilter *cf,
     return CURLE_OK;
   }
 
+  *done = FALSE;
+  connssl->io_need = CURL_SSL_IO_NEED_NONE;
+
   if(ssl_connect_1 == connssl->connecting_state) {
     ret = bearssl_connect_step1(cf, data);
     if(ret)
       return ret;
   }
 
-  while(ssl_connect_2 == connssl->connecting_state) {
-    /* check allowed time left */
-    timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
-    if(timeout_ms < 0) {
-      /* no need to continue if time already is up */
-      failf(data, "SSL connection timeout");
-      return CURLE_OPERATION_TIMEDOUT;
-    }
-
-    /* if ssl is expecting something, check if it is available. */
-    if(connssl->io_need) {
-      curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND) ?
-        sockfd : CURL_SOCKET_BAD;
-      curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV) ?
-        sockfd : CURL_SOCKET_BAD;
-
-      CURL_TRC_CF(data, cf, "connect_common, check socket");
-      what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
-                               nonblocking ? 0 : timeout_ms);
-      CURL_TRC_CF(data, cf, "connect_common, check socket -> %d", what);
-      if(what < 0) {
-        /* fatal error */
-        failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
-        return CURLE_SSL_CONNECT_ERROR;
-      }
-      else if(0 == what) {
-        if(nonblocking) {
-          *done = FALSE;
-          return CURLE_OK;
-        }
-        else {
-          /* timeout */
-          failf(data, "SSL connection timeout");
-          return CURLE_OPERATION_TIMEDOUT;
-        }
-      }
-      /* socket is readable or writable */
-    }
-
-    /* Run transaction, and return to the caller if it failed or if this
-     * connection is done nonblocking and this loop would execute again. This
-     * permits the owner of a multi handle to abort a connection attempt
-     * before step2 has completed while ensuring that a client using select()
-     * or epoll() will always have a valid fdset to wait on.
-     */
-    connssl->io_need = CURL_SSL_IO_NEED_NONE;
+  if(ssl_connect_2 == connssl->connecting_state) {
     ret = bearssl_connect_step2(cf, data);
-    if(ret || (nonblocking && (ssl_connect_2 == connssl->connecting_state)))
+    if(ret)
       return ret;
   }
 
@@ -998,11 +951,6 @@ static CURLcode bearssl_connect_common(struct Curl_cfilter *cf,
     connssl->state = ssl_connection_complete;
     *done = TRUE;
   }
-  else
-    *done = FALSE;
-
-  /* Reset our connect state machine */
-  connssl->connecting_state = ssl_connect_1;
 
   return CURLE_OK;
 }
@@ -1044,28 +992,6 @@ static CURLcode bearssl_random(struct Curl_easy *data UNUSED_PARAM,
   return CURLE_OK;
 }
 
-static CURLcode bearssl_connect(struct Curl_cfilter *cf,
-                                struct Curl_easy *data)
-{
-  CURLcode ret;
-  bool done = FALSE;
-
-  ret = bearssl_connect_common(cf, data, FALSE, &done);
-  if(ret)
-    return ret;
-
-  DEBUGASSERT(done);
-
-  return CURLE_OK;
-}
-
-static CURLcode bearssl_connect_nonblocking(struct Curl_cfilter *cf,
-                                            struct Curl_easy *data,
-                                            bool *done)
-{
-  return bearssl_connect_common(cf, data, TRUE, done);
-}
-
 static void *bearssl_get_internals(struct ssl_connect_data *connssl,
                                    CURLINFO info UNUSED_PARAM)
 {
@@ -1161,7 +1087,6 @@ const struct Curl_ssl Curl_ssl_bearssl = {
   bearssl_random,                  /* random */
   NULL,                            /* cert_status_request */
   bearssl_connect,                 /* connect */
-  bearssl_connect_nonblocking,     /* connect_nonblocking */
   Curl_ssl_adjust_pollset,         /* adjust_pollset */
   bearssl_get_internals,           /* get_internals */
   bearssl_close,                   /* close_one */
diff --git a/extra/curl/curl-8.12.1/lib/vtls/bearssl.h b/extra/curl/curl-8.14.1/lib/vtls/bearssl.h
similarity index 97%
rename from extra/curl/curl-8.12.1/lib/vtls/bearssl.h
rename to extra/curl/curl-8.14.1/lib/vtls/bearssl.h
index b3651b092c13..8bb254f7da07 100644
--- a/extra/curl/curl-8.12.1/lib/vtls/bearssl.h
+++ b/extra/curl/curl-8.14.1/lib/vtls/bearssl.h
@@ -24,7 +24,7 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #ifdef USE_BEARSSL
 
diff --git a/extra/curl/curl-8.12.1/lib/vtls/cipher_suite.c b/extra/curl/curl-8.14.1/lib/vtls/cipher_suite.c
similarity index 98%
rename from extra/curl/curl-8.12.1/lib/vtls/cipher_suite.c
rename to extra/curl/curl-8.14.1/lib/vtls/cipher_suite.c
index a694b1462793..d058bd3f0e06 100644
--- a/extra/curl/curl-8.12.1/lib/vtls/cipher_suite.c
+++ b/extra/curl/curl-8.14.1/lib/vtls/cipher_suite.c
@@ -21,13 +21,13 @@
  * SPDX-License-Identifier: curl
  *
  ***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #if defined(USE_SECTRANSP) || defined(USE_MBEDTLS) || \
     defined(USE_BEARSSL) || defined(USE_RUSTLS)
 #include "cipher_suite.h"
-#include "curl_printf.h"
-#include "strcase.h"
+#include "../curl_printf.h"
+#include "../strcase.h"
 #include <string.h>
 
 /*
@@ -725,7 +725,7 @@ static const struct cs_entry cs_list [] = {
   CS_ENTRY(0xCCAE, RSA,PSK,CHACHA20,POLY1305,,,,),
 #endif
 };
-#define CS_LIST_LEN (sizeof(cs_list) / sizeof(cs_list[0]))
+#define CS_LIST_LEN CURL_ARRAYSIZE(cs_list)
 
 static int cs_str_to_zip(const char *cs_str, size_t cs_len,
                          uint8_t zip[6])
@@ -786,12 +786,12 @@ static int cs_zip_to_str(const uint8_t zip[6],
 
   /* unzip the 8 indexes */
   indexes[0] = zip[0] >> 2;
-  indexes[1] = ((zip[0] << 4) & 0x3F) | zip[1] >> 4;
-  indexes[2] = ((zip[1] << 2) & 0x3F) | zip[2] >> 6;
+  indexes[1] = (uint8_t)(((zip[0] << 4) & 0x3F) | zip[1] >> 4);
+  indexes[2] = (uint8_t)(((zip[1] << 2) & 0x3F) | zip[2] >> 6);
   indexes[3] = ((zip[2] << 0) & 0x3F);
   indexes[4] = zip[3] >> 2;
-  indexes[5] = ((zip[3] << 4) & 0x3F) | zip[4] >> 4;
-  indexes[6] = ((zip[4] << 2) & 0x3F) | zip[5] >> 6;
+  indexes[5] = (uint8_t)(((zip[3] << 4) & 0x3F) | zip[4] >> 4);
+  indexes[6] = (uint8_t)(((zip[4] << 2) & 0x3F) | zip[5] >> 6);
   indexes[7] = ((zip[5] << 0) & 0x3F);
 
   if(indexes[0] == CS_TXT_IDX_TLS)
diff --git a/extra/curl/curl-8.12.1/lib/vtls/cipher_suite.h b/extra/curl/curl-8.14.1/lib/vtls/cipher_suite.h
similarity index 98%
rename from extra/curl/curl-8.12.1/lib/vtls/cipher_suite.h
rename to extra/curl/curl-8.14.1/lib/vtls/cipher_suite.h
index 6d980103a573..cd556db10f62 100644
--- a/extra/curl/curl-8.12.1/lib/vtls/cipher_suite.h
+++ b/extra/curl/curl-8.14.1/lib/vtls/cipher_suite.h
@@ -24,7 +24,7 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #if defined(USE_SECTRANSP) || defined(USE_MBEDTLS) || \
     defined(USE_BEARSSL) || defined(USE_RUSTLS)
diff --git a/extra/curl/curl-8.12.1/lib/vtls/gtls.c b/extra/curl/curl-8.14.1/lib/vtls/gtls.c
similarity index 84%
rename from extra/curl/curl-8.12.1/lib/vtls/gtls.c
rename to extra/curl/curl-8.14.1/lib/vtls/gtls.c
index a47d803c7275..74c36fed9437 100644
--- a/extra/curl/curl-8.12.1/lib/vtls/gtls.c
+++ b/extra/curl/curl-8.14.1/lib/vtls/gtls.c
@@ -30,7 +30,7 @@
  * since they were not present in 1.0.X.
  */
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #ifdef USE_GNUTLS
 
@@ -40,34 +40,28 @@
 #include <gnutls/crypto.h>
 #include <nettle/sha2.h>
 
-#include "urldata.h"
-#include "sendf.h"
-#include "inet_pton.h"
+#include "../urldata.h"
+#include "../sendf.h"
+#include "../curlx/inet_pton.h"
 #include "keylog.h"
 #include "gtls.h"
 #include "vtls.h"
 #include "vtls_int.h"
 #include "vtls_scache.h"
-#include "vauth/vauth.h"
-#include "parsedate.h"
-#include "connect.h" /* for the connect timeout */
-#include "progress.h"
-#include "select.h"
-#include "strcase.h"
-#include "strdup.h"
-#include "warnless.h"
+#include "../vauth/vauth.h"
+#include "../parsedate.h"
+#include "../connect.h" /* for the connect timeout */
+#include "../progress.h"
+#include "../select.h"
+#include "../strcase.h"
+#include "../strdup.h"
+#include "../curlx/warnless.h"
 #include "x509asn1.h"
-#include "multiif.h"
-#include "curl_printf.h"
-#include "curl_memory.h"
+#include "../multiif.h"
+#include "../curl_printf.h"
+#include "../curl_memory.h"
 /* The last #include file should be: */
-#include "memdebug.h"
-
-#define QUIC_PRIORITY \
-  "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:" \
-  "+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:+GROUP-SECP256R1:" \
-  "+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1:" \
-  "%DISABLE_TLS13_COMPAT_MODE"
+#include "../memdebug.h"
 
 /* Enable GnuTLS debugging by defining GTLSDEBUG */
 /*#define GTLSDEBUG */
@@ -106,6 +100,7 @@ static ssize_t gtls_push(void *s, const void *buf, size_t blen)
               blen, nwritten, result);
   backend->gtls.io_result = result;
   if(nwritten < 0) {
+    /* !checksrc! disable ERRNOVAR 1 */
     gnutls_transport_set_errno(backend->gtls.session,
                                (CURLE_AGAIN == result) ? EAGAIN : EINVAL);
     nwritten = -1;
@@ -127,6 +122,7 @@ static ssize_t gtls_pull(void *s, void *buf, size_t blen)
   if(!backend->gtls.shared_creds->trust_setup) {
     result = Curl_gtls_client_trust_setup(cf, data, &backend->gtls);
     if(result) {
+      /* !checksrc! disable ERRNOVAR 1 */
       gnutls_transport_set_errno(backend->gtls.session, EINVAL);
       backend->gtls.io_result = result;
       return -1;
@@ -138,6 +134,7 @@ static ssize_t gtls_pull(void *s, void *buf, size_t blen)
               blen, nread, result);
   backend->gtls.io_result = result;
   if(nread < 0) {
+    /* !checksrc! disable ERRNOVAR 1 */
     gnutls_transport_set_errno(backend->gtls.session,
                                (CURLE_AGAIN == result) ? EAGAIN : EINVAL);
     nread = -1;
@@ -236,117 +233,70 @@ static void unload_file(gnutls_datum_t data)
 }
 
 
-/* this function does a SSL/TLS (re-)handshake */
+/* this function does an SSL/TLS (re-)handshake */
 static CURLcode handshake(struct Curl_cfilter *cf,
-                          struct Curl_easy *data,
-                          bool duringconnect,
-                          bool nonblocking)
+                          struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
   struct gtls_ssl_backend_data *backend =
     (struct gtls_ssl_backend_data *)connssl->backend;
   gnutls_session_t session;
-  curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
+  int rc;
 
   DEBUGASSERT(backend);
   session = backend->gtls.session;
-  connssl->connecting_state = ssl_connect_2;
-
-  for(;;) {
-    timediff_t timeout_ms;
-    int rc;
-
-    /* check allowed time left */
-    timeout_ms = Curl_timeleft(data, NULL, duringconnect);
-
-    if(timeout_ms < 0) {
-      /* no need to continue if time already is up */
-      failf(data, "SSL connection timeout");
-      return CURLE_OPERATION_TIMEDOUT;
-    }
 
-    /* if ssl is expecting something, check if it is available. */
-    if(connssl->io_need) {
-      int what;
-      curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND) ?
-        sockfd : CURL_SOCKET_BAD;
-      curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV) ?
-        sockfd : CURL_SOCKET_BAD;
-
-      what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
-                               nonblocking ? 0 :
-                               timeout_ms ? timeout_ms : 1000);
-      if(what < 0) {
-        /* fatal error */
-        failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
-        return CURLE_SSL_CONNECT_ERROR;
-      }
-      else if(0 == what) {
-        if(nonblocking)
-          return CURLE_AGAIN;
-        else if(timeout_ms) {
-          /* timeout */
-          failf(data, "SSL connection timeout at %ld", (long)timeout_ms);
-          return CURLE_OPERATION_TIMEDOUT;
-        }
-      }
-      /* socket is readable or writable */
-    }
+  connssl->io_need = CURL_SSL_IO_NEED_NONE;
+  backend->gtls.io_result = CURLE_OK;
+  rc = gnutls_handshake(session);
 
-    connssl->io_need = CURL_SSL_IO_NEED_NONE;
-    backend->gtls.io_result = CURLE_OK;
-    rc = gnutls_handshake(session);
+  if(!backend->gtls.shared_creds->trust_setup) {
+    /* After having send off the ClientHello, we prepare the trust
+     * store to verify the coming certificate from the server */
+    CURLcode result = Curl_gtls_client_trust_setup(cf, data, &backend->gtls);
+    if(result)
+      return result;
+  }
 
-    if(!backend->gtls.shared_creds->trust_setup) {
-      /* After having send off the ClientHello, we prepare the trust
-       * store to verify the coming certificate from the server */
-      CURLcode result = Curl_gtls_client_trust_setup(cf, data, &backend->gtls);
-      if(result)
-        return result;
-    }
+  if((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) {
+    connssl->io_need =
+      gnutls_record_get_direction(session) ?
+      CURL_SSL_IO_NEED_SEND : CURL_SSL_IO_NEED_RECV;
+    return CURLE_AGAIN;
+  }
+  else if((rc < 0) && !gnutls_error_is_fatal(rc)) {
+    const char *strerr = NULL;
 
-    if((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) {
-      connssl->io_need =
-        gnutls_record_get_direction(session) ?
-        CURL_SSL_IO_NEED_SEND : CURL_SSL_IO_NEED_RECV;
-      continue;
+    if(rc == GNUTLS_E_WARNING_ALERT_RECEIVED) {
+      gnutls_alert_description_t alert = gnutls_alert_get(session);
+      strerr = gnutls_alert_get_name(alert);
     }
-    else if((rc < 0) && !gnutls_error_is_fatal(rc)) {
-      const char *strerr = NULL;
 
-      if(rc == GNUTLS_E_WARNING_ALERT_RECEIVED) {
-        gnutls_alert_description_t alert = gnutls_alert_get(session);
-        strerr = gnutls_alert_get_name(alert);
-      }
+    if(!strerr)
+      strerr = gnutls_strerror(rc);
 
-      if(!strerr)
-        strerr = gnutls_strerror(rc);
+    infof(data, "gnutls_handshake() warning: %s", strerr);
+    return CURLE_AGAIN;
+  }
+  else if((rc < 0) && backend->gtls.io_result) {
+    return backend->gtls.io_result;
+  }
+  else if(rc < 0) {
+    const char *strerr = NULL;
 
-      infof(data, "gnutls_handshake() warning: %s", strerr);
-      continue;
+    if(rc == GNUTLS_E_FATAL_ALERT_RECEIVED) {
+      gnutls_alert_description_t alert = gnutls_alert_get(session);
+      strerr = gnutls_alert_get_name(alert);
     }
-    else if((rc < 0) && backend->gtls.io_result) {
-      return backend->gtls.io_result;
-    }
-    else if(rc < 0) {
-      const char *strerr = NULL;
-
-      if(rc == GNUTLS_E_FATAL_ALERT_RECEIVED) {
-        gnutls_alert_description_t alert = gnutls_alert_get(session);
-        strerr = gnutls_alert_get_name(alert);
-      }
-
-      if(!strerr)
-        strerr = gnutls_strerror(rc);
 
-      failf(data, "GnuTLS, handshake failed: %s", strerr);
-      return CURLE_SSL_CONNECT_ERROR;
-    }
+    if(!strerr)
+      strerr = gnutls_strerror(rc);
 
-    /* Reset our connect state machine */
-    connssl->connecting_state = ssl_connect_1;
-    return CURLE_OK;
+    failf(data, "GnuTLS, handshake failed: %s", strerr);
+    return CURLE_SSL_CONNECT_ERROR;
   }
+
+  return CURLE_OK;
 }
 
 static gnutls_x509_crt_fmt_t gnutls_do_file_type(const char *type)
@@ -366,12 +316,18 @@ static gnutls_x509_crt_fmt_t gnutls_do_file_type(const char *type)
  */
 #define GNUTLS_SRP "+SRP"
 
+#define QUIC_PRIORITY \
+  "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:" \
+  "+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:+GROUP-SECP256R1:" \
+  "+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1:" \
+  "%DISABLE_TLS13_COMPAT_MODE"
+
 static CURLcode
 gnutls_set_ssl_version_min_max(struct Curl_easy *data,
                                struct ssl_peer *peer,
                                struct ssl_primary_config *conn_config,
                                const char **prioritylist,
-                               const char *tls13support)
+                               bool tls13support)
 {
   long ssl_version = conn_config->version;
   long ssl_version_max = conn_config->version_max;
@@ -470,7 +426,7 @@ CURLcode Curl_gtls_shared_creds_create(struct Curl_easy *data,
   }
 
   shared->refcount = 1;
-  shared->time = Curl_now();
+  shared->time = curlx_now();
   *pcreds = shared;
   return CURLE_OK;
 }
@@ -584,8 +540,8 @@ static bool gtls_shared_creds_expired(const struct Curl_easy *data,
                                       const struct gtls_shared_creds *sc)
 {
   const struct ssl_general_config *cfg = &data->set.general_ssl;
-  struct curltime now = Curl_now();
-  timediff_t elapsed_ms = Curl_timediff(now, sc->time);
+  struct curltime now = curlx_now();
+  timediff_t elapsed_ms = curlx_timediff(now, sc->time);
   timediff_t timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000;
 
   if(timeout_ms < 0)
@@ -611,7 +567,7 @@ gtls_get_cached_creds(struct Curl_cfilter *cf, struct Curl_easy *data)
 
   if(data->multi) {
     shared_creds = Curl_hash_pick(&data->multi->proto_hash,
-                                  (void *)MPROTO_GTLS_X509_KEY,
+                                  CURL_UNCONST(MPROTO_GTLS_X509_KEY),
                                   sizeof(MPROTO_GTLS_X509_KEY)-1);
      if(shared_creds && shared_creds->creds &&
         !gtls_shared_creds_expired(data, shared_creds) &&
@@ -655,7 +611,7 @@ static void gtls_set_cached_creds(struct Curl_cfilter *cf,
     return;
 
   if(!Curl_hash_add2(&data->multi->proto_hash,
-                    (void *)MPROTO_GTLS_X509_KEY,
+                    CURL_UNCONST(MPROTO_GTLS_X509_KEY),
                     sizeof(MPROTO_GTLS_X509_KEY)-1,
                     sc, gtls_shared_creds_hash_free)) {
     Curl_gtls_shared_creds_free(&sc); /* down reference again */
@@ -827,6 +783,63 @@ static int gtls_handshake_cb(gnutls_session_t session, unsigned int htype,
   return 0;
 }
 
+static CURLcode gtls_set_priority(struct Curl_cfilter *cf,
+                                 struct Curl_easy *data,
+                                 struct gtls_ctx *gtls,
+                                 const char *priority)
+{
+  struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+  struct dynbuf buf;
+  const char *err = NULL;
+  CURLcode result = CURLE_OK;
+  int rc;
+
+  curlx_dyn_init(&buf, 4096);
+
+#ifdef USE_GNUTLS_SRP
+  if(conn_config->username) {
+    /* Only add SRP to the cipher list if SRP is requested. Otherwise
+     * GnuTLS will disable TLS 1.3 support. */
+    result = curlx_dyn_add(&buf, priority);
+    if(!result)
+      result = curlx_dyn_add(&buf, ":" GNUTLS_SRP);
+    if(result)
+      goto out;
+    priority = curlx_dyn_ptr(&buf);
+  }
+#endif
+
+  if(conn_config->cipher_list) {
+    if((conn_config->cipher_list[0] == '+') ||
+       (conn_config->cipher_list[0] == '-') ||
+       (conn_config->cipher_list[0] == '!')) {
+       /* add it to out own */
+      if(!curlx_dyn_len(&buf)) {  /* not added yet */
+        result = curlx_dyn_add(&buf, priority);
+        if(result)
+          goto out;
+      }
+      result = curlx_dyn_addf(&buf, ":%s", conn_config->cipher_list);
+      if(result)
+        goto out;
+      priority = curlx_dyn_ptr(&buf);
+    }
+    else /* replace our own completely */
+      priority = conn_config->cipher_list;
+  }
+
+  infof(data, "GnuTLS priority: %s", priority);
+  rc = gnutls_priority_set_direct(gtls->session, priority, &err);
+  if(rc != GNUTLS_E_SUCCESS) {
+    failf(data, "Error %d setting GnuTLS priority: %s", rc, err);
+    result = CURLE_SSL_CONNECT_ERROR;
+  }
+
+out:
+  curlx_dyn_free(&buf);
+  return result;
+}
+
 static CURLcode gtls_client_init(struct Curl_cfilter *cf,
                                  struct Curl_easy *data,
                                  struct ssl_peer *peer,
@@ -839,8 +852,7 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf,
   int rc;
   bool sni = TRUE; /* default is SNI enabled */
   const char *prioritylist;
-  const char *err = NULL;
-  const char *tls13support;
+  bool tls13support;
   CURLcode result;
 
   if(!gtls_inited)
@@ -935,7 +947,7 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf,
     return CURLE_SSL_CONNECT_ERROR;
 
   /* "In GnuTLS 3.6.5, TLS 1.3 is enabled by default" */
-  tls13support = gnutls_check_version("3.6.5");
+  tls13support = !!gnutls_check_version("3.6.5");
 
   /* Ensure +SRP comes at the *end* of all relevant strings so that it can be
    * removed if a runtime error indicates that SRP is not supported by this
@@ -960,33 +972,9 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf,
   if(result)
     return result;
 
-#ifdef USE_GNUTLS_SRP
-  /* Only add SRP to the cipher list if SRP is requested. Otherwise
-   * GnuTLS will disable TLS 1.3 support. */
-  if(config->username) {
-    char *prioritysrp = aprintf("%s:" GNUTLS_SRP, prioritylist);
-    if(!prioritysrp)
-      return CURLE_OUT_OF_MEMORY;
-    rc = gnutls_priority_set_direct(gtls->session, prioritysrp, &err);
-    free(prioritysrp);
-
-    if((rc == GNUTLS_E_INVALID_REQUEST) && err) {
-      infof(data, "This GnuTLS does not support SRP");
-    }
-  }
-  else {
-#endif
-    infof(data, "GnuTLS ciphers: %s", prioritylist);
-    rc = gnutls_priority_set_direct(gtls->session, prioritylist, &err);
-#ifdef USE_GNUTLS_SRP
-  }
-#endif
-
-  if(rc != GNUTLS_E_SUCCESS) {
-    failf(data, "Error %d setting GnuTLS cipher list starting with %s",
-          rc, err);
-    return CURLE_SSL_CONNECT_ERROR;
-  }
+  result = gtls_set_priority(cf, data, gtls, prioritylist);
+  if(result)
+    return result;
 
   if(config->clientcert) {
     if(!gtls->shared_creds->trust_setup) {
@@ -1006,7 +994,7 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf,
         return CURLE_SSL_CONNECT_ERROR;
       }
     }
-    else if(ssl_config->key_passwd) {
+    else {
       const unsigned int supported_key_encryption_algorithms =
         GNUTLS_PKCS_USE_PKCS12_3DES | GNUTLS_PKCS_USE_PKCS12_ARCFOUR |
         GNUTLS_PKCS_USE_PKCS12_RC2_40 | GNUTLS_PKCS_USE_PBES2_3DES |
@@ -1021,22 +1009,12 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf,
            supported_key_encryption_algorithms);
       if(rc != GNUTLS_E_SUCCESS) {
         failf(data,
-              "error reading X.509 potentially-encrypted key file: %s",
+              "error reading X.509 %skey file: %s",
+              ssl_config->key_passwd ? "potentially-encrypted " : "",
               gnutls_strerror(rc));
         return CURLE_SSL_CONNECT_ERROR;
       }
     }
-    else {
-      if(gnutls_certificate_set_x509_key_file(
-           gtls->shared_creds->creds,
-           config->clientcert,
-           ssl_config->key ? ssl_config->key : config->clientcert,
-           gnutls_do_file_type(ssl_config->cert_type) ) !=
-         GNUTLS_E_SUCCESS) {
-        failf(data, "error reading X.509 key or certificate file");
-        return CURLE_SSL_CONNECT_ERROR;
-      }
-    }
   }
 
 #ifdef USE_GNUTLS_SRP
@@ -1089,6 +1067,7 @@ static int keylog_callback(gnutls_session_t session, const char *label,
 
 static CURLcode gtls_on_session_reuse(struct Curl_cfilter *cf,
                                       struct Curl_easy *data,
+                                      struct alpn_spec *alpns,
                                       struct Curl_ssl_session *scs,
                                       bool *do_early_data)
 {
@@ -1104,13 +1083,13 @@ static CURLcode gtls_on_session_reuse(struct Curl_cfilter *cf,
     /* Seems to be GnuTLS way to signal no EarlyData in session */
     CURL_TRC_CF(data, cf, "SSL session does not allow earlydata");
   }
-  else if(!Curl_alpn_contains_proto(connssl->alpn, scs->alpn)) {
+  else if(!Curl_alpn_contains_proto(alpns, scs->alpn)) {
     CURL_TRC_CF(data, cf, "SSL session has different ALPN, no early data");
   }
   else {
     infof(data, "SSL session allows %zu bytes of early data, "
           "reusing ALPN '%s'", connssl->earlydata_max, scs->alpn);
-    connssl->earlydata_state = ssl_earlydata_use;
+    connssl->earlydata_state = ssl_earlydata_await;
     connssl->state = ssl_connection_deferred;
     result = Curl_alpn_set_negotiated(cf, data, connssl,
                     (const unsigned char *)scs->alpn,
@@ -1124,7 +1103,7 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx,
                             struct Curl_cfilter *cf,
                             struct Curl_easy *data,
                             struct ssl_peer *peer,
-                            const unsigned char *alpn, size_t alpn_len,
+                            const struct alpn_spec *alpns_requested,
                             Curl_gtls_ctx_setup_cb *cb_setup,
                             void *cb_user_data,
                             void *ssl_user_data,
@@ -1133,13 +1112,16 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx,
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
   struct Curl_ssl_session *scs = NULL;
-  gnutls_datum_t gtls_alpns[5];
+  gnutls_datum_t gtls_alpns[ALPN_ENTRIES_MAX];
   size_t gtls_alpns_count = 0;
   bool gtls_session_setup = FALSE;
-  CURLcode result;
+  struct alpn_spec alpns;
+  CURLcode result = CURLE_OK;
   int rc;
 
   DEBUGASSERT(gctx);
+  Curl_alpn_copy(&alpns, alpns_requested);
+
   /* This might be a reconnect, so we check for a session ID in the cache
      to speed up things. We need to do this before constructing the gnutls
      session since we need to set flags depending on the kind of reuse. */
@@ -1148,7 +1130,8 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx,
     if(result)
       goto out;
 
-    if(scs && scs->sdata && scs->sdata_len) {
+    if(scs && scs->sdata && scs->sdata_len &&
+       (!scs->alpn || Curl_alpn_contains_proto(&alpns, scs->alpn))) {
       /* we got a cached session, use it! */
 
       result = gtls_client_init(cf, data, peer, scs->earlydata_max, gctx);
@@ -1162,30 +1145,19 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx,
       else {
         infof(data, "SSL reusing session with ALPN '%s'",
               scs->alpn ? scs->alpn : "-");
-        if(ssl_config->earlydata &&
+        if(ssl_config->earlydata && scs->alpn &&
            !cf->conn->connect_only &&
            (gnutls_protocol_get_version(gctx->session) == GNUTLS_TLS1_3)) {
           bool do_early_data = FALSE;
           if(sess_reuse_cb) {
-            result = sess_reuse_cb(cf, data, scs, &do_early_data);
+            result = sess_reuse_cb(cf, data, &alpns, scs, &do_early_data);
             if(result)
               goto  out;
           }
           if(do_early_data) {
             /* We only try the ALPN protocol the session used before,
              * otherwise we might send early data for the wrong protocol */
-            gtls_alpns[0].data = (unsigned char *)scs->alpn;
-            gtls_alpns[0].size = (unsigned)strlen(scs->alpn);
-            if(gnutls_alpn_set_protocols(gctx->session,
-                                         gtls_alpns, 1,
-                                         GNUTLS_ALPN_MANDATORY)) {
-              failf(data, "failed setting ALPN");
-              result = CURLE_SSL_CONNECT_ERROR;
-              goto out;
-            }
-            /* don't set again below */
-            gtls_alpns_count = 0;
-            alpn = NULL;
+            Curl_alpn_restrict_to(&alpns, scs->alpn);
           }
         }
       }
@@ -1215,24 +1187,14 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx,
   /* convert the ALPN string from our arguments to a list of strings that
    * gnutls wants and will convert internally back to this string for sending
    * to the server. nice. */
-  if(!gtls_alpns_count && alpn && alpn_len) {
-    size_t i, alen = alpn_len;
-    unsigned char *salpn = (unsigned char *)alpn;
-    unsigned char slen;
-    for(i = 0; (i < CURL_ARRAYSIZE(gtls_alpns)) && alen; ++i) {
-      slen = salpn[0];
-      if(slen >= alen)
-        return CURLE_FAILED_INIT;
-      gtls_alpns[i].data = salpn + 1;
-      gtls_alpns[i].size = slen;
-      salpn += slen + 1;
-      alen -= (size_t)slen + 1;
+  if(!gtls_alpns_count && alpns.count) {
+    size_t i;
+    DEBUGASSERT(CURL_ARRAYSIZE(gtls_alpns) >= alpns.count);
+    for(i = 0; i < alpns.count; ++i) {
+      gtls_alpns[i].data = (unsigned char *)alpns.entries[i];
+      gtls_alpns[i].size = (unsigned int)strlen(alpns.entries[i]);
     }
-    if(alen) { /* not all alpn chars used, wrong format or too many */
-      result = CURLE_FAILED_INIT;
-      goto out;
-    }
-    gtls_alpns_count = i;
+    gtls_alpns_count = alpns.count;
   }
 
   if(gtls_alpns_count &&
@@ -1254,33 +1216,24 @@ gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
   struct ssl_connect_data *connssl = cf->ctx;
   struct gtls_ssl_backend_data *backend =
     (struct gtls_ssl_backend_data *)connssl->backend;
-  struct alpn_proto_buf proto;
   CURLcode result;
 
   DEBUGASSERT(backend);
-  DEBUGASSERT(ssl_connect_1 == connssl->connecting_state);
 
   if(connssl->state == ssl_connection_complete)
     /* to make us tolerant against being called more than once for the
        same connection */
     return CURLE_OK;
 
-  memset(&proto, 0, sizeof(proto));
-  if(connssl->alpn) {
-    result = Curl_alpn_to_proto_buf(&proto, connssl->alpn);
-    if(result) {
-      failf(data, "Error determining ALPN");
-      return CURLE_SSL_CONNECT_ERROR;
-    }
-  }
-
   result = Curl_gtls_ctx_init(&backend->gtls, cf, data, &connssl->peer,
-                              proto.data, proto.len,
-                              NULL, NULL, cf, gtls_on_session_reuse);
+                              connssl->alpn, NULL, NULL, cf,
+                              gtls_on_session_reuse);
   if(result)
     return result;
 
   if(connssl->alpn && (connssl->state != ssl_connection_deferred)) {
+    struct alpn_proto_buf proto;
+    memset(&proto, 0, sizeof(proto));
     Curl_alpn_to_proto_str(&proto, connssl->alpn);
     infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
   }
@@ -1639,10 +1592,10 @@ Curl_gtls_verifyserver(struct Curl_easy *data,
     unsigned char addrbuf[sizeof(struct use_addr)];
     size_t addrlen = 0;
 
-    if(Curl_inet_pton(AF_INET, peer->hostname, addrbuf) > 0)
+    if(curlx_inet_pton(AF_INET, peer->hostname, addrbuf) > 0)
       addrlen = 4;
 #ifdef USE_IPV6
-    else if(Curl_inet_pton(AF_INET6, peer->hostname, addrbuf) > 0)
+    else if(curlx_inet_pton(AF_INET6, peer->hostname, addrbuf) > 0)
       addrlen = 16;
 #endif
 
@@ -1828,30 +1781,6 @@ static CURLcode gtls_verifyserver(struct Curl_cfilter *cf,
   return result;
 }
 
-static CURLcode gtls_set_earlydata(struct Curl_cfilter *cf,
-                                   struct Curl_easy *data,
-                                   const void *buf, size_t blen)
-{
-  struct ssl_connect_data *connssl = cf->ctx;
-  ssize_t nwritten = 0;
-  CURLcode result = CURLE_OK;
-
-  DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_use);
-  DEBUGASSERT(Curl_bufq_is_empty(&connssl->earlydata));
-  if(blen) {
-    if(blen > connssl->earlydata_max)
-      blen = connssl->earlydata_max;
-    nwritten = Curl_bufq_write(&connssl->earlydata, buf, blen, &result);
-    CURL_TRC_CF(data, cf, "gtls_set_earlydata(len=%zu) -> %zd",
-                blen, nwritten);
-    if(nwritten < 0)
-      return result;
-  }
-  connssl->earlydata_state = ssl_earlydata_sending;
-  connssl->earlydata_skip = Curl_bufq_len(&connssl->earlydata);
-  return CURLE_OK;
-}
-
 static CURLcode gtls_send_earlydata(struct Curl_cfilter *cf,
                                     struct Curl_easy *data)
 {
@@ -1886,8 +1815,7 @@ static CURLcode gtls_send_earlydata(struct Curl_cfilter *cf,
     Curl_bufq_skip(&connssl->earlydata, (size_t)n);
   }
   /* sent everything there was */
-  infof(data, "SSL sending %" FMT_OFF_T " bytes of early data",
-        connssl->earlydata_skip);
+  infof(data, "SSL sending %zu bytes of early data", connssl->earlydata_skip);
 out:
   return result;
 }
@@ -1901,17 +1829,22 @@ static CURLcode gtls_send_earlydata(struct Curl_cfilter *cf,
    'ssl_connect_2' (doing handshake with the server), and
    'ssl_connect_3' (verifying and getting stats).
  */
-static CURLcode
-gtls_connect_common(struct Curl_cfilter *cf,
-                    struct Curl_easy *data,
-                    bool nonblocking,
-                    bool *done) {
+static CURLcode gtls_connect_common(struct Curl_cfilter *cf,
+                                    struct Curl_easy *data,
+                                    bool *done) {
   struct ssl_connect_data *connssl = cf->ctx;
   struct gtls_ssl_backend_data *backend =
       (struct gtls_ssl_backend_data *)connssl->backend;
   CURLcode result = CURLE_OK;
 
   DEBUGASSERT(backend);
+  /* check if the connection has already been established */
+  if(ssl_connection_complete == connssl->state) {
+    *done = TRUE;
+    return CURLE_OK;
+  }
+
+  *done = FALSE;
 
   /* Initiate the connection, if not already done */
   if(connssl->connecting_state == ssl_connect_1) {
@@ -1922,7 +1855,7 @@ gtls_connect_common(struct Curl_cfilter *cf,
   }
 
   if(connssl->connecting_state == ssl_connect_2) {
-    if(connssl->earlydata_state == ssl_earlydata_use) {
+    if(connssl->earlydata_state == ssl_earlydata_await) {
       goto out;
     }
     else if(connssl->earlydata_state == ssl_earlydata_sending) {
@@ -1930,13 +1863,11 @@ gtls_connect_common(struct Curl_cfilter *cf,
       if(result)
         goto out;
       connssl->earlydata_state = ssl_earlydata_sent;
-      if(!Curl_ssl_cf_is_proxy(cf))
-        Curl_pgrsEarlyData(data, (curl_off_t)connssl->earlydata_skip);
     }
     DEBUGASSERT((connssl->earlydata_state == ssl_earlydata_none) ||
                 (connssl->earlydata_state == ssl_earlydata_sent));
 
-    result = handshake(cf, data, TRUE, nonblocking);
+    result = handshake(cf, data);
     if(result)
       goto out;
     connssl->connecting_state = ssl_connect_3;
@@ -1951,7 +1882,6 @@ gtls_connect_common(struct Curl_cfilter *cf,
       goto out;
 
     connssl->state = ssl_connection_complete;
-    connssl->connecting_state = ssl_connect_1;
 
     rc = gnutls_alpn_get_selected_protocol(backend->gtls.session, &proto);
     if(rc) {  /* No ALPN from server */
@@ -1964,82 +1894,43 @@ gtls_connect_common(struct Curl_cfilter *cf,
     if(result)
       goto out;
 
-    if(connssl->earlydata_state == ssl_earlydata_sent) {
-      /* report the true time the handshake was done */
-      connssl->handshake_done = Curl_now();
-      Curl_pgrsTimeWas(data, TIMER_APPCONNECT, connssl->handshake_done);
-      if(gnutls_session_get_flags(backend->gtls.session) &
-         GNUTLS_SFLAGS_EARLY_DATA) {
-        connssl->earlydata_state = ssl_earlydata_accepted;
-        infof(data, "Server accepted %zu bytes of TLS early data.",
-              connssl->earlydata_skip);
-      }
-      else {
-        connssl->earlydata_state = ssl_earlydata_rejected;
-        if(!Curl_ssl_cf_is_proxy(cf))
-          Curl_pgrsEarlyData(data, -(curl_off_t)connssl->earlydata_skip);
-        infof(data, "Server rejected TLS early data.");
-        connssl->earlydata_skip = 0;
-      }
+    if(connssl->earlydata_state > ssl_earlydata_none) {
+      /* We should be in this state by now */
+      DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_sent);
+      connssl->earlydata_state =
+        (gnutls_session_get_flags(backend->gtls.session) &
+         GNUTLS_SFLAGS_EARLY_DATA) ?
+        ssl_earlydata_accepted : ssl_earlydata_rejected;
     }
+    connssl->connecting_state = ssl_connect_done;
   }
 
+  if(connssl->connecting_state == ssl_connect_done)
+    DEBUGASSERT(connssl->state == ssl_connection_complete);
+
 out:
   if(result == CURLE_AGAIN) {
     *done = FALSE;
     return CURLE_OK;
   }
-  *done = ((connssl->connecting_state == ssl_connect_1) ||
+  *done = ((connssl->state == ssl_connection_complete) ||
            (connssl->state == ssl_connection_deferred));
+  CURL_TRC_CF(data, cf, "gtls_connect_common() -> %d, done=%d", result, *done);
   return result;
 }
 
-static CURLcode gtls_connect_nonblocking(struct Curl_cfilter *cf,
-                                         struct Curl_easy *data,
-                                         bool *done)
+static CURLcode gtls_connect(struct Curl_cfilter *cf,
+                             struct Curl_easy *data,
+                             bool *done)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  if(connssl->state == ssl_connection_deferred) {
+  if((connssl->state == ssl_connection_deferred) &&
+     (connssl->earlydata_state == ssl_earlydata_await)) {
     /* We refuse to be pushed, we are waiting for someone to send/recv. */
     *done = TRUE;
     return CURLE_OK;
   }
-  return gtls_connect_common(cf, data, TRUE, done);
-}
-
-static CURLcode gtls_connect(struct Curl_cfilter *cf,
-                             struct Curl_easy *data)
-{
-  CURLcode result;
-  bool done = FALSE;
-
-  result = gtls_connect_common(cf, data, FALSE, &done);
-  if(result)
-    return result;
-
-  DEBUGASSERT(done);
-
-  return CURLE_OK;
-}
-
-static CURLcode gtls_connect_deferred(struct Curl_cfilter *cf,
-                                      struct Curl_easy *data,
-                                      const void *buf,
-                                      size_t blen,
-                                      bool *done)
-{
-  struct ssl_connect_data *connssl = cf->ctx;
-  CURLcode result = CURLE_OK;
-
-  DEBUGASSERT(connssl->state == ssl_connection_deferred);
-  *done = FALSE;
-  if(connssl->earlydata_state == ssl_earlydata_use) {
-    result = gtls_set_earlydata(cf, data, buf, blen);
-    if(result)
-      return result;
-  }
-
-  return gtls_connect_common(cf, data, TRUE, done);
+  return gtls_connect_common(cf, data, done);
 }
 
 static bool gtls_data_pending(struct Curl_cfilter *cf,
@@ -2069,38 +1960,9 @@ static ssize_t gtls_send(struct Curl_cfilter *cf,
   ssize_t rc;
   size_t nwritten, total_written = 0;
 
+  (void)data;
   DEBUGASSERT(backend);
 
-  if(connssl->state == ssl_connection_deferred) {
-    bool done = FALSE;
-    *curlcode = gtls_connect_deferred(cf, data, buf, blen, &done);
-    if(*curlcode) {
-      rc = -1;
-      goto out;
-    }
-    else if(!done) {
-      *curlcode = CURLE_AGAIN;
-      rc = -1;
-      goto out;
-    }
-    DEBUGASSERT(connssl->state == ssl_connection_complete);
-  }
-
-  if(connssl->earlydata_skip) {
-    if(connssl->earlydata_skip >= blen) {
-      connssl->earlydata_skip -= blen;
-      *curlcode = CURLE_OK;
-      rc = (ssize_t)blen;
-      goto out;
-    }
-    else {
-      total_written += connssl->earlydata_skip;
-      buf = ((const char *)buf) + connssl->earlydata_skip;
-      blen -= connssl->earlydata_skip;
-      connssl->earlydata_skip = 0;
-    }
-  }
-
   while(blen) {
     backend->gtls.io_result = CURLE_OK;
     rc = gnutls_record_send(backend->gtls.session, buf, blen);
@@ -2121,7 +1983,7 @@ static ssize_t gtls_send(struct Curl_cfilter *cf,
     nwritten = (size_t)rc;
     total_written += nwritten;
     DEBUGASSERT(nwritten <= blen);
-    buf = (char *)buf + nwritten;
+    buf = (char *)CURL_UNCONST(buf) + nwritten;
     blen -= nwritten;
   }
   rc = total_written;
@@ -2247,21 +2109,6 @@ static ssize_t gtls_recv(struct Curl_cfilter *cf,
   (void)data;
   DEBUGASSERT(backend);
 
-  if(connssl->state == ssl_connection_deferred) {
-    bool done = FALSE;
-    *curlcode = gtls_connect_deferred(cf, data, NULL, 0, &done);
-    if(*curlcode) {
-      ret = -1;
-      goto out;
-    }
-    else if(!done) {
-      *curlcode = CURLE_AGAIN;
-      ret = -1;
-      goto out;
-    }
-    DEBUGASSERT(connssl->state == ssl_connection_complete);
-  }
-
   ret = gnutls_record_recv(backend->gtls.session, buf, buffersize);
   if((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED)) {
     *curlcode = CURLE_AGAIN;
@@ -2272,9 +2119,8 @@ static ssize_t gtls_recv(struct Curl_cfilter *cf,
   if(ret == GNUTLS_E_REHANDSHAKE) {
     /* BLOCKING call, this is bad but a work-around for now. Fixing this "the
        proper way" takes a whole lot of work. */
-    CURLcode result = handshake(cf, data, FALSE, FALSE);
+    CURLcode result = handshake(cf, data);
     if(result)
-      /* handshake() writes error message on its own */
       *curlcode = result;
     else
       *curlcode = CURLE_AGAIN; /* then return as if this was a wouldblock */
@@ -2344,6 +2190,7 @@ const struct Curl_ssl Curl_ssl_gnutls = {
   SSLSUPP_CERTINFO |
   SSLSUPP_PINNEDPUBKEY |
   SSLSUPP_HTTPS_PROXY |
+  SSLSUPP_CIPHER_LIST |
   SSLSUPP_CA_CACHE,
 
   sizeof(struct gtls_ssl_backend_data),
@@ -2356,7 +2203,6 @@ const struct Curl_ssl Curl_ssl_gnutls = {
   gtls_random,                   /* random */
   gtls_cert_status_request,      /* cert_status_request */
   gtls_connect,                  /* connect */
-  gtls_connect_nonblocking,      /* connect_nonblocking */
   Curl_ssl_adjust_pollset,       /* adjust_pollset */
   gtls_get_internals,            /* get_internals */
   gtls_close,                    /* close_one */
diff --git a/extra/curl/curl-8.12.1/lib/vtls/gtls.h b/extra/curl/curl-8.14.1/lib/vtls/gtls.h
similarity index 95%
rename from extra/curl/curl-8.12.1/lib/vtls/gtls.h
rename to extra/curl/curl-8.14.1/lib/vtls/gtls.h
index a17dcd7adbbf..35af9db13981 100644
--- a/extra/curl/curl-8.12.1/lib/vtls/gtls.h
+++ b/extra/curl/curl-8.14.1/lib/vtls/gtls.h
@@ -24,13 +24,13 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 #include <curl/curl.h>
 
 #ifdef USE_GNUTLS
 
 #include <gnutls/gnutls.h>
-#include "timeval.h"
+#include "../curlx/timeval.h"
 
 #ifdef HAVE_GNUTLS_SRP
 /* the function exists */
@@ -42,6 +42,7 @@
 
 struct Curl_easy;
 struct Curl_cfilter;
+struct alpn_spec;
 struct ssl_primary_config;
 struct ssl_config_data;
 struct ssl_peer;
@@ -81,6 +82,7 @@ typedef CURLcode Curl_gtls_ctx_setup_cb(struct Curl_cfilter *cf,
 
 typedef CURLcode Curl_gtls_init_session_reuse_cb(struct Curl_cfilter *cf,
                                                  struct Curl_easy *data,
+                                                 struct alpn_spec *alpns,
                                                  struct Curl_ssl_session *scs,
                                                  bool *do_early_data);
 
@@ -88,7 +90,7 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx,
                             struct Curl_cfilter *cf,
                             struct Curl_easy *data,
                             struct ssl_peer *peer,
-                            const unsigned char *alpn, size_t alpn_len,
+                            const struct alpn_spec *alpns,
                             Curl_gtls_ctx_setup_cb *cb_setup,
                             void *cb_user_data,
                             void *ssl_user_data,
diff --git a/extra/curl/curl-8.12.1/lib/vtls/hostcheck.c b/extra/curl/curl-8.14.1/lib/vtls/hostcheck.c
similarity index 96%
rename from extra/curl/curl-8.12.1/lib/vtls/hostcheck.c
rename to extra/curl/curl-8.14.1/lib/vtls/hostcheck.c
index e46439a5ecc7..8ca69941e824 100644
--- a/extra/curl/curl-8.12.1/lib/vtls/hostcheck.c
+++ b/extra/curl/curl-8.14.1/lib/vtls/hostcheck.c
@@ -22,7 +22,7 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #if defined(USE_OPENSSL)                        \
   || defined(USE_SCHANNEL)
@@ -34,15 +34,14 @@
 #ifdef HAVE_NETINET_IN6_H
 #include <netinet/in6.h>
 #endif
-#include "curl_memrchr.h"
-
+#include "../curl_memrchr.h"
 #include "hostcheck.h"
-#include "strcase.h"
-#include "hostip.h"
+#include "../strcase.h"
+#include "../hostip.h"
 
-#include "curl_memory.h"
+#include "../curl_memory.h"
 /* The last #include file should be: */
-#include "memdebug.h"
+#include "../memdebug.h"
 
 /* check the two input strings with given length, but do not
    assume they end in nul-bytes */
diff --git a/extra/curl/curl-8.12.1/lib/vtls/hostcheck.h b/extra/curl/curl-8.14.1/lib/vtls/hostcheck.h
similarity index 100%
rename from extra/curl/curl-8.12.1/lib/vtls/hostcheck.h
rename to extra/curl/curl-8.14.1/lib/vtls/hostcheck.h
diff --git a/extra/curl/curl-8.12.1/lib/vtls/keylog.c b/extra/curl/curl-8.14.1/lib/vtls/keylog.c
similarity index 80%
rename from extra/curl/curl-8.12.1/lib/vtls/keylog.c
rename to extra/curl/curl-8.14.1/lib/vtls/keylog.c
index ca86c1560823..8487d43e8c6b 100644
--- a/extra/curl/curl-8.12.1/lib/vtls/keylog.c
+++ b/extra/curl/curl-8.14.1/lib/vtls/keylog.c
@@ -21,32 +21,22 @@
  * SPDX-License-Identifier: curl
  *
  ***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #if defined(USE_OPENSSL) || \
   defined(USE_GNUTLS) || \
   defined(USE_WOLFSSL) || \
   (defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || \
-  defined(USE_QUICHE)
+  defined(USE_QUICHE) || \
+  defined(USE_RUSTLS)
 
 #include "keylog.h"
 #include <curl/curl.h>
+#include "../escape.h"
 
 /* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
-
-#define KEYLOG_LABEL_MAXLEN (sizeof("CLIENT_HANDSHAKE_TRAFFIC_SECRET") - 1)
-
-#define CLIENT_RANDOM_SIZE  32
-
-/*
- * The master secret in TLS 1.2 and before is always 48 bytes. In TLS 1.3, the
- * secret size depends on the cipher suite's hash function which is 32 bytes
- * for SHA-256 and 48 bytes for SHA-384.
- */
-#define SECRET_MAXLEN       48
-
+#include "../curl_memory.h"
+#include "../memdebug.h"
 
 /* The fp for the open SSLKEYLOGFILE, or NULL if not open */
 static FILE *keylog_file_fp;
@@ -104,7 +94,7 @@ Curl_tls_keylog_write_line(const char *line)
 
   linelen = strlen(line);
   if(linelen == 0 || linelen > sizeof(buf) - 2) {
-    /* Empty line or too big to fit in a LF and NUL. */
+    /* Empty line or too big to fit in an LF and NUL. */
     return FALSE;
   }
 
@@ -125,10 +115,9 @@ Curl_tls_keylog_write(const char *label,
                       const unsigned char client_random[CLIENT_RANDOM_SIZE],
                       const unsigned char *secret, size_t secretlen)
 {
-  const char *hex = "0123456789ABCDEF";
   size_t pos, i;
-  char line[KEYLOG_LABEL_MAXLEN + 1 + 2 * CLIENT_RANDOM_SIZE + 1 +
-            2 * SECRET_MAXLEN + 1 + 1];
+  unsigned char line[KEYLOG_LABEL_MAXLEN + 1 + 2 * CLIENT_RANDOM_SIZE + 1 +
+                     2 * SECRET_MAXLEN + 1 + 1];
 
   if(!keylog_file_fp) {
     return FALSE;
@@ -145,22 +134,22 @@ Curl_tls_keylog_write(const char *label,
 
   /* Client Random */
   for(i = 0; i < CLIENT_RANDOM_SIZE; i++) {
-    line[pos++] = hex[client_random[i] >> 4];
-    line[pos++] = hex[client_random[i] & 0xF];
+    Curl_hexbyte(&line[pos], client_random[i], FALSE);
+    pos += 2;
   }
   line[pos++] = ' ';
 
   /* Secret */
   for(i = 0; i < secretlen; i++) {
-    line[pos++] = hex[secret[i] >> 4];
-    line[pos++] = hex[secret[i] & 0xF];
+    Curl_hexbyte(&line[pos], secret[i], FALSE);
+    pos += 2;
   }
   line[pos++] = '\n';
   line[pos] = '\0';
 
   /* Using fputs here instead of fprintf since libcurl's fprintf replacement
      may not be thread-safe. */
-  fputs(line, keylog_file_fp);
+  fputs((char *)line, keylog_file_fp);
   return TRUE;
 }
 
diff --git a/extra/curl/curl-8.12.1/lib/vtls/keylog.h b/extra/curl/curl-8.14.1/lib/vtls/keylog.h
similarity index 84%
rename from extra/curl/curl-8.12.1/lib/vtls/keylog.h
rename to extra/curl/curl-8.14.1/lib/vtls/keylog.h
index eff5bf38f351..ec82abf547f0 100644
--- a/extra/curl/curl-8.12.1/lib/vtls/keylog.h
+++ b/extra/curl/curl-8.14.1/lib/vtls/keylog.h
@@ -23,7 +23,18 @@
  * SPDX-License-Identifier: curl
  *
  ***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
+
+#define KEYLOG_LABEL_MAXLEN (sizeof("CLIENT_HANDSHAKE_TRAFFIC_SECRET") - 1)
+
+#define CLIENT_RANDOM_SIZE  32
+
+/*
+ * The master secret in TLS 1.2 and before is always 48 bytes. In TLS 1.3, the
+ * secret size depends on the cipher suite's hash function which is 32 bytes
+ * for SHA-256 and 48 bytes for SHA-384.
+ */
+#define SECRET_MAXLEN       48
 
 /*
  * Opens the TLS key log file if requested by the user. The SSLKEYLOGFILE
@@ -50,7 +61,7 @@ bool Curl_tls_keylog_write(const char *label,
                            const unsigned char *secret, size_t secretlen);
 
 /*
- * Appends a line to the key log file, ensure it is terminated by a LF.
+ * Appends a line to the key log file, ensure it is terminated by an LF.
  * Returns true iff the key log file is open and a valid line was provided.
  */
 bool Curl_tls_keylog_write_line(const char *line);
diff --git a/extra/curl/curl-8.12.1/lib/vtls/mbedtls.c b/extra/curl/curl-8.14.1/lib/vtls/mbedtls.c
similarity index 92%
rename from extra/curl/curl-8.12.1/lib/vtls/mbedtls.c
rename to extra/curl/curl-8.14.1/lib/vtls/mbedtls.c
index 13e44c7c0188..7af207caa77b 100644
--- a/extra/curl/curl-8.12.1/lib/vtls/mbedtls.c
+++ b/extra/curl/curl-8.14.1/lib/vtls/mbedtls.c
@@ -29,7 +29,7 @@
  *
  */
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #ifdef USE_MBEDTLS
 
@@ -57,26 +57,26 @@
 #endif /* MBEDTLS_VERSION_MAJOR >= 2 */
 
 #include "cipher_suite.h"
-#include "strcase.h"
-#include "urldata.h"
-#include "sendf.h"
-#include "inet_pton.h"
+#include "../strcase.h"
+#include "../urldata.h"
+#include "../sendf.h"
+#include "../curlx/inet_pton.h"
 #include "mbedtls.h"
 #include "vtls.h"
 #include "vtls_int.h"
 #include "vtls_scache.h"
 #include "x509asn1.h"
-#include "parsedate.h"
-#include "connect.h" /* for the connect timeout */
-#include "select.h"
-#include "multiif.h"
+#include "../parsedate.h"
+#include "../connect.h" /* for the connect timeout */
+#include "../select.h"
+#include "../multiif.h"
 #include "mbedtls_threadlock.h"
-#include "strdup.h"
+#include "../strdup.h"
 
 /* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_printf.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
 
 /* ALPN for http2 */
 #if defined(USE_HTTP2) && defined(MBEDTLS_SSL_ALPN)
@@ -198,7 +198,7 @@ static int mbedtls_bio_cf_write(void *bio,
   if(!data)
     return 0;
 
-  nwritten = Curl_conn_cf_send(cf->next, data, (char *)buf, blen, FALSE,
+  nwritten = Curl_conn_cf_send(cf->next, data, (const char *)buf, blen, FALSE,
                                &result);
   CURL_TRC_CF(data, cf, "mbedtls_bio_cf_out_write(len=%zu) -> %zd, err=%d",
               blen, nwritten, result);
@@ -273,7 +273,13 @@ mbed_set_ssl_version_min_max(struct Curl_easy *data,
 #else
   /* mbedTLS 3.2.0 (2022) introduced new methods for setting TLS version */
   mbedtls_ssl_protocol_version ver_min = MBEDTLS_SSL_VERSION_TLS1_2;
-  mbedtls_ssl_protocol_version ver_max = MBEDTLS_SSL_VERSION_TLS1_2;
+  mbedtls_ssl_protocol_version ver_max =
+#ifdef HAS_TLS13_SUPPORT
+    MBEDTLS_SSL_VERSION_TLS1_3
+#else
+    MBEDTLS_SSL_VERSION_TLS1_2
+#endif
+    ;
 #endif
 
   switch(conn_config->version) {
@@ -292,7 +298,11 @@ mbed_set_ssl_version_min_max(struct Curl_easy *data,
   case CURL_SSLVERSION_TLSv1_1:
 #endif
   case CURL_SSLVERSION_TLSv1_2:
-    /* ver_min = MBEDTLS_SSL_VERSION_TLS1_2; */
+#if MBEDTLS_VERSION_NUMBER < 0x03020000
+    ver_min = MBEDTLS_SSL_MINOR_VERSION_3; /* TLS 1.2 */
+#else
+    ver_min = MBEDTLS_SSL_VERSION_TLS1_2;
+#endif
     break;
   case CURL_SSLVERSION_TLSv1_3:
 #ifdef HAS_TLS13_SUPPORT
@@ -314,7 +324,11 @@ mbed_set_ssl_version_min_max(struct Curl_easy *data,
     break;
 #endif
   case CURL_SSLVERSION_MAX_TLSv1_2:
-    /* ver_max = MBEDTLS_SSL_VERSION_TLS1_2; */
+#if MBEDTLS_VERSION_NUMBER < 0x03020000
+    ver_max = MBEDTLS_SSL_MINOR_VERSION_3; /* TLS 1.2 */
+#else
+    ver_max = MBEDTLS_SSL_VERSION_TLS1_2;
+#endif
     break;
 #if MBEDTLS_VERSION_NUMBER < 0x03000000
   case CURL_SSLVERSION_MAX_TLSv1_1:
@@ -726,6 +740,9 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
       ret = mbedtls_pk_parse_keyfile(&backend->pk, ssl_config->key,
                                      ssl_config->key_passwd);
 #endif
+      if(ret == 0 && !(mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_RSA) ||
+                       mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_ECKEY)))
+        ret = MBEDTLS_ERR_PK_TYPE_MISMATCH;
 
       if(ret) {
         mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
@@ -754,6 +771,9 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
                                  (const unsigned char *)passwd,
                                  passwd ? strlen(passwd) : 0);
 #endif
+      if(ret == 0 && !(mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_RSA) ||
+                       mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_ECKEY)))
+        ret = MBEDTLS_ERR_PK_TYPE_MISMATCH;
 
       if(ret) {
         mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
@@ -762,10 +782,6 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
         return CURLE_SSL_CERTPROBLEM;
       }
     }
-
-    if(ret == 0 && !(mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_RSA) ||
-                     mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_ECKEY)))
-      ret = MBEDTLS_ERR_PK_TYPE_MISMATCH;
   }
 
   /* Load the CRL */
@@ -1211,7 +1227,7 @@ static ssize_t mbed_send(struct Curl_cfilter *cf, struct Curl_easy *data,
     len = backend->send_blocked_len;
   }
 
-  ret = mbedtls_ssl_write(&backend->ssl, (unsigned char *)mem, len);
+  ret = mbedtls_ssl_write(&backend->ssl, (const unsigned char *)mem, len);
 
   if(ret < 0) {
     CURL_TRC_CF(data, cf, "mbedtls_ssl_write(len=%zu) -> -0x%04X",
@@ -1440,16 +1456,12 @@ static CURLcode mbedtls_random(struct Curl_easy *data,
 #endif
 }
 
-static CURLcode
-mbed_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data,
-                    bool nonblocking,
-                    bool *done)
+static CURLcode mbedtls_connect(struct Curl_cfilter *cf,
+                                struct Curl_easy *data,
+                                bool *done)
 {
   CURLcode retcode;
   struct ssl_connect_data *connssl = cf->ctx;
-  curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
-  timediff_t timeout_ms;
-  int what;
 
   /* check if the connection has already been established */
   if(ssl_connection_complete == connssl->state) {
@@ -1457,73 +1469,20 @@ mbed_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data,
     return CURLE_OK;
   }
 
-  if(ssl_connect_1 == connssl->connecting_state) {
-    /* Find out how much more time we are allowed */
-    timeout_ms = Curl_timeleft(data, NULL, TRUE);
+  *done = FALSE;
+  connssl->io_need = CURL_SSL_IO_NEED_NONE;
 
-    if(timeout_ms < 0) {
-      /* no need to continue if time already is up */
-      failf(data, "SSL connection timeout");
-      return CURLE_OPERATION_TIMEDOUT;
-    }
+  if(ssl_connect_1 == connssl->connecting_state) {
     retcode = mbed_connect_step1(cf, data);
     if(retcode)
       return retcode;
   }
 
-  while(ssl_connect_2 == connssl->connecting_state) {
-
-    /* check allowed time left */
-    timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
-    if(timeout_ms < 0) {
-      /* no need to continue if time already is up */
-      failf(data, "SSL connection timeout");
-      return CURLE_OPERATION_TIMEDOUT;
-    }
-
-    /* if ssl is expecting something, check if it is available. */
-    if(connssl->io_need) {
-      curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND) ?
-        sockfd : CURL_SOCKET_BAD;
-      curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV) ?
-        sockfd : CURL_SOCKET_BAD;
-
-      what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
-                               nonblocking ? 0 : timeout_ms);
-      if(what < 0) {
-        /* fatal error */
-        failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
-        return CURLE_SSL_CONNECT_ERROR;
-      }
-      else if(0 == what) {
-        if(nonblocking) {
-          *done = FALSE;
-          return CURLE_OK;
-        }
-        else {
-          /* timeout */
-          failf(data, "SSL connection timeout");
-          return CURLE_OPERATION_TIMEDOUT;
-        }
-      }
-      /* socket is readable or writable */
-    }
-
-    /* Run transaction, and return to the caller if it failed or if
-     * this connection is part of a multi handle and this loop would
-     * execute again. This permits the owner of a multi handle to
-     * abort a connection attempt before step2 has completed while
-     * ensuring that a client using select() or epoll() will always
-     * have a valid fdset to wait on.
-     */
-    connssl->io_need = CURL_SSL_IO_NEED_NONE;
+  if(ssl_connect_2 == connssl->connecting_state) {
     retcode = mbed_connect_step2(cf, data);
-    if(retcode ||
-       (nonblocking && (ssl_connect_2 == connssl->connecting_state)))
+    if(retcode)
       return retcode;
-
-  } /* repeat step2 until all transactions are done. */
+  }
 
   if(ssl_connect_3 == connssl->connecting_state) {
     /* For tls1.3 we get notified about new sessions */
@@ -1548,34 +1507,6 @@ mbed_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data,
     connssl->state = ssl_connection_complete;
     *done = TRUE;
   }
-  else
-    *done = FALSE;
-
-  /* Reset our connect state machine */
-  connssl->connecting_state = ssl_connect_1;
-
-  return CURLE_OK;
-}
-
-static CURLcode mbedtls_connect_nonblocking(struct Curl_cfilter *cf,
-                                            struct Curl_easy *data,
-                                            bool *done)
-{
-  return mbed_connect_common(cf, data, TRUE, done);
-}
-
-
-static CURLcode mbedtls_connect(struct Curl_cfilter *cf,
-                                struct Curl_easy *data)
-{
-  CURLcode retcode;
-  bool done = FALSE;
-
-  retcode = mbed_connect_common(cf, data, FALSE, &done);
-  if(retcode)
-    return retcode;
-
-  DEBUGASSERT(done);
 
   return CURLE_OK;
 }
@@ -1682,7 +1613,6 @@ const struct Curl_ssl Curl_ssl_mbedtls = {
   mbedtls_random,                   /* random */
   NULL,                             /* cert_status_request */
   mbedtls_connect,                  /* connect */
-  mbedtls_connect_nonblocking,      /* connect_nonblocking */
   Curl_ssl_adjust_pollset,          /* adjust_pollset */
   mbedtls_get_internals,            /* get_internals */
   mbedtls_close,                    /* close_one */
diff --git a/extra/curl/curl-8.12.1/lib/vtls/mbedtls.h b/extra/curl/curl-8.14.1/lib/vtls/mbedtls.h
similarity index 97%
rename from extra/curl/curl-8.12.1/lib/vtls/mbedtls.h
rename to extra/curl/curl-8.14.1/lib/vtls/mbedtls.h
index d8a0a06eb6aa..3876fe36fc5d 100644
--- a/extra/curl/curl-8.12.1/lib/vtls/mbedtls.h
+++ b/extra/curl/curl-8.14.1/lib/vtls/mbedtls.h
@@ -24,7 +24,7 @@
  * SPDX-License-Identifier: curl
  *
  ***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #ifdef USE_MBEDTLS
 
diff --git a/extra/curl/curl-8.12.1/lib/vtls/mbedtls_threadlock.c b/extra/curl/curl-8.14.1/lib/vtls/mbedtls_threadlock.c
similarity index 97%
rename from extra/curl/curl-8.12.1/lib/vtls/mbedtls_threadlock.c
rename to extra/curl/curl-8.14.1/lib/vtls/mbedtls_threadlock.c
index b96a904fcb1b..682c221852c8 100644
--- a/extra/curl/curl-8.12.1/lib/vtls/mbedtls_threadlock.c
+++ b/extra/curl/curl-8.14.1/lib/vtls/mbedtls_threadlock.c
@@ -22,7 +22,7 @@
  * SPDX-License-Identifier: curl
  *
  ***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #if defined(USE_MBEDTLS) &&                                     \
   ((defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)) ||   \
@@ -36,10 +36,10 @@
 #endif
 
 #include "mbedtls_threadlock.h"
-#include "curl_printf.h"
-#include "curl_memory.h"
+#include "../curl_printf.h"
+#include "../curl_memory.h"
 /* The last #include file should be: */
-#include "memdebug.h"
+#include "../memdebug.h"
 
 /* number of thread locks */
 #define NUMT                    2
diff --git a/extra/curl/curl-8.12.1/lib/vtls/mbedtls_threadlock.h b/extra/curl/curl-8.14.1/lib/vtls/mbedtls_threadlock.h
similarity index 98%
rename from extra/curl/curl-8.12.1/lib/vtls/mbedtls_threadlock.h
rename to extra/curl/curl-8.14.1/lib/vtls/mbedtls_threadlock.h
index 484626852f55..9402af6e415a 100644
--- a/extra/curl/curl-8.12.1/lib/vtls/mbedtls_threadlock.h
+++ b/extra/curl/curl-8.14.1/lib/vtls/mbedtls_threadlock.h
@@ -24,7 +24,7 @@
  * SPDX-License-Identifier: curl
  *
  ***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #ifdef USE_MBEDTLS
 
diff --git a/extra/curl/curl-8.12.1/lib/vtls/openssl.c b/extra/curl/curl-8.14.1/lib/vtls/openssl.c
similarity index 85%
rename from extra/curl/curl-8.12.1/lib/vtls/openssl.c
rename to extra/curl/curl-8.14.1/lib/vtls/openssl.c
index 6ded5f3c0791..0ce09ca06b75 100644
--- a/extra/curl/curl-8.12.1/lib/vtls/openssl.c
+++ b/extra/curl/curl-8.14.1/lib/vtls/openssl.c
@@ -27,14 +27,14 @@
  * but vtls.c should ever call or use these functions.
  */
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #if defined(USE_QUICHE) || defined(USE_OPENSSL)
 
 #include <limits.h>
 
 /* Wincrypt must be included before anything that could include OpenSSL. */
-#if defined(USE_WIN32_CRYPTO)
+#ifdef USE_WIN32_CRYPTO
 #include <wincrypt.h>
 /* Undefine wincrypt conflicting symbols for BoringSSL. */
 #undef X509_NAME
@@ -45,25 +45,27 @@
 #undef OCSP_RESPONSE
 #endif
 
-#include "urldata.h"
-#include "sendf.h"
-#include "formdata.h" /* for the boundary function */
-#include "url.h" /* for the ssl config check function */
-#include "inet_pton.h"
+#include "../urldata.h"
+#include "../sendf.h"
+#include "../formdata.h" /* for the boundary function */
+#include "../url.h" /* for the ssl config check function */
+#include "../curlx/inet_pton.h"
 #include "openssl.h"
-#include "connect.h"
-#include "slist.h"
-#include "select.h"
+#include "../connect.h"
+#include "../slist.h"
+#include "../select.h"
 #include "vtls.h"
 #include "vtls_int.h"
 #include "vtls_scache.h"
-#include "vauth/vauth.h"
+#include "../vauth/vauth.h"
 #include "keylog.h"
-#include "strcase.h"
+#include "../strcase.h"
 #include "hostcheck.h"
-#include "multiif.h"
-#include "strerror.h"
-#include "curl_printf.h"
+#include "../multiif.h"
+#include "../curlx/strparse.h"
+#include "../strdup.h"
+#include "../strerror.h"
+#include "../curl_printf.h"
 
 #include <openssl/ssl.h>
 #include <openssl/rand.h>
@@ -83,7 +85,7 @@
 #include <openssl/tls1.h>
 #include <openssl/evp.h>
 
-#if defined(HAVE_SSL_SET1_ECH_CONFIG_LIST)
+#ifdef HAVE_SSL_SET1_ECH_CONFIG_LIST
 #define USE_ECH_OPENSSL
 #endif
 
@@ -93,28 +95,38 @@
 # endif
 #endif /* USE_ECH_OPENSSL */
 
-#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_OCSP)
+#ifndef OPENSSL_NO_OCSP
 #include <openssl/ocsp.h>
 #endif
 
-#if (OPENSSL_VERSION_NUMBER >= 0x0090700fL) && /* 0.9.7 or later */     \
-  !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_UI_CONSOLE)
+#if !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_UI_CONSOLE)
 #define USE_OPENSSL_ENGINE
 #include <openssl/engine.h>
 #endif
 
-#if OPENSSL_VERSION_NUMBER >= 0x03000000fL && !defined(OPENSSL_NO_UI_CONSOLE)
+#ifdef LIBRESSL_VERSION_NUMBER
+# /* As of LibreSSL 2.0.0-4.0.0: OPENSSL_VERSION_NUMBER == 0x20000000L */
+# if LIBRESSL_VERSION_NUMBER < 0x2090100fL /* 2019-04-13 */
+#  error "LibreSSL 2.9.1 or later required"
+# endif
+#elif OPENSSL_VERSION_NUMBER < 0x1000201fL /* 2015-03-19 */
+# error "OpenSSL 1.0.2a or later required"
+#endif
+
+#if OPENSSL_VERSION_NUMBER >= 0x3000000fL && !defined(OPENSSL_NO_UI_CONSOLE)
 #include <openssl/provider.h>
 #include <openssl/store.h>
 /* this is used in the following conditions to make them easier to read */
 #define OPENSSL_HAS_PROVIDERS
+
+static void ossl_provider_cleanup(struct Curl_easy *data);
 #endif
 
-#include "warnless.h"
+#include "../curlx/warnless.h"
 
 /* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
 
 /* Uncomment the ALLOW_RENEG line to a real #define if you want to allow TLS
    renegotiations when built with BoringSSL. Renegotiating is non-compliant
@@ -131,49 +143,24 @@
 #include <openssl/ui.h>
 #endif
 
-#if OPENSSL_VERSION_NUMBER >= 0x00909000L
-#define SSL_METHOD_QUAL const
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+#define OSSL_UI_METHOD_CAST(x) (x)
 #else
-#define SSL_METHOD_QUAL
-#endif
-
-#if (OPENSSL_VERSION_NUMBER >= 0x10000000L)
-#define HAVE_ERR_REMOVE_THREAD_STATE 1
+#define OSSL_UI_METHOD_CAST(x) CURL_UNCONST(x)
 #endif
 
-#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && /* OpenSSL 1.1.0+ */ \
-    !(defined(LIBRESSL_VERSION_NUMBER) && \
-      LIBRESSL_VERSION_NUMBER < 0x20700000L)
-#define SSLEAY_VERSION_NUMBER OPENSSL_VERSION_NUMBER
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L /* OpenSSL 1.1.0+ and LibreSSL */
 #define HAVE_X509_GET0_EXTENSIONS 1 /* added in 1.1.0 -pre1 */
 #define HAVE_OPAQUE_EVP_PKEY 1 /* since 1.1.0 -pre3 */
 #define HAVE_OPAQUE_RSA_DSA_DH 1 /* since 1.1.0 -pre5 */
-#define CONST_EXTS const
 #define HAVE_ERR_REMOVE_THREAD_STATE_DEPRECATED 1
-
-/* funny typecast define due to difference in API */
-#ifdef LIBRESSL_VERSION_NUMBER
-#define ARG2_X509_signature_print (X509_ALGOR *)
-#else
-#define ARG2_X509_signature_print
-#endif
-
 #else
 /* For OpenSSL before 1.1.0 */
 #define ASN1_STRING_get0_data(x) ASN1_STRING_data(x)
 #define X509_get0_notBefore(x) X509_get_notBefore(x)
 #define X509_get0_notAfter(x) X509_get_notAfter(x)
-#define CONST_EXTS /* nope */
-#ifndef LIBRESSL_VERSION_NUMBER
 #define OpenSSL_version_num() SSLeay()
 #endif
-#endif
-
-#if (OPENSSL_VERSION_NUMBER >= 0x1000200fL) && /* 1.0.2 or later */ \
-    !(defined(LIBRESSL_VERSION_NUMBER) && \
-      LIBRESSL_VERSION_NUMBER < 0x20700000L)
-#define HAVE_X509_GET0_SIGNATURE 1
-#endif
 
 #if OPENSSL_VERSION_NUMBER >= 0x10002003L && \
   OPENSSL_VERSION_NUMBER <= 0x10002FFFL && \
@@ -181,11 +168,6 @@
 #define HAVE_SSL_COMP_FREE_COMPRESSION_METHODS 1
 #endif
 
-#if (OPENSSL_VERSION_NUMBER < 0x0090808fL)
-/* not present in older OpenSSL */
-#define OPENSSL_load_builtin_modules(x)
-#endif
-
 #if (OPENSSL_VERSION_NUMBER >= 0x30000000L)
 #define HAVE_EVP_PKEY_GET_PARAMS 1
 #endif
@@ -210,35 +192,32 @@
       LIBRESSL_VERSION_NUMBER >= 0x3040100fL)) && \
     !defined(OPENSSL_IS_BORINGSSL)
   #define HAVE_SSL_CTX_SET_CIPHERSUITES
-  #if !defined(OPENSSL_IS_AWSLC)
+  #ifndef OPENSSL_IS_AWSLC
     #define HAVE_SSL_CTX_SET_POST_HANDSHAKE_AUTH
   #endif
 #endif
 
-/*
- * Whether SSL_CTX_set1_curves_list is available.
- * OpenSSL: supported since 1.0.2, see
- *   https://docs.openssl.org/master/man3/SSL_CTX_set1_curves/
- * BoringSSL: supported since 5fd1807d95f7 (committed 2016-09-30)
- * LibreSSL: since 2.5.3 (April 12, 2017)
+/* Whether SSL_CTX_set1_sigalgs_list is available
+ * OpenSSL: supported since 1.0.2 (commit 0b362de5f575)
+ * BoringSSL: supported since 0.20240913.0 (commit 826ce15)
+ * LibreSSL: no
  */
-#if (OPENSSL_VERSION_NUMBER >= 0x10002000L) ||  \
-  defined(OPENSSL_IS_BORINGSSL)
-#define HAVE_SSL_CTX_SET_EC_CURVES
+#if (OPENSSL_VERSION_NUMBER >= 0x10002000L && \
+      !defined(LIBRESSL_VERSION_NUMBER))
+  #define HAVE_SSL_CTX_SET1_SIGALGS
 #endif
 
-#if defined(LIBRESSL_VERSION_NUMBER)
+#ifdef LIBRESSL_VERSION_NUMBER
 #define OSSL_PACKAGE "LibreSSL"
 #elif defined(OPENSSL_IS_BORINGSSL)
 #define OSSL_PACKAGE "BoringSSL"
 #elif defined(OPENSSL_IS_AWSLC)
 #define OSSL_PACKAGE "AWS-LC"
+#elif (defined(USE_NGTCP2) && defined(USE_NGHTTP3) && \
+       !defined(OPENSSL_QUIC_API2)) || defined(USE_MSH3)
+#define OSSL_PACKAGE "quictls"
 #else
-# if (defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || defined(USE_MSH3)
-#   define OSSL_PACKAGE "quictls"
-# else
-#   define OSSL_PACKAGE "OpenSSL"
-#endif
+#define OSSL_PACKAGE "OpenSSL"
 #endif
 
 #if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
@@ -272,8 +251,6 @@ typedef int numcert_t;
 #endif
 
 #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && \
-    !(defined(LIBRESSL_VERSION_NUMBER) && \
-      LIBRESSL_VERSION_NUMBER < 0x2070100fL) && \
     !defined(OPENSSL_IS_BORINGSSL) && \
     !defined(OPENSSL_IS_AWSLC)
 #define HAVE_OPENSSL_VERSION
@@ -294,13 +271,6 @@ typedef unsigned long sslerr_t;
 #define HAVE_SSL_X509_STORE_SHARE
 #endif
 
-/* What API version do we use? */
-#if defined(LIBRESSL_VERSION_NUMBER)
-#define USE_PRE_1_1_API (LIBRESSL_VERSION_NUMBER < 0x2070000f)
-#else /* !LIBRESSL_VERSION_NUMBER */
-#define USE_PRE_1_1_API (OPENSSL_VERSION_NUMBER < 0x10100000L)
-#endif /* !LIBRESSL_VERSION_NUMBER */
-
 static CURLcode ossl_certchain(struct Curl_easy *data, SSL *ssl);
 
 static CURLcode push_certinfo(struct Curl_easy *data,
@@ -364,10 +334,16 @@ static int asn1_object_dump(ASN1_OBJECT *a, char *buf, size_t len)
 
 static CURLcode X509V3_ext(struct Curl_easy *data,
                            int certnum,
-                           CONST_EXTS STACK_OF(X509_EXTENSION) *exts)
+                           const STACK_OF(X509_EXTENSION) *extsarg)
 {
   int i;
   CURLcode result = CURLE_OK;
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
+  !defined(LIBRESSL_VERSION_NUMBER)
+  const STACK_OF(X509_EXTENSION) *exts = extsarg;
+#else
+  STACK_OF(X509_EXTENSION) *exts = CURL_UNCONST(extsarg);
+#endif
 
   if((int)sk_X509_EXTENSION_num(exts) <= 0)
     /* no extensions, bail out */
@@ -456,7 +432,7 @@ static CURLcode ossl_certchain(struct Curl_easy *data, SSL *ssl)
     if(result)
       break;
 
-#if defined(HAVE_X509_GET0_SIGNATURE) && defined(HAVE_X509_GET0_EXTENSIONS)
+#ifdef HAVE_X509_GET0_EXTENSIONS
     {
       const X509_ALGOR *sigalg = NULL;
       X509_PUBKEY *xpubkey = NULL;
@@ -673,21 +649,19 @@ static CURLcode ossl_certchain(struct Curl_easy *data, SSL *ssl)
 
 #ifdef USE_OPENSSL
 
-#if USE_PRE_1_1_API
-#if !defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER < 0x2070000fL
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
 #define BIO_set_init(x,v)          ((x)->init=(v))
 #define BIO_get_data(x)            ((x)->ptr)
 #define BIO_set_data(x,v)          ((x)->ptr=(v))
-#endif
 #define BIO_get_shutdown(x)        ((x)->shutdown)
 #define BIO_set_shutdown(x,v)      ((x)->shutdown=(v))
-#endif /* USE_PRE_1_1_API */
+#endif /* HAVE_PRE_1_1_API */
 
 static int ossl_bio_cf_create(BIO *bio)
 {
   BIO_set_shutdown(bio, 1);
   BIO_set_init(bio, 1);
-#if USE_PRE_1_1_API
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
   bio->num = -1;
 #endif
   BIO_set_data(bio, NULL);
@@ -724,9 +698,11 @@ static long ossl_bio_cf_ctrl(BIO *bio, int cmd, long num, void *ptr)
     ret = 1;
     break;
 #ifdef BIO_CTRL_EOF
-  case BIO_CTRL_EOF:
+  case BIO_CTRL_EOF: {
     /* EOF has been reached on input? */
-    return !cf->next || !cf->next->connected;
+    struct ssl_connect_data *connssl = cf->ctx;
+    return connssl->peer_closed;
+  }
 #endif
   default:
     ret = 0;
@@ -804,7 +780,7 @@ static int ossl_bio_cf_in_read(BIO *bio, char *buf, int blen)
   return (int)nread;
 }
 
-#if USE_PRE_1_1_API
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
 
 static BIO_METHOD ossl_bio_cf_meth_1_0 = {
   BIO_TYPE_MEM,
@@ -850,14 +826,6 @@ static void ossl_bio_cf_method_free(BIO_METHOD *m)
 #endif
 
 
-/*
- * Number of bytes to read from the random number seed file. This must be
- * a finite value (because some entropy "files" like /dev/urandom have
- * an infinite length), but must be large enough to provide enough
- * entropy to properly seed OpenSSL's PRNG.
- */
-#define RAND_LOAD_LENGTH 1024
-
 #ifdef HAVE_KEYLOG_CALLBACK
 static void ossl_keylog_callback(const SSL *ssl, const char *line)
 {
@@ -881,9 +849,7 @@ ossl_log_tls12_secret(const SSL *ssl, bool *keylog_done)
   if(!session || *keylog_done)
     return;
 
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L &&    \
-  !(defined(LIBRESSL_VERSION_NUMBER) &&         \
-    LIBRESSL_VERSION_NUMBER < 0x20700000L)
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
   /* ssl->s3 is not checked in OpenSSL 1.1.0-pre6, but let's assume that
    * we have a valid SSL context if we have a non-NULL session. */
   SSL_get_client_random(ssl, client_random, SSL3_RANDOM_SIZE);
@@ -930,15 +896,15 @@ static const char *SSL_ERROR_to_str(int err)
     return "SSL_ERROR_WANT_CONNECT";
   case SSL_ERROR_WANT_ACCEPT:
     return "SSL_ERROR_WANT_ACCEPT";
-#if defined(SSL_ERROR_WANT_ASYNC)
+#ifdef SSL_ERROR_WANT_ASYNC
   case SSL_ERROR_WANT_ASYNC:
     return "SSL_ERROR_WANT_ASYNC";
 #endif
-#if defined(SSL_ERROR_WANT_ASYNC_JOB)
+#ifdef SSL_ERROR_WANT_ASYNC_JOB
   case SSL_ERROR_WANT_ASYNC_JOB:
     return "SSL_ERROR_WANT_ASYNC_JOB";
 #endif
-#if defined(SSL_ERROR_WANT_EARLY)
+#ifdef SSL_ERROR_WANT_EARLY
   case SSL_ERROR_WANT_EARLY:
     return "SSL_ERROR_WANT_EARLY";
 #endif
@@ -981,14 +947,14 @@ static char *ossl_strerror(unsigned long error, char *buf, size_t size)
 }
 
 static int passwd_callback(char *buf, int num, int encrypting,
-                           void *global_passwd)
+                           void *password)
 {
   DEBUGASSERT(0 == encrypting);
 
-  if(!encrypting && num >= 0) {
-    int klen = curlx_uztosi(strlen((char *)global_passwd));
+  if(!encrypting && num >= 0 && password) {
+    int klen = curlx_uztosi(strlen((char *)password));
     if(num > klen) {
-      memcpy(buf, global_passwd, klen + 1);
+      memcpy(buf, password, klen + 1);
       return klen;
     }
   }
@@ -1028,13 +994,13 @@ static CURLcode ossl_seed(struct Curl_easy *data)
     size_t len = sizeof(randb);
     size_t i, i_max;
     for(i = 0, i_max = len / sizeof(struct curltime); i < i_max; ++i) {
-      struct curltime tv = Curl_now();
+      struct curltime tv = curlx_now();
       Curl_wait_ms(1);
       tv.tv_sec *= (time_t)i + 1;
       tv.tv_usec *= (int)i + 2;
-      tv.tv_sec ^= ((Curl_now().tv_sec + (time_t)Curl_now().tv_usec) *
+      tv.tv_sec ^= ((curlx_now().tv_sec + (time_t)curlx_now().tv_usec) *
                     (time_t)(i + 3)) << 8;
-      tv.tv_usec ^= (int) ((Curl_now().tv_sec + (time_t)Curl_now().tv_usec) *
+      tv.tv_usec ^= (int) ((curlx_now().tv_sec + (time_t)curlx_now().tv_usec) *
                            (time_t)(i + 4)) << 16;
       memcpy(&randb[i * sizeof(struct curltime)], &tv,
              sizeof(struct curltime));
@@ -1042,6 +1008,14 @@ static CURLcode ossl_seed(struct Curl_easy *data)
     RAND_add(randb, (int)len, (double)len/2);
   } while(!rand_enough());
 
+  /*
+   * Number of bytes to read from the random number seed file. This must be
+   * a finite value (because some entropy "files" like /dev/urandom have
+   * an infinite length), but must be large enough to provide enough
+   * entropy to properly seed OpenSSL's PRNG.
+   */
+#  define RAND_LOAD_LENGTH 1024
+
   {
     /* generates a default path for the random seed file */
     char fname[256];
@@ -1141,7 +1115,7 @@ static bool is_pkcs11_uri(const char *string)
 #endif
 
 static CURLcode ossl_set_engine(struct Curl_easy *data, const char *engine);
-#if !defined(USE_OPENSSL_ENGINE) && defined(OPENSSL_HAS_PROVIDERS)
+#if defined(OPENSSL_HAS_PROVIDERS)
 static CURLcode ossl_set_provider(struct Curl_easy *data,
                                   const char *provider);
 #endif
@@ -1164,7 +1138,7 @@ static int use_certificate_blob(SSL_CTX *ctx, const struct curl_blob *blob,
   else if(type == SSL_FILETYPE_PEM) {
     /* ERR_R_PEM_LIB; */
     x = PEM_read_bio_X509(in, NULL,
-                          passwd_callback, (void *)key_passwd);
+                          passwd_callback, CURL_UNCONST(key_passwd));
   }
   else {
     ret = 0;
@@ -1194,7 +1168,7 @@ static int use_privatekey_blob(SSL_CTX *ctx, const struct curl_blob *blob,
 
   if(type == SSL_FILETYPE_PEM)
     pkey = PEM_read_bio_PrivateKey(in, NULL, passwd_callback,
-                                   (void *)key_passwd);
+                                   CURL_UNCONST(key_passwd));
   else if(type == SSL_FILETYPE_ASN1)
     pkey = d2i_PrivateKey_bio(in, NULL);
   else
@@ -1214,13 +1188,8 @@ static int
 use_certificate_chain_blob(SSL_CTX *ctx, const struct curl_blob *blob,
                            const char *key_passwd)
 {
-/* SSL_CTX_add1_chain_cert introduced in OpenSSL 1.0.2 */
-#if (OPENSSL_VERSION_NUMBER >= 0x1000200fL) && /* OpenSSL 1.0.2 or later */ \
-  !(defined(LIBRESSL_VERSION_NUMBER) &&                                     \
-    (LIBRESSL_VERSION_NUMBER < 0x2090100fL)) /* LibreSSL 2.9.1 or later */
   int ret = 0;
   X509 *x = NULL;
-  void *passwd_callback_userdata = (void *)key_passwd;
   BIO *in = BIO_new_mem_buf(blob->data, (int)(blob->len));
   if(!in)
     return CURLE_OUT_OF_MEMORY;
@@ -1228,7 +1197,7 @@ use_certificate_chain_blob(SSL_CTX *ctx, const struct curl_blob *blob,
   ERR_clear_error();
 
   x = PEM_read_bio_X509_AUX(in, NULL,
-                            passwd_callback, (void *)key_passwd);
+                            passwd_callback, CURL_UNCONST(key_passwd));
   if(!x)
     goto end;
 
@@ -1247,7 +1216,7 @@ use_certificate_chain_blob(SSL_CTX *ctx, const struct curl_blob *blob,
     }
 
     while((ca = PEM_read_bio_X509(in, NULL, passwd_callback,
-                                  passwd_callback_userdata))
+                                  CURL_UNCONST(key_passwd)))
           != NULL) {
 
       if(!SSL_CTX_add0_chain_cert(ctx, ca)) {
@@ -1269,12 +1238,6 @@ use_certificate_chain_blob(SSL_CTX *ctx, const struct curl_blob *blob,
   X509_free(x);
   BIO_free(in);
   return ret;
-#else
-  (void)ctx; /* unused */
-  (void)blob; /* unused */
-  (void)key_passwd; /* unused */
-  return 0;
-#endif
 }
 
 static
@@ -1296,9 +1259,7 @@ int cert_stuff(struct Curl_easy *data,
   if(cert_file || cert_blob || (file_type == SSL_FILETYPE_ENGINE) ||
      (file_type == SSL_FILETYPE_PROVIDER)) {
     SSL *ssl;
-    X509 *x509 = NULL;
-    EVP_PKEY *pri = NULL;
-    STACK_OF(X509) *ca = NULL;
+    X509 *x509;
     int cert_done = 0;
     int cert_use_result;
 
@@ -1372,7 +1333,7 @@ int cert_stuff(struct Curl_easy *data,
 
         /* Does the engine supports LOAD_CERT_CTRL ? */
         if(!ENGINE_ctrl(data->state.engine, ENGINE_CTRL_GET_CMD_FROM_NAME,
-                        0, (void *)cmd_name, NULL)) {
+                        0, CURL_UNCONST(cmd_name), NULL)) {
           failf(data, "ssl engine does not support loading certificates");
           return 0;
         }
@@ -1407,7 +1368,8 @@ int cert_stuff(struct Curl_easy *data,
       }
     }
     break;
-#elif defined(OPENSSL_HAS_PROVIDERS)
+#endif
+#if defined(OPENSSL_HAS_PROVIDERS)
       /* fall through to compatible provider */
     case SSL_FILETYPE_PROVIDER:
     {
@@ -1423,10 +1385,11 @@ int cert_stuff(struct Curl_easy *data,
 
       if(data->state.provider) {
         /* Load the certificate from the provider */
-        OSSL_STORE_CTX *store = NULL;
         OSSL_STORE_INFO *info = NULL;
         X509 *cert = NULL;
-        store = OSSL_STORE_open(cert_file, NULL, NULL, NULL, NULL);
+        OSSL_STORE_CTX *store =
+          OSSL_STORE_open_ex(cert_file, data->state.libctx,
+                             NULL, NULL, NULL, NULL, NULL, NULL);
         if(!store) {
           failf(data, "Failed to open OpenSSL store: %s",
                 ossl_strerror(ERR_get_error(), error_buffer,
@@ -1439,29 +1402,20 @@ int cert_stuff(struct Curl_easy *data,
                               sizeof(error_buffer)));
         }
 
-        for(info = OSSL_STORE_load(store);
-            info != NULL;
-            info = OSSL_STORE_load(store)) {
+        info = OSSL_STORE_load(store);
+        if(info) {
           int ossl_type = OSSL_STORE_INFO_get_type(info);
 
-          if(ossl_type == OSSL_STORE_INFO_CERT) {
+          if(ossl_type == OSSL_STORE_INFO_CERT)
             cert = OSSL_STORE_INFO_get1_CERT(info);
-          }
-          else {
-            failf(data, "Ignoring object not matching our type: %d",
-                  ossl_type);
-            OSSL_STORE_INFO_free(info);
-            continue;
-          }
           OSSL_STORE_INFO_free(info);
-          break;
         }
         OSSL_STORE_close(store);
         if(!cert) {
           failf(data, "No cert found in the openssl store: %s",
                 ossl_strerror(ERR_get_error(), error_buffer,
                               sizeof(error_buffer)));
-          goto fail;
+          return 0;
         }
 
         if(SSL_CTX_use_certificate(ctx, cert) != 1) {
@@ -1478,15 +1432,14 @@ int cert_stuff(struct Curl_easy *data,
       }
     }
     break;
-#else
-    failf(data, "file type ENG nor PROV for certificate not implemented");
-    return 0;
 #endif
 
     case SSL_FILETYPE_PKCS12:
     {
       BIO *cert_bio = NULL;
       PKCS12 *p12 = NULL;
+      EVP_PKEY *pri;
+      STACK_OF(X509) *ca = NULL;
       if(cert_blob) {
         cert_bio = BIO_new_mem_buf(cert_blob->data, (int)(cert_blob->len));
         if(!cert_bio) {
@@ -1637,7 +1590,7 @@ int cert_stuff(struct Curl_easy *data,
 
       if(data->state.engine) {
         UI_METHOD *ui_method =
-          UI_create_method((char *)"curl user interface");
+          UI_create_method(OSSL_UI_METHOD_CAST("curl user interface"));
         if(!ui_method) {
           failf(data, "unable do create " OSSL_PACKAGE
                 " user-interface method");
@@ -1668,14 +1621,15 @@ int cert_stuff(struct Curl_easy *data,
       }
     }
     break;
-#elif defined(OPENSSL_HAS_PROVIDERS)
+#endif
+#if defined(OPENSSL_HAS_PROVIDERS)
       /* fall through to compatible provider */
     case SSL_FILETYPE_PROVIDER:
     {
       /* Implicitly use pkcs11 provider if none was provided and the
-       * cert_file is a PKCS#11 URI */
+       * key_file is a PKCS#11 URI */
       if(!data->state.provider) {
-        if(is_pkcs11_uri(cert_file)) {
+        if(is_pkcs11_uri(key_file)) {
           if(ossl_set_provider(data, "pkcs11") != CURLE_OK) {
             return 0;
           }
@@ -1688,7 +1642,7 @@ int cert_stuff(struct Curl_easy *data,
         OSSL_STORE_CTX *store = NULL;
         OSSL_STORE_INFO *info = NULL;
         UI_METHOD *ui_method =
-          UI_create_method((char *)"curl user interface");
+          UI_create_method(OSSL_UI_METHOD_CAST("curl user interface"));
         if(!ui_method) {
           failf(data, "unable do create " OSSL_PACKAGE
                 " user-interface method");
@@ -1699,7 +1653,9 @@ int cert_stuff(struct Curl_easy *data,
         UI_method_set_reader(ui_method, ssl_ui_reader);
         UI_method_set_writer(ui_method, ssl_ui_writer);
 
-        store = OSSL_STORE_open(key_file, ui_method, NULL, NULL, NULL);
+        store = OSSL_STORE_open_ex(key_file, data->state.libctx,
+                                   data->state.propq, ui_method, NULL, NULL,
+                                   NULL, NULL);
         if(!store) {
           failf(data, "Failed to open OpenSSL store: %s",
                 ossl_strerror(ERR_get_error(), error_buffer,
@@ -1712,22 +1668,13 @@ int cert_stuff(struct Curl_easy *data,
                               sizeof(error_buffer)));
         }
 
-        for(info = OSSL_STORE_load(store);
-            info != NULL;
-            info = OSSL_STORE_load(store)) {
+        info = OSSL_STORE_load(store);
+        if(info) {
           int ossl_type = OSSL_STORE_INFO_get_type(info);
 
-          if(ossl_type == OSSL_STORE_INFO_PKEY) {
+          if(ossl_type == OSSL_STORE_INFO_PKEY)
             priv_key = OSSL_STORE_INFO_get1_PKEY(info);
-          }
-          else {
-            failf(data, "Ignoring object not matching our type: %d",
-                  ossl_type);
-            OSSL_STORE_INFO_free(info);
-            continue;
-          }
           OSSL_STORE_INFO_free(info);
-          break;
         }
         OSSL_STORE_close(store);
         UI_destroy_method(ui_method);
@@ -1735,7 +1682,7 @@ int cert_stuff(struct Curl_easy *data,
           failf(data, "No private key found in the openssl store: %s",
                 ossl_strerror(ERR_get_error(), error_buffer,
                               sizeof(error_buffer)));
-          goto fail;
+          return 0;
         }
 
         if(SSL_CTX_use_PrivateKey(ctx, priv_key) != 1) {
@@ -1753,9 +1700,6 @@ int cert_stuff(struct Curl_easy *data,
       }
     }
     break;
-#else
-    failf(data, "file type ENG nor PROV for private key not implemented");
-    return 0;
 #endif
 
     case SSL_FILETYPE_PKCS12:
@@ -1832,11 +1776,11 @@ static CURLcode x509_name_oneline(X509_NAME *a, struct dynbuf *d)
   CURLcode result = CURLE_OUT_OF_MEMORY;
 
   if(bio_out) {
-    Curl_dyn_reset(d);
+    curlx_dyn_reset(d);
     rc = X509_NAME_print_ex(bio_out, a, 0, XN_FLAG_SEP_SPLUS_SPC);
     if(rc != -1) {
       BIO_get_mem_ptr(bio_out, &biomem);
-      result = Curl_dyn_addn(d, biomem->data, biomem->length);
+      result = curlx_dyn_addn(d, biomem->data, biomem->length);
       BIO_free(bio_out);
     }
   }
@@ -1851,8 +1795,7 @@ static CURLcode x509_name_oneline(X509_NAME *a, struct dynbuf *d)
  */
 static int ossl_init(void)
 {
-#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) &&  \
-  (!defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER >= 0x2070000fL)
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
   const uint64_t flags =
 #ifdef OPENSSL_INIT_ENGINE_ALL_BUILTIN
     /* not present in BoringSSL */
@@ -1902,8 +1845,7 @@ static int ossl_init(void)
 /* Global cleanup */
 static void ossl_cleanup(void)
 {
-#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) &&  \
-  (!defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER >= 0x2070000fL)
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
   /* OpenSSL 1.1 deprecates all these cleanup functions and
      turns them into no-ops in OpenSSL 1.0 compatibility mode */
 #else
@@ -1919,11 +1861,7 @@ static void ossl_cleanup(void)
   ERR_free_strings();
 
   /* Free thread local error state, destroying hash upon zero refcount */
-#ifdef HAVE_ERR_REMOVE_THREAD_STATE
   ERR_remove_thread_state(NULL);
-#else
-  ERR_remove_state(0);
-#endif
 
   /* Free all memory allocated by all configuration modules */
   CONF_modules_free();
@@ -1936,47 +1874,39 @@ static void ossl_cleanup(void)
   Curl_tls_keylog_close();
 }
 
-/* Selects an OpenSSL crypto engine
+/* Selects an OpenSSL crypto engine or provider.
  */
-static CURLcode ossl_set_engine(struct Curl_easy *data, const char *engine)
+static CURLcode ossl_set_engine(struct Curl_easy *data, const char *name)
 {
 #ifdef USE_OPENSSL_ENGINE
-  ENGINE *e;
+  CURLcode result = CURLE_SSL_ENGINE_NOTFOUND;
+  ENGINE *e = ENGINE_by_id(name);
 
-#if OPENSSL_VERSION_NUMBER >= 0x00909000L
-  e = ENGINE_by_id(engine);
-#else
-  /* avoid memory leak */
-  for(e = ENGINE_get_first(); e; e = ENGINE_get_next(e)) {
-    const char *e_id = ENGINE_get_id(e);
-    if(!strcmp(engine, e_id))
-      break;
-  }
-#endif
-
-  if(!e) {
-    failf(data, "SSL Engine '%s' not found", engine);
-    return CURLE_SSL_ENGINE_NOTFOUND;
-  }
+  if(e) {
 
-  if(data->state.engine) {
-    ENGINE_finish(data->state.engine);
-    ENGINE_free(data->state.engine);
-    data->state.engine = NULL;
-  }
-  if(!ENGINE_init(e)) {
-    char buf[256];
-
-    ENGINE_free(e);
-    failf(data, "Failed to initialise SSL Engine '%s': %s",
-          engine, ossl_strerror(ERR_get_error(), buf, sizeof(buf)));
-    return CURLE_SSL_ENGINE_INITFAILED;
+    if(data->state.engine) {
+      ENGINE_finish(data->state.engine);
+      ENGINE_free(data->state.engine);
+      data->state.engine = NULL;
+    }
+    if(!ENGINE_init(e)) {
+      char buf[256];
+
+      ENGINE_free(e);
+      failf(data, "Failed to initialise SSL Engine '%s': %s",
+            name, ossl_strerror(ERR_get_error(), buf, sizeof(buf)));
+      result = CURLE_SSL_ENGINE_INITFAILED;
+      e = NULL;
+    }
+    data->state.engine = e;
+    return result;
   }
-  data->state.engine = e;
-  return CURLE_OK;
+#endif
+#ifdef OPENSSL_HAS_PROVIDERS
+  return ossl_set_provider(data, name);
 #else
-  (void)engine;
-  failf(data, "SSL Engine not supported");
+  (void)name;
+  failf(data, "OpenSSL engine not found");
   return CURLE_SSL_ENGINE_NOTFOUND;
 #endif
 }
@@ -2025,33 +1955,97 @@ static struct curl_slist *ossl_engines_list(struct Curl_easy *data)
   return list;
 }
 
-#if !defined(USE_OPENSSL_ENGINE) && defined(OPENSSL_HAS_PROVIDERS)
-/* Selects an OpenSSL crypto provider
+#if defined(OPENSSL_HAS_PROVIDERS)
+
+static void ossl_provider_cleanup(struct Curl_easy *data)
+{
+  if(data->state.baseprov) {
+    OSSL_PROVIDER_unload(data->state.baseprov);
+    data->state.baseprov = NULL;
+  }
+  if(data->state.provider) {
+    OSSL_PROVIDER_unload(data->state.provider);
+    data->state.provider = NULL;
+  }
+  OSSL_LIB_CTX_free(data->state.libctx);
+  data->state.libctx = NULL;
+  Curl_safefree(data->state.propq);
+  data->state.provider_loaded = FALSE;
+}
+
+#define MAX_PROVIDER_LEN 128 /* reasonable */
+
+/* Selects an OpenSSL crypto provider.
+ *
+ * A provider might need an associated property, a string passed on to
+ * OpenSSL. Specify this as [PROVIDER][:PROPERTY]: separate the name and the
+ * property with a colon. No colon means no property is set.
+ *
+ * An example provider + property looks like "tpm2:?provider=tpm2".
  */
-static CURLcode ossl_set_provider(struct Curl_easy *data, const char *provider)
+static CURLcode ossl_set_provider(struct Curl_easy *data, const char *iname)
 {
-  OSSL_PROVIDER *pkcs11_provider = NULL;
-  char error_buffer[256];
+  char name[MAX_PROVIDER_LEN + 1];
+  struct Curl_str prov;
+  const char *propq = NULL;
 
-  if(OSSL_PROVIDER_available(NULL, provider)) {
-    /* already loaded through the configuration - no action needed */
-    data->state.provider = TRUE;
+  if(!iname) {
+    /* clear and cleanup provider use */
+    ossl_provider_cleanup(data);
     return CURLE_OK;
   }
-  if(data->state.provider_failed) {
-    return CURLE_SSL_ENGINE_NOTFOUND;
+  if(curlx_str_until(&iname, &prov, MAX_PROVIDER_LEN, ':'))
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+
+  if(!curlx_str_single(&iname, ':'))
+    /* there was a colon, get the propq until the end of string */
+    propq = iname;
+
+  /* we need the name in a buffer, null-terminated */
+  memcpy(name, curlx_str(&prov), curlx_strlen(&prov));
+  name[curlx_strlen(&prov)] = 0;
+
+  if(!data->state.libctx) {
+    OSSL_LIB_CTX *libctx = OSSL_LIB_CTX_new();
+    if(!libctx)
+      return CURLE_OUT_OF_MEMORY;
+    if(propq) {
+      data->state.propq = strdup(propq);
+      if(!data->state.propq) {
+        OSSL_LIB_CTX_free(libctx);
+        return CURLE_OUT_OF_MEMORY;
+      }
+    }
+    data->state.libctx = libctx;
+  }
+
+  if(OSSL_PROVIDER_available(data->state.libctx, name)) {
+    /* already loaded through the configuration - no action needed */
+    data->state.provider_loaded = TRUE;
+    return CURLE_OK;
   }
 
-  pkcs11_provider = OSSL_PROVIDER_try_load(NULL, provider, 1);
-  if(!pkcs11_provider) {
+  data->state.provider =
+    OSSL_PROVIDER_try_load(data->state.libctx, name, 1);
+  if(!data->state.provider) {
+    char error_buffer[256];
     failf(data, "Failed to initialize provider: %s",
           ossl_strerror(ERR_get_error(), error_buffer,
                         sizeof(error_buffer)));
-    /* Do not attempt to load it again */
-    data->state.provider_failed = TRUE;
+    ossl_provider_cleanup(data);
+    return CURLE_SSL_ENGINE_NOTFOUND;
+  }
+
+  /* load the base provider as well */
+  data->state.baseprov =
+    OSSL_PROVIDER_try_load(data->state.libctx, "base", 1);
+  if(!data->state.baseprov) {
+    ossl_provider_cleanup(data);
+    failf(data, "Failed to load base");
     return CURLE_SSL_ENGINE_NOTFOUND;
   }
-  data->state.provider = TRUE;
+  else
+    data->state.provider_loaded = TRUE;
   return CURLE_OK;
 }
 #endif
@@ -2211,8 +2205,10 @@ static void ossl_close_all(struct Curl_easy *data)
 #else
   (void)data;
 #endif
-#if !defined(HAVE_ERR_REMOVE_THREAD_STATE_DEPRECATED) &&        \
-  defined(HAVE_ERR_REMOVE_THREAD_STATE)
+#ifdef OPENSSL_HAS_PROVIDERS
+  ossl_provider_cleanup(data);
+#endif
+#ifndef HAVE_ERR_REMOVE_THREAD_STATE_DEPRECATED
   /* OpenSSL 1.0.1 and 1.0.2 build an error queue that is stored per-thread
      so we need to clean it here in case the thread will be killed. All OpenSSL
      code should extract the error in association with the error so clearing
@@ -2289,14 +2285,14 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data,
   hostlen = strlen(peer->hostname);
   switch(peer->type) {
   case CURL_SSL_PEER_IPV4:
-    if(!Curl_inet_pton(AF_INET, peer->hostname, &addr))
+    if(!curlx_inet_pton(AF_INET, peer->hostname, &addr))
       return CURLE_PEER_FAILED_VERIFICATION;
     target = GEN_IPADD;
     addrlen = sizeof(struct in_addr);
     break;
 #ifdef USE_IPV6
   case CURL_SSL_PEER_IPV6:
-    if(!Curl_inet_pton(AF_INET6, peer->hostname, &addr))
+    if(!curlx_inet_pton(AF_INET6, peer->hostname, &addr))
       return CURLE_PEER_FAILED_VERIFICATION;
     target = GEN_IPADD;
     addrlen = sizeof(struct in6_addr);
@@ -2342,7 +2338,7 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data,
       /* only check alternatives of the same type the target is */
       if(check->type == target) {
         /* get data and length */
-        const char *altptr = (char *)ASN1_STRING_get0_data(check->d.ia5);
+        const char *altptr = (const char *)ASN1_STRING_get0_data(check->d.ia5);
         size_t altlen = (size_t) ASN1_STRING_length(check->d.ia5);
 
         switch(target) {
@@ -2428,7 +2424,7 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data,
       if(tmp) {
         if(ASN1_STRING_type(tmp) == V_ASN1_UTF8STRING) {
           cnlen = ASN1_STRING_length(tmp);
-          cn = (unsigned char *) ASN1_STRING_get0_data(tmp);
+          cn = (unsigned char *)CURL_UNCONST(ASN1_STRING_get0_data(tmp));
         }
         else { /* not a UTF8 name */
           cnlen = ASN1_STRING_to_UTF8(&cn, tmp);
@@ -2470,14 +2466,13 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data,
   return result;
 }
 
-#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
-  !defined(OPENSSL_NO_OCSP)
+#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_OCSP)
 static CURLcode verifystatus(struct Curl_cfilter *cf,
                              struct Curl_easy *data,
                              struct ossl_ctx *octx)
 {
   int i, ocsp_status;
-#if defined(OPENSSL_IS_AWSLC)
+#ifdef OPENSSL_IS_AWSLC
   const uint8_t *status;
 #else
   unsigned char *status;
@@ -2536,34 +2531,6 @@ static CURLcode verifystatus(struct Curl_cfilter *cf,
   }
   st = SSL_CTX_get_cert_store(octx->ssl_ctx);
 
-#if ((OPENSSL_VERSION_NUMBER <= 0x1000201fL) /* Fixed after 1.0.2a */ || \
-     (defined(LIBRESSL_VERSION_NUMBER) &&                               \
-      LIBRESSL_VERSION_NUMBER <= 0x2040200fL))
-  /* The authorized responder cert in the OCSP response MUST be signed by the
-     peer cert's issuer (see RFC6960 section 4.2.2.2). If that is a root cert,
-     no problem, but if it is an intermediate cert OpenSSL has a bug where it
-     expects this issuer to be present in the chain embedded in the OCSP
-     response. So we add it if necessary. */
-
-  /* First make sure the peer cert chain includes both a peer and an issuer,
-     and the OCSP response contains a responder cert. */
-  if(sk_X509_num(ch) >= 2 && sk_X509_num(br->certs) >= 1) {
-    X509 *responder = sk_X509_value(br->certs, sk_X509_num(br->certs) - 1);
-
-    /* Find issuer of responder cert and add it to the OCSP response chain */
-    for(i = 0; i < sk_X509_num(ch); i++) {
-      X509 *issuer = sk_X509_value(ch, i);
-      if(X509_check_issued(issuer, responder) == X509_V_OK) {
-        if(!OCSP_basic_add1_cert(br, issuer)) {
-          failf(data, "Could not add issuer cert to OCSP response");
-          result = CURLE_SSL_INVALIDCERTSTATUS;
-          goto end;
-        }
-      }
-    }
-  }
-#endif
-
   if(OCSP_basic_verify(br, ch, st, 0) <= 0) {
     failf(data, "OCSP response verification failed");
     result = CURLE_SSL_INVALIDCERTSTATUS;
@@ -2835,15 +2802,15 @@ static void ossl_trace(int direction, int ssl_ver, int content_type,
       tls_rt_name = "";
 
     if(content_type == SSL3_RT_CHANGE_CIPHER_SPEC) {
-      msg_type = *(char *)buf;
+      msg_type = *(const char *)buf;
       msg_name = "Change cipher spec";
     }
     else if(content_type == SSL3_RT_ALERT) {
-      msg_type = (((char *)buf)[0] << 8) + ((char *)buf)[1];
+      msg_type = (((const char *)buf)[0] << 8) + ((const char *)buf)[1];
       msg_name = SSL_alert_desc_string_long(msg_type);
     }
     else {
-      msg_type = *(char *)buf;
+      msg_type = *(const char *)buf;
       msg_name = ssl_msg_type(ssl_ver, msg_type);
     }
 
@@ -2855,7 +2822,7 @@ static void ossl_trace(int direction, int ssl_ver, int content_type,
   }
 
   Curl_debug(data, (direction == 1) ? CURLINFO_SSL_DATA_OUT :
-             CURLINFO_SSL_DATA_IN, (char *)buf, len);
+             CURLINFO_SSL_DATA_IN, (const char *)buf, len);
   (void) ssl;
 }
 #endif
@@ -2863,9 +2830,8 @@ static void ossl_trace(int direction, int ssl_ver, int content_type,
 #ifdef USE_OPENSSL
 /* ====================================================== */
 
-/* Check for OpenSSL 1.0.2 which has ALPN support. */
-#if OPENSSL_VERSION_NUMBER >= 0x10002000L       \
-  && !defined(OPENSSL_NO_TLSEXT)
+/* Check for ALPN support. */
+#ifndef OPENSSL_NO_TLSEXT
 #  define HAS_ALPN_OPENSSL
 #endif
 
@@ -2998,20 +2964,10 @@ ossl_set_ssl_version_min_max_legacy(ctx_option_t *ctx_options,
 #endif
   FALLTHROUGH();
   case CURL_SSLVERSION_TLSv1_2:
-#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
     *ctx_options |= SSL_OP_NO_TLSv1_1;
-#else
-    failf(data, OSSL_PACKAGE " was built without TLS 1.2 support");
-    return CURLE_NOT_BUILT_IN;
-#endif
     FALLTHROUGH();
   case CURL_SSLVERSION_TLSv1_1:
-#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
     *ctx_options |= SSL_OP_NO_TLSv1;
-#else
-    failf(data, OSSL_PACKAGE " was built without TLS 1.1 support");
-    return CURLE_NOT_BUILT_IN;
-#endif
     FALLTHROUGH();
   case CURL_SSLVERSION_TLSv1_0:
   case CURL_SSLVERSION_TLSv1:
@@ -3020,14 +2976,10 @@ ossl_set_ssl_version_min_max_legacy(ctx_option_t *ctx_options,
 
   switch(ssl_version_max) {
   case CURL_SSLVERSION_MAX_TLSv1_0:
-#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
     *ctx_options |= SSL_OP_NO_TLSv1_1;
-#endif
     FALLTHROUGH();
   case CURL_SSLVERSION_MAX_TLSv1_1:
-#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
     *ctx_options |= SSL_OP_NO_TLSv1_2;
-#endif
     FALLTHROUGH();
   case CURL_SSLVERSION_MAX_TLSv1_2:
 #ifdef TLS1_3_VERSION
@@ -3051,10 +3003,13 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf,
                                const char *ssl_peer_key,
                                SSL_SESSION *session,
                                int ietf_tls_id,
-                               const char *alpn)
+                               const char *alpn,
+                               unsigned char *quic_tp,
+                               size_t quic_tp_len)
 {
   const struct ssl_config_data *config;
   unsigned char *der_session_buf = NULL;
+  unsigned char *qtp_clone = NULL;
   CURLcode result = CURLE_OK;
 
   if(!cf || !data)
@@ -3065,6 +3020,7 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf,
     struct Curl_ssl_session *sc_session = NULL;
     size_t der_session_size;
     unsigned char *der_session_ptr;
+    size_t earlydata_max = 0;
 
     der_session_size = i2d_SSL_SESSION(session, NULL);
     if(der_session_size == 0) {
@@ -3084,11 +3040,23 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf,
       goto out;
     }
 
-    result = Curl_ssl_session_create(der_session_buf, der_session_size,
-                                     ietf_tls_id, alpn,
-                                     (curl_off_t)time(NULL) +
-                                     SSL_SESSION_get_timeout(session), 0,
-                                     &sc_session);
+#ifdef HAVE_OPENSSL_EARLYDATA
+    earlydata_max = SSL_SESSION_get_max_early_data(session);
+#endif
+    if(quic_tp && quic_tp_len) {
+      qtp_clone = Curl_memdup0((char *)quic_tp, quic_tp_len);
+      if(!qtp_clone) {
+        result = CURLE_OUT_OF_MEMORY;
+        goto out;
+      }
+    }
+
+    result = Curl_ssl_session_create2(der_session_buf, der_session_size,
+                                      ietf_tls_id, alpn,
+                                      (curl_off_t)time(NULL) +
+                                      SSL_SESSION_get_timeout(session),
+                                      earlydata_max, qtp_clone, quic_tp_len,
+                                      &sc_session);
     der_session_buf = NULL;  /* took ownership of sdata */
     if(!result) {
       result = Curl_ssl_scache_put(cf, data, ssl_peer_key, sc_session);
@@ -3111,7 +3079,8 @@ static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
     struct Curl_easy *data = CF_DATA_CURRENT(cf);
     struct ssl_connect_data *connssl = cf->ctx;
     Curl_ossl_add_session(cf, data, connssl->peer.scache_key, ssl_sessionid,
-                          SSL_version(ssl), connssl->negotiated.alpn);
+                          SSL_version(ssl), connssl->negotiated.alpn,
+                          NULL, 0);
   }
   return 0;
 }
@@ -3172,7 +3141,7 @@ static CURLcode load_cacert_from_memory(X509_STORE *store,
   return (count > 0) ? CURLE_OK : CURLE_SSL_CACERT_BADFILE;
 }
 
-#if defined(USE_WIN32_CRYPTO)
+#ifdef USE_WIN32_CRYPTO
 static CURLcode import_windows_cert_store(struct Curl_easy *data,
                                           const char *name,
                                           X509_STORE *store,
@@ -3340,7 +3309,7 @@ static CURLcode ossl_populate_x509_store(struct Curl_cfilter *cf,
     return CURLE_OUT_OF_MEMORY;
 
   if(verifypeer) {
-#if defined(USE_WIN32_CRYPTO)
+#ifdef USE_WIN32_CRYPTO
     /* Import certificates from the Windows root certificate store if
        requested.
        https://stackoverflow.com/questions/9507184/
@@ -3458,7 +3427,7 @@ static CURLcode ossl_populate_x509_store(struct Curl_cfilter *cf,
        https://web.archive.org/web/20190422050538/
        rt.openssl.org/Ticket/Display.html?id=3621
     */
-#if defined(X509_V_FLAG_TRUSTED_FIRST)
+#ifdef X509_V_FLAG_TRUSTED_FIRST
     X509_STORE_set_flags(store, X509_V_FLAG_TRUSTED_FIRST);
 #endif
 #ifdef X509_V_FLAG_PARTIAL_CHAIN
@@ -3479,7 +3448,7 @@ static CURLcode ossl_populate_x509_store(struct Curl_cfilter *cf,
   return result;
 }
 
-#if defined(HAVE_SSL_X509_STORE_SHARE)
+#ifdef HAVE_SSL_X509_STORE_SHARE
 
 /* key to use at `multi->proto_hash` */
 #define MPROTO_OSSL_X509_KEY   "tls:ossl:x509:share"
@@ -3512,8 +3481,8 @@ ossl_cached_x509_store_expired(const struct Curl_easy *data,
   if(cfg->ca_cache_timeout < 0)
     return FALSE;
   else {
-    struct curltime now = Curl_now();
-    timediff_t elapsed_ms = Curl_timediff(now, mb->time);
+    struct curltime now = curlx_now();
+    timediff_t elapsed_ms = curlx_timediff(now, mb->time);
     timediff_t timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000;
 
     return elapsed_ms >= timeout_ms;
@@ -3540,7 +3509,7 @@ static X509_STORE *ossl_get_cached_x509_store(struct Curl_cfilter *cf,
 
   DEBUGASSERT(multi);
   share = multi ? Curl_hash_pick(&multi->proto_hash,
-                                 (void *)MPROTO_OSSL_X509_KEY,
+                                 CURL_UNCONST(MPROTO_OSSL_X509_KEY),
                                  sizeof(MPROTO_OSSL_X509_KEY)-1) : NULL;
   if(share && share->store &&
      !ossl_cached_x509_store_expired(data, share) &&
@@ -3563,7 +3532,7 @@ static void ossl_set_cached_x509_store(struct Curl_cfilter *cf,
   if(!multi)
     return;
   share = Curl_hash_pick(&multi->proto_hash,
-                         (void *)MPROTO_OSSL_X509_KEY,
+                         CURL_UNCONST(MPROTO_OSSL_X509_KEY),
                          sizeof(MPROTO_OSSL_X509_KEY)-1);
 
   if(!share) {
@@ -3571,7 +3540,7 @@ static void ossl_set_cached_x509_store(struct Curl_cfilter *cf,
     if(!share)
       return;
     if(!Curl_hash_add2(&multi->proto_hash,
-                       (void *)MPROTO_OSSL_X509_KEY,
+                       CURL_UNCONST(MPROTO_OSSL_X509_KEY),
                        sizeof(MPROTO_OSSL_X509_KEY)-1,
                        share, oss_x509_share_free)) {
       free(share);
@@ -3595,7 +3564,7 @@ static void ossl_set_cached_x509_store(struct Curl_cfilter *cf,
       free(share->CAfile);
     }
 
-    share->time = Curl_now();
+    share->time = curlx_now();
     share->store = store;
     share->CAfile = CAfile;
   }
@@ -3647,118 +3616,421 @@ CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf,
 }
 #endif /* HAVE_SSL_X509_STORE_SHARE */
 
-CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
+
+static CURLcode
+ossl_init_session_and_alpns(struct ossl_ctx *octx,
                             struct Curl_cfilter *cf,
                             struct Curl_easy *data,
                             struct ssl_peer *peer,
-                            const unsigned char *alpn, size_t alpn_len,
-                            Curl_ossl_ctx_setup_cb *cb_setup,
-                            void *cb_user_data,
-                            Curl_ossl_new_session_cb *cb_new_session,
-                            void *ssl_user_data)
+                            const struct alpn_spec *alpns_requested,
+                            Curl_ossl_init_session_reuse_cb *sess_reuse_cb)
 {
-  CURLcode result = CURLE_OK;
-  const char *ciphers;
-  SSL_METHOD_QUAL SSL_METHOD *req_method = NULL;
-  ctx_option_t ctx_options = 0;
-  struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
-  unsigned int ssl_version_min = conn_config->version;
-  char * const ssl_cert = ssl_config->primary.clientcert;
-  const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob;
-  const char * const ssl_cert_type = ssl_config->cert_type;
-  const bool verifypeer = conn_config->verifypeer;
+  struct alpn_spec alpns;
   char error_buffer[256];
+  CURLcode result;
 
-  /* Make funny stuff to get random input */
-  result = ossl_seed(data);
-  if(result)
-    return result;
+  Curl_alpn_copy(&alpns, alpns_requested);
 
-  ssl_config->certverifyresult = !X509_V_OK;
+  octx->reused_session = FALSE;
+  if(ssl_config->primary.cache_session) {
+    struct Curl_ssl_session *scs = NULL;
 
-  switch(peer->transport) {
-  case TRNSPRT_TCP:
-    /* check to see if we have been told to use an explicit SSL/TLS version */
-    switch(ssl_version_min) {
-    case CURL_SSLVERSION_DEFAULT:
-    case CURL_SSLVERSION_TLSv1:
-    case CURL_SSLVERSION_TLSv1_0:
-    case CURL_SSLVERSION_TLSv1_1:
-    case CURL_SSLVERSION_TLSv1_2:
-    case CURL_SSLVERSION_TLSv1_3:
-      /* it will be handled later with the context options */
-  #if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
-      req_method = TLS_client_method();
-  #else
-      req_method = SSLv23_client_method();
-  #endif
-      break;
-    case CURL_SSLVERSION_SSLv2:
-      failf(data, "No SSLv2 support");
-      return CURLE_NOT_BUILT_IN;
-    case CURL_SSLVERSION_SSLv3:
-      failf(data, "No SSLv3 support");
-      return CURLE_NOT_BUILT_IN;
-    default:
-      failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
-      return CURLE_SSL_CONNECT_ERROR;
-    }
-    break;
-  case TRNSPRT_QUIC:
-    ssl_version_min = CURL_SSLVERSION_TLSv1_3;
-    if(conn_config->version_max &&
-       (conn_config->version_max != CURL_SSLVERSION_MAX_TLSv1_3)) {
-      failf(data, "QUIC needs at least TLS version 1.3");
-      return CURLE_SSL_CONNECT_ERROR;
-    }
+    result = Curl_ssl_scache_take(cf, data, peer->scache_key, &scs);
+    if(!result && scs && scs->sdata && scs->sdata_len) {
+      const unsigned char *der_sessionid = scs->sdata;
+      size_t der_sessionid_size = scs->sdata_len;
+      SSL_SESSION *ssl_session = NULL;
 
-#ifdef USE_OPENSSL_QUIC
-    req_method = OSSL_QUIC_client_method();
-#elif (OPENSSL_VERSION_NUMBER >= 0x10100000L)
-    req_method = TLS_method();
+      /* If OpenSSL does not accept the session from the cache, this
+       * is not an error. We just continue without it. */
+      ssl_session = d2i_SSL_SESSION(NULL, &der_sessionid,
+                                    (long)der_sessionid_size);
+      if(ssl_session) {
+        if(!SSL_set_session(octx->ssl, ssl_session)) {
+          infof(data, "SSL: SSL_set_session not accepted, "
+                "continuing without: %s",
+                ossl_strerror(ERR_get_error(), error_buffer,
+                              sizeof(error_buffer)));
+        }
+        else {
+          infof(data, "SSL reusing session with ALPN '%s'",
+                scs->alpn ? scs->alpn : "-");
+          octx->reused_session = TRUE;
+#ifdef HAVE_OPENSSL_EARLYDATA
+          if(ssl_config->earlydata && scs->alpn &&
+             SSL_SESSION_get_max_early_data(ssl_session) &&
+             !cf->conn->connect_only &&
+             (SSL_version(octx->ssl) == TLS1_3_VERSION)) {
+            bool do_early_data = FALSE;
+            if(sess_reuse_cb) {
+              result = sess_reuse_cb(cf, data, &alpns, scs, &do_early_data);
+              if(result)
+                return result;
+            }
+            if(do_early_data) {
+              /* We only try the ALPN protocol the session used before,
+               * otherwise we might send early data for the wrong protocol */
+              Curl_alpn_restrict_to(&alpns, scs->alpn);
+            }
+          }
 #else
-    req_method = SSLv23_client_method();
+          (void)sess_reuse_cb;
 #endif
-    break;
-  default:
-    failf(data, "unsupported transport %d in SSL init", peer->transport);
-    return CURLE_SSL_CONNECT_ERROR;
+        }
+        SSL_SESSION_free(ssl_session);
+      }
+      else {
+        infof(data, "SSL session not accepted by OpenSSL, continuing without");
+      }
+    }
+    Curl_ssl_scache_return(cf, data, peer->scache_key, scs);
   }
 
+#ifdef HAS_ALPN_OPENSSL
+  if(alpns.count) {
+    struct alpn_proto_buf proto;
+    memset(&proto, 0, sizeof(proto));
+    result = Curl_alpn_to_proto_buf(&proto, &alpns);
+    if(result) {
+      failf(data, "Error determining ALPN");
+      return CURLE_SSL_CONNECT_ERROR;
+    }
+    if(SSL_set_alpn_protos(octx->ssl, proto.data, (int)proto.len)) {
+      failf(data, "Error setting ALPN");
+      return CURLE_SSL_CONNECT_ERROR;
+    }
+  }
+#endif
 
-  DEBUGASSERT(!octx->ssl_ctx);
-  octx->ssl_ctx = SSL_CTX_new(req_method);
+  return CURLE_OK;
+}
 
-  if(!octx->ssl_ctx) {
-    failf(data, "SSL: could not create a context: %s",
-          ossl_strerror(ERR_peek_error(), error_buffer, sizeof(error_buffer)));
-    return CURLE_OUT_OF_MEMORY;
-  }
+#ifdef USE_ECH_OPENSSL
+static CURLcode ossl_init_ech(struct ossl_ctx *octx,
+                              struct Curl_cfilter *cf,
+                              struct Curl_easy *data,
+                              struct ssl_peer *peer)
+{
+  unsigned char *ech_config = NULL;
+  size_t ech_config_len = 0;
+  char *outername = data->set.str[STRING_ECH_PUBLIC];
+  int trying_ech_now = 0;
+  CURLcode result;
 
-  if(cb_setup) {
-    result = cb_setup(cf, data, cb_user_data);
-    if(result)
-      return result;
-  }
+  if(!ECH_ENABLED(data))
+    return CURLE_OK;
 
-#ifdef SSL_CTRL_SET_MSG_CALLBACK
-  if(data->set.fdebug && data->set.verbose) {
-    /* the SSL trace callback is only used for verbose logging */
-    SSL_CTX_set_msg_callback(octx->ssl_ctx, ossl_trace);
-    SSL_CTX_set_msg_callback_arg(octx->ssl_ctx, cf);
+  if(data->set.tls_ech & CURLECH_GREASE) {
+    infof(data, "ECH: will GREASE ClientHello");
+# if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
+    SSL_set_enable_ech_grease(octx->ssl, 1);
+# else
+    SSL_set_options(octx->ssl, SSL_OP_ECH_GREASE);
+# endif
   }
-#endif
-
-  /* OpenSSL contains code to work around lots of bugs and flaws in various
-     SSL-implementations. SSL_CTX_set_options() is used to enabled those
-     work-arounds. The manpage for this option states that SSL_OP_ALL enables
-     all the work-arounds and that "It is usually safe to use SSL_OP_ALL to
-     enable the bug workaround options if compatibility with somewhat broken
-     implementations is desired."
+  else if(data->set.tls_ech & CURLECH_CLA_CFG) {
+# if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
+    /* have to do base64 decode here for BoringSSL */
+    const char *b64 = data->set.str[STRING_ECH_CONFIG];
 
-     The "-no_ticket" option was introduced in OpenSSL 0.9.8j. it is a flag to
-     disable "rfc4507bis session ticket support". rfc4507bis was later turned
+    if(!b64) {
+      infof(data, "ECH: ECHConfig from command line empty");
+      return CURLE_SSL_CONNECT_ERROR;
+    }
+    ech_config_len = 2 * strlen(b64);
+    result = curlx_base64_decode(b64, &ech_config, &ech_config_len);
+    if(result || !ech_config) {
+      infof(data, "ECH: cannot base64 decode ECHConfig from command line");
+      if(data->set.tls_ech & CURLECH_HARD)
+        return result;
+    }
+    if(SSL_set1_ech_config_list(octx->ssl, ech_config,
+                                ech_config_len) != 1) {
+      infof(data, "ECH: SSL_ECH_set1_ech_config_list failed");
+      if(data->set.tls_ech & CURLECH_HARD) {
+        free(ech_config);
+        return CURLE_SSL_CONNECT_ERROR;
+      }
+    }
+    free(ech_config);
+    trying_ech_now = 1;
+# else
+    ech_config = (unsigned char *) data->set.str[STRING_ECH_CONFIG];
+    if(!ech_config) {
+      infof(data, "ECH: ECHConfig from command line empty");
+      return CURLE_SSL_CONNECT_ERROR;
+    }
+    ech_config_len = strlen(data->set.str[STRING_ECH_CONFIG]);
+    if(SSL_set1_ech_config_list(octx->ssl, ech_config,
+                                ech_config_len) != 1) {
+      infof(data, "ECH: SSL_ECH_set1_ech_config_list failed");
+      if(data->set.tls_ech & CURLECH_HARD)
+        return CURLE_SSL_CONNECT_ERROR;
+    }
+    else
+      trying_ech_now = 1;
+# endif
+    infof(data, "ECH: ECHConfig from command line");
+  }
+  else {
+    struct Curl_dns_entry *dns = NULL;
+
+    if(peer->hostname)
+      dns = Curl_dnscache_get(data, peer->hostname, peer->port,
+                              cf->conn->ip_version);
+    if(!dns) {
+      infof(data, "ECH: requested but no DNS info available");
+      if(data->set.tls_ech & CURLECH_HARD)
+        return CURLE_SSL_CONNECT_ERROR;
+    }
+    else {
+      struct Curl_https_rrinfo *rinfo = NULL;
+
+      rinfo = dns->hinfo;
+      if(rinfo && rinfo->echconfiglist) {
+        unsigned char *ecl = rinfo->echconfiglist;
+        size_t elen = rinfo->echconfiglist_len;
+
+        infof(data, "ECH: ECHConfig from DoH HTTPS RR");
+        if(SSL_set1_ech_config_list(octx->ssl, ecl, elen) != 1) {
+          infof(data, "ECH: SSL_set1_ech_config_list failed");
+          if(data->set.tls_ech & CURLECH_HARD)
+            return CURLE_SSL_CONNECT_ERROR;
+        }
+        else {
+          trying_ech_now = 1;
+          infof(data, "ECH: imported ECHConfigList of length %zu", elen);
+        }
+      }
+      else {
+        infof(data, "ECH: requested but no ECHConfig available");
+        if(data->set.tls_ech & CURLECH_HARD)
+          return CURLE_SSL_CONNECT_ERROR;
+      }
+      Curl_resolv_unlink(data, &dns);
+    }
+  }
+# if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
+  if(trying_ech_now && outername) {
+    infof(data, "ECH: setting public_name not supported with BoringSSL");
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+# else
+  if(trying_ech_now && outername) {
+    infof(data, "ECH: inner: '%s', outer: '%s'",
+          peer->hostname ? peer->hostname : "NULL", outername);
+    result = SSL_ech_set1_server_names(octx->ssl,
+                                      peer->hostname, outername,
+                                      0 /* do send outer */);
+    if(result != 1) {
+      infof(data, "ECH: rv failed to set server name(s) %d [ERROR]", result);
+      return CURLE_SSL_CONNECT_ERROR;
+    }
+  }
+# endif  /* OPENSSL_IS_BORINGSSL || OPENSSL_IS_AWSLC */
+  if(trying_ech_now
+     && SSL_set_min_proto_version(octx->ssl, TLS1_3_VERSION) != 1) {
+    infof(data, "ECH: cannot force TLSv1.3 [ERROR]");
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+
+  return CURLE_OK;
+}
+#endif /* USE_ECH_OPENSSL */
+
+
+static CURLcode ossl_init_ssl(struct ossl_ctx *octx,
+                              struct Curl_cfilter *cf,
+                              struct Curl_easy *data,
+                              struct ssl_peer *peer,
+                              const struct alpn_spec *alpns_requested,
+                              void *ssl_user_data,
+                              Curl_ossl_init_session_reuse_cb *sess_reuse_cb)
+{
+  /* Let's make an SSL structure */
+  if(octx->ssl)
+    SSL_free(octx->ssl);
+  octx->ssl = SSL_new(octx->ssl_ctx);
+  if(!octx->ssl) {
+    failf(data, "SSL: could not create a context (handle)");
+    return CURLE_OUT_OF_MEMORY;
+  }
+
+  SSL_set_app_data(octx->ssl, ssl_user_data);
+
+#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_OCSP)
+  if(Curl_ssl_cf_get_primary_config(cf)->verifystatus)
+    SSL_set_tlsext_status_type(octx->ssl, TLSEXT_STATUSTYPE_ocsp);
+#endif
+
+#if (defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)) && \
+    defined(ALLOW_RENEG)
+  SSL_set_renegotiate_mode(octx->ssl, ssl_renegotiate_freely);
+#endif
+
+  SSL_set_connect_state(octx->ssl);
+
+  octx->server_cert = NULL;
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+  if(peer->sni) {
+    if(!SSL_set_tlsext_host_name(octx->ssl, peer->sni)) {
+      failf(data, "Failed set SNI");
+      return CURLE_SSL_CONNECT_ERROR;
+    }
+  }
+
+#ifdef USE_ECH_OPENSSL
+  {
+    CURLcode result = ossl_init_ech(octx, cf, data, peer);
+    if(result)
+      return result;
+  }
+#endif  /* USE_ECH_OPENSSL */
+
+#endif
+
+  return ossl_init_session_and_alpns(octx, cf, data, peer,
+                                     alpns_requested, sess_reuse_cb);
+}
+
+
+static CURLcode ossl_init_method(struct Curl_cfilter *cf,
+                                 struct Curl_easy *data,
+                                 struct ssl_peer *peer,
+                                 const SSL_METHOD **pmethod,
+                                 unsigned int *pssl_version_min)
+{
+  struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+
+  *pmethod = NULL;
+  *pssl_version_min = conn_config->version;
+  switch(peer->transport) {
+  case TRNSPRT_TCP:
+    /* check to see if we have been told to use an explicit SSL/TLS version */
+    switch(*pssl_version_min) {
+    case CURL_SSLVERSION_DEFAULT:
+    case CURL_SSLVERSION_TLSv1:
+    case CURL_SSLVERSION_TLSv1_0:
+    case CURL_SSLVERSION_TLSv1_1:
+    case CURL_SSLVERSION_TLSv1_2:
+    case CURL_SSLVERSION_TLSv1_3:
+      /* it will be handled later with the context options */
+  #if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
+      *pmethod = TLS_client_method();
+  #else
+      *pmethod = SSLv23_client_method();
+  #endif
+      break;
+    case CURL_SSLVERSION_SSLv2:
+      failf(data, "No SSLv2 support");
+      return CURLE_NOT_BUILT_IN;
+    case CURL_SSLVERSION_SSLv3:
+      failf(data, "No SSLv3 support");
+      return CURLE_NOT_BUILT_IN;
+    default:
+      failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
+      return CURLE_SSL_CONNECT_ERROR;
+    }
+    break;
+  case TRNSPRT_QUIC:
+    *pssl_version_min = CURL_SSLVERSION_TLSv1_3;
+    if(conn_config->version_max &&
+       (conn_config->version_max != CURL_SSLVERSION_MAX_TLSv1_3)) {
+      failf(data, "QUIC needs at least TLS version 1.3");
+      return CURLE_SSL_CONNECT_ERROR;
+    }
+
+#ifdef USE_OPENSSL_QUIC
+    *pmethod = OSSL_QUIC_client_method();
+#elif (OPENSSL_VERSION_NUMBER >= 0x10100000L)
+    *pmethod = TLS_method();
+#else
+    *pmethod = SSLv23_client_method();
+#endif
+    break;
+  default:
+    failf(data, "unsupported transport %d in SSL init", peer->transport);
+    return CURLE_SSL_CONNECT_ERROR;
+  }
+
+  return *pmethod ? CURLE_OK : CURLE_SSL_CONNECT_ERROR;
+}
+
+
+CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
+                            struct Curl_cfilter *cf,
+                            struct Curl_easy *data,
+                            struct ssl_peer *peer,
+                            const struct alpn_spec *alpns_requested,
+                            Curl_ossl_ctx_setup_cb *cb_setup,
+                            void *cb_user_data,
+                            Curl_ossl_new_session_cb *cb_new_session,
+                            void *ssl_user_data,
+                            Curl_ossl_init_session_reuse_cb *sess_reuse_cb)
+{
+  CURLcode result = CURLE_OK;
+  const char *ciphers;
+  const SSL_METHOD *req_method = NULL;
+  ctx_option_t ctx_options = 0;
+  struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+  struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+  char * const ssl_cert = ssl_config->primary.clientcert;
+  const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob;
+  const char * const ssl_cert_type = ssl_config->cert_type;
+  const bool verifypeer = conn_config->verifypeer;
+  unsigned int ssl_version_min;
+  char error_buffer[256];
+
+  /* Make funny stuff to get random input */
+  result = ossl_seed(data);
+  if(result)
+    return result;
+
+  ssl_config->certverifyresult = !X509_V_OK;
+
+  result = ossl_init_method(cf, data, peer, &req_method, &ssl_version_min);
+  if(result)
+    return result;
+  DEBUGASSERT(req_method);
+
+  DEBUGASSERT(!octx->ssl_ctx);
+  octx->ssl_ctx =
+#ifdef OPENSSL_HAS_PROVIDERS
+    data->state.libctx ?
+    SSL_CTX_new_ex(data->state.libctx, data->state.propq, req_method):
+#endif
+    SSL_CTX_new(req_method);
+
+  if(!octx->ssl_ctx) {
+    failf(data, "SSL: could not create a context: %s",
+          ossl_strerror(ERR_peek_error(), error_buffer, sizeof(error_buffer)));
+    return CURLE_OUT_OF_MEMORY;
+  }
+
+  if(cb_setup) {
+    result = cb_setup(cf, data, cb_user_data);
+    if(result)
+      return result;
+  }
+
+#ifdef SSL_CTRL_SET_MSG_CALLBACK
+  if(data->set.fdebug && data->set.verbose) {
+    /* the SSL trace callback is only used for verbose logging */
+    SSL_CTX_set_msg_callback(octx->ssl_ctx, ossl_trace);
+    SSL_CTX_set_msg_callback_arg(octx->ssl_ctx, cf);
+  }
+#endif
+
+  /* OpenSSL contains code to work around lots of bugs and flaws in various
+     SSL-implementations. SSL_CTX_set_options() is used to enabled those
+     work-arounds. The manpage for this option states that SSL_OP_ALL enables
+     all the work-arounds and that "It is usually safe to use SSL_OP_ALL to
+     enable the bug workaround options if compatibility with somewhat broken
+     implementations is desired."
+
+     The "-no_ticket" option was introduced in OpenSSL 0.9.8j. it is a flag to
+     disable "rfc4507bis session ticket support". rfc4507bis was later turned
      into the proper RFC5077: https://datatracker.ietf.org/doc/html/rfc5077
 
      The enabled extension concerns the session management. I wonder how often
@@ -3778,7 +4050,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
      CVE-2010-4180 when using previous OpenSSL versions we no longer enable
      this option regardless of OpenSSL version and SSL_OP_ALL definition.
 
-     OpenSSL added a work-around for a SSL 3.0/TLS 1.0 CBC vulnerability:
+     OpenSSL added a work-around for an SSL 3.0/TLS 1.0 CBC vulnerability:
      https://web.archive.org/web/20240114184648/openssl.org/~bodo/tls-cbc.txt.
      In 0.9.6e they added a bit to SSL_OP_ALL that _disables_ that work-around
      despite the fact that SSL_OP_ALL is documented to do "rather harmless"
@@ -3846,27 +4118,6 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
   SSL_CTX_set_mode(octx->ssl_ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
 #endif
 
-  if(alpn && alpn_len) {
-#ifdef HAS_ALPN_OPENSSL
-    if(SSL_CTX_set_alpn_protos(octx->ssl_ctx, alpn, (int)alpn_len)) {
-      failf(data, "Error setting ALPN");
-      return CURLE_SSL_CONNECT_ERROR;
-    }
-#endif
-  }
-
-  if(ssl_cert || ssl_cert_blob || ssl_cert_type) {
-    if(!result &&
-       !cert_stuff(data, octx->ssl_ctx,
-                   ssl_cert, ssl_cert_blob, ssl_cert_type,
-                   ssl_config->key, ssl_config->key_blob,
-                   ssl_config->key_type, ssl_config->key_passwd))
-      result = CURLE_SSL_CERTPROBLEM;
-    if(result)
-      /* failf() is already done in cert_stuff() */
-      return result;
-  }
-
   ciphers = conn_config->cipher_list;
   if(!ciphers && (peer->transport != TRNSPRT_QUIC))
     ciphers = DEFAULT_CIPHER_SELECTION;
@@ -3893,21 +4144,51 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
   }
 #endif
 
+  if(ssl_cert || ssl_cert_blob || ssl_cert_type) {
+    if(!result &&
+       !cert_stuff(data, octx->ssl_ctx,
+                   ssl_cert, ssl_cert_blob, ssl_cert_type,
+                   ssl_config->key, ssl_config->key_blob,
+                   ssl_config->key_type, ssl_config->key_passwd))
+      result = CURLE_SSL_CERTPROBLEM;
+    if(result)
+      /* failf() is already done in cert_stuff() */
+      return result;
+  }
+
 #ifdef HAVE_SSL_CTX_SET_POST_HANDSHAKE_AUTH
   /* OpenSSL 1.1.1 requires clients to opt-in for PHA */
   SSL_CTX_set_post_handshake_auth(octx->ssl_ctx, 1);
 #endif
 
-#ifdef HAVE_SSL_CTX_SET_EC_CURVES
   {
     const char *curves = conn_config->curves;
     if(curves) {
-      if(!SSL_CTX_set1_curves_list(octx->ssl_ctx, curves)) {
+#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
+#define OSSL_CURVE_CAST(x) (x)
+#else
+#define OSSL_CURVE_CAST(x) (char *)CURL_UNCONST(x)
+#endif
+      if(!SSL_CTX_set1_curves_list(octx->ssl_ctx, OSSL_CURVE_CAST(curves))) {
         failf(data, "failed setting curves list: '%s'", curves);
         return CURLE_SSL_CIPHER;
       }
     }
   }
+
+#ifdef HAVE_SSL_CTX_SET1_SIGALGS
+#define OSSL_SIGALG_CAST(x) OSSL_CURVE_CAST(x)
+  {
+    const char *signature_algorithms = conn_config->signature_algorithms;
+    if(signature_algorithms) {
+      if(!SSL_CTX_set1_sigalgs_list(octx->ssl_ctx,
+                                    OSSL_SIGALG_CAST(signature_algorithms))) {
+        failf(data, "failed setting signature algorithms: '%s'",
+              signature_algorithms);
+        return CURLE_SSL_CIPHER;
+      }
+    }
+  }
 #endif
 
 #ifdef USE_OPENSSL_SRP
@@ -3981,198 +4262,38 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
     }
   }
 
-  /* Let's make an SSL structure */
-  if(octx->ssl)
-    SSL_free(octx->ssl);
-  octx->ssl = SSL_new(octx->ssl_ctx);
-  if(!octx->ssl) {
-    failf(data, "SSL: could not create a context (handle)");
-    return CURLE_OUT_OF_MEMORY;
-  }
-
-  SSL_set_app_data(octx->ssl, ssl_user_data);
-
-#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
-  !defined(OPENSSL_NO_OCSP)
-  if(conn_config->verifystatus)
-    SSL_set_tlsext_status_type(octx->ssl, TLSEXT_STATUSTYPE_ocsp);
-#endif
-
-#if (defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)) && \
-    defined(ALLOW_RENEG)
-  SSL_set_renegotiate_mode(octx->ssl, ssl_renegotiate_freely);
-#endif
+  return ossl_init_ssl(octx, cf, data, peer, alpns_requested,
+                       ssl_user_data, sess_reuse_cb);
+}
 
-  SSL_set_connect_state(octx->ssl);
+static CURLcode ossl_on_session_reuse(struct Curl_cfilter *cf,
+                                      struct Curl_easy *data,
+                                      struct alpn_spec *alpns,
+                                      struct Curl_ssl_session *scs,
+                                      bool *do_early_data)
+{
+  struct ssl_connect_data *connssl = cf->ctx;
+  CURLcode result = CURLE_OK;
 
-  octx->server_cert = 0x0;
-#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
-  if(peer->sni) {
-    if(!SSL_set_tlsext_host_name(octx->ssl, peer->sni)) {
-      failf(data, "Failed set SNI");
-      return CURLE_SSL_CONNECT_ERROR;
-    }
+  *do_early_data = FALSE;
+  connssl->earlydata_max = scs->earlydata_max;
+  if(!connssl->earlydata_max) {
+    CURL_TRC_CF(data, cf, "SSL session does not allow earlydata");
   }
-
-#ifdef USE_ECH_OPENSSL
-  if(ECH_ENABLED(data)) {
-    unsigned char *ech_config = NULL;
-    size_t ech_config_len = 0;
-    char *outername = data->set.str[STRING_ECH_PUBLIC];
-    int trying_ech_now = 0;
-
-    if(data->set.tls_ech & CURLECH_GREASE) {
-      infof(data, "ECH: will GREASE ClientHello");
-# if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
-      SSL_set_enable_ech_grease(octx->ssl, 1);
-# else
-      SSL_set_options(octx->ssl, SSL_OP_ECH_GREASE);
-# endif
-    }
-    else if(data->set.tls_ech & CURLECH_CLA_CFG) {
-# if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
-      /* have to do base64 decode here for BoringSSL */
-      const char *b64 = data->set.str[STRING_ECH_CONFIG];
-
-      if(!b64) {
-        infof(data, "ECH: ECHConfig from command line empty");
-        return CURLE_SSL_CONNECT_ERROR;
-      }
-      ech_config_len = 2 * strlen(b64);
-      result = Curl_base64_decode(b64, &ech_config, &ech_config_len);
-      if(result || !ech_config) {
-        infof(data, "ECH: cannot base64 decode ECHConfig from command line");
-        if(data->set.tls_ech & CURLECH_HARD)
-          return result;
-      }
-      if(SSL_set1_ech_config_list(octx->ssl, ech_config,
-                                  ech_config_len) != 1) {
-        infof(data, "ECH: SSL_ECH_set1_ech_config_list failed");
-        if(data->set.tls_ech & CURLECH_HARD) {
-          free(ech_config);
-          return CURLE_SSL_CONNECT_ERROR;
-        }
-      }
-      free(ech_config);
-      trying_ech_now = 1;
-# else
-      ech_config = (unsigned char *) data->set.str[STRING_ECH_CONFIG];
-      if(!ech_config) {
-        infof(data, "ECH: ECHConfig from command line empty");
-        return CURLE_SSL_CONNECT_ERROR;
-      }
-      ech_config_len = strlen(data->set.str[STRING_ECH_CONFIG]);
-      if(SSL_set1_ech_config_list(octx->ssl, ech_config,
-                                  ech_config_len) != 1) {
-        infof(data, "ECH: SSL_ECH_set1_ech_config_list failed");
-        if(data->set.tls_ech & CURLECH_HARD)
-          return CURLE_SSL_CONNECT_ERROR;
-      }
-      else
-        trying_ech_now = 1;
-# endif
-      infof(data, "ECH: ECHConfig from command line");
-    }
-    else {
-      struct Curl_dns_entry *dns = NULL;
-
-      if(peer->hostname)
-        dns = Curl_fetch_addr(data, peer->hostname, peer->port);
-      if(!dns) {
-        infof(data, "ECH: requested but no DNS info available");
-        if(data->set.tls_ech & CURLECH_HARD)
-          return CURLE_SSL_CONNECT_ERROR;
-      }
-      else {
-        struct Curl_https_rrinfo *rinfo = NULL;
-
-        rinfo = dns->hinfo;
-        if(rinfo && rinfo->echconfiglist) {
-          unsigned char *ecl = rinfo->echconfiglist;
-          size_t elen = rinfo->echconfiglist_len;
-
-          infof(data, "ECH: ECHConfig from DoH HTTPS RR");
-          if(SSL_set1_ech_config_list(octx->ssl, ecl, elen) != 1) {
-            infof(data, "ECH: SSL_set1_ech_config_list failed");
-            if(data->set.tls_ech & CURLECH_HARD)
-              return CURLE_SSL_CONNECT_ERROR;
-          }
-          else {
-            trying_ech_now = 1;
-            infof(data, "ECH: imported ECHConfigList of length %zu", elen);
-          }
-        }
-        else {
-          infof(data, "ECH: requested but no ECHConfig available");
-          if(data->set.tls_ech & CURLECH_HARD)
-            return CURLE_SSL_CONNECT_ERROR;
-        }
-        Curl_resolv_unlink(data, &dns);
-      }
-    }
-# if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
-    if(trying_ech_now && outername) {
-      infof(data, "ECH: setting public_name not supported with BoringSSL");
-      return CURLE_SSL_CONNECT_ERROR;
-    }
-# else
-    if(trying_ech_now && outername) {
-      infof(data, "ECH: inner: '%s', outer: '%s'",
-            peer->hostname ? peer->hostname : "NULL", outername);
-      result = SSL_ech_set1_server_names(octx->ssl,
-                                        peer->hostname, outername,
-                                        0 /* do send outer */);
-      if(result != 1) {
-        infof(data, "ECH: rv failed to set server name(s) %d [ERROR]", result);
-        return CURLE_SSL_CONNECT_ERROR;
-      }
-    }
-# endif  /* OPENSSL_IS_BORINGSSL || OPENSSL_IS_AWSLC */
-    if(trying_ech_now
-       && SSL_set_min_proto_version(octx->ssl, TLS1_3_VERSION) != 1) {
-      infof(data, "ECH: cannot force TLSv1.3 [ERROR]");
-      return CURLE_SSL_CONNECT_ERROR;
-    }
+  else if(!Curl_alpn_contains_proto(alpns, scs->alpn)) {
+    CURL_TRC_CF(data, cf, "SSL session has different ALPN, no early data");
   }
-#endif  /* USE_ECH_OPENSSL */
-
-#endif
-
-  octx->reused_session = FALSE;
-  if(ssl_config->primary.cache_session) {
-    struct Curl_ssl_session *sc_session = NULL;
-
-    result = Curl_ssl_scache_take(cf, data, peer->scache_key, &sc_session);
-    if(!result && sc_session && sc_session->sdata && sc_session->sdata_len) {
-      const unsigned char *der_sessionid = sc_session->sdata;
-      size_t der_sessionid_size = sc_session->sdata_len;
-      SSL_SESSION *ssl_session = NULL;
-
-      /* If OpenSSL does not accept the session from the cache, this
-       * is not an error. We just continue without it. */
-      ssl_session = d2i_SSL_SESSION(NULL, &der_sessionid,
-                                    (long)der_sessionid_size);
-      if(ssl_session) {
-        if(!SSL_set_session(octx->ssl, ssl_session)) {
-          infof(data, "SSL: SSL_set_session not accepted, "
-                "continuing without: %s",
-                ossl_strerror(ERR_get_error(), error_buffer,
-                              sizeof(error_buffer)));
-        }
-        else {
-          infof(data, "SSL reusing session");
-          octx->reused_session = TRUE;
-        }
-        SSL_SESSION_free(ssl_session);
-      }
-      else {
-        infof(data, "SSL session not accepted by OpenSSL, continuing without");
-      }
-    }
-    Curl_ssl_scache_return(cf, data, peer->scache_key, sc_session);
+  else {
+    infof(data, "SSL session allows %zu bytes of early data, "
+          "reusing ALPN '%s'", connssl->earlydata_max, scs->alpn);
+    connssl->earlydata_state = ssl_earlydata_await;
+    connssl->state = ssl_connection_deferred;
+    result = Curl_alpn_set_negotiated(cf, data, connssl,
+                    (const unsigned char *)scs->alpn,
+                    scs->alpn ? strlen(scs->alpn) : 0);
+    *do_early_data = !result;
   }
-
-  return CURLE_OK;
+  return result;
 }
 
 static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
@@ -4180,26 +4301,16 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
 {
   struct ssl_connect_data *connssl = cf->ctx;
   struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
-  struct alpn_proto_buf proto;
   BIO *bio;
   CURLcode result;
 
   DEBUGASSERT(ssl_connect_1 == connssl->connecting_state);
   DEBUGASSERT(octx);
-  memset(&proto, 0, sizeof(proto));
-#ifdef HAS_ALPN_OPENSSL
-  if(connssl->alpn) {
-    result = Curl_alpn_to_proto_buf(&proto, connssl->alpn);
-    if(result) {
-      failf(data, "Error determining ALPN");
-      return CURLE_SSL_CONNECT_ERROR;
-    }
-  }
-#endif
 
   result = Curl_ossl_ctx_init(octx, cf, data, &connssl->peer,
-                              proto.data, proto.len, NULL, NULL,
-                              ossl_new_session_cb, cf);
+                              connssl->alpn, NULL, NULL,
+                              ossl_new_session_cb, cf,
+                              ossl_on_session_reuse);
   if(result)
     return result;
 
@@ -4225,7 +4336,9 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
 #endif
 
 #ifdef HAS_ALPN_OPENSSL
-  if(connssl->alpn) {
+  if(connssl->alpn && (connssl->state != ssl_connection_deferred)) {
+    struct alpn_proto_buf proto;
+    memset(&proto, 0, sizeof(proto));
     Curl_alpn_to_proto_str(&proto, connssl->alpn);
     infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
   }
@@ -4268,7 +4381,7 @@ static void ossl_trace_ech_retry_configs(struct Curl_easy *data, SSL* ssl,
     char *b64str = NULL;
     size_t blen = 0;
 
-    result = Curl_base64_encode((const char *)rcs, rcl, &b64str, &blen);
+    result = curlx_base64_encode((const char *)rcs, rcl, &b64str, &blen);
     if(!result && b64str) {
       infof(data, "ECH: retry_configs %s", b64str);
       free(b64str);
@@ -4338,27 +4451,25 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
     if(SSL_ERROR_WANT_READ == detail) {
       CURL_TRC_CF(data, cf, "SSL_connect() -> want recv");
       connssl->io_need = CURL_SSL_IO_NEED_RECV;
-      return CURLE_OK;
+      return CURLE_AGAIN;
     }
     if(SSL_ERROR_WANT_WRITE == detail) {
       CURL_TRC_CF(data, cf, "SSL_connect() -> want send");
       connssl->io_need = CURL_SSL_IO_NEED_SEND;
-      return CURLE_OK;
+      return CURLE_AGAIN;
     }
 #ifdef SSL_ERROR_WANT_ASYNC
     if(SSL_ERROR_WANT_ASYNC == detail) {
       CURL_TRC_CF(data, cf, "SSL_connect() -> want async");
       connssl->io_need = CURL_SSL_IO_NEED_RECV;
-      connssl->connecting_state = ssl_connect_2;
-      return CURLE_OK;
+      return CURLE_AGAIN;
     }
 #endif
 #ifdef SSL_ERROR_WANT_RETRY_VERIFY
     if(SSL_ERROR_WANT_RETRY_VERIFY == detail) {
       CURL_TRC_CF(data, cf, "SSL_connect() -> want retry_verify");
       connssl->io_need = CURL_SSL_IO_NEED_RECV;
-      connssl->connecting_state = ssl_connect_2;
-      return CURLE_OK;
+      return CURLE_AGAIN;
     }
 #endif
     else {
@@ -4395,7 +4506,7 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
         else
           failf(data, "%s", "SSL certificate verification failed");
       }
-#if defined(SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED)
+#ifdef SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED
       /* SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED is only available on
          OpenSSL version above v1.1.1, not LibreSSL, BoringSSL, or AWS-LC */
       else if((lib == ERR_LIB_SSL) &&
@@ -4661,7 +4772,7 @@ static void infof_certstack(struct Curl_easy *data, const SSL *ssl)
                                                sizeof(group_name), NULL);
       msnprintf(group_name_final, sizeof(group_name_final), "/%s", group_name);
     }
-    type_name = EVP_PKEY_get0_type_name(current_pkey);
+    type_name = current_pkey ? EVP_PKEY_get0_type_name(current_pkey) : NULL;
 #else
     get_group_name = 0;
     type_name = NULL;
@@ -4701,7 +4812,7 @@ CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf,
 
   DEBUGASSERT(octx);
 
-  Curl_dyn_init(&dname, MAX_CERT_NAME_LENGTH);
+  curlx_dyn_init(&dname, MAX_CERT_NAME_LENGTH);
 
   if(!mem) {
     failf(data,
@@ -4731,19 +4842,20 @@ CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf,
 
   result = x509_name_oneline(X509_get_subject_name(octx->server_cert),
                              &dname);
-  infof(data, " subject: %s", result ? "[NONE]" : Curl_dyn_ptr(&dname));
+  infof(data, " subject: %s", result ? "[NONE]" : curlx_dyn_ptr(&dname));
 
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
   {
+    char *buf;
     long len;
     ASN1_TIME_print(mem, X509_get0_notBefore(octx->server_cert));
-    len = BIO_get_mem_data(mem, (char **) &ptr);
-    infof(data, " start date: %.*s", (int)len, ptr);
+    len = BIO_get_mem_data(mem, (char **) &buf);
+    infof(data, " start date: %.*s", (int)len, buf);
     (void)BIO_reset(mem);
 
     ASN1_TIME_print(mem, X509_get0_notAfter(octx->server_cert));
-    len = BIO_get_mem_data(mem, (char **) &ptr);
-    infof(data, " expire date: %.*s", (int)len, ptr);
+    len = BIO_get_mem_data(mem, (char **) &buf);
+    infof(data, " expire date: %.*s", (int)len, buf);
     (void)BIO_reset(mem);
   }
 #endif
@@ -4755,7 +4867,7 @@ CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf,
     if(result) {
       X509_free(octx->server_cert);
       octx->server_cert = NULL;
-      Curl_dyn_free(&dname);
+      curlx_dyn_free(&dname);
       return result;
     }
   }
@@ -4768,8 +4880,8 @@ CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf,
     result = CURLE_PEER_FAILED_VERIFICATION;
   }
   else {
-    infof(data, " issuer: %s", Curl_dyn_ptr(&dname));
-    Curl_dyn_free(&dname);
+    infof(data, " issuer: %s", curlx_dyn_ptr(&dname));
+    curlx_dyn_free(&dname);
 
     /* We could do all sorts of certificate verification stuff here before
        deallocating the certificate. */
@@ -4864,8 +4976,7 @@ CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf,
   }
   infof_certstack(data, octx->ssl);
 
-#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
-  !defined(OPENSSL_NO_OCSP)
+#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_OCSP)
   if(conn_config->verifystatus && !octx->reused_session) {
     /* do not do this after Session ID reuse */
     result = verifystatus(cf, data, octx);
@@ -4917,137 +5028,169 @@ static CURLcode ossl_connect_step3(struct Curl_cfilter *cf,
    */
 
   result = Curl_oss_check_peer_cert(cf, data, octx, &connssl->peer);
-  if(!result)
-    connssl->connecting_state = ssl_connect_done;
-  else
+  if(result)
     /* on error, remove sessions we might have in the pool */
     Curl_ssl_scache_remove_all(cf, data, connssl->peer.scache_key);
 
   return result;
 }
 
-static CURLcode ossl_connect_common(struct Curl_cfilter *cf,
-                                    struct Curl_easy *data,
-                                    bool nonblocking,
-                                    bool *done)
+#ifdef HAVE_OPENSSL_EARLYDATA
+static CURLcode ossl_send_earlydata(struct Curl_cfilter *cf,
+                                    struct Curl_easy *data)
+{
+  struct ssl_connect_data *connssl = cf->ctx;
+  struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
+  CURLcode result = CURLE_OK;
+  const unsigned char *buf;
+  size_t blen, nwritten;
+  int rc;
+
+  DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_sending);
+  octx->io_result = CURLE_OK;
+  while(Curl_bufq_peek(&connssl->earlydata, &buf, &blen)) {
+    nwritten = 0;
+    rc = SSL_write_early_data(octx->ssl, buf, blen, &nwritten);
+    CURL_TRC_CF(data, cf, "SSL_write_early_data(len=%zu) -> %d, %zu",
+                blen, rc, nwritten);
+    if(rc <= 0) {
+      long sslerror;
+      char error_buffer[256];
+      int err = SSL_get_error(octx->ssl, rc);
+
+      switch(err) {
+      case SSL_ERROR_WANT_READ:
+        connssl->io_need = CURL_SSL_IO_NEED_RECV;
+        result = CURLE_AGAIN;
+        goto out;
+      case SSL_ERROR_WANT_WRITE:
+        connssl->io_need = CURL_SSL_IO_NEED_SEND;
+        result = CURLE_AGAIN;
+        goto out;
+      case SSL_ERROR_SYSCALL: {
+        int sockerr = SOCKERRNO;
+
+        if(octx->io_result == CURLE_AGAIN) {
+          result = CURLE_AGAIN;
+          goto out;
+        }
+        sslerror = ERR_get_error();
+        if(sslerror)
+          ossl_strerror(sslerror, error_buffer, sizeof(error_buffer));
+        else if(sockerr)
+          Curl_strerror(sockerr, error_buffer, sizeof(error_buffer));
+        else
+          msnprintf(error_buffer, sizeof(error_buffer), "%s",
+                    SSL_ERROR_to_str(err));
+
+        failf(data, OSSL_PACKAGE " SSL_write:early_data: %s, errno %d",
+              error_buffer, sockerr);
+        result = CURLE_SEND_ERROR;
+        goto out;
+      }
+      case SSL_ERROR_SSL: {
+        /*  A failure in the SSL library occurred, usually a protocol error.
+            The OpenSSL error queue contains more information on the error. */
+        sslerror = ERR_get_error();
+        failf(data, "SSL_write_early_data() error: %s",
+              ossl_strerror(sslerror, error_buffer, sizeof(error_buffer)));
+        result = CURLE_SEND_ERROR;
+        goto out;
+      }
+      default:
+        /* a true error */
+        failf(data, OSSL_PACKAGE " SSL_write_early_data: %s, errno %d",
+              SSL_ERROR_to_str(err), SOCKERRNO);
+        result = CURLE_SEND_ERROR;
+        goto out;
+      }
+    }
+    Curl_bufq_skip(&connssl->earlydata, nwritten);
+  }
+  /* sent everything there was */
+  infof(data, "SSL sending %zu bytes of early data", connssl->earlydata_skip);
+out:
+  return result;
+}
+#endif /* HAVE_OPENSSL_EARLYDATA */
+
+static CURLcode ossl_connect(struct Curl_cfilter *cf,
+                             struct Curl_easy *data,
+                             bool *done)
 {
   CURLcode result = CURLE_OK;
   struct ssl_connect_data *connssl = cf->ctx;
-  curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
-  int what;
 
-  connssl->io_need = CURL_SSL_IO_NEED_NONE;
   /* check if the connection has already been established */
   if(ssl_connection_complete == connssl->state) {
     *done = TRUE;
     return CURLE_OK;
   }
 
-  if(ssl_connect_1 == connssl->connecting_state) {
-    /* Find out how much more time we are allowed */
-    const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
-    if(timeout_ms < 0) {
-      /* no need to continue if time is already up */
-      failf(data, "SSL connection timeout");
-      return CURLE_OPERATION_TIMEDOUT;
-    }
+  *done = FALSE;
+  connssl->io_need = CURL_SSL_IO_NEED_NONE;
 
+  if(ssl_connect_1 == connssl->connecting_state) {
+    CURL_TRC_CF(data, cf, "ossl_connect, step1");
     result = ossl_connect_step1(cf, data);
     if(result)
       goto out;
   }
 
-  while(ssl_connect_2 == connssl->connecting_state) {
-
-    /* check allowed time left */
-    const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
-    if(timeout_ms < 0) {
-      /* no need to continue if time already is up */
-      failf(data, "SSL connection timeout");
-      result = CURLE_OPERATION_TIMEDOUT;
+  if(ssl_connect_2 == connssl->connecting_state) {
+    CURL_TRC_CF(data, cf, "ossl_connect, step2");
+#ifdef HAVE_OPENSSL_EARLYDATA
+    if(connssl->earlydata_state == ssl_earlydata_await) {
       goto out;
     }
-
-    /* if ssl is expecting something, check if it is available. */
-    if(!nonblocking && connssl->io_need) {
-      curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND) ?
-        sockfd : CURL_SOCKET_BAD;
-      curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV) ?
-        sockfd : CURL_SOCKET_BAD;
-
-      what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
-                               timeout_ms);
-      if(what < 0) {
-        /* fatal error */
-        failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
-        result = CURLE_SSL_CONNECT_ERROR;
-        goto out;
-      }
-      if(0 == what) {
-        /* timeout */
-        failf(data, "SSL connection timeout");
-        result = CURLE_OPERATION_TIMEDOUT;
+    else if(connssl->earlydata_state == ssl_earlydata_sending) {
+      result = ossl_send_earlydata(cf, data);
+      if(result)
         goto out;
-      }
-      /* socket is readable or writable */
+      connssl->earlydata_state = ssl_earlydata_sent;
     }
+#endif
+    DEBUGASSERT((connssl->earlydata_state == ssl_earlydata_none) ||
+                (connssl->earlydata_state == ssl_earlydata_sent));
 
-    /* Run transaction, and return to the caller if it failed or if this
-     * connection is done nonblocking and this loop would execute again. This
-     * permits the owner of a multi handle to abort a connection attempt
-     * before step2 has completed while ensuring that a client using select()
-     * or epoll() will always have a valid fdset to wait on.
-     */
     result = ossl_connect_step2(cf, data);
-    if(result || (nonblocking && (ssl_connect_2 == connssl->connecting_state)))
+    if(result)
       goto out;
-
-  } /* repeat step2 until all transactions are done. */
+  }
 
   if(ssl_connect_3 == connssl->connecting_state) {
+    CURL_TRC_CF(data, cf, "ossl_connect, step3");
     result = ossl_connect_step3(cf, data);
     if(result)
       goto out;
+    connssl->connecting_state = ssl_connect_done;
+#ifdef HAVE_OPENSSL_EARLYDATA
+    if(connssl->earlydata_state > ssl_earlydata_none) {
+      struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
+      /* We should be in this state by now */
+      DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_sent);
+      connssl->earlydata_state =
+        (SSL_get_early_data_status(octx->ssl) == SSL_EARLY_DATA_ACCEPTED) ?
+        ssl_earlydata_accepted : ssl_earlydata_rejected;
+    }
+#endif
   }
 
   if(ssl_connect_done == connssl->connecting_state) {
+    CURL_TRC_CF(data, cf, "ossl_connect, done");
     connssl->state = ssl_connection_complete;
-    *done = TRUE;
   }
-  else
-    *done = FALSE;
-
-  /* Reset our connect state machine */
-  connssl->connecting_state = ssl_connect_1;
 
 out:
+  if(result == CURLE_AGAIN) {
+    *done = FALSE;
+    return CURLE_OK;
+  }
+  *done = ((connssl->state == ssl_connection_complete) ||
+           (connssl->state == ssl_connection_deferred));
   return result;
 }
 
-static CURLcode ossl_connect_nonblocking(struct Curl_cfilter *cf,
-                                         struct Curl_easy *data,
-                                         bool *done)
-{
-  return ossl_connect_common(cf, data, TRUE, done);
-}
-
-static CURLcode ossl_connect(struct Curl_cfilter *cf,
-                             struct Curl_easy *data)
-{
-  CURLcode result;
-  bool done = FALSE;
-
-  result = ossl_connect_common(cf, data, FALSE, &done);
-  if(result)
-    return result;
-
-  DEBUGASSERT(done);
-
-  return CURLE_OK;
-}
-
 static bool ossl_data_pending(struct Curl_cfilter *cf,
                               const struct Curl_easy *data)
 {
@@ -5321,10 +5464,10 @@ static CURLcode ossl_get_channel_binding(struct Curl_easy *data, int sockindex,
   }
 
   /* Append "tls-server-end-point:" */
-  if(Curl_dyn_addn(binding, prefix, sizeof(prefix) - 1) != CURLE_OK)
+  if(curlx_dyn_addn(binding, prefix, sizeof(prefix) - 1) != CURLE_OK)
     return CURLE_OUT_OF_MEMORY;
   /* Append digest */
-  if(Curl_dyn_addn(binding, buf, length))
+  if(curlx_dyn_addn(binding, buf, length))
     return CURLE_OUT_OF_MEMORY;
 
   return CURLE_OK;
@@ -5340,7 +5483,6 @@ static CURLcode ossl_get_channel_binding(struct Curl_easy *data, int sockindex,
 size_t Curl_ossl_version(char *buffer, size_t size)
 {
 #ifdef LIBRESSL_VERSION_NUMBER
-#ifdef HAVE_OPENSSL_VERSION
   char *p;
   size_t count;
   const char *ver = OpenSSL_version(OPENSSL_VERSION);
@@ -5354,13 +5496,6 @@ size_t Curl_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)
 #ifdef CURL_BORINGSSL_VERSION
   return msnprintf(buffer, size, "%s/%s",
@@ -5384,25 +5519,19 @@ size_t Curl_ossl_version(char *buffer, size_t size)
   sub[2]='\0';
   sub[1]='\0';
   ssleay_value = OpenSSL_version_num();
-  if(ssleay_value < 0x906000) {
-    ssleay_value = SSLEAY_VERSION_NUMBER;
-    sub[0]='\0';
-  }
-  else {
-    if(ssleay_value&0xff0) {
-      int minor_ver = (ssleay_value >> 4) & 0xff;
-      if(minor_ver > 26) {
-        /* handle extended version introduced for 0.9.8za */
-        sub[1] = (char) ((minor_ver - 1) % 26 + 'a' + 1);
-        sub[0] = 'z';
-      }
-      else {
-        sub[0] = (char) (minor_ver + 'a' - 1);
-      }
+  if(ssleay_value&0xff0) {
+    int minor_ver = (ssleay_value >> 4) & 0xff;
+    if(minor_ver > 26) {
+      /* handle extended version introduced for 0.9.8za */
+      sub[1] = (char) ((minor_ver - 1) % 26 + 'a' + 1);
+      sub[0] = 'z';
+    }
+    else {
+      sub[0] = (char) (minor_ver + 'a' - 1);
     }
-    else
-      sub[0]='\0';
   }
+  else
+    sub[0]='\0';
 
   return msnprintf(buffer, size, "%s/%lx.%lx.%lx%s"
 #ifdef OPENSSL_FIPS
@@ -5435,7 +5564,7 @@ static CURLcode ossl_random(struct Curl_easy *data,
   return rc == 1 ? CURLE_OK : CURLE_FAILED_INIT;
 }
 
-#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256)
+#ifndef OPENSSL_NO_SHA256
 static CURLcode ossl_sha256sum(const unsigned char *tmp, /* input */
                                size_t tmplen,
                                unsigned char *sha256sum /* output */,
@@ -5461,8 +5590,7 @@ static CURLcode ossl_sha256sum(const unsigned char *tmp, /* input */
 
 static bool ossl_cert_status_request(void)
 {
-#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
-  !defined(OPENSSL_NO_OCSP)
+#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_OCSP)
   return TRUE;
 #else
   return FALSE;
@@ -5490,6 +5618,9 @@ const struct Curl_ssl Curl_ssl_openssl = {
 #ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
   SSLSUPP_TLS13_CIPHERSUITES |
 #endif
+#ifdef HAVE_SSL_CTX_SET1_SIGALGS
+  SSLSUPP_SIGNATURE_ALGORITHMS |
+#endif
 #ifdef USE_ECH_OPENSSL
   SSLSUPP_ECH |
 #endif
@@ -5507,16 +5638,15 @@ const struct Curl_ssl Curl_ssl_openssl = {
   ossl_random,              /* random */
   ossl_cert_status_request, /* cert_status_request */
   ossl_connect,             /* connect */
-  ossl_connect_nonblocking, /* connect_nonblocking */
   Curl_ssl_adjust_pollset,  /* adjust_pollset */
   ossl_get_internals,       /* get_internals */
   ossl_close,               /* close_one */
   ossl_close_all,           /* close_all */
-  ossl_set_engine,          /* set_engine */
+  ossl_set_engine,          /* set_engine or provider */
   ossl_set_engine_default,  /* set_engine_default */
   ossl_engines_list,        /* engines_list */
   NULL,                     /* false_start */
-#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256)
+#ifndef OPENSSL_NO_SHA256
   ossl_sha256sum,           /* sha256sum */
 #else
   NULL,                     /* sha256sum */
diff --git a/extra/curl/curl-8.12.1/lib/vtls/openssl.h b/extra/curl/curl-8.14.1/lib/vtls/openssl.h
similarity index 81%
rename from extra/curl/curl-8.12.1/lib/vtls/openssl.h
rename to extra/curl/curl-8.14.1/lib/vtls/openssl.h
index b2940f430993..8d063e25acc5 100644
--- a/extra/curl/curl-8.12.1/lib/vtls/openssl.h
+++ b/extra/curl/curl-8.14.1/lib/vtls/openssl.h
@@ -24,7 +24,7 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #ifdef USE_OPENSSL
 /*
@@ -35,7 +35,7 @@
 #include <openssl/ossl_typ.h>
 #include <openssl/ssl.h>
 
-#include "urldata.h"
+#include "../urldata.h"
 
 /*
  * Whether SSL_CTX_set_keylog_callback is available.
@@ -49,7 +49,16 @@
 #define HAVE_KEYLOG_CALLBACK
 #endif
 
+/* Check for OpenSSL 1.1.1 which has early data support. */
+#undef HAVE_OPENSSL_EARLYDATA
+#if OPENSSL_VERSION_NUMBER >= 0x10100010L && defined(TLS1_3_VERSION) && \
+    !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC)
+#define HAVE_OPENSSL_EARLYDATA
+#endif
+
+struct alpn_spec;
 struct ssl_peer;
+struct Curl_ssl_session;
 
 /* Struct to hold a curl OpenSSL instance */
 struct ossl_ctx {
@@ -75,16 +84,22 @@ typedef CURLcode Curl_ossl_ctx_setup_cb(struct Curl_cfilter *cf,
                                         void *user_data);
 
 typedef int Curl_ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid);
+typedef CURLcode Curl_ossl_init_session_reuse_cb(struct Curl_cfilter *cf,
+                                                 struct Curl_easy *data,
+                                                 struct alpn_spec *alpns,
+                                                 struct Curl_ssl_session *scs,
+                                                 bool *do_early_data);
 
 CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
                             struct Curl_cfilter *cf,
                             struct Curl_easy *data,
                             struct ssl_peer *peer,
-                            const unsigned char *alpn, size_t alpn_len,
+                            const struct alpn_spec *alpns,
                             Curl_ossl_ctx_setup_cb *cb_setup,
                             void *cb_user_data,
                             Curl_ossl_new_session_cb *cb_new_session,
-                            void *ssl_user_data);
+                            void *ssl_user_data,
+                            Curl_ossl_init_session_reuse_cb *sess_reuse_cb);
 
 #if (OPENSSL_VERSION_NUMBER < 0x30000000L)
 #define SSL_get1_peer_certificate SSL_get_peer_certificate
@@ -113,7 +128,9 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf,
                                const char *ssl_peer_key,
                                SSL_SESSION *ssl_sessionid,
                                int ietf_tls_id,
-                               const char *alpn);
+                               const char *alpn,
+                               unsigned char *quic_tp,
+                               size_t quic_tp_len);
 
 /*
  * Get the server cert, verify it and show it, etc., only call failf() if
diff --git a/extra/curl/curl-8.12.1/lib/vtls/rustls.c b/extra/curl/curl-8.14.1/lib/vtls/rustls.c
similarity index 52%
rename from extra/curl/curl-8.12.1/lib/vtls/rustls.c
rename to extra/curl/curl-8.14.1/lib/vtls/rustls.c
index 948d0e9cce63..cb9fd6230a5c 100644
--- a/extra/curl/curl-8.12.1/lib/vtls/rustls.c
+++ b/extra/curl/curl-8.14.1/lib/vtls/rustls.c
@@ -8,6 +8,7 @@
  * Copyright (C) Jacob Hoffman-Andrews,
  * <github@hoffman-andrews.com>
  * Copyright (C) kpcyrd, <kpcyrd@archlinux.org>
+ * Copyright (C) Daniel McCarney, <daniel@binaryparadox.net>
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -23,27 +24,24 @@
  * SPDX-License-Identifier: curl
  *
  ***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #ifdef USE_RUSTLS
 
-#include "curl_printf.h"
+#include "../curl_printf.h"
 
-#include <errno.h>
 #include <rustls.h>
 
-#include "inet_pton.h"
-#include "urldata.h"
-#include "sendf.h"
+#include "../curlx/inet_pton.h"
+#include "../urldata.h"
+#include "../sendf.h"
 #include "vtls.h"
 #include "vtls_int.h"
 #include "rustls.h"
-#include "select.h"
-#include "strerror.h"
-#include "multiif.h"
-#include "connect.h" /* for the connect timeout */
+#include "keylog.h"
+#include "../strerror.h"
 #include "cipher_suite.h"
-#include "rand.h"
+#include "x509asn1.h"
 
 struct rustls_ssl_backend_data
 {
@@ -55,7 +53,7 @@ struct rustls_ssl_backend_data
 };
 
 /* For a given rustls_result error code, return the best-matching CURLcode. */
-static CURLcode map_error(rustls_result r)
+static CURLcode map_error(const rustls_result r)
 {
   if(rustls_result_is_cert_error(r)) {
     return CURLE_PEER_FAILED_VERIFICATION;
@@ -70,10 +68,19 @@ static CURLcode map_error(rustls_result r)
   }
 }
 
+static void
+rustls_failf(struct Curl_easy *data, const rustls_result rr, const char *msg)
+{
+  char errorbuf[STRERROR_LEN];
+  size_t errorlen;
+  rustls_error(rr, errorbuf, sizeof(errorbuf), &errorlen);
+  failf(data, "%s: %.*s", msg, (int)errorlen, errorbuf);
+}
+
 static bool
 cr_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data)
 {
-  struct ssl_connect_data *ctx = cf->ctx;
+  const struct ssl_connect_data *ctx = cf->ctx;
   struct rustls_ssl_backend_data *backend;
 
   (void)data;
@@ -90,7 +97,7 @@ struct io_ctx {
 static int
 read_cb(void *userdata, uint8_t *buf, uintptr_t len, uintptr_t *out_n)
 {
-  struct io_ctx *io_ctx = userdata;
+  const struct io_ctx *io_ctx = userdata;
   struct ssl_connect_data *const connssl = io_ctx->cf->ctx;
   CURLcode result;
   int ret = 0;
@@ -98,6 +105,7 @@ read_cb(void *userdata, uint8_t *buf, uintptr_t len, uintptr_t *out_n)
                                     (char *)buf, len, &result);
   if(nread < 0) {
     nread = 0;
+    /* !checksrc! disable ERRNOVAR 4 */
     if(CURLE_AGAIN == result)
       ret = EAGAIN;
     else
@@ -114,7 +122,7 @@ read_cb(void *userdata, uint8_t *buf, uintptr_t len, uintptr_t *out_n)
 static int
 write_cb(void *userdata, const uint8_t *buf, uintptr_t len, uintptr_t *out_n)
 {
-  struct io_ctx *io_ctx = userdata;
+  const struct io_ctx *io_ctx = userdata;
   CURLcode result;
   int ret = 0;
   ssize_t nwritten = Curl_conn_cf_send(io_ctx->cf->next, io_ctx->data,
@@ -136,7 +144,7 @@ write_cb(void *userdata, const uint8_t *buf, uintptr_t len, uintptr_t *out_n)
 static ssize_t tls_recv_more(struct Curl_cfilter *cf,
                              struct Curl_easy *data, CURLcode *err)
 {
-  struct ssl_connect_data *const connssl = cf->ctx;
+  const struct ssl_connect_data *const connssl = cf->ctx;
   struct rustls_ssl_backend_data *const backend =
     (struct rustls_ssl_backend_data *)connssl->backend;
   struct io_ctx io_ctx;
@@ -162,11 +170,7 @@ static ssize_t tls_recv_more(struct Curl_cfilter *cf,
 
   rresult = rustls_connection_process_new_packets(backend->conn);
   if(rresult != RUSTLS_RESULT_OK) {
-    char errorbuf[255];
-    size_t errorlen;
-    rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen);
-    failf(data, "rustls_connection_process_new_packets: %.*s",
-      (int)errorlen, errorbuf);
+    rustls_failf(data, rresult, "rustls_connection_process_new_packets");
     *err = map_error(rresult);
     return -1;
   }
@@ -192,7 +196,7 @@ static ssize_t
 cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
             char *plainbuf, size_t plainlen, CURLcode *err)
 {
-  struct ssl_connect_data *const connssl = cf->ctx;
+  const struct ssl_connect_data *const connssl = cf->ctx;
   struct rustls_ssl_backend_data *const backend =
     (struct rustls_ssl_backend_data *)connssl->backend;
   struct rustls_connection *rconn = NULL;
@@ -232,10 +236,7 @@ cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
     }
     else if(rresult != RUSTLS_RESULT_OK) {
       /* n always equals 0 in this case, do not need to check it */
-      char errorbuf[255];
-      size_t errorlen;
-      rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen);
-      failf(data, "rustls_connection_read: %.*s", (int)errorlen, errorbuf);
+      rustls_failf(data, rresult, "rustls_connection_read");
       *err = CURLE_RECV_ERROR;
       nread = -1;
       goto out;
@@ -278,7 +279,6 @@ static CURLcode cr_flush_out(struct Curl_cfilter *cf, struct Curl_easy *data,
   rustls_io_result io_error;
   size_t tlswritten = 0;
   size_t tlswritten_total = 0;
-  CURLcode result = CURLE_OK;
 
   io_ctx.cf = cf;
   io_ctx.data = data;
@@ -304,7 +304,7 @@ static CURLcode cr_flush_out(struct Curl_cfilter *cf, struct Curl_easy *data,
     CURL_TRC_CF(data, cf, "cf_send: wrote %zu TLS bytes", tlswritten);
     tlswritten_total += tlswritten;
   }
-  return result;
+  return CURLE_OK;
 }
 
 /*
@@ -321,14 +321,11 @@ static ssize_t
 cr_send(struct Curl_cfilter *cf, struct Curl_easy *data,
         const void *plainbuf, size_t plainlen, CURLcode *err)
 {
-  struct ssl_connect_data *const connssl = cf->ctx;
+  const struct ssl_connect_data *const connssl = cf->ctx;
   struct rustls_ssl_backend_data *const backend =
     (struct rustls_ssl_backend_data *)connssl->backend;
   struct rustls_connection *rconn = NULL;
   size_t plainwritten = 0;
-  rustls_result rresult;
-  char errorbuf[256];
-  size_t errorlen;
   const unsigned char *buf = plainbuf;
   size_t blen = plainlen;
   ssize_t nwritten = 0;
@@ -360,11 +357,11 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data,
   }
 
   if(blen > 0) {
+    rustls_result rresult;
     CURL_TRC_CF(data, cf, "cf_send: adding %zu plain bytes to Rustls", blen);
     rresult = rustls_connection_write(rconn, buf, blen, &plainwritten);
     if(rresult != RUSTLS_RESULT_OK) {
-      rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen);
-      failf(data, "rustls_connection_write: %.*s", (int)errorlen, errorbuf);
+      rustls_failf(data, rresult, "rustls_connection_write");
       *err = CURLE_WRITE_ERROR;
       return -1;
     }
@@ -418,9 +415,9 @@ read_file_into(const char *filename,
 
   while(!feof(f)) {
     uint8_t buf[256];
-    size_t rr = fread(buf, 1, sizeof(buf), f);
+    const size_t rr = fread(buf, 1, sizeof(buf), f);
     if(rr == 0 ||
-       CURLE_OK != Curl_dyn_addn(out, buf, rr)) {
+       CURLE_OK != curlx_dyn_addn(out, buf, rr)) {
       fclose(f);
       return 0;
     }
@@ -436,8 +433,8 @@ cr_get_selected_ciphers(struct Curl_easy *data,
                         const struct rustls_supported_ciphersuite **selected,
                         size_t *selected_size)
 {
-  size_t supported_len = *selected_size;
-  size_t default_len = rustls_default_crypto_provider_ciphersuites_len();
+  const size_t supported_len = *selected_size;
+  const size_t default_len = rustls_default_crypto_provider_ciphersuites_len();
   const struct rustls_supported_ciphersuite *entry;
   const char *ciphers = ciphers12;
   size_t count = 0, default13_count = 0, i, j;
@@ -522,247 +519,608 @@ cr_get_selected_ciphers(struct Curl_easy *data,
   *selected_size = count;
 }
 
+static void
+cr_keylog_log_cb(struct rustls_str label,
+                 const uint8_t *client_random, size_t client_random_len,
+                 const uint8_t *secret, size_t secret_len)
+{
+  char clabel[KEYLOG_LABEL_MAXLEN];
+  (void)client_random_len;
+  DEBUGASSERT(client_random_len == CLIENT_RANDOM_SIZE);
+  /* Turning a "rustls_str" into a null delimited "c" string */
+  msnprintf(clabel, label.len + 1, "%.*s", (int)label.len, label.data);
+  Curl_tls_keylog_write(clabel, client_random, secret, secret_len);
+}
+
 static CURLcode
-cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data,
-                struct rustls_ssl_backend_data *const backend)
+init_config_builder(struct Curl_easy *data,
+                    const struct ssl_primary_config *conn_config,
+                    struct rustls_client_config_builder **config_builder)
 {
-  struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+  const struct rustls_supported_ciphersuite **cipher_suites = NULL;
   struct rustls_crypto_provider_builder *custom_provider_builder = NULL;
   const struct rustls_crypto_provider *custom_provider = NULL;
-  struct rustls_connection *rconn = NULL;
-  struct rustls_client_config_builder *config_builder = NULL;
-  const struct rustls_root_cert_store *roots = NULL;
-  struct rustls_root_cert_store_builder *roots_builder = NULL;
-  struct rustls_web_pki_server_cert_verifier_builder *verifier_builder = NULL;
-  struct rustls_server_cert_verifier *server_cert_verifier = NULL;
-  const struct curl_blob *ca_info_blob = conn_config->ca_info_blob;
-  const char * const ssl_cafile =
-    /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */
-    (ca_info_blob ? NULL : conn_config->CAfile);
-  const bool verifypeer = conn_config->verifypeer;
-  char errorbuf[256];
-  size_t errorlen;
-  rustls_result result;
 
-  DEBUGASSERT(backend);
-  rconn = backend->conn;
+  uint16_t tls_versions[2] = {
+      RUSTLS_TLS_VERSION_TLSV1_2,
+      RUSTLS_TLS_VERSION_TLSV1_3,
+  };
+  size_t tls_versions_len = 2;
+  size_t cipher_suites_len =
+    rustls_default_crypto_provider_ciphersuites_len();
 
-  {
-    uint16_t tls_versions[2] = {
-        RUSTLS_TLS_VERSION_TLSV1_2,
-        RUSTLS_TLS_VERSION_TLSV1_3,
-    };
-    size_t tls_versions_len = 2;
-    const struct rustls_supported_ciphersuite **cipher_suites;
-    size_t cipher_suites_len =
-      rustls_default_crypto_provider_ciphersuites_len();
-
-    switch(conn_config->version) {
-    case CURL_SSLVERSION_DEFAULT:
-    case CURL_SSLVERSION_TLSv1:
-    case CURL_SSLVERSION_TLSv1_0:
-    case CURL_SSLVERSION_TLSv1_1:
-    case CURL_SSLVERSION_TLSv1_2:
-      break;
-    case CURL_SSLVERSION_TLSv1_3:
-      tls_versions[0] = RUSTLS_TLS_VERSION_TLSV1_3;
+  CURLcode result = CURLE_OK;
+  rustls_result rr;
+
+  switch(conn_config->version) {
+  case CURL_SSLVERSION_DEFAULT:
+  case CURL_SSLVERSION_TLSv1:
+  case CURL_SSLVERSION_TLSv1_0:
+  case CURL_SSLVERSION_TLSv1_1:
+  case CURL_SSLVERSION_TLSv1_2:
+    break;
+  case CURL_SSLVERSION_TLSv1_3:
+    tls_versions[0] = RUSTLS_TLS_VERSION_TLSV1_3;
+    tls_versions_len = 1;
+    break;
+  default:
+    failf(data, "rustls: unsupported minimum TLS version value");
+    result = CURLE_BAD_FUNCTION_ARGUMENT;
+    goto cleanup;
+  }
+
+  switch(conn_config->version_max) {
+  case CURL_SSLVERSION_MAX_DEFAULT:
+  case CURL_SSLVERSION_MAX_NONE:
+  case CURL_SSLVERSION_MAX_TLSv1_3:
+    break;
+  case CURL_SSLVERSION_MAX_TLSv1_2:
+    if(tls_versions[0] == RUSTLS_TLS_VERSION_TLSV1_2) {
       tls_versions_len = 1;
       break;
-    default:
-      failf(data, "rustls: unsupported minimum TLS version value");
-      return CURLE_BAD_FUNCTION_ARGUMENT;
     }
+    FALLTHROUGH();
+  case CURL_SSLVERSION_MAX_TLSv1_1:
+  case CURL_SSLVERSION_MAX_TLSv1_0:
+  default:
+    failf(data, "rustls: unsupported maximum TLS version value");
+    result = CURLE_BAD_FUNCTION_ARGUMENT;
+    goto cleanup;
+  }
 
-    switch(conn_config->version_max) {
-    case CURL_SSLVERSION_MAX_DEFAULT:
-    case CURL_SSLVERSION_MAX_NONE:
-    case CURL_SSLVERSION_MAX_TLSv1_3:
-      break;
-    case CURL_SSLVERSION_MAX_TLSv1_2:
-      if(tls_versions[0] == RUSTLS_TLS_VERSION_TLSV1_2) {
-        tls_versions_len = 1;
-        break;
-      }
-      FALLTHROUGH();
-    case CURL_SSLVERSION_MAX_TLSv1_1:
-    case CURL_SSLVERSION_MAX_TLSv1_0:
-    default:
-      failf(data, "rustls: unsupported maximum TLS version value");
-      return CURLE_BAD_FUNCTION_ARGUMENT;
-    }
+#if defined(USE_ECH)
+  if(ECH_ENABLED(data)) {
+    tls_versions[0] = RUSTLS_TLS_VERSION_TLSV1_3;
+    tls_versions_len = 1;
+    infof(data, "rustls: ECH enabled, forcing TLSv1.3");
+  }
+#endif /* USE_ECH */
 
-    cipher_suites = malloc(sizeof(cipher_suites) * (cipher_suites_len));
-    if(!cipher_suites)
-      return CURLE_OUT_OF_MEMORY;
-
-    cr_get_selected_ciphers(data,
-                            conn_config->cipher_list,
-                            conn_config->cipher_list13,
-                            cipher_suites, &cipher_suites_len);
-    if(cipher_suites_len == 0) {
-      failf(data, "rustls: no supported cipher in list");
-      free(cipher_suites);
-      return CURLE_SSL_CIPHER;
-    }
+  cipher_suites = malloc(sizeof(cipher_suites) * (cipher_suites_len));
+  if(!cipher_suites) {
+    result = CURLE_OUT_OF_MEMORY;
+    goto cleanup;
+  }
 
-    result = rustls_crypto_provider_builder_new_from_default(
-      &custom_provider_builder);
-    if(result != RUSTLS_RESULT_OK) {
-      failf(data,
-            "rustls: failed to create crypto provider builder from default");
-      return CURLE_SSL_CIPHER;
-    }
+  cr_get_selected_ciphers(data,
+                          conn_config->cipher_list,
+                          conn_config->cipher_list13,
+                          cipher_suites, &cipher_suites_len);
+  if(cipher_suites_len == 0) {
+    failf(data, "rustls: no supported cipher in list");
+    result = CURLE_SSL_CIPHER;
+    goto cleanup;
+  }
 
-    result =
-      rustls_crypto_provider_builder_set_cipher_suites(
-        custom_provider_builder,
-        cipher_suites,
-        cipher_suites_len);
-    if(result != RUSTLS_RESULT_OK) {
-      failf(data,
-            "rustls: failed to set ciphersuites for crypto provider builder");
-      rustls_crypto_provider_builder_free(custom_provider_builder);
-      return CURLE_SSL_CIPHER;
-    }
+  rr = rustls_crypto_provider_builder_new_from_default(
+    &custom_provider_builder);
+  if(rr != RUSTLS_RESULT_OK) {
+    rustls_failf(data, rr,
+      "failed to create crypto provider builder from default");
+    result = CURLE_SSL_CIPHER;
+    goto cleanup;
+  }
 
-    result = rustls_crypto_provider_builder_build(
-      custom_provider_builder, &custom_provider);
-    if(result != RUSTLS_RESULT_OK) {
-      failf(data, "rustls: failed to build custom crypto provider");
-      rustls_crypto_provider_builder_free(custom_provider_builder);
-      return CURLE_SSL_CIPHER;
-    }
+  rr =
+    rustls_crypto_provider_builder_set_cipher_suites(
+      custom_provider_builder,
+      cipher_suites,
+      cipher_suites_len);
+  if(rr != RUSTLS_RESULT_OK) {
+    rustls_failf(data, rr,
+      "failed to set ciphersuites for crypto provider builder");
+    result = CURLE_SSL_CIPHER;
+    goto cleanup;
+  }
 
-    result = rustls_client_config_builder_new_custom(custom_provider,
+  rr = rustls_crypto_provider_builder_build(
+    custom_provider_builder, &custom_provider);
+  if(rr != RUSTLS_RESULT_OK) {
+    rustls_failf(data, rr, "failed to build custom crypto provider");
+    result = CURLE_SSL_CIPHER;
+    goto cleanup;
+  }
+
+  rr = rustls_client_config_builder_new_custom(custom_provider,
                                                      tls_versions,
                                                      tls_versions_len,
-                                                     &config_builder);
+                                                     config_builder);
+  if(rr != RUSTLS_RESULT_OK) {
+    rustls_failf(data, rr, "failed to create client config builder");
+    result = CURLE_SSL_CIPHER;
+    goto cleanup;
+  }
+
+cleanup:
+  if(cipher_suites) {
     free(cipher_suites);
-    if(result != RUSTLS_RESULT_OK) {
-      failf(data, "rustls: failed to create client config");
-      return CURLE_SSL_CIPHER;
-    }
   }
+  if(custom_provider_builder) {
+    rustls_crypto_provider_builder_free(custom_provider_builder);
+  }
+  if(custom_provider) {
+    rustls_crypto_provider_free(custom_provider);
+  }
+  return result;
+}
 
-  rustls_crypto_provider_builder_free(custom_provider_builder);
-  rustls_crypto_provider_free(custom_provider);
+static void
+init_config_builder_alpn(struct Curl_easy *data,
+                         const struct ssl_connect_data *connssl,
+                         struct rustls_client_config_builder *config_builder) {
+  struct alpn_proto_buf proto;
+  rustls_slice_bytes alpn[ALPN_ENTRIES_MAX];
+  size_t i;
 
-  if(connssl->alpn) {
-    struct alpn_proto_buf proto;
-    rustls_slice_bytes alpn[ALPN_ENTRIES_MAX];
-    size_t i;
+  for(i = 0; i < connssl->alpn->count; ++i) {
+    alpn[i].data = (const uint8_t *)connssl->alpn->entries[i];
+    alpn[i].len = strlen(connssl->alpn->entries[i]);
+  }
+  rustls_client_config_builder_set_alpn_protocols(config_builder, alpn,
+                                                  connssl->alpn->count);
+  Curl_alpn_to_proto_str(&proto, connssl->alpn);
+  infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
+}
 
-    for(i = 0; i < connssl->alpn->count; ++i) {
-      alpn[i].data = (const uint8_t *)connssl->alpn->entries[i];
-      alpn[i].len = strlen(connssl->alpn->entries[i]);
-    }
-    rustls_client_config_builder_set_alpn_protocols(config_builder, alpn,
-                                                    connssl->alpn->count);
-    Curl_alpn_to_proto_str(&proto, connssl->alpn);
-    infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
+static CURLcode
+init_config_builder_verifier_crl(
+  struct Curl_easy *data,
+  const struct ssl_primary_config *conn_config,
+  struct rustls_web_pki_server_cert_verifier_builder *builder)
+{
+  CURLcode result = CURLE_OK;
+  struct dynbuf crl_contents;
+  rustls_result rr;
+
+  curlx_dyn_init(&crl_contents, DYN_CRLFILE_SIZE);
+  if(!read_file_into(conn_config->CRLfile, &crl_contents)) {
+    failf(data, "rustls: failed to read revocation list file");
+    result = CURLE_SSL_CRL_BADFILE;
+    goto cleanup;
   }
-  if(!verifypeer) {
-    rustls_client_config_builder_dangerous_set_certificate_verifier(
-      config_builder, cr_verify_none);
+
+  rr = rustls_web_pki_server_cert_verifier_builder_add_crl(
+    builder,
+    curlx_dyn_uptr(&crl_contents),
+    curlx_dyn_len(&crl_contents));
+  if(rr != RUSTLS_RESULT_OK) {
+    rustls_failf(data, rr, "failed to parse revocation list");
+    result = CURLE_SSL_CRL_BADFILE;
+    goto cleanup;
   }
-  else if(ca_info_blob || ssl_cafile) {
-    roots_builder = rustls_root_cert_store_builder_new();
-
-    if(ca_info_blob) {
-      /* Enable strict parsing only if verification is not disabled. */
-      result = rustls_root_cert_store_builder_add_pem(roots_builder,
-                                                      ca_info_blob->data,
-                                                      ca_info_blob->len,
-                                                      verifypeer);
-      if(result != RUSTLS_RESULT_OK) {
-        failf(data, "rustls: failed to parse trusted certificates from blob");
-        rustls_root_cert_store_builder_free(roots_builder);
-        rustls_client_config_builder_free(config_builder);
-        return CURLE_SSL_CACERT_BADFILE;
-      }
+
+cleanup:
+  curlx_dyn_free(&crl_contents);
+  return result;
+}
+
+static CURLcode
+init_config_builder_verifier(struct Curl_easy *data,
+                             struct rustls_client_config_builder *builder,
+                             const struct ssl_primary_config *conn_config,
+                             const struct curl_blob *ca_info_blob,
+                             const char * const ssl_cafile) {
+  const struct rustls_root_cert_store *roots = NULL;
+  struct rustls_root_cert_store_builder *roots_builder = NULL;
+  struct rustls_web_pki_server_cert_verifier_builder *verifier_builder = NULL;
+  struct rustls_server_cert_verifier *server_cert_verifier = NULL;
+  rustls_result rr = RUSTLS_RESULT_OK;
+  CURLcode result = CURLE_OK;
+
+  roots_builder = rustls_root_cert_store_builder_new();
+  if(ca_info_blob) {
+    rr = rustls_root_cert_store_builder_add_pem(roots_builder,
+                                                ca_info_blob->data,
+                                                ca_info_blob->len,
+                                                1);
+    if(rr != RUSTLS_RESULT_OK) {
+      rustls_failf(data, rr, "failed to parse trusted certificates from blob");
+
+      result = CURLE_SSL_CACERT_BADFILE;
+      goto cleanup;
     }
-    else if(ssl_cafile) {
-      /* Enable strict parsing only if verification is not disabled. */
-      result = rustls_root_cert_store_builder_load_roots_from_file(
-        roots_builder, ssl_cafile, verifypeer);
-      if(result != RUSTLS_RESULT_OK) {
-        failf(data, "rustls: failed to load trusted certificates");
-        rustls_root_cert_store_builder_free(roots_builder);
-        rustls_client_config_builder_free(config_builder);
-        return CURLE_SSL_CACERT_BADFILE;
-      }
+  }
+  else if(ssl_cafile) {
+    rr = rustls_root_cert_store_builder_load_roots_from_file(roots_builder,
+                                                             ssl_cafile,
+                                                             1);
+    if(rr != RUSTLS_RESULT_OK) {
+      rustls_failf(data, rr, "failed to load trusted certificates");
+
+      result = CURLE_SSL_CACERT_BADFILE;
+      goto cleanup;
     }
+  }
 
-    result = rustls_root_cert_store_builder_build(roots_builder, &roots);
-    rustls_root_cert_store_builder_free(roots_builder);
-    if(result != RUSTLS_RESULT_OK) {
-      failf(data, "rustls: failed to build trusted root certificate store");
-      rustls_client_config_builder_free(config_builder);
-      return CURLE_SSL_CACERT_BADFILE;
+  rr = rustls_root_cert_store_builder_build(roots_builder, &roots);
+  if(rr != RUSTLS_RESULT_OK) {
+    rustls_failf(data, rr, "failed to build trusted root certificate store");
+    result = CURLE_SSL_CACERT_BADFILE;
+  }
+
+  verifier_builder = rustls_web_pki_server_cert_verifier_builder_new(roots);
+
+  if(conn_config->CRLfile) {
+    result = init_config_builder_verifier_crl(data,
+                                             conn_config,
+                                             verifier_builder);
+    if(result != CURLE_OK) {
+      goto cleanup;
     }
+  }
 
-    verifier_builder = rustls_web_pki_server_cert_verifier_builder_new(roots);
+  rr = rustls_web_pki_server_cert_verifier_builder_build(
+    verifier_builder, &server_cert_verifier);
+  if(rr != RUSTLS_RESULT_OK) {
+    rustls_failf(data, rr, "failed to build certificate verifier");
+    result = CURLE_SSL_CACERT_BADFILE;
+    goto cleanup;
+  }
+
+  rustls_client_config_builder_set_server_verifier(builder,
+                                                   server_cert_verifier);
+cleanup:
+  if(roots_builder) {
+    rustls_root_cert_store_builder_free(roots_builder);
+  }
+  if(roots) {
     rustls_root_cert_store_free(roots);
+  }
+  if(verifier_builder) {
+    rustls_web_pki_server_cert_verifier_builder_free(verifier_builder);
+  }
+  if(server_cert_verifier) {
+    rustls_server_cert_verifier_free(server_cert_verifier);
+  }
 
-    if(conn_config->CRLfile) {
-      struct dynbuf crl_contents;
-      Curl_dyn_init(&crl_contents, SIZE_MAX);
-      if(!read_file_into(conn_config->CRLfile, &crl_contents)) {
-        failf(data, "rustls: failed to read revocation list file");
-        Curl_dyn_free(&crl_contents);
-        rustls_web_pki_server_cert_verifier_builder_free(verifier_builder);
-        return CURLE_SSL_CRL_BADFILE;
-      }
+  return result;
+}
 
-      result = rustls_web_pki_server_cert_verifier_builder_add_crl(
-        verifier_builder,
-        Curl_dyn_uptr(&crl_contents),
-        Curl_dyn_len(&crl_contents));
-      Curl_dyn_free(&crl_contents);
-      if(result != RUSTLS_RESULT_OK) {
-        failf(data, "rustls: failed to parse revocation list");
-        rustls_web_pki_server_cert_verifier_builder_free(verifier_builder);
-        return CURLE_SSL_CRL_BADFILE;
-      }
+static CURLcode
+init_config_builder_platform_verifier(
+  struct Curl_easy *data,
+  struct rustls_client_config_builder *builder)
+{
+  struct rustls_server_cert_verifier *server_cert_verifier = NULL;
+  CURLcode result = CURLE_OK;
+  rustls_result rr;
+
+  rr = rustls_platform_server_cert_verifier(&server_cert_verifier);
+  if(rr != RUSTLS_RESULT_OK) {
+    rustls_failf(data, rr, "failed to create platform certificate verifier");
+    result = CURLE_SSL_CACERT_BADFILE;
+    goto cleanup;
+  }
+
+  rustls_client_config_builder_set_server_verifier(builder,
+                                                   server_cert_verifier);
+
+cleanup:
+  if(server_cert_verifier) {
+    rustls_server_cert_verifier_free(server_cert_verifier);
+  }
+  return result;
+}
+
+static CURLcode
+init_config_builder_keylog(struct Curl_easy *data,
+                           struct rustls_client_config_builder *builder)
+{
+  rustls_result rr;
+
+  Curl_tls_keylog_open();
+  if(!Curl_tls_keylog_enabled()) {
+    return CURLE_OK;
+  }
+
+  rr = rustls_client_config_builder_set_key_log(builder,
+                                                cr_keylog_log_cb,
+                                                NULL);
+  if(rr != RUSTLS_RESULT_OK) {
+    rustls_failf(data, rr, "rustls_client_config_builder_set_key_log");
+    Curl_tls_keylog_close();
+    return map_error(rr);
+  }
+
+  return CURLE_OK;
+}
+
+static CURLcode
+init_config_builder_client_auth(struct Curl_easy *data,
+                                const struct ssl_primary_config *conn_config,
+                                const struct ssl_config_data *ssl_config,
+                                struct rustls_client_config_builder *builder)
+{
+  struct dynbuf cert_contents;
+  struct dynbuf key_contents;
+  rustls_result rr;
+  const struct rustls_certified_key *certified_key = NULL;
+  CURLcode result = CURLE_OK;
+
+  if(conn_config->clientcert && !ssl_config->key) {
+    failf(data, "rustls: must provide key with certificate '%s'",
+          conn_config->clientcert);
+    return CURLE_SSL_CERTPROBLEM;
+  }
+  else if(!conn_config->clientcert && ssl_config->key) {
+    failf(data, "rustls: must provide certificate with key '%s'",
+          conn_config->clientcert);
+    return CURLE_SSL_CERTPROBLEM;
+  }
+
+  curlx_dyn_init(&cert_contents, DYN_CERTFILE_SIZE);
+  curlx_dyn_init(&key_contents, DYN_KEYFILE_SIZE);
+
+  if(!read_file_into(conn_config->clientcert, &cert_contents)) {
+    failf(data, "rustls: failed to read client certificate file: '%s'",
+          conn_config->clientcert);
+    result = CURLE_SSL_CERTPROBLEM;
+    goto cleanup;
+  }
+
+  if(!read_file_into(ssl_config->key, &key_contents)) {
+    failf(data, "rustls: failed to read key file: '%s'", ssl_config->key);
+    result = CURLE_SSL_CERTPROBLEM;
+    goto cleanup;
+  }
+
+  rr = rustls_certified_key_build(curlx_dyn_uptr(&cert_contents),
+                                  curlx_dyn_len(&cert_contents),
+                                  curlx_dyn_uptr(&key_contents),
+                                  curlx_dyn_len(&key_contents),
+                                  &certified_key);
+  if(rr != RUSTLS_RESULT_OK) {
+    rustls_failf(data, rr, "rustls: failed to build certified key");
+    result = CURLE_SSL_CERTPROBLEM;
+    goto cleanup;
+  }
+
+  rr = rustls_certified_key_keys_match(certified_key);
+  if(rr != RUSTLS_RESULT_OK) {
+    rustls_failf(data,
+                 rr,
+                 "rustls: client certificate and keypair files do not match:");
+
+    result = CURLE_SSL_CERTPROBLEM;
+    goto cleanup;
+  }
+
+  rr = rustls_client_config_builder_set_certified_key(builder,
+                                                      &certified_key,
+                                                      1);
+  if(rr != RUSTLS_RESULT_OK) {
+    rustls_failf(data, rr, "rustls: failed to set certified key");
+    result = CURLE_SSL_CERTPROBLEM;
+    goto cleanup;
+  }
+
+cleanup:
+  curlx_dyn_free(&cert_contents);
+  curlx_dyn_free(&key_contents);
+  if(certified_key) {
+    rustls_certified_key_free(certified_key);
+  }
+  return result;
+}
+
+#if defined(USE_ECH)
+static CURLcode
+init_config_builder_ech(struct Curl_easy *data,
+                        const struct ssl_connect_data *connssl,
+                        struct rustls_client_config_builder *builder)
+{
+  const rustls_hpke *hpke = rustls_supported_hpke();
+  unsigned char *ech_config = NULL;
+  size_t ech_config_len = 0;
+  struct Curl_dns_entry *dns = NULL;
+  struct Curl_https_rrinfo *rinfo = NULL;
+  CURLcode result = CURLE_OK;
+  rustls_result rr;
+
+  if(!hpke) {
+    failf(data,
+          "rustls: ECH unavailable, rustls-ffi built without "
+          "HPKE compatible crypto provider");
+    result = CURLE_SSL_CONNECT_ERROR;
+    goto cleanup;
+  }
+
+  if(data->set.str[STRING_ECH_PUBLIC]) {
+    failf(data, "rustls: ECH outername not supported");
+    result = CURLE_SSL_CONNECT_ERROR;
+    goto cleanup;
+  }
+
+  if(data->set.tls_ech == CURLECH_GREASE) {
+    rr = rustls_client_config_builder_enable_ech_grease(builder, hpke);
+    if(rr != RUSTLS_RESULT_OK) {
+      rustls_failf(data, rr, "rustls: failed to configure ECH GREASE");
+      result = CURLE_SSL_CONNECT_ERROR;
+      goto cleanup;
     }
+    return CURLE_OK;
+  }
 
-    result = rustls_web_pki_server_cert_verifier_builder_build(
-      verifier_builder, &server_cert_verifier);
-    rustls_web_pki_server_cert_verifier_builder_free(verifier_builder);
-    if(result != RUSTLS_RESULT_OK) {
-      failf(data, "rustls: failed to build certificate verifier");
-      rustls_server_cert_verifier_free(server_cert_verifier);
+  if(data->set.tls_ech & CURLECH_CLA_CFG && data->set.str[STRING_ECH_CONFIG]) {
+    const char *b64 = data->set.str[STRING_ECH_CONFIG];
+    size_t decode_result;
+    if(!b64) {
+      infof(data, "rustls: ECHConfig from command line empty");
+      result = CURLE_SSL_CONNECT_ERROR;
+      goto cleanup;
+    }
+    /* rustls-ffi expects the raw TLS encoded ECHConfigList bytes */
+    decode_result = curlx_base64_decode(b64, &ech_config, &ech_config_len);
+    if(decode_result || !ech_config) {
+      infof(data, "rustls: cannot base64 decode ECHConfig from command line");
+      result = CURLE_SSL_CONNECT_ERROR;
+      goto cleanup;
+    }
+  }
+  else {
+    if(connssl->peer.hostname) {
+      dns = Curl_dnscache_get(data, connssl->peer.hostname,
+                              connssl->peer.port, data->conn->ip_version);
+    }
+    if(!dns) {
+      failf(data, "rustls: ECH requested but no DNS info available");
+      result = CURLE_SSL_CONNECT_ERROR;
+      goto cleanup;
+    }
+    rinfo = dns->hinfo;
+    if(!rinfo || !rinfo->echconfiglist) {
+      failf(data, "rustls: ECH requested but no ECHConfig available");
+      result = CURLE_SSL_CONNECT_ERROR;
+      goto cleanup;
+    }
+    ech_config = rinfo->echconfiglist;
+    ech_config_len = rinfo->echconfiglist_len;
+  }
+
+  rr = rustls_client_config_builder_enable_ech(builder,
+                                               ech_config,
+                                               ech_config_len,
+                                               hpke);
+  if(rr != RUSTLS_RESULT_OK) {
+    rustls_failf(data, rr, "rustls: failed to configure ECH");
+    result = CURLE_SSL_CONNECT_ERROR;
+    goto cleanup;
+  }
+cleanup:
+  /* if we base64 decoded, we can free now */
+  if(data->set.tls_ech & CURLECH_CLA_CFG && data->set.str[STRING_ECH_CONFIG]) {
+    free(ech_config);
+  }
+  if(dns) {
+    Curl_resolv_unlink(data, &dns);
+  }
+  return result;
+}
+#endif /* USE_ECH */
+
+static CURLcode
+cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data,
+                struct rustls_ssl_backend_data *const backend)
+{
+  const struct ssl_connect_data *connssl = cf->ctx;
+  const struct ssl_primary_config *conn_config =
+    Curl_ssl_cf_get_primary_config(cf);
+  struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+  struct rustls_connection *rconn = NULL;
+  struct rustls_client_config_builder *config_builder = NULL;
+
+  const struct curl_blob *ca_info_blob = conn_config->ca_info_blob;
+  const char * const ssl_cafile =
+    /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */
+    (ca_info_blob ? NULL : conn_config->CAfile);
+  CURLcode result = CURLE_OK;
+  rustls_result rr;
+
+  DEBUGASSERT(backend);
+  rconn = backend->conn;
+
+  result = init_config_builder(data, conn_config, &config_builder);
+  if(result != CURLE_OK) {
+    return result;
+  }
+
+  if(connssl->alpn) {
+    init_config_builder_alpn(data, connssl, config_builder);
+  }
+
+  if(!conn_config->verifypeer) {
+    rustls_client_config_builder_dangerous_set_certificate_verifier(
+      config_builder, cr_verify_none);
+  }
+  else if(ssl_config->native_ca_store) {
+    result = init_config_builder_platform_verifier(data, config_builder);
+    if(result != CURLE_OK) {
+      rustls_client_config_builder_free(config_builder);
+      return result;
+    }
+  }
+  else if(ca_info_blob || ssl_cafile) {
+    result = init_config_builder_verifier(data,
+                                          config_builder,
+                                          conn_config,
+                                          ca_info_blob,
+                                          ssl_cafile);
+    if(result != CURLE_OK) {
       rustls_client_config_builder_free(config_builder);
-      return CURLE_SSL_CACERT_BADFILE;
+      return result;
     }
+  }
 
-    rustls_client_config_builder_set_server_verifier(config_builder,
-                                                     server_cert_verifier);
-    rustls_server_cert_verifier_free(server_cert_verifier);
+  if(conn_config->clientcert || ssl_config->key) {
+    result = init_config_builder_client_auth(data,
+                                             conn_config,
+                                             ssl_config,
+                                             config_builder);
+    if(result != CURLE_OK) {
+      rustls_client_config_builder_free(config_builder);
+      return result;
+    }
+  }
+
+#if defined(USE_ECH)
+  if(ECH_ENABLED(data)) {
+    result = init_config_builder_ech(data, connssl, config_builder);
+    if(result != CURLE_OK && data->set.tls_ech & CURLECH_HARD) {
+      rustls_client_config_builder_free(config_builder);
+      return result;
+    }
+  }
+#endif /* USE_ECH */
+
+  result = init_config_builder_keylog(data, config_builder);
+  if(result != CURLE_OK) {
+    rustls_client_config_builder_free(config_builder);
+    return result;
   }
 
-  result = rustls_client_config_builder_build(
+  rr = rustls_client_config_builder_build(
     config_builder,
     &backend->config);
-  if(result != RUSTLS_RESULT_OK) {
-    failf(data, "rustls: failed to build client config");
+  if(rr != RUSTLS_RESULT_OK) {
+    rustls_failf(data, rr, "failed to build client config");
+    rustls_client_config_builder_free(config_builder);
     rustls_client_config_free(backend->config);
     return CURLE_SSL_CONNECT_ERROR;
   }
 
   DEBUGASSERT(rconn == NULL);
-  result = rustls_client_connection_new(backend->config,
-                                        connssl->peer.hostname, &rconn);
-  if(result != RUSTLS_RESULT_OK) {
-    rustls_error(result, errorbuf, sizeof(errorbuf), &errorlen);
-    failf(data, "rustls_client_connection_new: %.*s", (int)errorlen, errorbuf);
+  rr = rustls_client_connection_new(backend->config,
+                                    connssl->peer.hostname,
+                                    &rconn);
+  if(rr != RUSTLS_RESULT_OK) {
+    rustls_failf(data, result, "rustls_client_connection_new");
     return CURLE_COULDNT_CONNECT;
   }
   DEBUGASSERT(rconn);
   rustls_connection_set_userdata(rconn, backend);
   backend->conn = rconn;
-  return CURLE_OK;
+
+  return result;
 }
 
 static void
@@ -779,48 +1137,36 @@ cr_set_negotiated_alpn(struct Curl_cfilter *cf, struct Curl_easy *data,
 
 /* Given an established network connection, do a TLS handshake.
  *
- * If `blocking` is true, this function will block until the handshake is
- * complete. Otherwise it will return as soon as I/O would block.
- *
- * For the non-blocking I/O case, this function will set `*done` to true
- * once the handshake is complete. This function never reads the value of
- * `*done*`.
+ * This function will set `*done` to true once the handshake is complete.
+ * This function never reads the value of `*done*`.
  */
 static CURLcode
-cr_connect_common(struct Curl_cfilter *cf,
-                  struct Curl_easy *data,
-                  bool blocking,
-                  bool *done)
+cr_connect(struct Curl_cfilter *cf,
+           struct Curl_easy *data, bool *done)
 {
   struct ssl_connect_data *const connssl = cf->ctx;
-  curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
-  struct rustls_ssl_backend_data *const backend =
+  const struct rustls_ssl_backend_data *const backend =
     (struct rustls_ssl_backend_data *)connssl->backend;
-  struct rustls_connection *rconn = NULL;
+  const struct rustls_connection *rconn = NULL;
   CURLcode tmperr = CURLE_OK;
   int result;
-  int what;
   bool wants_read;
   bool wants_write;
-  curl_socket_t writefd;
-  curl_socket_t readfd;
-  timediff_t timeout_ms;
-  timediff_t socket_check_timeout;
 
   DEBUGASSERT(backend);
 
-  CURL_TRC_CF(data, cf, "cr_connect_common, state=%d", connssl->state);
+  CURL_TRC_CF(data, cf, "cr_connect, state=%d", connssl->state);
   *done = FALSE;
+
   if(!backend->conn) {
     result = cr_init_backend(cf, data,
                (struct rustls_ssl_backend_data *)connssl->backend);
-    CURL_TRC_CF(data, cf, "cr_connect_common, init backend -> %d", result);
+    CURL_TRC_CF(data, cf, "cr_connect, init backend -> %d", result);
     if(result != CURLE_OK) {
       return result;
     }
     connssl->state = ssl_connection_negotiating;
   }
-
   rconn = backend->conn;
 
   /* Read/write data until the handshake is done or the socket would block. */
@@ -846,8 +1192,10 @@ cr_connect_common(struct Curl_cfilter *cf,
       }
       /* REALLY Done with the handshake. */
       {
-        uint16_t proto = rustls_connection_get_protocol_version(rconn);
-        uint16_t cipher = rustls_connection_get_negotiated_ciphersuite(rconn);
+        const uint16_t proto =
+          rustls_connection_get_protocol_version(rconn);
+        const uint16_t cipher =
+          rustls_connection_get_negotiated_ciphersuite(rconn);
         char buf[64] = "";
         const char *ver = "TLS version unknown";
         if(proto == RUSTLS_TLS_VERSION_TLSV1_3)
@@ -858,6 +1206,43 @@ cr_connect_common(struct Curl_cfilter *cf,
         infof(data, "rustls: handshake complete, %s, cipher: %s",
               ver, buf);
       }
+      if(data->set.ssl.certinfo) {
+        size_t num_certs = 0;
+        while(rustls_connection_get_peer_certificate(rconn, (int)num_certs)) {
+          num_certs++;
+        }
+        result = Curl_ssl_init_certinfo(data, (int)num_certs);
+        if(result)
+          return result;
+        for(size_t i = 0; i < num_certs; i++) {
+          const rustls_certificate *cert;
+          const unsigned char *der_data;
+          size_t der_len;
+          rustls_result rresult = RUSTLS_RESULT_OK;
+          cert = rustls_connection_get_peer_certificate(rconn, i);
+          DEBUGASSERT(cert); /* Should exist since we counted already */
+          rresult = rustls_certificate_get_der(cert, &der_data, &der_len);
+          if(rresult != RUSTLS_RESULT_OK) {
+            char errorbuf[255];
+            size_t errorlen;
+            rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen);
+            failf(data,
+              "Failed getting DER of server certificate #%ld: %.*s", i,
+              (int)errorlen, errorbuf);
+            return map_error(rresult);
+          }
+          {
+            const char *beg;
+            const char *end;
+            beg = (const char *)der_data;
+            end = (const char *)(der_data + der_len);
+            result = Curl_extract_certinfo(data, (int)i, beg, end);
+            if(result)
+              return result;
+          }
+        }
+      }
+
       connssl->state = ssl_connection_complete;
       *done = TRUE;
       return CURLE_OK;
@@ -868,50 +1253,14 @@ cr_connect_common(struct Curl_cfilter *cf,
     wants_write = rustls_connection_wants_write(rconn) ||
                   backend->plain_out_buffered;
     DEBUGASSERT(wants_read || wants_write);
-    writefd = wants_write ? sockfd : CURL_SOCKET_BAD;
-    readfd = wants_read ? sockfd : CURL_SOCKET_BAD;
-
-    /* check allowed time left */
-    timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
-    if(timeout_ms < 0) {
-      /* no need to continue if time already is up */
-      failf(data, "rustls: operation timed out before socket check");
-      return CURLE_OPERATION_TIMEDOUT;
-    }
-
-    socket_check_timeout = blocking ? timeout_ms : 0;
-
-    what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
-                             socket_check_timeout);
-    if(what < 0) {
-      /* fatal error */
-      failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
-      return CURLE_SSL_CONNECT_ERROR;
-    }
-    if(blocking && 0 == what) {
-      failf(data, "rustls: connection timeout after %" FMT_TIMEDIFF_T " ms",
-            socket_check_timeout);
-      return CURLE_OPERATION_TIMEDOUT;
-    }
-    if(0 == what) {
-      CURL_TRC_CF(data, cf, "Curl_socket_check: %s would block",
-            wants_read && wants_write ? "writing and reading" :
-            wants_write ? "writing" : "reading");
-      if(wants_write)
-        connssl->io_need |= CURL_SSL_IO_NEED_SEND;
-      if(wants_read)
-        connssl->io_need |= CURL_SSL_IO_NEED_RECV;
-      return CURLE_OK;
-    }
-    /* socket is readable or writable */
 
     if(wants_write) {
       CURL_TRC_CF(data, cf, "rustls_connection wants us to write_tls.");
       cr_send(cf, data, NULL, 0, &tmperr);
       if(tmperr == CURLE_AGAIN) {
         CURL_TRC_CF(data, cf, "writing would block");
-        /* fall through */
+        connssl->io_need = CURL_SSL_IO_NEED_SEND;
+        return CURLE_OK;
       }
       else if(tmperr != CURLE_OK) {
         return tmperr;
@@ -923,7 +1272,8 @@ cr_connect_common(struct Curl_cfilter *cf,
       if(tls_recv_more(cf, data, &tmperr) < 0) {
         if(tmperr == CURLE_AGAIN) {
           CURL_TRC_CF(data, cf, "reading would block");
-          /* fall through */
+          connssl->io_need = CURL_SSL_IO_NEED_RECV;
+          return CURLE_OK;
         }
         else if(tmperr == CURLE_RECV_ERROR) {
           return CURLE_SSL_CONNECT_ERROR;
@@ -940,20 +1290,6 @@ cr_connect_common(struct Curl_cfilter *cf,
   DEBUGASSERT(FALSE);
 }
 
-static CURLcode
-cr_connect_nonblocking(struct Curl_cfilter *cf,
-                       struct Curl_easy *data, bool *done)
-{
-  return cr_connect_common(cf, data, false, done);
-}
-
-static CURLcode
-cr_connect_blocking(struct Curl_cfilter *cf, struct Curl_easy *data)
-{
-  bool done; /* unused */
-  return cr_connect_common(cf, data, true, &done);
-}
-
 static void *
 cr_get_internals(struct ssl_connect_data *connssl,
                  CURLINFO info UNUSED_PARAM)
@@ -967,14 +1303,13 @@ cr_get_internals(struct ssl_connect_data *connssl,
 static CURLcode
 cr_shutdown(struct Curl_cfilter *cf,
             struct Curl_easy *data,
-            bool send_shutdown, bool *done)
+            const bool send_shutdown, bool *done)
 {
   struct ssl_connect_data *connssl = cf->ctx;
   struct rustls_ssl_backend_data *backend =
     (struct rustls_ssl_backend_data *)connssl->backend;
   CURLcode result = CURLE_OK;
   ssize_t nwritten, nread;
-  char buf[1024];
   size_t i;
 
   DEBUGASSERT(backend);
@@ -1007,6 +1342,7 @@ cr_shutdown(struct Curl_cfilter *cf,
   }
 
   for(i = 0; i < 10; ++i) {
+    char buf[1024];
     nread = cr_recv(cf, data, buf, (int)sizeof(buf), &result);
     if(nread <= 0)
       break;
@@ -1036,7 +1372,7 @@ cr_shutdown(struct Curl_cfilter *cf,
 static void
 cr_close(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
-  struct ssl_connect_data *connssl = cf->ctx;
+  const struct ssl_connect_data *connssl = cf->ctx;
   struct rustls_ssl_backend_data *backend =
     (struct rustls_ssl_backend_data *)connssl->backend;
 
@@ -1054,7 +1390,7 @@ cr_close(struct Curl_cfilter *cf, struct Curl_easy *data)
 
 static size_t cr_version(char *buffer, size_t size)
 {
-  struct rustls_str ver = rustls_version();
+  const struct rustls_str ver = rustls_version();
   return msnprintf(buffer, size, "%.*s", (int)ver.len, ver.data);
 }
 
@@ -1068,23 +1404,29 @@ cr_random(struct Curl_easy *data, unsigned char *entropy, size_t length)
   return map_error(rresult);
 }
 
+static void cr_cleanup(void)
+{
+  Curl_tls_keylog_close();
+}
+
 const struct Curl_ssl Curl_ssl_rustls = {
   { CURLSSLBACKEND_RUSTLS, "rustls" },
   SSLSUPP_CAINFO_BLOB |            /* supports */
   SSLSUPP_HTTPS_PROXY |
   SSLSUPP_CIPHER_LIST |
-  SSLSUPP_TLS13_CIPHERSUITES,
+  SSLSUPP_TLS13_CIPHERSUITES |
+  SSLSUPP_CERTINFO |
+  SSLSUPP_ECH,
   sizeof(struct rustls_ssl_backend_data),
 
   NULL,                            /* init */
-  NULL,                            /* cleanup */
+  cr_cleanup,                      /* cleanup */
   cr_version,                      /* version */
   cr_shutdown,                     /* shutdown */
   cr_data_pending,                 /* data_pending */
   cr_random,                       /* random */
   NULL,                            /* cert_status_request */
-  cr_connect_blocking,             /* connect */
-  cr_connect_nonblocking,          /* connect_nonblocking */
+  cr_connect,                      /* connect */
   Curl_ssl_adjust_pollset,         /* adjust_pollset */
   cr_get_internals,                /* get_internals */
   cr_close,                        /* close_one */
diff --git a/extra/curl/curl-8.12.1/lib/vtls/rustls.h b/extra/curl/curl-8.14.1/lib/vtls/rustls.h
similarity index 97%
rename from extra/curl/curl-8.12.1/lib/vtls/rustls.h
rename to extra/curl/curl-8.14.1/lib/vtls/rustls.h
index bfbe23de3e6b..74d39d4d1155 100644
--- a/extra/curl/curl-8.12.1/lib/vtls/rustls.h
+++ b/extra/curl/curl-8.14.1/lib/vtls/rustls.h
@@ -25,7 +25,7 @@
 #ifndef HEADER_CURL_RUSTLS_H
 #define HEADER_CURL_RUSTLS_H
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #ifdef USE_RUSTLS
 
diff --git a/extra/curl/curl-8.12.1/lib/vtls/schannel.c b/extra/curl/curl-8.14.1/lib/vtls/schannel.c
similarity index 93%
rename from extra/curl/curl-8.12.1/lib/vtls/schannel.c
rename to extra/curl/curl-8.14.1/lib/vtls/schannel.c
index 2af29a42ec8d..bea8eef8c0d9 100644
--- a/extra/curl/curl-8.12.1/lib/vtls/schannel.c
+++ b/extra/curl/curl-8.14.1/lib/vtls/schannel.c
@@ -29,7 +29,7 @@
  * but vtls.c should ever call or use these functions.
  */
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #ifdef USE_SCHANNEL
 
@@ -42,23 +42,25 @@
 #include "vtls.h"
 #include "vtls_int.h"
 #include "vtls_scache.h"
-#include "strcase.h"
-#include "sendf.h"
-#include "connect.h" /* for the connect timeout */
-#include "strerror.h"
-#include "select.h" /* for the socket readiness */
-#include "inet_pton.h" /* for IP addr SNI check */
-#include "curl_multibyte.h"
-#include "warnless.h"
+#include "../strcase.h"
+#include "../sendf.h"
+#include "../connect.h" /* for the connect timeout */
+#include "../strerror.h"
+#include "../select.h" /* for the socket readiness */
+#include "../curlx/inet_pton.h" /* for IP addr SNI check */
+#include "../curlx/multibyte.h"
+#include "../curlx/warnless.h"
 #include "x509asn1.h"
-#include "curl_printf.h"
-#include "multiif.h"
-#include "version_win32.h"
-#include "rand.h"
+#include "../curl_printf.h"
+#include "../multiif.h"
+#include "../system_win32.h"
+#include "../curlx/version_win32.h"
+#include "../rand.h"
+#include "../curlx/strparse.h"
 
 /* The last #include file should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
 
 /* Some verbose debug messages are wrapped by SCH_DEV() instead of DEBUGF()
  * and only shown if CURL_SCHANNEL_DEV_DEBUG was defined at build time. These
@@ -71,19 +73,6 @@
 #define SCH_DEV(x) do { } while(0)
 #endif
 
-/* ALPN requires version 8.1 of the Windows SDK, which was
-   shipped with Visual Studio 2013, aka _MSC_VER 1800:
-
-   https://technet.microsoft.com/en-us/library/hh831771%28v=ws.11%29.aspx
-*/
-#if defined(_MSC_VER) && (_MSC_VER >= 1800) && !defined(_USING_V110_SDK71_)
-#  define HAS_ALPN_SCHANNEL
-#endif
-
-#ifndef BCRYPT_CHACHA20_POLY1305_ALGORITHM
-#define BCRYPT_CHACHA20_POLY1305_ALGORITHM L"CHACHA20_POLY1305"
-#endif
-
 #ifndef BCRYPT_CHAIN_MODE_CCM
 #define BCRYPT_CHAIN_MODE_CCM L"ChainingModeCCM"
 #endif
@@ -152,10 +141,31 @@
 #define CALG_SHA_256 0x0000800c
 #endif
 
+/* Work around typo in CeGCC (as of 0.59.1) w32api headers */
+#if defined(__MINGW32CE__) && \
+  !defined(ALG_CLASS_DHASH) && defined(ALG_CLASS_HASH)
+#define ALG_CLASS_DHASH ALG_CLASS_HASH
+#endif
+
 #ifndef PKCS12_NO_PERSIST_KEY
 #define PKCS12_NO_PERSIST_KEY 0x00008000
 #endif
 
+#ifndef CERT_FIND_HAS_PRIVATE_KEY
+#define CERT_FIND_HAS_PRIVATE_KEY (21 << CERT_COMPARE_SHIFT)
+#endif
+
+/* ALPN requires version 8.1 of the Windows SDK, which was
+   shipped with Visual Studio 2013, aka _MSC_VER 1800:
+     https://technet.microsoft.com/en-us/library/hh831771%28v=ws.11%29.aspx
+   Or mingw-w64 9.0 or upper.
+*/
+#if (defined(__MINGW64_VERSION_MAJOR) && __MINGW64_VERSION_MAJOR >= 9) || \
+  (defined(_MSC_VER) && (_MSC_VER >= 1800) && !defined(_USING_V110_SDK71_))
+#define HAS_ALPN_SCHANNEL
+static bool s_win_has_alpn;
+#endif
+
 static CURLcode schannel_pkp_pin_peer_pubkey(struct Curl_cfilter *cf,
                                              struct Curl_easy *data,
                                              const char *pinnedpubkey);
@@ -232,8 +242,6 @@ schannel_set_ssl_version_min_max(DWORD *enabled_protocols,
   return CURLE_OK;
 }
 
-/* longest is 26, buffer is slightly bigger */
-#define LONGEST_ALG_ID 32
 #define CIPHEROPTION(x) {#x, x}
 
 struct algo {
@@ -350,9 +358,9 @@ static const struct algo algs[]= {
 };
 
 static int
-get_alg_id_by_name(char *name)
+get_alg_id_by_name(const char *name)
 {
-  char *nameEnd = strchr(name, ':');
+  const char *nameEnd = strchr(name, ':');
   size_t n = nameEnd ? (size_t)(nameEnd - name) : strlen(name);
   int i;
 
@@ -369,12 +377,13 @@ static CURLcode
 set_ssl_ciphers(SCHANNEL_CRED *schannel_cred, char *ciphers,
                 ALG_ID *algIds)
 {
-  char *startCur = ciphers;
+  const char *startCur = ciphers;
   int algCount = 0;
   while(startCur && (0 != *startCur) && (algCount < NUM_CIPHERS)) {
-    long alg = strtol(startCur, 0, 0);
-    if(!alg)
+    curl_off_t alg;
+    if(curlx_str_number(&startCur, &alg, INT_MAX) || !alg)
       alg = get_alg_id_by_name(startCur);
+
     if(alg)
       algIds[algCount++] = (ALG_ID)alg;
     else if(!strncmp(startCur, "USE_STRONG_CRYPTO",
@@ -606,6 +615,7 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
       WCHAR* pszPassword;
       size_t pwd_len = 0;
       int str_w_len = 0;
+      int cert_find_flags;
       const char *cert_showfilename_error = blob ?
         "(memory blob)" : data->set.ssl.primary.clientcert;
       curlx_unicodefree(cert_path);
@@ -654,8 +664,7 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
         else
           pszPassword[0] = 0;
 
-        if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT,
-                                        VERSION_GREATER_THAN_EQUAL))
+        if(Curl_isVistaOrGreater)
           cert_store = PFXImportCertStore(&datablob, pszPassword,
                                           PKCS12_NO_PERSIST_KEY);
         else
@@ -678,9 +687,17 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
         return CURLE_SSL_CERTPROBLEM;
       }
 
+      /* CERT_FIND_HAS_PRIVATE_KEY is only available in Windows 8 / Server
+         2012, (NT v6.2). For earlier versions we use CURL_FIND_ANY. */
+      if(curlx_verify_windows_version(6, 2, 0, PLATFORM_WINNT,
+                                      VERSION_GREATER_THAN_EQUAL))
+        cert_find_flags = CERT_FIND_HAS_PRIVATE_KEY;
+      else
+        cert_find_flags = CERT_FIND_ANY;
+
       client_certs[0] = CertFindCertificateInStore(
         cert_store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0,
-        CERT_FIND_ANY, NULL, NULL);
+        cert_find_flags, NULL, NULL);
 
       if(!client_certs[0]) {
         failf(data, "schannel: Failed to get certificate from file %s"
@@ -802,11 +819,12 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
 #endif
 
     sspi_status =
-      Curl_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR*)UNISP_NAME,
-                                         SECPKG_CRED_OUTBOUND, NULL,
-                                         &credentials, NULL, NULL,
-                                         &backend->cred->cred_handle,
-                                         &backend->cred->time_stamp);
+      Curl_pSecFn->AcquireCredentialsHandle(NULL,
+                                            (TCHAR *)CURL_UNCONST(UNISP_NAME),
+                                            SECPKG_CRED_OUTBOUND, NULL,
+                                            &credentials, NULL, NULL,
+                                            &backend->cred->cred_handle,
+                                            &backend->cred->time_stamp);
   }
   else {
     /* Pre-Windows 10 1809 or the user set a legacy algorithm list.
@@ -842,11 +860,12 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
 #endif
 
     sspi_status =
-      Curl_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR*)UNISP_NAME,
-                                         SECPKG_CRED_OUTBOUND, NULL,
-                                         &schannel_cred, NULL, NULL,
-                                         &backend->cred->cred_handle,
-                                         &backend->cred->time_stamp);
+      Curl_pSecFn->AcquireCredentialsHandle(NULL,
+                                            (TCHAR *)CURL_UNCONST(UNISP_NAME),
+                                            SECPKG_CRED_OUTBOUND, NULL,
+                                            &schannel_cred, NULL, NULL,
+                                            &backend->cred->cred_handle,
+                                            &backend->cred->time_stamp);
   }
 
 #ifdef HAS_CLIENT_CERT_PATH
@@ -882,7 +901,9 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
   struct ssl_connect_data *connssl = cf->ctx;
   struct schannel_ssl_backend_data *backend =
     (struct schannel_ssl_backend_data *)connssl->backend;
+#ifndef UNDER_CE
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+#endif
   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
   SecBuffer outbuf;
   SecBufferDesc outbuf_desc;
@@ -892,7 +913,6 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
   unsigned char alpn_buffer[128];
 #endif
   SECURITY_STATUS sspi_status = SEC_E_OK;
-  struct Curl_schannel_cred *old_cred = NULL;
   CURLcode result;
 
   DEBUGASSERT(backend);
@@ -909,20 +929,14 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
   }
 
 #ifdef HAS_ALPN_SCHANNEL
-  /* ALPN is only supported on Windows 8.1 / Server 2012 R2 and above.
-     Also it does not seem to be supported for WINE, see curl bug #983. */
-  backend->use_alpn = connssl->alpn &&
-    !GetProcAddress(GetModuleHandle(TEXT("ntdll")),
-                    "wine_get_version") &&
-    curlx_verify_windows_version(6, 3, 0, PLATFORM_WINNT,
-                                 VERSION_GREATER_THAN_EQUAL);
+  backend->use_alpn = connssl->alpn && s_win_has_alpn;
 #else
   backend->use_alpn = FALSE;
 #endif
 
-#ifdef _WIN32_WCE
+#ifdef UNDER_CE
 #ifdef HAS_MANUAL_VERIFY_API
-  /* certificate validation on CE does not seem to work right; we will
+  /* certificate validation on Windows CE does not seem to work right; we will
    * do it following a more manual process. */
   backend->use_manual_cred_validation = TRUE;
 #else
@@ -955,9 +969,10 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
 
   /* check for an existing reusable credential handle */
   if(ssl_config->primary.cache_session) {
+    struct Curl_schannel_cred *old_cred;
     Curl_ssl_scache_lock(data);
-    if(Curl_ssl_scache_get_obj(cf, data, connssl->peer.scache_key,
-                               (void **)&old_cred)) {
+    old_cred = Curl_ssl_scache_get_obj(cf, data, connssl->peer.scache_key);
+    if(old_cred) {
       backend->cred = old_cred;
       DEBUGF(infof(data, "schannel: reusing existing credential handle"));
 
@@ -973,7 +988,7 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
   if(!backend->cred) {
     char *snihost;
     result = schannel_acquire_credential_handle(cf, data);
-    if(result)
+    if(result || !backend->cred)
       return result;
     /* schannel_acquire_credential_handle() sets backend->cred accordingly or
        it returns error otherwise. */
@@ -1629,6 +1644,7 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
       args.data = data;
       args.idx = 0;
       args.certs_count = certs_count;
+      args.result = CURLE_OK;
       traverse_cert_store(ccert_context, add_cert_to_certinfo, &args);
       result = args.result;
     }
@@ -1642,16 +1658,12 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
   return CURLE_OK;
 }
 
-static CURLcode
-schannel_connect_common(struct Curl_cfilter *cf,
-                        struct Curl_easy *data,
-                        bool nonblocking, bool *done)
+static CURLcode schannel_connect(struct Curl_cfilter *cf,
+                                 struct Curl_easy *data,
+                                 bool *done)
 {
-  CURLcode result;
   struct ssl_connect_data *connssl = cf->ctx;
-  curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
-  timediff_t timeout_ms;
-  int what;
+  CURLcode result;
 
   /* check if the connection has already been established */
   if(ssl_connection_complete == connssl->state) {
@@ -1659,73 +1671,19 @@ schannel_connect_common(struct Curl_cfilter *cf,
     return CURLE_OK;
   }
 
-  if(ssl_connect_1 == connssl->connecting_state) {
-    /* check out how much more time we are allowed */
-    timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
-    if(timeout_ms < 0) {
-      /* no need to continue if time already is up */
-      failf(data, "SSL/TLS connection timeout");
-      return CURLE_OPERATION_TIMEDOUT;
-    }
+  *done = FALSE;
 
+  if(ssl_connect_1 == connssl->connecting_state) {
     result = schannel_connect_step1(cf, data);
     if(result)
       return result;
   }
 
-  while(ssl_connect_2 == connssl->connecting_state) {
-
-    /* check out how much more time we are allowed */
-    timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
-    if(timeout_ms < 0) {
-      /* no need to continue if time already is up */
-      failf(data, "SSL/TLS connection timeout");
-      return CURLE_OPERATION_TIMEDOUT;
-    }
-
-    /* if ssl is expecting something, check if it is available. */
-    if(connssl->io_need) {
-
-      curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND) ?
-        sockfd : CURL_SOCKET_BAD;
-      curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV) ?
-        sockfd : CURL_SOCKET_BAD;
-
-      what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
-                               nonblocking ? 0 : timeout_ms);
-      if(what < 0) {
-        /* fatal error */
-        failf(data, "select/poll on SSL/TLS socket, errno: %d", SOCKERRNO);
-        return CURLE_SSL_CONNECT_ERROR;
-      }
-      else if(0 == what) {
-        if(nonblocking) {
-          *done = FALSE;
-          return CURLE_OK;
-        }
-        else {
-          /* timeout */
-          failf(data, "SSL/TLS connection timeout");
-          return CURLE_OPERATION_TIMEDOUT;
-        }
-      }
-      /* socket is readable or writable */
-    }
-
-    /* Run transaction, and return to the caller if it failed or if
-     * this connection is part of a multi handle and this loop would
-     * execute again. This permits the owner of a multi handle to
-     * abort a connection attempt before step2 has completed while
-     * ensuring that a client using select() or epoll() will always
-     * have a valid fdset to wait on.
-     */
+  if(ssl_connect_2 == connssl->connecting_state) {
     result = schannel_connect_step2(cf, data);
-    if(result || (nonblocking && (ssl_connect_2 == connssl->connecting_state)))
+    if(result)
       return result;
-
-  } /* repeat step2 until all transactions are done. */
+  }
 
   if(ssl_connect_3 == connssl->connecting_state) {
     result = schannel_connect_step3(cf, data);
@@ -1752,11 +1710,6 @@ schannel_connect_common(struct Curl_cfilter *cf,
 
     *done = TRUE;
   }
-  else
-    *done = FALSE;
-
-  /* reset our connection state machine */
-  connssl->connecting_state = ssl_connect_1;
 
   return CURLE_OK;
 }
@@ -2002,7 +1955,6 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
                                     backend->encdata_offset),
                               size, err);
     if(*err) {
-      nread = -1;
       if(*err == CURLE_AGAIN)
         SCH_DEV(infof(data, "schannel: recv returned CURLE_AGAIN"));
       else if(*err == CURLE_RECV_ERROR)
@@ -2129,7 +2081,7 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
         connssl->connecting_state = ssl_connect_2;
         connssl->io_need = CURL_SSL_IO_NEED_SEND;
         backend->recv_renegotiating = TRUE;
-        *err = schannel_connect_common(cf, data, FALSE, &done);
+        *err = schannel_connect(cf, data, &done);
         backend->recv_renegotiating = FALSE;
         if(*err) {
           infof(data, "schannel: renegotiation failed");
@@ -2240,28 +2192,6 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
   return *err ? -1 : 0;
 }
 
-static CURLcode schannel_connect_nonblocking(struct Curl_cfilter *cf,
-                                             struct Curl_easy *data,
-                                             bool *done)
-{
-  return schannel_connect_common(cf, data, TRUE, done);
-}
-
-static CURLcode schannel_connect(struct Curl_cfilter *cf,
-                                 struct Curl_easy *data)
-{
-  CURLcode result;
-  bool done = FALSE;
-
-  result = schannel_connect_common(cf, data, FALSE, &done);
-  if(result)
-    return result;
-
-  DEBUGASSERT(done);
-
-  return CURLE_OK;
-}
-
 static bool schannel_data_pending(struct Curl_cfilter *cf,
                                   const struct Curl_easy *data)
 {
@@ -2466,6 +2396,34 @@ static void schannel_close(struct Curl_cfilter *cf, struct Curl_easy *data)
 
 static int schannel_init(void)
 {
+#if defined(HAS_ALPN_SCHANNEL) && !defined(UNDER_CE)
+  bool wine = FALSE;
+  bool wine_has_alpn = FALSE;
+
+#ifndef CURL_WINDOWS_UWP
+  typedef const char *(APIENTRY *WINE_GET_VERSION_FN)(void);
+  /* GetModuleHandle() not available for UWP.
+     Assume no WINE because WINE has no UWP support. */
+  WINE_GET_VERSION_FN p_wine_get_version =
+    CURLX_FUNCTION_CAST(WINE_GET_VERSION_FN,
+                        (GetProcAddress(GetModuleHandleA("ntdll"),
+                                        "wine_get_version")));
+  wine = !!p_wine_get_version;
+  if(wine) {
+    const char *wine_version = p_wine_get_version();  /* e.g. "6.0.2" */
+    /* Assume ALPN support with WINE 6.0 or upper */
+    wine_has_alpn = wine_version && atoi(wine_version) >= 6;
+  }
+#endif
+  if(wine)
+    s_win_has_alpn = wine_has_alpn;
+  else {
+    /* ALPN is supported on Windows 8.1 / Server 2012 R2 and above. */
+    s_win_has_alpn = curlx_verify_windows_version(6, 3, 0, PLATFORM_WINNT,
+                                                  VERSION_GREATER_THAN_EQUAL);
+  }
+#endif /* HAS_ALPN_SCHANNEL && !UNDER_CE */
+
   return Curl_sspi_global_init() == CURLE_OK ? 1 : 0;
 }
 
@@ -2589,7 +2547,12 @@ static void schannel_checksum(const unsigned char *input,
     if(!CryptCreateHash(hProv, algId, 0, 0, &hHash))
       break; /* failed */
 
+#ifdef __MINGW32CE__
+    /* workaround for CeGCC, should be (const BYTE*) */
+    if(!CryptHashData(hHash, (BYTE*)CURL_UNCONST(input), (DWORD)inputlen, 0))
+#else
     if(!CryptHashData(hHash, input, (DWORD)inputlen, 0))
+#endif
       break; /* failed */
 
     /* get hash size */
@@ -2653,7 +2616,7 @@ HCERTSTORE Curl_schannel_get_cached_cert_store(struct Curl_cfilter *cf,
   }
 
   share = Curl_hash_pick(&multi->proto_hash,
-                         (void *)MPROTO_SCHANNEL_CERT_SHARE_KEY,
+                         CURL_UNCONST(MPROTO_SCHANNEL_CERT_SHARE_KEY),
                          sizeof(MPROTO_SCHANNEL_CERT_SHARE_KEY)-1);
   if(!share || !share->cert_store) {
     return NULL;
@@ -2669,8 +2632,8 @@ HCERTSTORE Curl_schannel_get_cached_cert_store(struct Curl_cfilter *cf,
      negative timeout means retain forever. */
   timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000;
   if(timeout_ms >= 0) {
-    now = Curl_now();
-    elapsed_ms = Curl_timediff(now, share->time);
+    now = curlx_now();
+    elapsed_ms = curlx_timediff(now, share->time);
     if(elapsed_ms >= timeout_ms) {
       return NULL;
     }
@@ -2731,7 +2694,7 @@ bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf,
   }
 
   share = Curl_hash_pick(&multi->proto_hash,
-                         (void *)MPROTO_SCHANNEL_CERT_SHARE_KEY,
+                         CURL_UNCONST(MPROTO_SCHANNEL_CERT_SHARE_KEY),
                          sizeof(MPROTO_SCHANNEL_CERT_SHARE_KEY)-1);
   if(!share) {
     share = calloc(1, sizeof(*share));
@@ -2739,7 +2702,7 @@ bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf,
       return FALSE;
     }
     if(!Curl_hash_add2(&multi->proto_hash,
-                       (void *)MPROTO_SCHANNEL_CERT_SHARE_KEY,
+                       CURL_UNCONST(MPROTO_SCHANNEL_CERT_SHARE_KEY),
                        sizeof(MPROTO_SCHANNEL_CERT_SHARE_KEY)-1,
                        share, schannel_cert_share_free)) {
       free(share);
@@ -2769,7 +2732,7 @@ bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf,
   }
   free(share->CAfile);
 
-  share->time = Curl_now();
+  share->time = curlx_now();
   share->cert_store = cert_store;
   share->CAinfo_blob_size = CAinfo_blob_size;
   share->CAfile = CAfile;
@@ -2800,7 +2763,6 @@ const struct Curl_ssl Curl_ssl_schannel = {
   schannel_random,                   /* random */
   NULL,                              /* cert_status_request */
   schannel_connect,                  /* connect */
-  schannel_connect_nonblocking,      /* connect_nonblocking */
   Curl_ssl_adjust_pollset,           /* adjust_pollset */
   schannel_get_internals,            /* get_internals */
   schannel_close,                    /* close_one */
diff --git a/extra/curl/curl-8.12.1/lib/vtls/schannel.h b/extra/curl/curl-8.14.1/lib/vtls/schannel.h
similarity index 96%
rename from extra/curl/curl-8.12.1/lib/vtls/schannel.h
rename to extra/curl/curl-8.14.1/lib/vtls/schannel.h
index 69f1baddb848..c3512dd13232 100644
--- a/extra/curl/curl-8.12.1/lib/vtls/schannel.h
+++ b/extra/curl/curl-8.14.1/lib/vtls/schannel.h
@@ -24,7 +24,7 @@
  * SPDX-License-Identifier: curl
  *
  ***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #ifdef USE_SCHANNEL
 
@@ -50,10 +50,10 @@
 
 #include <schnlsp.h>
 #include <schannel.h>
-#include "curl_sspi.h"
+#include "../curl_sspi.h"
 
-#include "cfilters.h"
-#include "urldata.h"
+#include "../cfilters.h"
+#include "../urldata.h"
 
 /* <wincrypt.h> has been included via the above <schnlsp.h>.
  * Or in case of ldap.c, it was included via <winldap.h>.
diff --git a/extra/curl/curl-8.12.1/lib/vtls/schannel_int.h b/extra/curl/curl-8.14.1/lib/vtls/schannel_int.h
similarity index 94%
rename from extra/curl/curl-8.12.1/lib/vtls/schannel_int.h
rename to extra/curl/curl-8.14.1/lib/vtls/schannel_int.h
index 81476bc6d817..fe1012545674 100644
--- a/extra/curl/curl-8.12.1/lib/vtls/schannel_int.h
+++ b/extra/curl/curl-8.14.1/lib/vtls/schannel_int.h
@@ -24,7 +24,7 @@
  * SPDX-License-Identifier: curl
  *
  ***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #ifdef USE_SCHANNEL
 
@@ -150,17 +150,17 @@ struct schannel_ssl_backend_data {
      cannot be decrypted without another recv() (that is, status is
      SEC_E_INCOMPLETE_MESSAGE) then set this true. after an recv() adds
      more bytes into encdata then set this back to false. */
-  bool encdata_is_incomplete;
   unsigned long req_flags, ret_flags;
   CURLcode recv_unrecoverable_err; /* schannel_recv had an unrecoverable err */
-  bool recv_sspi_close_notify; /* true if connection closed by close_notify */
-  bool recv_connection_closed; /* true if connection closed, regardless how */
-  bool recv_renegotiating;     /* true if recv is doing renegotiation */
-  bool use_alpn; /* true if ALPN is used for this connection */
+  BIT(recv_sspi_close_notify); /* true if connection closed by close_notify */
+  BIT(recv_connection_closed); /* true if connection closed, regardless how */
+  BIT(recv_renegotiating);     /* true if recv is doing renegotiation */
+  BIT(use_alpn); /* true if ALPN is used for this connection */
 #ifdef HAS_MANUAL_VERIFY_API
-  bool use_manual_cred_validation; /* true if manual cred validation is used */
+  BIT(use_manual_cred_validation); /* true if manual cred validation is used */
 #endif
   BIT(sent_shutdown);
+  BIT(encdata_is_incomplete);
 };
 
 /* key to use at `multi->proto_hash` */
diff --git a/extra/curl/curl-8.12.1/lib/vtls/schannel_verify.c b/extra/curl/curl-8.14.1/lib/vtls/schannel_verify.c
similarity index 85%
rename from extra/curl/curl-8.12.1/lib/vtls/schannel_verify.c
rename to extra/curl/curl-8.14.1/lib/vtls/schannel_verify.c
index fede3908feba..f843342b9cea 100644
--- a/extra/curl/curl-8.12.1/lib/vtls/schannel_verify.c
+++ b/extra/curl/curl-8.14.1/lib/vtls/schannel_verify.c
@@ -29,7 +29,7 @@
  * only be invoked by code in schannel.c.
  */
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #ifdef USE_SCHANNEL
 #ifndef USE_WINDOWS_SSPI
@@ -39,24 +39,41 @@
 #include "schannel.h"
 #include "schannel_int.h"
 
-#include "inet_pton.h"
+#include "../curlx/inet_pton.h"
 #include "vtls.h"
 #include "vtls_int.h"
-#include "sendf.h"
-#include "strerror.h"
-#include "curl_multibyte.h"
-#include "curl_printf.h"
+#include "../sendf.h"
+#include "../strerror.h"
+#include "../curlx/winapi.h"
+#include "../curlx/multibyte.h"
+#include "../curl_printf.h"
 #include "hostcheck.h"
-#include "version_win32.h"
+#include "../curlx/version_win32.h"
 
 /* The last #include file should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
 
 #define BACKEND ((struct schannel_ssl_backend_data *)connssl->backend)
 
 #ifdef HAS_MANUAL_VERIFY_API
 
+#ifdef __MINGW32CE__
+#define CERT_QUERY_OBJECT_BLOB 0x00000002
+#define CERT_QUERY_CONTENT_CERT 1
+#define CERT_QUERY_CONTENT_FLAG_CERT (1 << CERT_QUERY_CONTENT_CERT)
+#define CERT_QUERY_FORMAT_BINARY 1
+#define CERT_QUERY_FORMAT_BASE64_ENCODED 2
+#define CERT_QUERY_FORMAT_ASN_ASCII_HEX_ENCODED 3
+#define CERT_QUERY_FORMAT_FLAG_ALL               \
+  (1 << CERT_QUERY_FORMAT_BINARY) |              \
+  (1 << CERT_QUERY_FORMAT_BASE64_ENCODED) |      \
+  (1 << CERT_QUERY_FORMAT_ASN_ASCII_HEX_ENCODED)
+#define CERT_CHAIN_REVOCATION_CHECK_CHAIN 0x20000000
+#define CERT_NAME_DISABLE_IE4_UTF8_FLAG 0x00010000
+#define CERT_TRUST_IS_OFFLINE_REVOCATION 0x01000000
+#endif /* __MINGW32CE__ */
+
 #define MAX_CAFILE_SIZE 1048576 /* 1 MiB */
 #define BEGIN_CERT "-----BEGIN CERTIFICATE-----"
 #define END_CERT "\n-----END CERTIFICATE-----"
@@ -76,6 +93,7 @@ struct cert_chain_engine_config_win7 {
   HCERTSTORE hExclusiveTrustedPeople;
 };
 
+#ifndef UNDER_CE
 static int is_cr_or_lf(char c)
 {
   return c == '\r' || c == '\n';
@@ -138,13 +156,13 @@ static CURLcode add_certs_data_to_store(HCERTSTORE trust_store,
       }
       else {
         CERT_BLOB cert_blob;
-        CERT_CONTEXT *cert_context = NULL;
+        const CERT_CONTEXT *cert_context = NULL;
         BOOL add_cert_result = FALSE;
         DWORD actual_content_type = 0;
         DWORD cert_size = (DWORD)
           ((end_cert_ptr + end_cert_len) - begin_cert_ptr);
 
-        cert_blob.pbData = (BYTE *)begin_cert_ptr;
+        cert_blob.pbData = (BYTE *)CURL_UNCONST(begin_cert_ptr);
         cert_blob.cbData = cert_size;
         if(!CryptQueryObject(CERT_QUERY_OBJECT_BLOB,
                              &cert_blob,
@@ -157,12 +175,12 @@ static CURLcode add_certs_data_to_store(HCERTSTORE trust_store,
                              NULL,
                              NULL,
                              (const void **)&cert_context)) {
-          char buffer[STRERROR_LEN];
+          char buffer[WINAPI_ERROR_LEN];
           failf(data,
                 "schannel: failed to extract certificate from CA file "
                 "'%s': %s",
                 ca_file_text,
-                Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
+                curlx_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
           result = CURLE_SSL_CACERT_BADFILE;
           more_certs = 0;
         }
@@ -186,13 +204,13 @@ static CURLcode add_certs_data_to_store(HCERTSTORE trust_store,
                                                NULL);
             CertFreeCertificateContext(cert_context);
             if(!add_cert_result) {
-              char buffer[STRERROR_LEN];
+              char buffer[WINAPI_ERROR_LEN];
               failf(data,
                     "schannel: failed to add certificate from CA file '%s' "
                     "to certificate store: %s",
                     ca_file_text,
-                    Curl_winapi_strerror(GetLastError(), buffer,
-                                         sizeof(buffer)));
+                    curlx_winapi_strerror(GetLastError(), buffer,
+                                          sizeof(buffer)));
               result = CURLE_SSL_CACERT_BADFILE;
               more_certs = 0;
             }
@@ -232,13 +250,13 @@ static CURLcode add_certs_file_to_store(HCERTSTORE trust_store,
   size_t ca_file_bufsize = 0;
   DWORD total_bytes_read = 0;
 
-  ca_file_tstr = curlx_convert_UTF8_to_tchar((char *)ca_file);
+  ca_file_tstr = curlx_convert_UTF8_to_tchar(ca_file);
   if(!ca_file_tstr) {
-    char buffer[STRERROR_LEN];
+    char buffer[WINAPI_ERROR_LEN];
     failf(data,
           "schannel: invalid path name for CA file '%s': %s",
           ca_file,
-          Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
+          curlx_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
     result = CURLE_SSL_CACERT_BADFILE;
     goto cleanup;
   }
@@ -256,21 +274,21 @@ static CURLcode add_certs_file_to_store(HCERTSTORE trust_store,
                               FILE_ATTRIBUTE_NORMAL,
                               NULL);
   if(ca_file_handle == INVALID_HANDLE_VALUE) {
-    char buffer[STRERROR_LEN];
+    char buffer[WINAPI_ERROR_LEN];
     failf(data,
           "schannel: failed to open CA file '%s': %s",
           ca_file,
-          Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
+          curlx_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
     result = CURLE_SSL_CACERT_BADFILE;
     goto cleanup;
   }
 
   if(!GetFileSizeEx(ca_file_handle, &file_size)) {
-    char buffer[STRERROR_LEN];
+    char buffer[WINAPI_ERROR_LEN];
     failf(data,
           "schannel: failed to determine size of CA file '%s': %s",
           ca_file,
-          Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
+          curlx_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
     result = CURLE_SSL_CACERT_BADFILE;
     goto cleanup;
   }
@@ -296,11 +314,11 @@ static CURLcode add_certs_file_to_store(HCERTSTORE trust_store,
 
     if(!ReadFile(ca_file_handle, ca_file_buffer + total_bytes_read,
                  bytes_to_read, &bytes_read, NULL)) {
-      char buffer[STRERROR_LEN];
+      char buffer[WINAPI_ERROR_LEN];
       failf(data,
             "schannel: failed to read from CA file '%s': %s",
             ca_file,
-            Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
+            curlx_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
       result = CURLE_SSL_CACERT_BADFILE;
       goto cleanup;
     }
@@ -313,7 +331,7 @@ static CURLcode add_certs_file_to_store(HCERTSTORE trust_store,
     }
   }
 
-  /* Null terminate the buffer */
+  /* null-terminate the buffer */
   ca_file_buffer[ca_file_bufsize] = '\0';
 
   result = add_certs_data_to_store(trust_store,
@@ -330,9 +348,11 @@ static CURLcode add_certs_file_to_store(HCERTSTORE trust_store,
 
   return result;
 }
+#endif
 
 #endif /* HAS_MANUAL_VERIFY_API */
 
+#ifndef UNDER_CE
 /*
  * Returns the number of characters necessary to populate all the host_names.
  * If host_names is not NULL, populate it with all the hostnames. Each string
@@ -380,6 +400,9 @@ static DWORD cert_get_name_string(struct Curl_easy *data,
   (void)Win8_compat;
 #endif
 
+  if(!alt_name_info)
+    return 0;
+
   compute_content = host_names != NULL && length != 0;
 
   /* Initialize default return values. */
@@ -442,14 +465,14 @@ static bool get_num_host_info(struct num_ip_data *ip_blob,
   struct in6_addr ia6;
   bool result = FALSE;
 
-  int res = Curl_inet_pton(AF_INET, hostname, &ia);
+  int res = curlx_inet_pton(AF_INET, hostname, &ia);
   if(res) {
     ip_blob->size = sizeof(struct in_addr);
     memcpy(&ip_blob->bData.ia, &ia, sizeof(struct in_addr));
     result = TRUE;
   }
   else {
-    res = Curl_inet_pton(AF_INET6, hostname, &ia6);
+    res = curlx_inet_pton(AF_INET6, hostname, &ia6);
     if(res) {
       ip_blob->size = sizeof(struct in6_addr);
       memcpy(&ip_blob->bData.ia6, &ia6, sizeof(struct in6_addr));
@@ -511,15 +534,68 @@ static bool get_alt_name_info(struct Curl_easy *data,
 #endif
   return result;
 }
+#endif /* !UNDER_CE */
 
 /* Verify the server's hostname */
 CURLcode Curl_verify_host(struct Curl_cfilter *cf,
                           struct Curl_easy *data)
 {
-  struct ssl_connect_data *connssl = cf->ctx;
-  SECURITY_STATUS sspi_status;
   CURLcode result = CURLE_PEER_FAILED_VERIFICATION;
+  struct ssl_connect_data *connssl = cf->ctx;
   CERT_CONTEXT *pCertContextServer = NULL;
+#ifdef UNDER_CE
+  TCHAR cert_hostname_buff[256];
+  DWORD len;
+
+  /* This code does not support certificates with multiple alternative names.
+   * Right now we are only asking for the first preferred alternative name.
+   * Instead we would need to do all via CERT_NAME_SEARCH_ALL_NAMES_FLAG
+   * (If Windows CE supports that?) and run this section in a loop for each.
+   * https://msdn.microsoft.com/en-us/library/windows/desktop/aa376086.aspx
+   * curl: (51) schannel: CertGetNameString() certificate hostname
+   * (.google.com) did not match connection (google.com)
+   */
+  len = CertGetNameString(pCertContextServer,
+                          CERT_NAME_DNS_TYPE,
+                          CERT_NAME_DISABLE_IE4_UTF8_FLAG,
+                          NULL,
+                          cert_hostname_buff,
+                          256);
+  if(len > 0) {
+    /* Comparing the cert name and the connection hostname encoded as UTF-8
+     * is acceptable since both values are assumed to use ASCII
+     * (or some equivalent) encoding
+     */
+    char *cert_hostname = curlx_convert_tchar_to_UTF8(cert_hostname_buff);
+    if(!cert_hostname) {
+      result = CURLE_OUT_OF_MEMORY;
+    }
+    else{
+      const char *conn_hostname = connssl->peer.hostname;
+      if(Curl_cert_hostcheck(cert_hostname, strlen(cert_hostname),
+                             conn_hostname, strlen(conn_hostname))) {
+        infof(data,
+              "schannel: connection hostname (%s) validated "
+              "against certificate name (%s)\n",
+              conn_hostname, cert_hostname);
+        result = CURLE_OK;
+      }
+      else{
+        failf(data,
+              "schannel: connection hostname (%s) "
+              "does not match certificate name (%s)",
+              conn_hostname, cert_hostname);
+      }
+      Curl_safefree(cert_hostname);
+    }
+  }
+  else {
+    failf(data,
+          "schannel: CertGetNameString did not provide any "
+          "certificate name information");
+  }
+#else
+  SECURITY_STATUS sspi_status;
   TCHAR *cert_hostname_buff = NULL;
   size_t cert_hostname_buff_index = 0;
   const char *conn_hostname = connssl->peer.hostname;
@@ -539,7 +615,7 @@ CURLcode Curl_verify_host(struct Curl_cfilter *cf,
                                      &pCertContextServer);
 
   if((sspi_status != SEC_E_OK) || !pCertContextServer) {
-    char buffer[STRERROR_LEN];
+    char buffer[WINAPI_ERROR_LEN];
     failf(data, "schannel: Failed to read remote certificate context: %s",
           Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
     goto cleanup;
@@ -664,6 +740,7 @@ CURLcode Curl_verify_host(struct Curl_cfilter *cf,
 
   if(pCertContextServer)
     CertFreeCertificateContext(pCertContextServer);
+#endif /* !UNDER_CE */
 
   return result;
 }
@@ -681,23 +758,26 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf,
   CERT_CONTEXT *pCertContextServer = NULL;
   const CERT_CHAIN_CONTEXT *pChainContext = NULL;
   HCERTCHAINENGINE cert_chain_engine = NULL;
+#ifndef UNDER_CE
   HCERTSTORE trust_store = NULL;
   HCERTSTORE own_trust_store = NULL;
+#endif /* !UNDER_CE */
 
   DEBUGASSERT(BACKEND);
 
   sspi_status =
     Curl_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle,
-                                     SECPKG_ATTR_REMOTE_CERT_CONTEXT,
-                                     &pCertContextServer);
+                                        SECPKG_ATTR_REMOTE_CERT_CONTEXT,
+                                        &pCertContextServer);
 
   if((sspi_status != SEC_E_OK) || !pCertContextServer) {
-    char buffer[STRERROR_LEN];
+    char buffer[WINAPI_ERROR_LEN];
     failf(data, "schannel: Failed to read remote certificate context: %s",
           Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
     result = CURLE_PEER_FAILED_VERIFICATION;
   }
 
+#ifndef UNDER_CE
   if(result == CURLE_OK &&
       (conn_config->CAfile || conn_config->ca_info_blob) &&
       BACKEND->use_manual_cred_validation) {
@@ -727,9 +807,9 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf,
                                     CERT_STORE_CREATE_NEW_FLAG,
                                     NULL);
         if(!trust_store) {
-          char buffer[STRERROR_LEN];
+          char buffer[WINAPI_ERROR_LEN];
           failf(data, "schannel: failed to create certificate store: %s",
-                Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
+                curlx_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
           result = CURLE_SSL_CACERT_BADFILE;
         }
         else {
@@ -774,14 +854,15 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf,
         CertCreateCertificateChainEngine(
           (CERT_CHAIN_ENGINE_CONFIG *)&engine_config, &cert_chain_engine);
       if(!create_engine_result) {
-        char buffer[STRERROR_LEN];
+        char buffer[WINAPI_ERROR_LEN];
         failf(data,
               "schannel: failed to create certificate chain engine: %s",
-              Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
+              curlx_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
         result = CURLE_SSL_CACERT_BADFILE;
       }
     }
   }
+#endif /* !UNDER_CE */
 
   if(result == CURLE_OK) {
     CERT_CHAIN_PARA ChainPara;
@@ -798,9 +879,9 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf,
                                  CERT_CHAIN_REVOCATION_CHECK_CHAIN),
                                 NULL,
                                 &pChainContext)) {
-      char buffer[STRERROR_LEN];
+      char buffer[WINAPI_ERROR_LEN];
       failf(data, "schannel: CertGetCertificateChain failed: %s",
-            Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
+            curlx_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
       pChainContext = NULL;
       result = CURLE_PEER_FAILED_VERIFICATION;
     }
@@ -848,6 +929,7 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf,
     }
   }
 
+#ifndef UNDER_CE
   if(cert_chain_engine) {
     CertFreeCertificateChainEngine(cert_chain_engine);
   }
@@ -855,6 +937,7 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf,
   if(own_trust_store) {
     CertCloseStore(own_trust_store, 0);
   }
+#endif /* !UNDER_CE */
 
   if(pChainContext)
     CertFreeCertificateChain(pChainContext);
diff --git a/extra/curl/curl-8.12.1/lib/vtls/sectransp.c b/extra/curl/curl-8.14.1/lib/vtls/sectransp.c
similarity index 93%
rename from extra/curl/curl-8.12.1/lib/vtls/sectransp.c
rename to extra/curl/curl-8.14.1/lib/vtls/sectransp.c
index a0f6dccc28de..2ae2ef35a2bb 100644
--- a/extra/curl/curl-8.12.1/lib/vtls/sectransp.c
+++ b/extra/curl/curl-8.14.1/lib/vtls/sectransp.c
@@ -28,18 +28,18 @@
  * TLS/SSL layer. No code but vtls.c should ever call or use these functions.
  */
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #ifdef USE_SECTRANSP
 
-#include "urldata.h" /* for the Curl_easy definition */
-#include "curl_base64.h"
-#include "strtok.h"
-#include "multiif.h"
-#include "strcase.h"
+#include "../urldata.h" /* for the Curl_easy definition */
+#include "../curlx/base64.h"
+#include "../curlx/strparse.h"
+#include "../multiif.h"
+#include "../strcase.h"
 #include "x509asn1.h"
 #include "vtls_scache.h"
-#include "strerror.h"
+#include "../strerror.h"
 #include "cipher_suite.h"
 
 #ifdef __clang__
@@ -134,19 +134,19 @@
 #include <sys/sysctl.h>
 #endif /* CURL_BUILD_MAC */
 
-#include "sendf.h"
-#include "inet_pton.h"
-#include "connect.h"
-#include "select.h"
+#include "../sendf.h"
+#include "../curlx/inet_pton.h"
+#include "../connect.h"
+#include "../select.h"
 #include "vtls.h"
 #include "vtls_int.h"
 #include "sectransp.h"
-#include "curl_printf.h"
-#include "strdup.h"
+#include "../curl_printf.h"
+#include "../strdup.h"
 
-#include "curl_memory.h"
+#include "../curl_memory.h"
 /* The last #include file should be: */
-#include "memdebug.h"
+#include "../memdebug.h"
 
 
 /* From MacTypes.h (which we cannot include because it is not present in
@@ -211,9 +211,6 @@ static const uint16_t default_ciphers[] = {
 #endif /* CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 */
 };
 
-#define DEFAULT_CIPHERS_LEN sizeof(default_ciphers)/sizeof(default_ciphers[0])
-
-
 /* pinned public key support tests */
 
 /* version 1 supports macOS 10.12+ and iOS 10+ */
@@ -266,7 +263,7 @@ static OSStatus sectransp_bio_cf_in_read(SSLConnectionRef connection,
                                          void *buf,
                                          size_t *dataLength)  /* IN/OUT */
 {
-  struct Curl_cfilter *cf = (struct Curl_cfilter *)connection;
+  const struct Curl_cfilter *cf = (const struct Curl_cfilter *)connection;
   struct ssl_connect_data *connssl = cf->ctx;
   struct st_ssl_backend_data *backend =
     (struct st_ssl_backend_data *)connssl->backend;
@@ -306,7 +303,7 @@ static OSStatus sectransp_bio_cf_out_write(SSLConnectionRef connection,
                                            const void *buf,
                                            size_t *dataLength)  /* IN/OUT */
 {
-  struct Curl_cfilter *cf = (struct Curl_cfilter *)connection;
+  const struct Curl_cfilter *cf = (const struct Curl_cfilter *)connection;
   struct ssl_connect_data *connssl = cf->ctx;
   struct st_ssl_backend_data *backend =
     (struct st_ssl_backend_data *)connssl->backend;
@@ -341,30 +338,28 @@ static OSStatus sectransp_bio_cf_out_write(SSLConnectionRef connection,
 CF_INLINE void GetDarwinVersionNumber(int *major, int *minor)
 {
   int mib[2];
-  char *os_version;
   size_t os_version_len;
-  char *os_version_major, *os_version_minor;
-  char *tok_buf;
+  char buf[256];
 
   /* Get the Darwin kernel version from the kernel using sysctl(): */
   mib[0] = CTL_KERN;
   mib[1] = KERN_OSRELEASE;
   if(sysctl(mib, 2, NULL, &os_version_len, NULL, 0) == -1)
     return;
-  os_version = malloc(os_version_len*sizeof(char));
-  if(!os_version)
-    return;
-  if(sysctl(mib, 2, os_version, &os_version_len, NULL, 0) == -1) {
-    free(os_version);
-    return;
+  if(os_version_len < sizeof(buf)) {
+    if(sysctl(mib, 2, buf, &os_version_len, NULL, 0) != -1) {
+      const char *os = buf;
+      curl_off_t fnum;
+      curl_off_t snum;
+      /* Parse the version: */
+      if(!curlx_str_number(&os, &fnum, INT_MAX) &&
+         !curlx_str_single(&os, '.') &&
+         !curlx_str_number(&os, &snum, INT_MAX)) {
+        *major = (int)fnum;
+        *minor = (int)snum;
+      }
+    }
   }
-
-  /* Parse the version: */
-  os_version_major = Curl_strtok_r(os_version, ".", &tok_buf);
-  os_version_minor = Curl_strtok_r(NULL, ".", &tok_buf);
-  *major = atoi(os_version_major);
-  *minor = atoi(os_version_minor);
-  free(os_version);
 }
 #endif /* CURL_BUILD_MAC */
 
@@ -541,8 +536,8 @@ static OSStatus CopyIdentityWithLabel(char *label,
       for(i = 0; i < keys_list_count; i++) {
         OSStatus err = noErr;
         SecCertificateRef cert = NULL;
-        SecIdentityRef identity =
-          (SecIdentityRef) CFArrayGetValueAtIndex(keys_list, i);
+        const void *item = CFArrayGetValueAtIndex(keys_list, i);
+        SecIdentityRef identity = (SecIdentityRef)CURL_UNCONST(item);
         err = SecIdentityCopyCertificate(identity, &cert);
         if(err == noErr) {
           CFStringRef common_name = NULL;
@@ -671,22 +666,22 @@ static OSStatus CopyIdentityFromPKCS12File(const char *cPath,
       count = CFArrayGetCount(items);
 
       for(i = 0; i < count; i++) {
-        CFTypeRef item = (CFTypeRef) CFArrayGetValueAtIndex(items, i);
-        CFTypeID  itemID = CFGetTypeID(item);
+        const CFTypeRef item = CFArrayGetValueAtIndex(items, i);
+        CFTypeID itemID = CFGetTypeID(item);
 
         if(itemID == CFDictionaryGetTypeID()) {
-          CFTypeRef identity = (CFTypeRef) CFDictionaryGetValue(
-                                                 (CFDictionaryRef) item,
-                                                 kSecImportItemIdentity);
+          const CFTypeRef identity = CFDictionaryGetValue(
+                                           (CFDictionaryRef)item,
+                                           kSecImportItemIdentity);
           CFRetain(identity);
-          *out_cert_and_key = (SecIdentityRef) identity;
+          *out_cert_and_key = (SecIdentityRef)CURL_UNCONST(identity);
           break;
         }
 #if CURL_BUILD_MAC_10_7
         else if(itemID == SecCertificateGetTypeID()) {
           status = SecIdentityCreateWithCertificate(NULL,
-                                                 (SecCertificateRef) item,
-                                                 out_cert_and_key);
+                                         (SecCertificateRef)CURL_UNCONST(item),
+                                         out_cert_and_key);
           break;
         }
 #endif
@@ -927,7 +922,7 @@ static CURLcode sectransp_set_default_ciphers(struct Curl_easy *data,
   /* Intersect the ciphers supported by Secure Transport with the default
    * ciphers, using the order of the former. */
   for(i = 0; i < supported_len; i++) {
-    for(j = 0; j < DEFAULT_CIPHERS_LEN; j++) {
+    for(j = 0; j < CURL_ARRAYSIZE(default_ciphers); j++) {
       if(default_ciphers[j] == ciphers[i]) {
         ciphers[count++] = ciphers[i];
         break;
@@ -1096,10 +1091,13 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf,
   if(result != CURLE_OK)
     return result;
 
-#if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && \
-    defined(HAVE_BUILTIN_AVAILABLE)
   if(connssl->alpn) {
+#if CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11
+#ifdef HAVE_BUILTIN_AVAILABLE
     if(__builtin_available(macOS 10.13.4, iOS 11, tvOS 11, *)) {
+#else
+    if(&SSLSetALPNProtocols && &SSLCopyALPNProtocols) {
+#endif
       struct alpn_proto_buf proto;
       size_t i;
       CFStringRef cstr;
@@ -1121,8 +1119,8 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf,
       Curl_alpn_to_proto_str(&proto, connssl->alpn);
       infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
     }
+#endif /* CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 */
   }
-#endif
 
   if(ssl_config->key) {
     infof(data, "WARNING: SSL: CURLOPT_SSLKEY is ignored by Secure "
@@ -1338,8 +1336,9 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf,
     size_t ssl_sessionid_len;
 
     Curl_ssl_scache_lock(data);
-    if(Curl_ssl_scache_get_obj(cf, data, connssl->peer.scache_key,
-                               (void **)&ssl_sessionid)) {
+    ssl_sessionid = Curl_ssl_scache_get_obj(cf, data,
+                                            connssl->peer.scache_key);
+    if(ssl_sessionid) {
       /* we got a session id, use it! */
       err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid,
                          strlen(ssl_sessionid));
@@ -1436,7 +1435,7 @@ static long pem_to_der(const char *in, unsigned char **out, size_t *outlen)
   }
   b64[j] = '\0';
 
-  err = Curl_base64_decode((const char *)b64, out, outlen);
+  err = curlx_base64_decode((const char *)b64, out, outlen);
   free(b64);
   if(err) {
     free(*out);
@@ -1455,7 +1454,7 @@ static int read_cert(const char *file, unsigned char **out, size_t *outlen)
   unsigned char buf[512];
   struct dynbuf certs;
 
-  Curl_dyn_init(&certs, MAX_CERTS_SIZE);
+  curlx_dyn_init(&certs, MAX_CERTS_SIZE);
 
   fd = open(file, 0);
   if(fd < 0)
@@ -1467,18 +1466,18 @@ static int read_cert(const char *file, unsigned char **out, size_t *outlen)
       break;
     if(n < 0) {
       close(fd);
-      Curl_dyn_free(&certs);
+      curlx_dyn_free(&certs);
       return -1;
     }
-    if(Curl_dyn_addn(&certs, buf, n)) {
+    if(curlx_dyn_addn(&certs, buf, n)) {
       close(fd);
       return -1;
     }
   }
   close(fd);
 
-  *out = Curl_dyn_uptr(&certs);
-  *outlen = Curl_dyn_len(&certs);
+  *out = curlx_dyn_uptr(&certs);
+  *outlen = curlx_dyn_len(&certs);
 
   return 0;
 }
@@ -1696,7 +1695,8 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data,
                                     const char *pinnedpubkey)
 {  /* Scratch */
   size_t pubkeylen, realpubkeylen, spkiHeaderLength = 24;
-  unsigned char *pubkey = NULL, *realpubkey = NULL;
+  const unsigned char *pubkey = NULL;
+  unsigned char *realpubkey = NULL;
   const unsigned char *spkiHeader = NULL;
   CFDataRef publicKeyBits = NULL;
 
@@ -1746,7 +1746,7 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data,
 #endif /* SECTRANSP_PINNEDPUBKEY_V2 */
 
     pubkeylen = (size_t)CFDataGetLength(publicKeyBits);
-    pubkey = (unsigned char *)CFDataGetBytePtr(publicKeyBits);
+    pubkey = (const unsigned char *)CFDataGetBytePtr(publicKeyBits);
 
     switch(pubkeylen) {
       case 526:
@@ -2092,10 +2092,13 @@ static CURLcode sectransp_connect_step2(struct Curl_cfilter *cf,
         break;
     }
 
-#if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && \
-    defined(HAVE_BUILTIN_AVAILABLE)
     if(connssl->alpn) {
+#if CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11
+#ifdef HAVE_BUILTIN_AVAILABLE
       if(__builtin_available(macOS 10.13.4, iOS 11, tvOS 11, *)) {
+#else
+      if(&SSLSetALPNProtocols && &SSLCopyALPNProtocols) {
+#endif
         CFArrayRef alpnArr = NULL;
         CFStringRef chosenProtocol = NULL;
         err = SSLCopyALPNProtocols(backend->ssl_ctx, &alpnArr);
@@ -2122,8 +2125,8 @@ static CURLcode sectransp_connect_step2(struct Curl_cfilter *cf,
         if(alpnArr)
           CFRelease(alpnArr);
       }
+#endif /* CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 */
     }
-#endif
 
     return CURLE_OK;
   }
@@ -2131,7 +2134,7 @@ static CURLcode sectransp_connect_step2(struct Curl_cfilter *cf,
 
 static CURLcode
 add_cert_to_certinfo(struct Curl_easy *data,
-                     SecCertificateRef server_cert,
+                     const SecCertificateRef server_cert,
                      int idx)
 {
   CURLcode result = CURLE_OK;
@@ -2151,7 +2154,7 @@ add_cert_to_certinfo(struct Curl_easy *data,
 
 static CURLcode
 collect_server_cert_single(struct Curl_cfilter *cf, struct Curl_easy *data,
-                           SecCertificateRef server_cert,
+                           const SecCertificateRef server_cert,
                            CFIndex idx)
 {
   CURLcode result = CURLE_OK;
@@ -2248,8 +2251,8 @@ static CURLcode collect_server_cert(struct Curl_cfilter *cf,
       if(ssl_config->certinfo)
         result = Curl_ssl_init_certinfo(data, (int)count);
       for(i = 0L ; !result && (i < count) ; i++) {
-        server_cert = (SecCertificateRef)CFArrayGetValueAtIndex(server_certs,
-                                                                i);
+        const void *item = CFArrayGetValueAtIndex(server_certs, i);
+        server_cert = (SecCertificateRef)CURL_UNCONST(item);
         result = collect_server_cert_single(cf, data, server_cert, i);
       }
       CFRelease(server_certs);
@@ -2265,7 +2268,8 @@ static CURLcode collect_server_cert(struct Curl_cfilter *cf,
     if(ssl_config->certinfo)
       result = Curl_ssl_init_certinfo(data, (int)count);
     for(i = 0L ; !result && (i < count) ; i++) {
-      server_cert = (SecCertificateRef)CFArrayGetValueAtIndex(server_certs, i);
+      const void *item = CFArrayGetValueAtIndex(server_certs, i);
+      server_cert = (SecCertificateRef)CURL_UNCONST(item);
       result = collect_server_cert_single(cf, data, server_cert, i);
     }
     CFRelease(server_certs);
@@ -2292,15 +2296,12 @@ static CURLcode sectransp_connect_step3(struct Curl_cfilter *cf,
   return CURLE_OK;
 }
 
-static CURLcode
-sectransp_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data,
-                         bool nonblocking,
-                         bool *done)
+static CURLcode sectransp_connect(struct Curl_cfilter *cf,
+                                  struct Curl_easy *data,
+                                  bool *done)
 {
   CURLcode result;
   struct ssl_connect_data *connssl = cf->ctx;
-  curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
-  int what;
 
   /* check if the connection has already been established */
   if(ssl_connection_complete == connssl->state) {
@@ -2308,73 +2309,20 @@ sectransp_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data,
     return CURLE_OK;
   }
 
-  if(ssl_connect_1 == connssl->connecting_state) {
-    /* Find out how much more time we are allowed */
-    const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
-    if(timeout_ms < 0) {
-      /* no need to continue if time already is up */
-      failf(data, "SSL connection timeout");
-      return CURLE_OPERATION_TIMEDOUT;
-    }
+  *done = FALSE;
+  connssl->io_need = CURL_SSL_IO_NEED_NONE;
 
+  if(ssl_connect_1 == connssl->connecting_state) {
     result = sectransp_connect_step1(cf, data);
     if(result)
       return result;
   }
 
-  while(ssl_connect_2 == connssl->connecting_state) {
-
-    /* check allowed time left */
-    const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
-    if(timeout_ms < 0) {
-      /* no need to continue if time already is up */
-      failf(data, "SSL connection timeout");
-      return CURLE_OPERATION_TIMEDOUT;
-    }
-
-    /* if ssl is expecting something, check if it is available. */
-    if(connssl->io_need) {
-
-      curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND) ?
-        sockfd : CURL_SOCKET_BAD;
-      curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV) ?
-        sockfd : CURL_SOCKET_BAD;
-
-      what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
-                               nonblocking ? 0 : timeout_ms);
-      if(what < 0) {
-        /* fatal error */
-        failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
-        return CURLE_SSL_CONNECT_ERROR;
-      }
-      else if(0 == what) {
-        if(nonblocking) {
-          *done = FALSE;
-          return CURLE_OK;
-        }
-        else {
-          /* timeout */
-          failf(data, "SSL connection timeout");
-          return CURLE_OPERATION_TIMEDOUT;
-        }
-      }
-      /* socket is readable or writable */
-    }
-
-    /* Run transaction, and return to the caller if it failed or if this
-     * connection is done nonblocking and this loop would execute again. This
-     * permits the owner of a multi handle to abort a connection attempt
-     * before step2 has completed while ensuring that a client using select()
-     * or epoll() will always have a valid fdset to wait on.
-     */
+  if(ssl_connect_2 == connssl->connecting_state) {
     result = sectransp_connect_step2(cf, data);
-    if(result || (nonblocking && (ssl_connect_2 == connssl->connecting_state)))
+    if(result)
       return result;
-
-  } /* repeat step2 until all transactions are done. */
-
+  }
 
   if(ssl_connect_3 == connssl->connecting_state) {
     result = sectransp_connect_step3(cf, data);
@@ -2387,34 +2335,6 @@ sectransp_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data,
     connssl->state = ssl_connection_complete;
     *done = TRUE;
   }
-  else
-    *done = FALSE;
-
-  /* Reset our connect state machine */
-  connssl->connecting_state = ssl_connect_1;
-
-  return CURLE_OK;
-}
-
-static CURLcode sectransp_connect_nonblocking(struct Curl_cfilter *cf,
-                                              struct Curl_easy *data,
-                                              bool *done)
-{
-  return sectransp_connect_common(cf, data, TRUE, done);
-}
-
-static CURLcode sectransp_connect(struct Curl_cfilter *cf,
-                                  struct Curl_easy *data)
-{
-  CURLcode result;
-  bool done = FALSE;
-
-  result = sectransp_connect_common(cf, data, FALSE, &done);
-
-  if(result)
-    return result;
-
-  DEBUGASSERT(done);
 
   return CURLE_OK;
 }
@@ -2549,7 +2469,7 @@ static bool sectransp_data_pending(struct Curl_cfilter *cf,
   DEBUGASSERT(backend);
 
   if(backend->ssl_ctx) {  /* SSL is in use */
-    CURL_TRC_CF((struct Curl_easy *)data, cf, "data_pending");
+    CURL_TRC_CF((struct Curl_easy *)CURL_UNCONST(data), cf, "data_pending");
     err = SSLGetBufferedReadSize(backend->ssl_ctx, &buffer);
     if(err == noErr)
       return buffer > 0UL;
@@ -2759,7 +2679,6 @@ const struct Curl_ssl Curl_ssl_sectransp = {
   sectransp_random,                   /* random */
   NULL,                               /* cert_status_request */
   sectransp_connect,                  /* connect */
-  sectransp_connect_nonblocking,      /* connect_nonblocking */
   Curl_ssl_adjust_pollset,            /* adjust_pollset */
   sectransp_get_internals,            /* get_internals */
   sectransp_close,                    /* close_one */
diff --git a/extra/curl/curl-8.12.1/lib/vtls/sectransp.h b/extra/curl/curl-8.14.1/lib/vtls/sectransp.h
similarity index 97%
rename from extra/curl/curl-8.12.1/lib/vtls/sectransp.h
rename to extra/curl/curl-8.14.1/lib/vtls/sectransp.h
index 0f1085ad91b2..c82dc184456b 100644
--- a/extra/curl/curl-8.12.1/lib/vtls/sectransp.h
+++ b/extra/curl/curl-8.14.1/lib/vtls/sectransp.h
@@ -24,7 +24,7 @@
  * SPDX-License-Identifier: curl
  *
  ***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #ifdef USE_SECTRANSP
 
diff --git a/extra/curl/curl-8.12.1/lib/vtls/vtls.c b/extra/curl/curl-8.14.1/lib/vtls/vtls.c
similarity index 86%
rename from extra/curl/curl-8.12.1/lib/vtls/vtls.c
rename to extra/curl/curl-8.14.1/lib/vtls/vtls.c
index 9c59b4c97bbb..c6fb60d0a4f6 100644
--- a/extra/curl/curl-8.12.1/lib/vtls/vtls.c
+++ b/extra/curl/curl-8.14.1/lib/vtls/vtls.c
@@ -38,7 +38,7 @@
    https://httpd.apache.org/docs/2.0/ssl/ssl_intro.html
 */
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
@@ -50,8 +50,8 @@
 #include <fcntl.h>
 #endif
 
-#include "urldata.h"
-#include "cfilters.h"
+#include "../urldata.h"
+#include "../cfilters.h"
 
 #include "vtls.h" /* generic SSL protos etc */
 #include "vtls_int.h"
@@ -66,28 +66,28 @@
 #include "bearssl.h"        /* BearSSL versions */
 #include "rustls.h"         /* Rustls versions */
 
-#include "slist.h"
-#include "sendf.h"
-#include "strcase.h"
-#include "url.h"
-#include "progress.h"
-#include "share.h"
-#include "multiif.h"
-#include "timeval.h"
-#include "curl_md5.h"
-#include "curl_sha256.h"
-#include "warnless.h"
-#include "curl_base64.h"
-#include "curl_printf.h"
-#include "inet_pton.h"
-#include "connect.h"
-#include "select.h"
-#include "strdup.h"
-#include "rand.h"
+#include "../slist.h"
+#include "../sendf.h"
+#include "../strcase.h"
+#include "../url.h"
+#include "../progress.h"
+#include "../share.h"
+#include "../multiif.h"
+#include "../curlx/timeval.h"
+#include "../curl_md5.h"
+#include "../curl_sha256.h"
+#include "../curlx/warnless.h"
+#include "../curlx/base64.h"
+#include "../curl_printf.h"
+#include "../curlx/inet_pton.h"
+#include "../connect.h"
+#include "../select.h"
+#include "../strdup.h"
+#include "../rand.h"
 
 /* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
 
 
 #define CLONE_STRING(var)                    \
@@ -154,17 +154,19 @@ static const struct alpn_spec ALPN_SPEC_H2_H11 = {
 };
 #endif
 
-static const struct alpn_spec *alpn_get_spec(int httpwant, bool use_alpn)
+static const struct alpn_spec *
+alpn_get_spec(http_majors allowed, bool use_alpn)
 {
   if(!use_alpn)
     return NULL;
 #ifdef USE_HTTP2
-  if(httpwant == CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE)
+  if(allowed & CURL_HTTP_V2x) {
+    if(allowed & CURL_HTTP_V1x)
+      return &ALPN_SPEC_H2_H11;
     return &ALPN_SPEC_H2;
-  if(httpwant >= CURL_HTTP_VERSION_2)
-    return &ALPN_SPEC_H2_H11;
+  }
 #else
-  (void)httpwant;
+  (void)allowed;
 #endif
   /* Use the ALPN protocol "http/1.1" for HTTP/1.x.
      Avoid "http/1.0" because some servers do not support it. */
@@ -213,6 +215,7 @@ match_ssl_primary_config(struct Curl_easy *data,
      strcasecompare(c1->cipher_list, c2->cipher_list) &&
      strcasecompare(c1->cipher_list13, c2->cipher_list13) &&
      strcasecompare(c1->curves, c2->curves) &&
+     strcasecompare(c1->signature_algorithms, c2->signature_algorithms) &&
      strcasecompare(c1->CRLfile, c2->CRLfile) &&
      strcasecompare(c1->pinned_key, c2->pinned_key))
     return TRUE;
@@ -257,6 +260,7 @@ static bool clone_ssl_primary_config(struct ssl_primary_config *source,
   CLONE_STRING(cipher_list13);
   CLONE_STRING(pinned_key);
   CLONE_STRING(curves);
+  CLONE_STRING(signature_algorithms);
   CLONE_STRING(CRLfile);
 #ifdef USE_TLS_SRP
   CLONE_STRING(username);
@@ -279,6 +283,7 @@ static void free_primary_ssl_config(struct ssl_primary_config *sslc)
   Curl_safefree(sslc->ca_info_blob);
   Curl_safefree(sslc->issuercert_blob);
   Curl_safefree(sslc->curves);
+  Curl_safefree(sslc->signature_algorithms);
   Curl_safefree(sslc->CRLfile);
 #ifdef USE_TLS_SRP
   Curl_safefree(sslc->username);
@@ -297,6 +302,8 @@ CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data)
     data->set.str[STRING_SSL_CIPHER_LIST];
   data->set.ssl.primary.cipher_list13 =
     data->set.str[STRING_SSL_CIPHER13_LIST];
+  data->set.ssl.primary.signature_algorithms =
+    data->set.str[STRING_SSL_SIGNATURE_ALGORITHMS];
   data->set.ssl.primary.pinned_key =
     data->set.str[STRING_SSL_PINNEDPUBLICKEY];
   data->set.ssl.primary.cert_blob = data->set.blobs[BLOB_CERT];
@@ -483,39 +490,6 @@ static void cf_ctx_free(struct ssl_connect_data *ctx)
   }
 }
 
-static CURLcode ssl_connect(struct Curl_cfilter *cf, struct Curl_easy *data)
-{
-  struct ssl_connect_data *connssl = cf->ctx;
-  CURLcode result;
-
-  if(!ssl_prefs_check(data))
-    return CURLE_SSL_CONNECT_ERROR;
-
-  /* mark this is being ssl-enabled from here on. */
-  connssl->state = ssl_connection_negotiating;
-
-  result = connssl->ssl_impl->connect_blocking(cf, data);
-
-  if(!result) {
-    DEBUGASSERT(connssl->state == ssl_connection_complete);
-  }
-
-  return result;
-}
-
-static CURLcode
-ssl_connect_nonblocking(struct Curl_cfilter *cf, struct Curl_easy *data,
-                        bool *done)
-{
-  struct ssl_connect_data *connssl = cf->ctx;
-
-  if(!ssl_prefs_check(data))
-    return CURLE_SSL_CONNECT_ERROR;
-
-  /* mark this is being ssl requested from here on. */
-  return connssl->ssl_impl->connect_nonblocking(cf, data, done);
-}
-
 CURLcode Curl_ssl_get_channel_binding(struct Curl_easy *data, int sockindex,
                                        struct dynbuf *binding)
 {
@@ -642,17 +616,17 @@ CURLcode Curl_ssl_push_certinfo_len(struct Curl_easy *data,
 
   DEBUGASSERT(certnum < ci->num_of_certs);
 
-  Curl_dyn_init(&build, CURL_X509_STR_MAX);
+  curlx_dyn_init(&build, CURL_X509_STR_MAX);
 
-  if(Curl_dyn_add(&build, label) ||
-     Curl_dyn_addn(&build, ":", 1) ||
-     Curl_dyn_addn(&build, value, valuelen))
+  if(curlx_dyn_add(&build, label) ||
+     curlx_dyn_addn(&build, ":", 1) ||
+     curlx_dyn_addn(&build, value, valuelen))
     return CURLE_OUT_OF_MEMORY;
 
   nl = Curl_slist_append_nodup(ci->certinfo[certnum],
-                               Curl_dyn_ptr(&build));
+                               curlx_dyn_ptr(&build));
   if(!nl) {
-    Curl_dyn_free(&build);
+    curlx_dyn_free(&build);
     curl_slist_free_all(ci->certinfo[certnum]);
     result = CURLE_OUT_OF_MEMORY;
   }
@@ -689,7 +663,7 @@ static CURLcode pubkey_pem_to_der(const char *pem,
   if(!pem)
     return CURLE_BAD_CONTENT_ENCODING;
 
-  Curl_dyn_init(&pbuf, MAX_PINNED_PUBKEY_SIZE);
+  curlx_dyn_init(&pbuf, MAX_PINNED_PUBKEY_SIZE);
 
   begin_pos = strstr(pem, "-----BEGIN PUBLIC KEY-----");
   if(!begin_pos)
@@ -717,16 +691,19 @@ static CURLcode pubkey_pem_to_der(const char *pem,
    */
   while(pem_count < pem_len) {
     if('\n' != pem[pem_count] && '\r' != pem[pem_count]) {
-      result = Curl_dyn_addn(&pbuf, &pem[pem_count], 1);
+      result = curlx_dyn_addn(&pbuf, &pem[pem_count], 1);
       if(result)
         return result;
     }
     ++pem_count;
   }
 
-  result = Curl_base64_decode(Curl_dyn_ptr(&pbuf), der, der_len);
-
-  Curl_dyn_free(&pbuf);
+  if(curlx_dyn_len(&pbuf)) {
+    result = curlx_base64_decode(curlx_dyn_ptr(&pbuf), der, der_len);
+    curlx_dyn_free(&pbuf);
+  }
+  else
+    result = CURLE_BAD_CONTENT_ENCODING;
 
   return result;
 }
@@ -770,9 +747,9 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data,
                                  sha256sumdigest, CURL_SHA256_DIGEST_LENGTH);
 
     if(!encode)
-      encode = Curl_base64_encode((char *)sha256sumdigest,
-                                  CURL_SHA256_DIGEST_LENGTH, &encoded,
-                                  &encodedlen);
+      encode = curlx_base64_encode((char *)sha256sumdigest,
+                                   CURL_SHA256_DIGEST_LENGTH, &encoded,
+                                   &encodedlen);
     Curl_safefree(sha256sumdigest);
 
     if(encode)
@@ -791,8 +768,8 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data,
     do {
       end_pos = strstr(begin_pos, ";sha256//");
       /*
-       * if there is an end_pos, null terminate,
-       * otherwise it will go to the end of the original string
+       * if there is an end_pos, null-terminate, otherwise it will go to the
+       * end of the original string
        */
       if(end_pos)
         end_pos[0] = '\0';
@@ -827,7 +804,7 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data,
     if(!fp)
       return result;
 
-    Curl_dyn_init(&buf, MAX_PINNED_PUBKEY_SIZE);
+    curlx_dyn_init(&buf, MAX_PINNED_PUBKEY_SIZE);
 
     /* Determine the file's size */
     if(fseek(fp, 0, SEEK_END))
@@ -855,23 +832,23 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data,
       size_t want = left > sizeof(buffer) ? sizeof(buffer) : left;
       if(want != fread(buffer, 1, want, fp))
         goto end;
-      if(Curl_dyn_addn(&buf, buffer, want))
+      if(curlx_dyn_addn(&buf, buffer, want))
         goto end;
       left -= want;
     } while(left);
 
     /* If the sizes are the same, it cannot be base64 encoded, must be der */
     if(pubkeylen == size) {
-      if(!memcmp(pubkey, Curl_dyn_ptr(&buf), pubkeylen))
+      if(!memcmp(pubkey, curlx_dyn_ptr(&buf), pubkeylen))
         result = CURLE_OK;
       goto end;
     }
 
     /*
-     * Otherwise we will assume it is PEM and try to decode it
-     * after placing null terminator
+     * Otherwise we will assume it is PEM and try to decode it after placing
+     * null-terminator
      */
-    pem_read = pubkey_pem_to_der(Curl_dyn_ptr(&buf), &pem_ptr, &pem_len);
+    pem_read = pubkey_pem_to_der(curlx_dyn_ptr(&buf), &pem_ptr, &pem_len);
     /* if it was not read successfully, exit */
     if(pem_read)
       goto end;
@@ -883,7 +860,7 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data,
     if(pubkeylen == pem_len && !memcmp(pubkey, pem_ptr, pubkeylen))
       result = CURLE_OK;
 end:
-    Curl_dyn_free(&buf);
+    curlx_dyn_free(&buf);
     Curl_safefree(pem_ptr);
     fclose(fp);
   }
@@ -921,20 +898,11 @@ static int multissl_init(void)
 }
 
 static CURLcode multissl_connect(struct Curl_cfilter *cf,
-                                 struct Curl_easy *data)
+                                 struct Curl_easy *data, bool *done)
 {
   if(multissl_setup(NULL))
     return CURLE_FAILED_INIT;
-  return Curl_ssl->connect_blocking(cf, data);
-}
-
-static CURLcode multissl_connect_nonblocking(struct Curl_cfilter *cf,
-                                             struct Curl_easy *data,
-                                             bool *done)
-{
-  if(multissl_setup(NULL))
-    return CURLE_FAILED_INIT;
-  return Curl_ssl->connect_nonblocking(cf, data, done);
+  return Curl_ssl->do_connect(cf, data, done);
 }
 
 static void multissl_adjust_pollset(struct Curl_cfilter *cf,
@@ -993,7 +961,6 @@ static const struct Curl_ssl Curl_ssl_multi = {
   NULL,                              /* random */
   NULL,                              /* cert_status_request */
   multissl_connect,                  /* connect */
-  multissl_connect_nonblocking,      /* connect_nonblocking */
   multissl_adjust_pollset,           /* adjust_pollset */
   multissl_get_internals,            /* get_internals */
   multissl_close,                    /* close_one */
@@ -1232,10 +1199,10 @@ static ssl_peer_type get_peer_type(const char *hostname)
 #else
     struct in_addr addr;
 #endif
-    if(Curl_inet_pton(AF_INET, hostname, &addr))
+    if(curlx_inet_pton(AF_INET, hostname, &addr))
       return CURL_SSL_PEER_IPV4;
 #ifdef USE_IPV6
-    else if(Curl_inet_pton(AF_INET6, hostname, &addr)) {
+    else if(curlx_inet_pton(AF_INET6, hostname, &addr)) {
       return CURL_SSL_PEER_IPV6;
     }
 #endif
@@ -1341,13 +1308,13 @@ static void ssl_cf_close(struct Curl_cfilter *cf,
 
 static CURLcode ssl_cf_connect(struct Curl_cfilter *cf,
                                struct Curl_easy *data,
-                               bool blocking, bool *done)
+                               bool *done)
 {
   struct ssl_connect_data *connssl = cf->ctx;
   struct cf_call_data save;
   CURLcode result;
 
-  if(cf->connected) {
+  if(cf->connected && (connssl->state != ssl_connection_deferred)) {
     *done = TRUE;
     return CURLE_OK;
   }
@@ -1358,15 +1325,13 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf,
   }
 
   if(!cf->next->connected) {
-    result = cf->next->cft->do_connect(cf->next, data, blocking, done);
+    result = cf->next->cft->do_connect(cf->next, data, done);
     if(result || !*done)
       return result;
   }
 
   CF_DATA_SAVE(save, cf, data);
   CURL_TRC_CF(data, cf, "cf_connect()");
-  DEBUGASSERT(data->conn);
-  DEBUGASSERT(data->conn == cf->conn);
   DEBUGASSERT(connssl);
 
   *done = FALSE;
@@ -1378,21 +1343,23 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf,
       goto out;
   }
 
-  if(blocking) {
-    result = ssl_connect(cf, data);
-    *done = (result == CURLE_OK);
-  }
-  else {
-    result = ssl_connect_nonblocking(cf, data, done);
+  if(!connssl->prefs_checked) {
+    if(!ssl_prefs_check(data))
+      return CURLE_SSL_CONNECT_ERROR;
+    connssl->prefs_checked = TRUE;
   }
 
+  result = connssl->ssl_impl->do_connect(cf, data, done);
+
   if(!result && *done) {
     cf->connected = TRUE;
     if(connssl->state == ssl_connection_complete)
-      connssl->handshake_done = Curl_now();
+      connssl->handshake_done = curlx_now();
     /* Connection can be deferred when sending early data */
     DEBUGASSERT(connssl->state == ssl_connection_complete ||
                 connssl->state == ssl_connection_deferred);
+    DEBUGASSERT(connssl->state != ssl_connection_deferred ||
+                connssl->earlydata_state > ssl_earlydata_none);
   }
 out:
   CURL_TRC_CF(data, cf, "cf_connect() -> %d, done=%d", result, *done);
@@ -1400,6 +1367,77 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf,
   return result;
 }
 
+static CURLcode ssl_cf_set_earlydata(struct Curl_cfilter *cf,
+                                     struct Curl_easy *data,
+                                     const void *buf, size_t blen)
+{
+  struct ssl_connect_data *connssl = cf->ctx;
+  ssize_t nwritten = 0;
+  CURLcode result = CURLE_OK;
+
+  DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_await);
+  DEBUGASSERT(Curl_bufq_is_empty(&connssl->earlydata));
+  if(blen) {
+    if(blen > connssl->earlydata_max)
+      blen = connssl->earlydata_max;
+    nwritten = Curl_bufq_write(&connssl->earlydata, buf, blen, &result);
+    CURL_TRC_CF(data, cf, "ssl_cf_set_earlydata(len=%zu) -> %zd",
+                blen, nwritten);
+    if(nwritten < 0)
+      return result;
+  }
+  return CURLE_OK;
+}
+
+static CURLcode ssl_cf_connect_deferred(struct Curl_cfilter *cf,
+                                        struct Curl_easy *data,
+                                        const void *buf, size_t blen,
+                                        bool *done)
+{
+  struct ssl_connect_data *connssl = cf->ctx;
+  CURLcode result = CURLE_OK;
+
+  DEBUGASSERT(connssl->state == ssl_connection_deferred);
+  *done = FALSE;
+  if(connssl->earlydata_state == ssl_earlydata_await) {
+    result = ssl_cf_set_earlydata(cf, data, buf, blen);
+    if(result)
+      return result;
+    /* we buffered any early data we'd like to send. Actually
+     * do the connect now which sends it and performs the handshake. */
+    connssl->earlydata_state = ssl_earlydata_sending;
+    connssl->earlydata_skip = Curl_bufq_len(&connssl->earlydata);
+  }
+
+  result = ssl_cf_connect(cf, data, done);
+
+  if(!result && *done) {
+    Curl_pgrsTimeWas(data, TIMER_APPCONNECT, connssl->handshake_done);
+    switch(connssl->earlydata_state) {
+    case ssl_earlydata_none:
+      break;
+    case ssl_earlydata_accepted:
+      if(!Curl_ssl_cf_is_proxy(cf))
+        Curl_pgrsEarlyData(data, (curl_off_t)connssl->earlydata_skip);
+      infof(data, "Server accepted %zu bytes of TLS early data.",
+            connssl->earlydata_skip);
+      break;
+    case ssl_earlydata_rejected:
+      if(!Curl_ssl_cf_is_proxy(cf))
+        Curl_pgrsEarlyData(data, -(curl_off_t)connssl->earlydata_skip);
+      infof(data, "Server rejected TLS early data.");
+      connssl->earlydata_skip = 0;
+      break;
+    default:
+      /* This should not happen. Either we do not use early data or we
+       * should know if it was accepted or not. */
+      DEBUGASSERT(NULL);
+      break;
+    }
+  }
+  return result;
+}
+
 static bool ssl_cf_data_pending(struct Curl_cfilter *cf,
                                 const struct Curl_easy *data)
 {
@@ -1418,21 +1456,57 @@ static bool ssl_cf_data_pending(struct Curl_cfilter *cf,
 }
 
 static ssize_t ssl_cf_send(struct Curl_cfilter *cf,
-                           struct Curl_easy *data, const void *buf, size_t len,
+                           struct Curl_easy *data,
+                           const void *buf, size_t blen,
                            bool eos, CURLcode *err)
 {
   struct ssl_connect_data *connssl = cf->ctx;
   struct cf_call_data save;
-  ssize_t nwritten = 0;
+  ssize_t nwritten = 0, early_written = 0;
 
   (void)eos;
-  /* OpenSSL and maybe other TLS libs do not like 0-length writes. Skip. */
   *err = CURLE_OK;
-  if(len > 0) {
-    CF_DATA_SAVE(save, cf, data);
-    nwritten = connssl->ssl_impl->send_plain(cf, data, buf, len, err);
-    CF_DATA_RESTORE(cf, save);
+  CF_DATA_SAVE(save, cf, data);
+
+  if(connssl->state == ssl_connection_deferred) {
+    bool done = FALSE;
+    *err = ssl_cf_connect_deferred(cf, data, buf, blen, &done);
+    if(*err) {
+      nwritten = -1;
+      goto out;
+    }
+    else if(!done) {
+      *err = CURLE_AGAIN;
+      nwritten = -1;
+      goto out;
+    }
+    DEBUGASSERT(connssl->state == ssl_connection_complete);
+  }
+
+  if(connssl->earlydata_skip) {
+    if(connssl->earlydata_skip >= blen) {
+      connssl->earlydata_skip -= blen;
+      *err = CURLE_OK;
+      nwritten = (ssize_t)blen;
+      goto out;
+    }
+    else {
+      early_written = connssl->earlydata_skip;
+      buf = ((const char *)buf) + connssl->earlydata_skip;
+      blen -= connssl->earlydata_skip;
+      connssl->earlydata_skip = 0;
+    }
   }
+
+  /* OpenSSL and maybe other TLS libs do not like 0-length writes. Skip. */
+  if(blen > 0)
+    nwritten = connssl->ssl_impl->send_plain(cf, data, buf, blen, err);
+
+  if(nwritten >= 0)
+    nwritten += early_written;
+
+out:
+  CF_DATA_RESTORE(cf, save);
   return nwritten;
 }
 
@@ -1446,6 +1520,21 @@ static ssize_t ssl_cf_recv(struct Curl_cfilter *cf,
 
   CF_DATA_SAVE(save, cf, data);
   *err = CURLE_OK;
+  if(connssl->state == ssl_connection_deferred) {
+    bool done = FALSE;
+    *err = ssl_cf_connect_deferred(cf, data, NULL, 0, &done);
+    if(*err) {
+      nread = -1;
+      goto out;
+    }
+    else if(!done) {
+      *err = CURLE_AGAIN;
+      nread = -1;
+      goto out;
+    }
+    DEBUGASSERT(connssl->state == ssl_connection_complete);
+  }
+
   nread = connssl->ssl_impl->recv_plain(cf, data, buf, len, err);
   if(nread > 0) {
     DEBUGASSERT((size_t)nread <= len);
@@ -1454,6 +1543,8 @@ static ssize_t ssl_cf_recv(struct Curl_cfilter *cf,
     /* eof */
     *err = CURLE_OK;
   }
+
+out:
   CURL_TRC_CF(data, cf, "cf_recv(len=%zu) -> %zd, %d", len,
               nread, *err);
   CF_DATA_RESTORE(cf, save);
@@ -1468,7 +1559,9 @@ static CURLcode ssl_cf_shutdown(struct Curl_cfilter *cf,
   CURLcode result = CURLE_OK;
 
   *done = TRUE;
-  if(!cf->shutdown && Curl_ssl->shut_down) {
+  /* If we have done the SSL handshake, shut down the connection cleanly */
+  if(cf->connected && (connssl->state == ssl_connection_complete) &&
+    !cf->shutdown && Curl_ssl->shut_down) {
     struct cf_call_data save;
 
     CF_DATA_SAVE(save, cf, data);
@@ -1576,8 +1669,14 @@ static CURLcode cf_ssl_create(struct Curl_cfilter **pcf,
 
   DEBUGASSERT(data->conn);
 
-  ctx = cf_ctx_new(data, alpn_get_spec(data->state.httpwant,
+#ifdef CURL_DISABLE_HTTP
+  /* We only support ALPN for HTTP so far. */
+  DEBUGASSERT(!conn->bits.tls_enable_alpn);
+  ctx = cf_ctx_new(data, NULL);
+#else
+  ctx = cf_ctx_new(data, alpn_get_spec(data->state.http_neg.wanted,
                                        conn->bits.tls_enable_alpn));
+#endif
   if(!ctx) {
     result = CURLE_OUT_OF_MEMORY;
     goto out;
@@ -1627,16 +1726,16 @@ static CURLcode cf_ssl_proxy_create(struct Curl_cfilter **pcf,
   struct ssl_connect_data *ctx;
   CURLcode result;
   bool use_alpn = conn->bits.tls_enable_alpn;
-  int httpwant = CURL_HTTP_VERSION_1_1;
+  http_majors allowed = CURL_HTTP_V1x;
 
 #ifdef USE_HTTP2
   if(conn->http_proxy.proxytype == CURLPROXY_HTTPS2) {
     use_alpn = TRUE;
-    httpwant = CURL_HTTP_VERSION_2;
+    allowed = (CURL_HTTP_V1x|CURL_HTTP_V2x);
   }
 #endif
 
-  ctx = cf_ctx_new(data, alpn_get_spec(httpwant, use_alpn));
+  ctx = cf_ctx_new(data, alpn_get_spec(allowed, use_alpn));
   if(!ctx) {
     result = CURLE_OUT_OF_MEMORY;
     goto out;
@@ -1768,7 +1867,7 @@ CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data,
     if(cf->cft == &Curl_cft_ssl) {
       bool done;
       CURL_TRC_CF(data, cf, "shutdown and remove SSL, start");
-      Curl_shutdown_start(data, sockindex, NULL);
+      Curl_shutdown_start(data, sockindex, 0, NULL);
       result = vtls_shutdown_blocking(cf, data, send_shutdown, &done);
       Curl_shutdown_clear(data, sockindex);
       if(!result && !done) /* blocking failed? */
@@ -1866,6 +1965,24 @@ bool Curl_alpn_contains_proto(const struct alpn_spec *spec,
   return FALSE;
 }
 
+void Curl_alpn_restrict_to(struct alpn_spec *spec, const char *proto)
+{
+  size_t plen = strlen(proto);
+  DEBUGASSERT(plen < sizeof(spec->entries[0]));
+  if(plen < sizeof(spec->entries[0])) {
+    memcpy(spec->entries[0], proto, plen + 1);
+    spec->count = 1;
+  }
+}
+
+void Curl_alpn_copy(struct alpn_spec *dest, const struct alpn_spec *src)
+{
+  if(src)
+    memcpy(dest, src, sizeof(*dest));
+  else
+    memset(dest, 0, sizeof(*dest));
+}
+
 CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf,
                                   struct Curl_easy *data,
                                   struct ssl_connect_data *connssl,
diff --git a/extra/curl/curl-8.12.1/lib/vtls/vtls.h b/extra/curl/curl-8.14.1/lib/vtls/vtls.h
similarity index 98%
rename from extra/curl/curl-8.12.1/lib/vtls/vtls.h
rename to extra/curl/curl-8.14.1/lib/vtls/vtls.h
index b751c3743c78..0bb333b987a3 100644
--- a/extra/curl/curl-8.12.1/lib/vtls/vtls.h
+++ b/extra/curl/curl-8.14.1/lib/vtls/vtls.h
@@ -23,7 +23,7 @@
  * SPDX-License-Identifier: curl
  *
  ***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 struct connectdata;
 struct ssl_config_data;
@@ -42,9 +42,10 @@ struct dynbuf;
 #define SSLSUPP_ECH          (1<<7)
 #define SSLSUPP_CA_CACHE     (1<<8)
 #define SSLSUPP_CIPHER_LIST  (1<<9) /* supports TLS 1.0-1.2 ciphersuites */
+#define SSLSUPP_SIGNATURE_ALGORITHMS (1<<10) /* supports TLS sigalgs */
 
 #ifdef USE_ECH
-# include "curl_base64.h"
+# include "../curlx/base64.h"
 # define ECH_ENABLED(__data__) \
     (__data__->set.tls_ech && \
      !(__data__->set.tls_ech & CURLECH_DISABLE)\
diff --git a/extra/curl/curl-8.12.1/lib/vtls/vtls_int.h b/extra/curl/curl-8.14.1/lib/vtls/vtls_int.h
similarity index 93%
rename from extra/curl/curl-8.12.1/lib/vtls/vtls_int.h
rename to extra/curl/curl-8.14.1/lib/vtls/vtls_int.h
index 3a5611dfe43a..0632a07657d8 100644
--- a/extra/curl/curl-8.12.1/lib/vtls/vtls_int.h
+++ b/extra/curl/curl-8.14.1/lib/vtls/vtls_int.h
@@ -23,9 +23,9 @@
  * SPDX-License-Identifier: curl
  *
  ***************************************************************************/
-#include "curl_setup.h"
-#include "cfilters.h"
-#include "urldata.h"
+#include "../curl_setup.h"
+#include "../cfilters.h"
+#include "../urldata.h"
 #include "vtls.h"
 
 #ifdef USE_SSL
@@ -49,7 +49,7 @@ struct ssl_connect_data;
 #define ALPN_PROTO_BUF_MAX   (ALPN_ENTRIES_MAX * (ALPN_NAME_MAX + 1))
 
 struct alpn_spec {
-  const char entries[ALPN_ENTRIES_MAX][ALPN_NAME_MAX];
+  char entries[ALPN_ENTRIES_MAX][ALPN_NAME_MAX];
   size_t count; /* number of entries */
 };
 
@@ -62,6 +62,8 @@ CURLcode Curl_alpn_to_proto_buf(struct alpn_proto_buf *buf,
                                 const struct alpn_spec *spec);
 CURLcode Curl_alpn_to_proto_str(struct alpn_proto_buf *buf,
                                 const struct alpn_spec *spec);
+void Curl_alpn_restrict_to(struct alpn_spec *spec, const char *proto);
+void Curl_alpn_copy(struct alpn_spec *dest, const struct alpn_spec *src);
 
 CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf,
                                   struct Curl_easy *data,
@@ -89,7 +91,7 @@ typedef enum {
 
 typedef enum {
   ssl_earlydata_none,
-  ssl_earlydata_use,
+  ssl_earlydata_await,
   ssl_earlydata_sending,
   ssl_earlydata_sent,
   ssl_earlydata_accepted,
@@ -124,6 +126,7 @@ struct ssl_connect_data {
   int io_need;                      /* TLS signals special SEND/RECV needs */
   BIT(use_alpn);                    /* if ALPN shall be used in handshake */
   BIT(peer_closed);                 /* peer has closed connection */
+  BIT(prefs_checked);               /* SSL preferences have been checked */
 };
 
 
@@ -157,11 +160,8 @@ struct Curl_ssl {
                      size_t length);
   bool (*cert_status_request)(void);
 
-  CURLcode (*connect_blocking)(struct Curl_cfilter *cf,
-                               struct Curl_easy *data);
-  CURLcode (*connect_nonblocking)(struct Curl_cfilter *cf,
-                                  struct Curl_easy *data,
-                                  bool *done);
+  CURLcode (*do_connect)(struct Curl_cfilter *cf, struct Curl_easy *data,
+                         bool *done);
 
   /* During handshake/shutdown, adjust the pollset to include the socket
    * for POLLOUT or POLLIN as needed. Mandatory. */
diff --git a/extra/curl/curl-8.12.1/lib/vtls/vtls_scache.c b/extra/curl/curl-8.14.1/lib/vtls/vtls_scache.c
similarity index 86%
rename from extra/curl/curl-8.12.1/lib/vtls/vtls_scache.c
rename to extra/curl/curl-8.14.1/lib/vtls/vtls_scache.c
index 365e945920da..1fe4b5bbdee6 100644
--- a/extra/curl/curl-8.12.1/lib/vtls/vtls_scache.c
+++ b/extra/curl/curl-8.14.1/lib/vtls/vtls_scache.c
@@ -22,7 +22,7 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #ifdef USE_SSL
 
@@ -36,28 +36,31 @@
 #include <fcntl.h>
 #endif
 
-#include "urldata.h"
-#include "cfilters.h"
+#include "../urldata.h"
+#include "../cfilters.h"
 
 #include "vtls.h" /* generic SSL protos etc */
 #include "vtls_int.h"
 #include "vtls_scache.h"
 #include "vtls_spack.h"
 
-#include "strcase.h"
-#include "url.h"
-#include "llist.h"
-#include "share.h"
-#include "curl_trc.h"
-#include "curl_sha256.h"
-#include "rand.h"
-#include "warnless.h"
-#include "curl_printf.h"
-#include "strdup.h"
+#include "../strcase.h"
+#include "../url.h"
+#include "../llist.h"
+#include "../share.h"
+#include "../curl_trc.h"
+#include "../curl_sha256.h"
+#include "../rand.h"
+#include "../curlx/warnless.h"
+#include "../curl_printf.h"
+#include "../strdup.h"
 
 /* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
+
+
+static bool cf_ssl_peer_key_is_global(const char *peer_key);
 
 /* a peer+tls-config we cache sessions for */
 struct Curl_ssl_scache_peer {
@@ -73,6 +76,7 @@ struct Curl_ssl_scache_peer {
   size_t max_sessions;
   long age;                /* just a number, the higher the more recent */
   BIT(hmac_set);           /* if key_salt and key_hmac are present */
+  BIT(exportable);         /* sessions for this peer can be exported */
 };
 
 #define CURL_SCACHE_MAGIC 0x000e1551
@@ -104,32 +108,18 @@ static struct Curl_ssl_scache *cf_ssl_scache_get(struct Curl_easy *data)
   return scache;
 }
 
-static void cf_ssl_scache_clear_session(struct Curl_ssl_session *s)
-{
-  if(s->sdata) {
-    free((void *)s->sdata);
-    s->sdata = NULL;
-  }
-  s->sdata_len = 0;
-  if(s->quic_tp) {
-    free((void *)s->quic_tp);
-    s->quic_tp = NULL;
-  }
-  s->quic_tp_len = 0;
-  s->ietf_tls_id = 0;
-  s->valid_until = 0;
-  Curl_safefree(s->alpn);
-}
-
-static void cf_ssl_scache_sesssion_ldestroy(void *udata, void *s)
+static void cf_ssl_scache_session_ldestroy(void *udata, void *obj)
 {
+  struct Curl_ssl_session *s = obj;
   (void)udata;
-  cf_ssl_scache_clear_session(s);
+  free(CURL_UNCONST(s->sdata));
+  free(CURL_UNCONST(s->quic_tp));
+  free((void *)s->alpn);
   free(s);
 }
 
 CURLcode
-Curl_ssl_session_create(unsigned char *sdata, size_t sdata_len,
+Curl_ssl_session_create(void *sdata, size_t sdata_len,
                         int ietf_tls_id, const char *alpn,
                         curl_off_t valid_until, size_t earlydata_max,
                         struct Curl_ssl_session **psession)
@@ -140,7 +130,7 @@ Curl_ssl_session_create(unsigned char *sdata, size_t sdata_len,
 }
 
 CURLcode
-Curl_ssl_session_create2(unsigned char *sdata, size_t sdata_len,
+Curl_ssl_session_create2(void *sdata, size_t sdata_len,
                          int ietf_tls_id, const char *alpn,
                          curl_off_t valid_until, size_t earlydata_max,
                          unsigned char *quic_tp, size_t quic_tp_len,
@@ -171,7 +161,7 @@ Curl_ssl_session_create2(unsigned char *sdata, size_t sdata_len,
   if(alpn) {
     s->alpn = strdup(alpn);
     if(!s->alpn) {
-      cf_ssl_scache_sesssion_ldestroy(NULL, s);
+      cf_ssl_scache_session_ldestroy(NULL, s);
       return CURLE_OUT_OF_MEMORY;
     }
   }
@@ -186,7 +176,7 @@ void Curl_ssl_session_destroy(struct Curl_ssl_session *s)
     if(Curl_node_llist(&s->list))
       Curl_node_remove(&s->list);
     else {
-      cf_ssl_scache_sesssion_ldestroy(NULL, s);
+      cf_ssl_scache_session_ldestroy(NULL, s);
     }
   }
 }
@@ -223,6 +213,19 @@ static void cf_ssl_scache_peer_set_obj(struct Curl_ssl_scache_peer *peer,
   peer->sobj_free = sobj_free;
 }
 
+static void cf_ssl_cache_peer_update(struct Curl_ssl_scache_peer *peer)
+{
+  /* The sessions of this peer are exportable if
+   * - it has no confidential information
+   * - its peer key is not yet known, because sessions were
+   *   imported using only the salt+hmac
+   * - the peer key is global, e.g. carrying no relative paths */
+  peer->exportable = (!peer->clientcert && !peer->srp_username &&
+                      !peer->srp_password &&
+                      (!peer->ssl_peer_key ||
+                       cf_ssl_peer_key_is_global(peer->ssl_peer_key)));
+}
+
 static CURLcode
 cf_ssl_scache_peer_init(struct Curl_ssl_scache_peer *peer,
                         const char *ssl_peer_key,
@@ -265,6 +268,8 @@ cf_ssl_scache_peer_init(struct Curl_ssl_scache_peer *peer,
     if(!peer->srp_password)
       goto out;
   }
+
+  cf_ssl_cache_peer_update(peer);
   result = CURLE_OK;
 out:
   if(result)
@@ -336,7 +341,7 @@ CURLcode Curl_ssl_scache_create(size_t max_peers,
   for(i = 0; i < scache->peer_count; ++i) {
     scache->peers[i].max_sessions = max_sessions_per_peer;
     Curl_llist_init(&scache->peers[i].sessions,
-                    cf_ssl_scache_sesssion_ldestroy);
+                    cf_ssl_scache_session_ldestroy);
   }
 
   *pscache = scache;
@@ -372,28 +377,34 @@ void Curl_ssl_scache_unlock(struct Curl_easy *data)
 
 static CURLcode cf_ssl_peer_key_add_path(struct dynbuf *buf,
                                           const char *name,
-                                          char *path)
+                                          char *path,
+                                          bool *is_local)
 {
   if(path && path[0]) {
     /* We try to add absolute paths, so that the session key can stay
      * valid when used in another process with different CWD. However,
      * when a path does not exist, this does not work. Then, we add
      * the path as is. */
-#ifdef _WIN32
+#ifdef UNDER_CE
+    (void)is_local;
+    return curlx_dyn_addf(buf, ":%s-%s", name, path);
+#elif defined(_WIN32)
     char abspath[_MAX_PATH];
     if(_fullpath(abspath, path, _MAX_PATH))
-      return Curl_dyn_addf(buf, ":%s-%s", name, abspath);
+      return curlx_dyn_addf(buf, ":%s-%s", name, abspath);
+    *is_local = TRUE;
 #elif defined(HAVE_REALPATH)
     if(path[0] != '/') {
       char *abspath = realpath(path, NULL);
       if(abspath) {
-        CURLcode r = Curl_dyn_addf(buf, ":%s-%s", name, abspath);
+        CURLcode r = curlx_dyn_addf(buf, ":%s-%s", name, abspath);
         (free)(abspath); /* allocated by libc, free without memdebug */
         return r;
       }
+      *is_local = TRUE;
     }
 #endif
-    return Curl_dyn_addf(buf, ":%s-%s", name, path);
+    return curlx_dyn_addf(buf, ":%s-%s", name, path);
   }
   return CURLE_OK;
 }
@@ -407,14 +418,14 @@ static CURLcode cf_ssl_peer_key_add_hash(struct dynbuf *buf,
     unsigned char hash[CURL_SHA256_DIGEST_LENGTH];
     size_t i;
 
-    r = Curl_dyn_addf(buf, ":%s-", name);
+    r = curlx_dyn_addf(buf, ":%s-", name);
     if(r)
       goto out;
     r = Curl_sha256it(hash, blob->data, blob->len);
     if(r)
       goto out;
     for(i = 0; i < CURL_SHA256_DIGEST_LENGTH; ++i) {
-      r = Curl_dyn_addf(buf, "%02x", hash[i]);
+      r = curlx_dyn_addf(buf, "%02x", hash[i]);
       if(r)
         goto out;
     }
@@ -423,6 +434,17 @@ static CURLcode cf_ssl_peer_key_add_hash(struct dynbuf *buf,
   return r;
 }
 
+#define CURL_SSLS_LOCAL_SUFFIX     ":L"
+#define CURL_SSLS_GLOBAL_SUFFIX    ":G"
+
+static bool cf_ssl_peer_key_is_global(const char *peer_key)
+{
+  size_t len = peer_key ? strlen(peer_key) : 0;
+  return (len > 2) &&
+         (peer_key[len - 1] == 'G') &&
+         (peer_key[len - 2] == ':');
+}
+
 CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf,
                                 const struct ssl_peer *peer,
                                 const char *tls_id,
@@ -431,12 +453,13 @@ CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf,
   struct ssl_primary_config *ssl = Curl_ssl_cf_get_primary_config(cf);
   struct dynbuf buf;
   size_t key_len;
+  bool is_local = FALSE;
   CURLcode r;
 
   *ppeer_key = NULL;
-  Curl_dyn_init(&buf, 10 * 1024);
+  curlx_dyn_init(&buf, 10 * 1024);
 
-  r = Curl_dyn_addf(&buf, "%s:%d", peer->hostname, peer->port);
+  r = curlx_dyn_addf(&buf, "%s:%d", peer->hostname, peer->port);
   if(r)
     goto out;
 
@@ -444,86 +467,86 @@ CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf,
   case TRNSPRT_TCP:
     break;
   case TRNSPRT_UDP:
-    r = Curl_dyn_add(&buf, ":UDP");
+    r = curlx_dyn_add(&buf, ":UDP");
     break;
   case TRNSPRT_QUIC:
-    r = Curl_dyn_add(&buf, ":QUIC");
+    r = curlx_dyn_add(&buf, ":QUIC");
     break;
   case TRNSPRT_UNIX:
-    r = Curl_dyn_add(&buf, ":UNIX");
+    r = curlx_dyn_add(&buf, ":UNIX");
     break;
   default:
-    r = Curl_dyn_addf(&buf, ":TRNSPRT-%d", peer->transport);
+    r = curlx_dyn_addf(&buf, ":TRNSPRT-%d", peer->transport);
     break;
   }
   if(r)
     goto out;
 
   if(!ssl->verifypeer) {
-    r = Curl_dyn_add(&buf, ":NO-VRFY-PEER");
+    r = curlx_dyn_add(&buf, ":NO-VRFY-PEER");
     if(r)
       goto out;
   }
   if(!ssl->verifyhost) {
-    r = Curl_dyn_add(&buf, ":NO-VRFY-HOST");
+    r = curlx_dyn_add(&buf, ":NO-VRFY-HOST");
     if(r)
       goto out;
   }
   if(ssl->verifystatus) {
-    r = Curl_dyn_add(&buf, ":VRFY-STATUS");
+    r = curlx_dyn_add(&buf, ":VRFY-STATUS");
     if(r)
       goto out;
   }
   if(!ssl->verifypeer || !ssl->verifyhost) {
     if(cf->conn->bits.conn_to_host) {
-      r = Curl_dyn_addf(&buf, ":CHOST-%s", cf->conn->conn_to_host.name);
+      r = curlx_dyn_addf(&buf, ":CHOST-%s", cf->conn->conn_to_host.name);
       if(r)
         goto out;
     }
     if(cf->conn->bits.conn_to_port) {
-      r = Curl_dyn_addf(&buf, ":CPORT-%d", cf->conn->conn_to_port);
+      r = curlx_dyn_addf(&buf, ":CPORT-%d", cf->conn->conn_to_port);
       if(r)
         goto out;
     }
   }
 
   if(ssl->version || ssl->version_max) {
-    r = Curl_dyn_addf(&buf, ":TLSVER-%d-%d", ssl->version,
+    r = curlx_dyn_addf(&buf, ":TLSVER-%d-%d", ssl->version,
                       (ssl->version_max >> 16));
     if(r)
       goto out;
   }
   if(ssl->ssl_options) {
-    r = Curl_dyn_addf(&buf, ":TLSOPT-%x", ssl->ssl_options);
+    r = curlx_dyn_addf(&buf, ":TLSOPT-%x", ssl->ssl_options);
     if(r)
       goto out;
   }
   if(ssl->cipher_list) {
-    r = Curl_dyn_addf(&buf, ":CIPHER-%s", ssl->cipher_list);
+    r = curlx_dyn_addf(&buf, ":CIPHER-%s", ssl->cipher_list);
     if(r)
       goto out;
   }
   if(ssl->cipher_list13) {
-    r = Curl_dyn_addf(&buf, ":CIPHER13-%s", ssl->cipher_list13);
+    r = curlx_dyn_addf(&buf, ":CIPHER13-%s", ssl->cipher_list13);
     if(r)
       goto out;
   }
   if(ssl->curves) {
-    r = Curl_dyn_addf(&buf, ":CURVES-%s", ssl->curves);
+    r = curlx_dyn_addf(&buf, ":CURVES-%s", ssl->curves);
     if(r)
       goto out;
   }
   if(ssl->verifypeer) {
-    r = cf_ssl_peer_key_add_path(&buf, "CA", ssl->CAfile);
+    r = cf_ssl_peer_key_add_path(&buf, "CA", ssl->CAfile, &is_local);
     if(r)
       goto out;
-    r = cf_ssl_peer_key_add_path(&buf, "CApath", ssl->CApath);
+    r = cf_ssl_peer_key_add_path(&buf, "CApath", ssl->CApath, &is_local);
     if(r)
       goto out;
-    r = cf_ssl_peer_key_add_path(&buf, "CRL", ssl->CRLfile);
+    r = cf_ssl_peer_key_add_path(&buf, "CRL", ssl->CRLfile, &is_local);
     if(r)
       goto out;
-    r = cf_ssl_peer_key_add_path(&buf, "Issuer", ssl->issuercert);
+    r = cf_ssl_peer_key_add_path(&buf, "Issuer", ssl->issuercert, &is_local);
     if(r)
       goto out;
     if(ssl->cert_blob) {
@@ -543,19 +566,19 @@ CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf,
     }
   }
   if(ssl->pinned_key && ssl->pinned_key[0]) {
-    r = Curl_dyn_addf(&buf, ":Pinned-%s", ssl->pinned_key);
+    r = curlx_dyn_addf(&buf, ":Pinned-%s", ssl->pinned_key);
     if(r)
       goto out;
   }
 
   if(ssl->clientcert && ssl->clientcert[0]) {
-    r = Curl_dyn_add(&buf, ":CCERT");
+    r = curlx_dyn_add(&buf, ":CCERT");
     if(r)
       goto out;
   }
 #ifdef USE_TLS_SRP
   if(ssl->username || ssl->password) {
-    r = Curl_dyn_add(&buf, ":SRP-AUTH");
+    r = curlx_dyn_add(&buf, ":SRP-AUTH");
     if(r)
       goto out;
   }
@@ -565,17 +588,21 @@ CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf,
     r = CURLE_FAILED_INIT;
     goto out;
   }
-  r = Curl_dyn_addf(&buf, ":IMPL-%s", tls_id);
+  r = curlx_dyn_addf(&buf, ":IMPL-%s", tls_id);
   if(r)
     goto out;
 
-  *ppeer_key = Curl_dyn_take(&buf, &key_len);
-  /* we just added printable char, and dynbuf always 0 terminates,
-   * no need to track length */
+  r = curlx_dyn_addf(&buf, is_local ?
+                     CURL_SSLS_LOCAL_SUFFIX : CURL_SSLS_GLOBAL_SUFFIX);
+  if(r)
+    goto out;
 
+  *ppeer_key = curlx_dyn_take(&buf, &key_len);
+  /* we just added printable char, and dynbuf always null-terminates, no need
+   * to track length */
 
 out:
-  Curl_dyn_free(&buf);
+  curlx_dyn_free(&buf);
   return r;
 }
 
@@ -655,6 +682,7 @@ cf_ssl_find_peer_by_key(struct Curl_easy *data,
           result = CURLE_OUT_OF_MEMORY;
           goto out;
         }
+        cf_ssl_cache_peer_update(&scache->peers[i]);
         *ppeer = &scache->peers[i];
         goto out;
       }
@@ -832,10 +860,6 @@ CURLcode Curl_ssl_scache_put(struct Curl_cfilter *cf,
     Curl_ssl_session_destroy(s);
     return CURLE_OK;
   }
-  if(!GOOD_SCACHE(scache)) {
-    Curl_ssl_session_destroy(s);
-    return CURLE_BAD_FUNCTION_ARGUMENT;
-  }
 
   Curl_ssl_scache_lock(data);
   result = cf_scache_add_session(cf, data, scache, ssl_peer_key, s);
@@ -933,31 +957,29 @@ CURLcode Curl_ssl_scache_add_obj(struct Curl_cfilter *cf,
   return result;
 }
 
-bool Curl_ssl_scache_get_obj(struct Curl_cfilter *cf,
-                             struct Curl_easy *data,
-                             const char *ssl_peer_key,
-                             void **sobj)
+void *Curl_ssl_scache_get_obj(struct Curl_cfilter *cf,
+                              struct Curl_easy *data,
+                              const char *ssl_peer_key)
 {
   struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   struct Curl_ssl_scache_peer *peer = NULL;
   CURLcode result;
+  void *sobj;
 
-  *sobj = NULL;
   if(!scache)
-    return FALSE;
+    return NULL;
 
   result = cf_ssl_find_peer_by_key(data, scache, ssl_peer_key, conn_config,
                                    &peer);
   if(result)
-    return FALSE;
+    return NULL;
 
-  if(peer)
-    *sobj = peer->sobj;
+  sobj = peer ? peer->sobj : NULL;
 
   CURL_TRC_SSLS(data, "%s cached session for '%s'",
-                *sobj ? "Found" : "No", ssl_peer_key);
-  return !!*sobj;
+                sobj ? "Found" : "No", ssl_peer_key);
+  return sobj;
 }
 
 void Curl_ssl_scache_remove_all(struct Curl_cfilter *cf,
@@ -1062,7 +1084,7 @@ cf_ssl_find_peer_by_hmac(struct Curl_ssl_scache *scache,
 CURLcode Curl_ssl_session_import(struct Curl_easy *data,
                                  const char *ssl_peer_key,
                                  const unsigned char *shmac, size_t shmac_len,
-                                 const unsigned char *sdata, size_t sdata_len)
+                                 const void *sdata, size_t sdata_len)
 {
   struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
   struct Curl_ssl_scache_peer *peer = NULL;
@@ -1150,17 +1172,17 @@ CURLcode Curl_ssl_session_export(struct Curl_easy *data,
 
   Curl_ssl_scache_lock(data);
 
-  Curl_dyn_init(&hbuf, (CURL_SHA256_DIGEST_LENGTH * 2) + 1);
-  Curl_dyn_init(&sbuf, CURL_SSL_TICKET_MAX);
+  curlx_dyn_init(&hbuf, (CURL_SHA256_DIGEST_LENGTH * 2) + 1);
+  curlx_dyn_init(&sbuf, CURL_SSL_TICKET_MAX);
 
   for(i = 0; scache && i < scache->peer_count; i++) {
     peer = &scache->peers[i];
     if(!peer->ssl_peer_key && !peer->hmac_set)
       continue;  /* skip free entry */
-    if(peer->clientcert || peer->srp_username || peer->srp_password)
-      continue;  /* not exporting those */
+    if(!peer->exportable)
+      continue;
 
-    Curl_dyn_reset(&hbuf);
+    curlx_dyn_reset(&hbuf);
     cf_scache_peer_remove_expired(peer, now);
     n = Curl_llist_head(&peer->sessions);
     if(n)
@@ -1172,22 +1194,22 @@ CURLcode Curl_ssl_session_export(struct Curl_easy *data,
         if(r)
           goto out;
       }
-      if(!Curl_dyn_len(&hbuf)) {
-        r = Curl_dyn_addn(&hbuf, peer->key_salt, sizeof(peer->key_salt));
+      if(!curlx_dyn_len(&hbuf)) {
+        r = curlx_dyn_addn(&hbuf, peer->key_salt, sizeof(peer->key_salt));
         if(r)
           goto out;
-        r = Curl_dyn_addn(&hbuf, peer->key_hmac, sizeof(peer->key_hmac));
+        r = curlx_dyn_addn(&hbuf, peer->key_hmac, sizeof(peer->key_hmac));
         if(r)
           goto out;
       }
-      Curl_dyn_reset(&sbuf);
+      curlx_dyn_reset(&sbuf);
       r = Curl_ssl_session_pack(data, s, &sbuf);
       if(r)
         goto out;
 
       r = export_fn(data, userptr, peer->ssl_peer_key,
-                    Curl_dyn_uptr(&hbuf), Curl_dyn_len(&hbuf),
-                    Curl_dyn_uptr(&sbuf), Curl_dyn_len(&sbuf),
+                    curlx_dyn_uptr(&hbuf), curlx_dyn_len(&hbuf),
+                    curlx_dyn_uptr(&sbuf), curlx_dyn_len(&sbuf),
                     s->valid_until, s->ietf_tls_id,
                     s->alpn, s->earlydata_max);
       if(r)
@@ -1203,8 +1225,8 @@ CURLcode Curl_ssl_session_export(struct Curl_easy *data,
 
 out:
   Curl_ssl_scache_unlock(data);
-  Curl_dyn_free(&hbuf);
-  Curl_dyn_free(&sbuf);
+  curlx_dyn_free(&hbuf);
+  curlx_dyn_free(&sbuf);
   return r;
 }
 
diff --git a/extra/curl/curl-8.12.1/lib/vtls/vtls_scache.h b/extra/curl/curl-8.14.1/lib/vtls/vtls_scache.h
similarity index 93%
rename from extra/curl/curl-8.12.1/lib/vtls/vtls_scache.h
rename to extra/curl/curl-8.14.1/lib/vtls/vtls_scache.h
index b42873f7873a..eef50805e4e1 100644
--- a/extra/curl/curl-8.12.1/lib/vtls/vtls_scache.h
+++ b/extra/curl/curl-8.14.1/lib/vtls/vtls_scache.h
@@ -23,9 +23,9 @@
  * SPDX-License-Identifier: curl
  *
  ***************************************************************************/
-#include "curl_setup.h"
-#include "cfilters.h"
-#include "urldata.h"
+#include "../curl_setup.h"
+#include "../cfilters.h"
+#include "../urldata.h"
 
 #ifdef USE_SSL
 
@@ -85,12 +85,11 @@ void Curl_ssl_scache_unlock(struct Curl_easy *data);
  * @param cf      the connection filter wanting to use it
  * @param data    the transfer involved
  * @param ssl_peer_key the key for lookup
- * @param sobj    on return, the object for the peer key or NULL
+ * @retval sobj   the object for the peer key or NULL
  */
-bool Curl_ssl_scache_get_obj(struct Curl_cfilter *cf,
-                             struct Curl_easy *data,
-                             const char *ssl_peer_key,
-                             void **sobj);
+void *Curl_ssl_scache_get_obj(struct Curl_cfilter *cf,
+                              struct Curl_easy *data,
+                              const char *ssl_peer_key);
 
 typedef void Curl_ssl_scache_obj_dtor(void *sobj);
 
@@ -114,9 +113,9 @@ CURLcode Curl_ssl_scache_add_obj(struct Curl_cfilter *cf,
                                  void *sobj,
                                  Curl_ssl_scache_obj_dtor *sobj_dtor_cb);
 
-/* All about a SSL session ticket */
+/* All about an SSL session ticket */
 struct Curl_ssl_session {
-  const unsigned char *sdata;  /* session ticket data, plain bytes */
+  const void *sdata;           /* session ticket data, plain bytes */
   size_t sdata_len;            /* number of bytes in sdata */
   curl_off_t valid_until;      /* seconds since EPOCH until ticket expires */
   int ietf_tls_id;             /* TLS protocol identifier negotiated */
@@ -138,7 +137,7 @@ struct Curl_ssl_session {
  * @param psession on return the scached session instance created
  */
 CURLcode
-Curl_ssl_session_create(unsigned char *sdata, size_t sdata_len,
+Curl_ssl_session_create(void *sdata, size_t sdata_len,
                         int ietf_tls_id, const char *alpn,
                         curl_off_t valid_until,
                         size_t earlydata_max,
@@ -147,7 +146,7 @@ Curl_ssl_session_create(unsigned char *sdata, size_t sdata_len,
 /* Variation of session creation with quic transport parameter bytes,
  * Takes ownership of `quic_tp` regardless of return code. */
 CURLcode
-Curl_ssl_session_create2(unsigned char *sdata, size_t sdata_len,
+Curl_ssl_session_create2(void *sdata, size_t sdata_len,
                          int ietf_tls_id, const char *alpn,
                          curl_off_t valid_until,
                          size_t earlydata_max,
@@ -200,7 +199,7 @@ void Curl_ssl_scache_remove_all(struct Curl_cfilter *cf,
 CURLcode Curl_ssl_session_import(struct Curl_easy *data,
                                  const char *ssl_peer_key,
                                  const unsigned char *shmac, size_t shmac_len,
-                                 const unsigned char *sdata, size_t sdata_len);
+                                 const void *sdata, size_t sdata_len);
 
 CURLcode Curl_ssl_session_export(struct Curl_easy *data,
                                  curl_ssls_export_cb *export_fn,
diff --git a/extra/curl/curl-8.12.1/lib/vtls/vtls_spack.c b/extra/curl/curl-8.14.1/lib/vtls/vtls_spack.c
similarity index 93%
rename from extra/curl/curl-8.12.1/lib/vtls/vtls_spack.c
rename to extra/curl/curl-8.14.1/lib/vtls/vtls_spack.c
index 6dec8e567bb5..152fad7eb303 100644
--- a/extra/curl/curl-8.12.1/lib/vtls/vtls_spack.c
+++ b/extra/curl/curl-8.14.1/lib/vtls/vtls_spack.c
@@ -22,19 +22,19 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #ifdef USE_SSLS_EXPORT
 
-#include "urldata.h"
-#include "curl_trc.h"
+#include "../urldata.h"
+#include "../curl_trc.h"
 #include "vtls_scache.h"
 #include "vtls_spack.h"
-#include "strdup.h"
+#include "../strdup.h"
 
 /* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
 
 #ifdef _MSC_VER
 #if _MSC_VER >= 1600
@@ -64,7 +64,7 @@ typedef unsigned __int64 uint64_t;
 
 static CURLcode spack_enc8(struct dynbuf *buf, uint8_t b)
 {
-  return Curl_dyn_addn(buf, &b, 1);
+  return curlx_dyn_addn(buf, &b, 1);
 }
 
 static CURLcode
@@ -82,7 +82,7 @@ static CURLcode spack_enc16(struct dynbuf *buf, uint16_t val)
   uint8_t nval[2];
   nval[0] = (uint8_t)(val >> 8);
   nval[1] = (uint8_t)val;
-  return Curl_dyn_addn(buf, nval, sizeof(nval));
+  return curlx_dyn_addn(buf, nval, sizeof(nval));
 }
 
 static CURLcode
@@ -102,7 +102,7 @@ static CURLcode spack_enc32(struct dynbuf *buf, uint32_t val)
   nval[1] = (uint8_t)(val >> 16);
   nval[2] = (uint8_t)(val >> 8);
   nval[3] = (uint8_t)val;
-  return Curl_dyn_addn(buf, nval, sizeof(nval));
+  return curlx_dyn_addn(buf, nval, sizeof(nval));
 }
 
 static CURLcode
@@ -127,7 +127,7 @@ static CURLcode spack_enc64(struct dynbuf *buf, uint64_t val)
   nval[5] = (uint8_t)(val >> 16);
   nval[6] = (uint8_t)(val >> 8);
   nval[7] = (uint8_t)val;
-  return Curl_dyn_addn(buf, nval, sizeof(nval));
+  return curlx_dyn_addn(buf, nval, sizeof(nval));
 }
 
 static CURLcode
@@ -151,7 +151,7 @@ static CURLcode spack_encstr16(struct dynbuf *buf, const char *s)
     return CURLE_BAD_FUNCTION_ARGUMENT;
   r = spack_enc16(buf, (uint16_t)slen);
   if(!r) {
-    r = Curl_dyn_addn(buf, s, slen);
+    r = curlx_dyn_addn(buf, s, slen);
   }
   return r;
 }
@@ -181,7 +181,7 @@ static CURLcode spack_encdata16(struct dynbuf *buf,
     return CURLE_BAD_FUNCTION_ARGUMENT;
   r = spack_enc16(buf, (uint16_t)data_len);
   if(!r) {
-    r = Curl_dyn_addn(buf, data, data_len);
+    r = curlx_dyn_addn(buf, data, data_len);
   }
   return r;
 }
@@ -254,10 +254,11 @@ CURLcode Curl_ssl_session_pack(struct Curl_easy *data,
 }
 
 CURLcode Curl_ssl_session_unpack(struct Curl_easy *data,
-                                 const unsigned char *buf, size_t buflen,
+                                 const void *bufv, size_t buflen,
                                  struct Curl_ssl_session **ps)
 {
   struct Curl_ssl_session *s = NULL;
+  const unsigned char *buf = (const unsigned char *)bufv;
   const unsigned char *end = buf + buflen;
   uint8_t val8, *pval8;
   uint16_t val16;
diff --git a/extra/curl/curl-8.12.1/lib/vtls/vtls_spack.h b/extra/curl/curl-8.14.1/lib/vtls/vtls_spack.h
similarity index 93%
rename from extra/curl/curl-8.12.1/lib/vtls/vtls_spack.h
rename to extra/curl/curl-8.14.1/lib/vtls/vtls_spack.h
index 8905d7febf86..4cdabae30e6d 100644
--- a/extra/curl/curl-8.12.1/lib/vtls/vtls_spack.h
+++ b/extra/curl/curl-8.14.1/lib/vtls/vtls_spack.h
@@ -23,7 +23,7 @@
  * SPDX-License-Identifier: curl
  *
  ***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #ifdef USE_SSLS_EXPORT
 
@@ -35,7 +35,7 @@ CURLcode Curl_ssl_session_pack(struct Curl_easy *data,
                                struct dynbuf *buf);
 
 CURLcode Curl_ssl_session_unpack(struct Curl_easy *data,
-                                 const unsigned char *buf, size_t buflen,
+                                 const void *bufv, size_t buflen,
                                  struct Curl_ssl_session **ps);
 
 #endif /* USE_SSLS_EXPORT */
diff --git a/extra/curl/curl-8.12.1/lib/vtls/wolfssl.c b/extra/curl/curl-8.14.1/lib/vtls/wolfssl.c
similarity index 58%
rename from extra/curl/curl-8.12.1/lib/vtls/wolfssl.c
rename to extra/curl/curl-8.14.1/lib/vtls/wolfssl.c
index 546ba034af47..9c6f51826058 100644
--- a/extra/curl/curl-8.12.1/lib/vtls/wolfssl.c
+++ b/extra/curl/curl-8.14.1/lib/vtls/wolfssl.c
@@ -28,7 +28,7 @@
  *
  */
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #ifdef USE_WOLFSSL
 
@@ -36,6 +36,7 @@
 #include <wolfssl/options.h>
 #include <wolfssl/version.h>
 
+
 #if LIBWOLFSSL_VERSION_HEX < 0x03004006 /* wolfSSL 3.4.6 (2015) */
 #error "wolfSSL version should be at least 3.4.6"
 #endif
@@ -55,28 +56,30 @@
 
 #include <limits.h>
 
-#include "urldata.h"
-#include "sendf.h"
-#include "inet_pton.h"
+#include "../urldata.h"
+#include "../sendf.h"
+#include "../curlx/inet_pton.h"
 #include "vtls.h"
 #include "vtls_int.h"
 #include "vtls_scache.h"
 #include "keylog.h"
-#include "parsedate.h"
-#include "connect.h" /* for the connect timeout */
-#include "select.h"
-#include "strcase.h"
+#include "../parsedate.h"
+#include "../connect.h" /* for the connect timeout */
+#include "../progress.h"
+#include "../select.h"
+#include "../strcase.h"
+#include "../strdup.h"
 #include "x509asn1.h"
-#include "curl_printf.h"
-#include "multiif.h"
+#include "../curl_printf.h"
+#include "../multiif.h"
 
 #include <wolfssl/ssl.h>
 #include <wolfssl/error-ssl.h>
 #include "wolfssl.h"
 
 /* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
 
 #ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG
 #define USE_ECH_WOLFSSL
@@ -93,11 +96,11 @@
 #endif
 #endif
 
-#ifdef HAVE_WOLFSSL_BIO
+#ifdef HAVE_WOLFSSL_BIO_NEW
 #define USE_BIO_CHAIN
-#ifdef HAVE_WOLFSSL_FULL_BIO
+#ifdef HAVE_WOLFSSL_BIO_SET_SHUTDOWN
 #define USE_FULL_BIO
-#else /* HAVE_WOLFSSL_FULL_BIO */
+#else /* HAVE_WOLFSSL_BIO_SET_SHUTDOWN */
 #undef USE_FULL_BIO
 #endif
 /* wolfSSL 5.7.4 and older do not have these symbols, but only the
@@ -111,10 +114,14 @@
 #define wolfSSL_BIO_set_retry_read    BIO_set_retry_read
 #endif /* !WOLFSSL_BIO_CTRL_GET_CLOSE */
 
-#else /* HAVE_WOLFSSL_BIO */
+#else /* HAVE_WOLFSSL_BIO_NEW */
 #undef USE_BIO_CHAIN
 #endif
 
+static CURLcode wssl_connect(struct Curl_cfilter *cf,
+                             struct Curl_easy *data,
+                             bool *done);
+
 #ifdef OPENSSL_EXTRA
 /*
  * Availability note:
@@ -122,13 +129,13 @@
  * wolfSSL 4.4.0, but requires the -DHAVE_SECRET_CALLBACK build option. If that
  * option is not set, then TLS 1.3 will not be logged.
  * For TLS 1.2 and before, we use wolfSSL_get_keys().
- * SSL_get_client_random and wolfSSL_get_keys require OPENSSL_EXTRA
+ * wolfSSL_get_client_random and wolfSSL_get_keys require OPENSSL_EXTRA
  * (--enable-opensslextra or --enable-all).
  */
 #if defined(HAVE_SECRET_CALLBACK) && defined(WOLFSSL_TLS13)
 static int
-wolfssl_tls13_secret_callback(SSL *ssl, int id, const unsigned char *secret,
-                              int secretSz, void *ctx)
+wssl_tls13_secret_callback(SSL *ssl, int id, const unsigned char *secret,
+                           int secretSz, void *ctx)
 {
   const char *label;
   unsigned char client_random[SSL3_RANDOM_SIZE];
@@ -164,7 +171,7 @@ wolfssl_tls13_secret_callback(SSL *ssl, int id, const unsigned char *secret,
     return 0;
   }
 
-  if(SSL_get_client_random(ssl, client_random, SSL3_RANDOM_SIZE) == 0) {
+  if(wolfSSL_get_client_random(ssl, client_random, SSL3_RANDOM_SIZE) == 0) {
     /* Should never happen as wolfSSL_KeepArrays() was called before. */
     return 0;
   }
@@ -174,8 +181,7 @@ wolfssl_tls13_secret_callback(SSL *ssl, int id, const unsigned char *secret,
 }
 #endif /* defined(HAVE_SECRET_CALLBACK) && defined(WOLFSSL_TLS13) */
 
-static void
-wolfssl_log_tls12_secret(WOLFSSL *ssl)
+static void wssl_log_tls12_secret(WOLFSSL *ssl)
 {
   unsigned char *ms, *sr, *cr;
   unsigned int msLen, srLen, crLen, i, x = 0;
@@ -217,7 +223,7 @@ wolfssl_log_tls12_secret(WOLFSSL *ssl)
 }
 #endif /* OPENSSL_EXTRA */
 
-static int wolfssl_do_file_type(const char *type)
+static int wssl_do_file_type(const char *type)
 {
   if(!type || !type[0])
     return WOLFSSL_FILETYPE_PEM;
@@ -235,19 +241,19 @@ struct group_name_map {
 };
 
 static const struct group_name_map gnm[] = {
-  { WOLFSSL_KYBER_LEVEL1, "KYBER_LEVEL1" },
-  { WOLFSSL_KYBER_LEVEL3, "KYBER_LEVEL3" },
-  { WOLFSSL_KYBER_LEVEL5, "KYBER_LEVEL5" },
-  { WOLFSSL_P256_KYBER_LEVEL1, "P256_KYBER_LEVEL1" },
-  { WOLFSSL_P384_KYBER_LEVEL3, "P384_KYBER_LEVEL3" },
-  { WOLFSSL_P521_KYBER_LEVEL5, "P521_KYBER_LEVEL5" },
+  { WOLFSSL_ML_KEM_512, "ML_KEM_512" },
+  { WOLFSSL_ML_KEM_768, "ML_KEM_768" },
+  { WOLFSSL_ML_KEM_1024, "ML_KEM_1024" },
+  { WOLFSSL_P256_ML_KEM_512, "P256_ML_KEM_512" },
+  { WOLFSSL_P384_ML_KEM_768, "P384_ML_KEM_768" },
+  { WOLFSSL_P521_ML_KEM_1024, "P521_ML_KEM_1024" },
   { 0, NULL }
 };
 #endif
 
 #ifdef USE_BIO_CHAIN
 
-static int wolfssl_bio_cf_create(WOLFSSL_BIO *bio)
+static int wssl_bio_cf_create(WOLFSSL_BIO *bio)
 {
 #ifdef USE_FULL_BIO
   wolfSSL_BIO_set_shutdown(bio, 1);
@@ -256,14 +262,14 @@ static int wolfssl_bio_cf_create(WOLFSSL_BIO *bio)
   return 1;
 }
 
-static int wolfssl_bio_cf_destroy(WOLFSSL_BIO *bio)
+static int wssl_bio_cf_destroy(WOLFSSL_BIO *bio)
 {
   if(!bio)
     return 0;
   return 1;
 }
 
-static long wolfssl_bio_cf_ctrl(WOLFSSL_BIO *bio, int cmd, long num, void *ptr)
+static long wssl_bio_cf_ctrl(WOLFSSL_BIO *bio, int cmd, long num, void *ptr)
 {
   struct Curl_cfilter *cf = wolfSSL_BIO_get_data(bio);
   long ret = 1;
@@ -293,9 +299,11 @@ static long wolfssl_bio_cf_ctrl(WOLFSSL_BIO *bio, int cmd, long num, void *ptr)
     ret = 1;
     break;
 #ifdef WOLFSSL_BIO_CTRL_EOF
-  case WOLFSSL_BIO_CTRL_EOF:
+  case WOLFSSL_BIO_CTRL_EOF: {
     /* EOF has been reached on input? */
-    return !cf->next || !cf->next->connected;
+    struct ssl_connect_data *connssl = cf->ctx;
+    return connssl->peer_closed;
+  }
 #endif
   default:
     ret = 0;
@@ -304,30 +312,29 @@ static long wolfssl_bio_cf_ctrl(WOLFSSL_BIO *bio, int cmd, long num, void *ptr)
   return ret;
 }
 
-static int wolfssl_bio_cf_out_write(WOLFSSL_BIO *bio,
-                                    const char *buf, int blen)
+static int wssl_bio_cf_out_write(WOLFSSL_BIO *bio,
+                                 const char *buf, int blen)
 {
   struct Curl_cfilter *cf = wolfSSL_BIO_get_data(bio);
   struct ssl_connect_data *connssl = cf->ctx;
-  struct wolfssl_ctx *backend =
-    (struct wolfssl_ctx *)connssl->backend;
+  struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend;
   struct Curl_easy *data = CF_DATA_CURRENT(cf);
   ssize_t nwritten, skiplen = 0;
   CURLcode result = CURLE_OK;
 
   DEBUGASSERT(data);
-  if(backend->shutting_down && backend->io_send_blocked_len &&
-     (backend->io_send_blocked_len < blen)) {
+  if(wssl->shutting_down && wssl->io_send_blocked_len &&
+     (wssl->io_send_blocked_len < blen)) {
     /* bug in wolfSSL: <https://github.com/wolfSSL/wolfssl/issues/7784>
      * It adds the close notify message again every time we retry
      * sending during shutdown. */
     CURL_TRC_CF(data, cf, "bio_write, shutdown restrict send of %d"
-                " to %d bytes", blen, backend->io_send_blocked_len);
-    skiplen = (ssize_t)(blen - backend->io_send_blocked_len);
-    blen = backend->io_send_blocked_len;
+                " to %d bytes", blen, wssl->io_send_blocked_len);
+    skiplen = (ssize_t)(blen - wssl->io_send_blocked_len);
+    blen = wssl->io_send_blocked_len;
   }
   nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, FALSE, &result);
-  backend->io_result = result;
+  wssl->io_result = result;
   CURL_TRC_CF(data, cf, "bio_write(len=%d) -> %zd, %d",
               blen, nwritten, result);
 #ifdef USE_FULL_BIO
@@ -335,20 +342,19 @@ static int wolfssl_bio_cf_out_write(WOLFSSL_BIO *bio,
 #endif
   if(nwritten < 0 && CURLE_AGAIN == result) {
     wolfSSL_BIO_set_retry_write(bio);
-    if(backend->shutting_down && !backend->io_send_blocked_len)
-      backend->io_send_blocked_len = blen;
+    if(wssl->shutting_down && !wssl->io_send_blocked_len)
+      wssl->io_send_blocked_len = blen;
   }
   else if(!result && skiplen)
     nwritten += skiplen;
   return (int)nwritten;
 }
 
-static int wolfssl_bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen)
+static int wssl_bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen)
 {
   struct Curl_cfilter *cf = wolfSSL_BIO_get_data(bio);
   struct ssl_connect_data *connssl = cf->ctx;
-  struct wolfssl_ctx *backend =
-    (struct wolfssl_ctx *)connssl->backend;
+  struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend;
   struct Curl_easy *data = CF_DATA_CURRENT(cf);
   ssize_t nread;
   CURLcode result = CURLE_OK;
@@ -358,8 +364,20 @@ static int wolfssl_bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen)
   if(!buf)
     return 0;
 
+  if((connssl->connecting_state == ssl_connect_2) &&
+     !wssl->x509_store_setup) {
+    /* During handshake, init the x509 store before receiving the
+     * server response. This allows sending of ClientHello without delay. */
+    result = Curl_wssl_setup_x509_store(cf, data, wssl);
+    if(result) {
+      CURL_TRC_CF(data, cf, "Curl_wssl_setup_x509_store() -> %d", result);
+      wssl->io_result = result;
+      return -1;
+    }
+  }
+
   nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result);
-  backend->io_result = result;
+  wssl->io_result = result;
   CURL_TRC_CF(data, cf, "bio_read(len=%d) -> %zd, %d", blen, nread, result);
 #ifdef USE_FULL_BIO
   wolfSSL_BIO_clear_retry_flags(bio);
@@ -371,28 +389,28 @@ static int wolfssl_bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen)
   return (int)nread;
 }
 
-static WOLFSSL_BIO_METHOD *wolfssl_bio_cf_method = NULL;
+static WOLFSSL_BIO_METHOD *wssl_bio_cf_method = NULL;
 
-static void wolfssl_bio_cf_init_methods(void)
+static void wssl_bio_cf_init_methods(void)
 {
-  wolfssl_bio_cf_method = wolfSSL_BIO_meth_new(WOLFSSL_BIO_MEMORY,
+  wssl_bio_cf_method = wolfSSL_BIO_meth_new(WOLFSSL_BIO_MEMORY,
                                                "wolfSSL CF BIO");
-  wolfSSL_BIO_meth_set_write(wolfssl_bio_cf_method, &wolfssl_bio_cf_out_write);
-  wolfSSL_BIO_meth_set_read(wolfssl_bio_cf_method, &wolfssl_bio_cf_in_read);
-  wolfSSL_BIO_meth_set_ctrl(wolfssl_bio_cf_method, &wolfssl_bio_cf_ctrl);
-  wolfSSL_BIO_meth_set_create(wolfssl_bio_cf_method, &wolfssl_bio_cf_create);
-  wolfSSL_BIO_meth_set_destroy(wolfssl_bio_cf_method, &wolfssl_bio_cf_destroy);
+  wolfSSL_BIO_meth_set_write(wssl_bio_cf_method, &wssl_bio_cf_out_write);
+  wolfSSL_BIO_meth_set_read(wssl_bio_cf_method, &wssl_bio_cf_in_read);
+  wolfSSL_BIO_meth_set_ctrl(wssl_bio_cf_method, &wssl_bio_cf_ctrl);
+  wolfSSL_BIO_meth_set_create(wssl_bio_cf_method, &wssl_bio_cf_create);
+  wolfSSL_BIO_meth_set_destroy(wssl_bio_cf_method, &wssl_bio_cf_destroy);
 }
 
-static void wolfssl_bio_cf_free_methods(void)
+static void wssl_bio_cf_free_methods(void)
 {
-  wolfSSL_BIO_meth_free(wolfssl_bio_cf_method);
+  wolfSSL_BIO_meth_free(wssl_bio_cf_method);
 }
 
 #else /* USE_BIO_CHAIN */
 
-#define wolfssl_bio_cf_init_methods() Curl_nop_stmt
-#define wolfssl_bio_cf_free_methods() Curl_nop_stmt
+#define wssl_bio_cf_init_methods() Curl_nop_stmt
+#define wssl_bio_cf_free_methods() Curl_nop_stmt
 
 #endif /* !USE_BIO_CHAIN */
 
@@ -401,12 +419,15 @@ CURLcode Curl_wssl_cache_session(struct Curl_cfilter *cf,
                                  const char *ssl_peer_key,
                                  WOLFSSL_SESSION *session,
                                  int ietf_tls_id,
-                                 const char *alpn)
+                                 const char *alpn,
+                                 unsigned char *quic_tp,
+                                 size_t quic_tp_len)
 {
   CURLcode result = CURLE_OK;
   struct Curl_ssl_session *sc_session = NULL;
-  unsigned char *sdata = NULL;
+  unsigned char *sdata = NULL, *qtp_clone = NULL;
   unsigned int sdata_len;
+  unsigned int earlydata_max = 0;
 
   if(!session)
     goto out;
@@ -429,12 +450,23 @@ CURLcode Curl_wssl_cache_session(struct Curl_cfilter *cf,
     result = CURLE_FAILED_INIT;
     goto out;
   }
+  if(quic_tp && quic_tp_len) {
+    qtp_clone = Curl_memdup0((char *)quic_tp, quic_tp_len);
+    if(!qtp_clone) {
+      free(sdata);
+      return CURLE_OUT_OF_MEMORY;
+    }
+  }
+#ifdef WOLFSSL_EARLY_DATA
+  earlydata_max = wolfSSL_SESSION_get_max_early_data(session);
+#endif
 
-  result = Curl_ssl_session_create(sdata, sdata_len,
-                                   ietf_tls_id, alpn,
-                                   (curl_off_t)time(NULL) +
-                                   wolfSSL_SESSION_get_timeout(session), 0,
-                                   &sc_session);
+  result = Curl_ssl_session_create2(sdata, sdata_len,
+                                    ietf_tls_id, alpn,
+                                    (curl_off_t)time(NULL) +
+                                    wolfSSL_SESSION_get_timeout(session),
+                                    earlydata_max, qtp_clone, quic_tp_len,
+                                    &sc_session);
   sdata = NULL;  /* took ownership of sdata */
   if(!result) {
     result = Curl_ssl_scache_put(cf, data, ssl_peer_key, sc_session);
@@ -460,51 +492,120 @@ static int wssl_vtls_new_session_cb(WOLFSSL *ssl, WOLFSSL_SESSION *session)
     if(connssl && data) {
       (void)Curl_wssl_cache_session(cf, data, connssl->peer.scache_key,
                                     session, wolfSSL_version(ssl),
-                                    connssl->negotiated.alpn);
+                                    connssl->negotiated.alpn, NULL, 0);
     }
   }
   return 0;
 }
 
-CURLcode Curl_wssl_setup_session(struct Curl_cfilter *cf,
-                                 struct Curl_easy *data,
-                                 struct wolfssl_ctx *wss,
-                                 const char *ssl_peer_key)
+static CURLcode wssl_on_session_reuse(struct Curl_cfilter *cf,
+                                      struct Curl_easy *data,
+                                      struct alpn_spec *alpns,
+                                      struct Curl_ssl_session *scs,
+                                      bool *do_early_data)
 {
-  struct Curl_ssl_session *sc_session = NULL;
+  struct ssl_connect_data *connssl = cf->ctx;
+  struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend;
+  CURLcode result = CURLE_OK;
+
+  *do_early_data = FALSE;
+#ifdef WOLFSSL_EARLY_DATA
+  connssl->earlydata_max = wolfSSL_SESSION_get_max_early_data(
+    wolfSSL_get_session(wssl->ssl));
+#else
+  (void)wssl;
+  connssl->earlydata_max = 0;
+#endif
+
+  if(!connssl->earlydata_max) {
+    /* Seems to be GnuTLS way to signal no EarlyData in session */
+    CURL_TRC_CF(data, cf, "SSL session does not allow earlydata");
+  }
+  else if(!Curl_alpn_contains_proto(alpns, scs->alpn)) {
+    CURL_TRC_CF(data, cf, "SSL session has different ALPN, no early data");
+  }
+  else {
+    infof(data, "SSL session allows %zu bytes of early data, "
+          "reusing ALPN '%s'", connssl->earlydata_max, scs->alpn);
+    connssl->earlydata_state = ssl_earlydata_await;
+    connssl->state = ssl_connection_deferred;
+    result = Curl_alpn_set_negotiated(cf, data, connssl,
+                    (const unsigned char *)scs->alpn,
+                    scs->alpn ? strlen(scs->alpn) : 0);
+    *do_early_data = !result;
+  }
+  return result;
+}
+
+static CURLcode
+wssl_setup_session(struct Curl_cfilter *cf,
+                   struct Curl_easy *data,
+                   struct wssl_ctx *wss,
+                   struct alpn_spec *alpns,
+                   const char *ssl_peer_key,
+                   Curl_wssl_init_session_reuse_cb *sess_reuse_cb)
+{
+  struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+  struct Curl_ssl_session *scs = NULL;
   CURLcode result;
 
-  result = Curl_ssl_scache_take(cf, data, ssl_peer_key, &sc_session);
-  if(!result && sc_session && sc_session->sdata && sc_session->sdata_len) {
+  result = Curl_ssl_scache_take(cf, data, ssl_peer_key, &scs);
+  if(!result && scs && scs->sdata && scs->sdata_len &&
+     (!scs->alpn || Curl_alpn_contains_proto(alpns, scs->alpn))) {
     WOLFSSL_SESSION *session;
     /* wolfSSL changes the passed pointer for whatever reasons, yikes */
-    const unsigned char *sdata = sc_session->sdata;
-    session = wolfSSL_d2i_SSL_SESSION(NULL, &sdata,
-                                      (long)sc_session->sdata_len);
+    const unsigned char *sdata = scs->sdata;
+    session = wolfSSL_d2i_SSL_SESSION(NULL, &sdata, (long)scs->sdata_len);
     if(session) {
-      int ret = wolfSSL_set_session(wss->handle, session);
+      int ret = wolfSSL_set_session(wss->ssl, session);
       if(ret != WOLFSSL_SUCCESS) {
-        Curl_ssl_session_destroy(sc_session);
-        sc_session = NULL;
+        Curl_ssl_session_destroy(scs);
+        scs = NULL;
         infof(data, "cached session not accepted (%d), "
               "removing from cache", ret);
       }
-      else
-        infof(data, "SSL reusing session ID");
+      else {
+        infof(data, "SSL reusing session with ALPN '%s'",
+              scs->alpn ? scs->alpn : "-");
+        if(ssl_config->earlydata &&
+           !cf->conn->connect_only &&
+           !strcmp("TLSv1.3", wolfSSL_get_version(wss->ssl))) {
+          bool do_early_data = FALSE;
+          if(sess_reuse_cb) {
+            result = sess_reuse_cb(cf, data, alpns, scs, &do_early_data);
+            if(result)
+              goto  out;
+          }
+#ifdef WOLFSSL_EARLY_DATA
+          if(do_early_data) {
+            unsigned int edmax = (scs->earlydata_max < UINT_MAX) ?
+              (unsigned int)scs->earlydata_max : UINT_MAX;
+            /* We only try the ALPN protocol the session used before,
+             * otherwise we might send early data for the wrong protocol */
+            Curl_alpn_restrict_to(alpns, scs->alpn);
+            wolfSSL_set_max_early_data(wss->ssl, edmax);
+          }
+#else
+          /* Should never enable when not supported */
+          DEBUGASSERT(!do_early_data);
+#endif
+        }
+      }
       wolfSSL_SESSION_free(session);
     }
     else {
       failf(data, "could not decode previous session");
     }
   }
-  Curl_ssl_scache_return(cf, data, ssl_peer_key, sc_session);
+out:
+  Curl_ssl_scache_return(cf, data, ssl_peer_key, scs);
   return result;
 }
 
 static CURLcode wssl_populate_x509_store(struct Curl_cfilter *cf,
                                          struct Curl_easy *data,
                                          WOLFSSL_X509_STORE *store,
-                                         struct wolfssl_ctx *wssl)
+                                         struct wssl_ctx *wssl)
 {
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   const struct curl_blob *ca_info_blob = conn_config->ca_info_blob;
@@ -514,38 +615,41 @@ static CURLcode wssl_populate_x509_store(struct Curl_cfilter *cf,
   const char * const ssl_capath = conn_config->CApath;
   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
   bool imported_native_ca = FALSE;
+  bool imported_ca_info_blob = FALSE;
 
-#if !defined(NO_FILESYSTEM) && defined(WOLFSSL_SYS_CA_CERTS)
+  /* We do not want to do this again, no matter the outcome */
+  wssl->x509_store_setup = TRUE;
+
+#ifndef NO_FILESYSTEM
   /* load native CA certificates */
   if(ssl_config->native_ca_store) {
-    if(wolfSSL_CTX_load_system_CA_certs(wssl->ctx) != WOLFSSL_SUCCESS) {
+#ifdef WOLFSSL_SYS_CA_CERTS
+    if(wolfSSL_CTX_load_system_CA_certs(wssl->ssl_ctx) != WOLFSSL_SUCCESS) {
       infof(data, "error importing native CA store, continuing anyway");
     }
     else {
       imported_native_ca = TRUE;
       infof(data, "successfully imported native CA store");
-      wssl->x509_store_setup = TRUE;
     }
+#else
+    infof(data, "ignoring native CA option because wolfSSL was built without "
+          "native CA support");
+#endif
   }
 #endif /* !NO_FILESYSTEM */
 
   /* load certificate blob */
   if(ca_info_blob) {
-    if(wolfSSL_CTX_load_verify_buffer(wssl->ctx, ca_info_blob->data,
+    if(wolfSSL_CTX_load_verify_buffer(wssl->ssl_ctx, ca_info_blob->data,
                                       (long)ca_info_blob->len,
                                       WOLFSSL_FILETYPE_PEM) !=
        WOLFSSL_SUCCESS) {
-      if(imported_native_ca) {
-        infof(data, "error importing CA certificate blob, continuing anyway");
-      }
-      else {
-        failf(data, "error importing CA certificate blob");
-        return CURLE_SSL_CACERT_BADFILE;
-      }
+      failf(data, "error importing CA certificate blob");
+      return CURLE_SSL_CACERT_BADFILE;
     }
     else {
+      imported_ca_info_blob = TRUE;
       infof(data, "successfully imported CA certificate blob");
-      wssl->x509_store_setup = TRUE;
     }
   }
 
@@ -557,14 +661,15 @@ static CURLcode wssl_populate_x509_store(struct Curl_cfilter *cf,
   if(!store)
     return CURLE_OUT_OF_MEMORY;
 
-  if((ssl_cafile || ssl_capath) && (!wssl->x509_store_setup)) {
+  if(ssl_cafile || ssl_capath) {
     int rc =
-      wolfSSL_CTX_load_verify_locations_ex(wssl->ctx,
+      wolfSSL_CTX_load_verify_locations_ex(wssl->ssl_ctx,
                                            ssl_cafile,
                                            ssl_capath,
                                            WOLFSSL_LOAD_FLAG_IGNORE_ERR);
     if(WOLFSSL_SUCCESS != rc) {
-      if(conn_config->verifypeer) {
+      if(conn_config->verifypeer &&
+         !imported_native_ca && !imported_ca_info_blob) {
         /* Fail if we insist on successfully verifying the server. */
         failf(data, "error setting certificate verify locations:"
               " CAfile: %s CApath: %s",
@@ -588,7 +693,6 @@ static CURLcode wssl_populate_x509_store(struct Curl_cfilter *cf,
   }
 #endif
   (void)store;
-  wssl->x509_store_setup = TRUE;
   return CURLE_OK;
 }
 
@@ -620,8 +724,8 @@ wssl_cached_x509_store_expired(const struct Curl_easy *data,
                                const struct wssl_x509_share *mb)
 {
   const struct ssl_general_config *cfg = &data->set.general_ssl;
-  struct curltime now = Curl_now();
-  timediff_t elapsed_ms = Curl_timediff(now, mb->time);
+  struct curltime now = curlx_now();
+  timediff_t elapsed_ms = curlx_timediff(now, mb->time);
   timediff_t timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000;
 
   if(timeout_ms < 0)
@@ -650,7 +754,7 @@ static WOLFSSL_X509_STORE *wssl_get_cached_x509_store(struct Curl_cfilter *cf,
 
   DEBUGASSERT(multi);
   share = multi ? Curl_hash_pick(&multi->proto_hash,
-                                 (void *)MPROTO_WSSL_X509_KEY,
+                                 CURL_UNCONST(MPROTO_WSSL_X509_KEY),
                                  sizeof(MPROTO_WSSL_X509_KEY)-1) : NULL;
   if(share && share->store &&
      !wssl_cached_x509_store_expired(data, share) &&
@@ -673,7 +777,7 @@ static void wssl_set_cached_x509_store(struct Curl_cfilter *cf,
   if(!multi)
     return;
   share = Curl_hash_pick(&multi->proto_hash,
-                         (void *)MPROTO_WSSL_X509_KEY,
+                         CURL_UNCONST(MPROTO_WSSL_X509_KEY),
                          sizeof(MPROTO_WSSL_X509_KEY)-1);
 
   if(!share) {
@@ -681,7 +785,7 @@ static void wssl_set_cached_x509_store(struct Curl_cfilter *cf,
     if(!share)
       return;
     if(!Curl_hash_add2(&multi->proto_hash,
-                       (void *)MPROTO_WSSL_X509_KEY,
+                       CURL_UNCONST(MPROTO_WSSL_X509_KEY),
                        sizeof(MPROTO_WSSL_X509_KEY)-1,
                        share, wssl_x509_share_free)) {
       free(share);
@@ -705,7 +809,7 @@ static void wssl_set_cached_x509_store(struct Curl_cfilter *cf,
       free(share->CAfile);
     }
 
-    share->time = Curl_now();
+    share->time = curlx_now();
     share->store = store;
     share->CAfile = CAfile;
   }
@@ -713,7 +817,7 @@ static void wssl_set_cached_x509_store(struct Curl_cfilter *cf,
 
 CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf,
                                     struct Curl_easy *data,
-                                    struct wolfssl_ctx *wssl)
+                                    struct wssl_ctx *wssl)
 {
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
@@ -733,11 +837,12 @@ CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf,
 
   cached_store = cache_criteria_met ? wssl_get_cached_x509_store(cf, data)
                                     : NULL;
-  if(cached_store && wolfSSL_CTX_get_cert_store(wssl->ctx) == cached_store) {
+  if(cached_store &&
+     wolfSSL_CTX_get_cert_store(wssl->ssl_ctx) == cached_store) {
     /* The cached store is already in use, do nothing. */
   }
   else if(cached_store && wolfSSL_X509_STORE_up_ref(cached_store)) {
-    wolfSSL_CTX_set_cert_store(wssl->ctx, cached_store);
+    wolfSSL_CTX_set_cert_store(wssl->ssl_ctx, cached_store);
   }
   else if(cache_criteria_met) {
     /* wolfSSL's initial store in CTX is not shareable by default.
@@ -747,7 +852,7 @@ CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf,
       failf(data, "SSL: could not create a X509 store");
       return CURLE_OUT_OF_MEMORY;
     }
-    wolfSSL_CTX_set_cert_store(wssl->ctx, store);
+    wolfSSL_CTX_set_cert_store(wssl->ssl_ctx, store);
 
     result = wssl_populate_x509_store(cf, data, store, wssl);
     if(!result) {
@@ -756,7 +861,7 @@ CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf,
   }
   else {
    /* We never share the CTX's store, use it. */
-   WOLFSSL_X509_STORE *store = wolfSSL_CTX_get_cert_store(wssl->ctx);
+   WOLFSSL_X509_STORE *store = wolfSSL_CTX_get_cert_store(wssl->ssl_ctx);
    result = wssl_populate_x509_store(cf, data, store, wssl);
   }
 
@@ -776,14 +881,14 @@ wssl_add_default_ciphers(bool tls13, struct dynbuf *buf)
       continue;
 
     /* if there already is data in the string, add colon separator */
-    if(Curl_dyn_len(buf)) {
-      CURLcode result = Curl_dyn_addn(buf, ":", 1);
+    if(curlx_dyn_len(buf)) {
+      CURLcode result = curlx_dyn_addn(buf, ":", 1);
       if(result)
         return result;
     }
 
     n = strlen(str);
-    if(Curl_dyn_addn(buf, str, n))
+    if(curlx_dyn_addn(buf, str, n))
       return CURLE_OUT_OF_MEMORY;
   }
 
@@ -831,30 +936,41 @@ wssl_legacy_CTX_set_max_proto_version(WOLFSSL_CTX* ctx, int version)
 #define wolfSSL_CTX_set_max_proto_version wssl_legacy_CTX_set_max_proto_version
 #endif
 
-/*
- * This function loads all the client/CA certificates and CRLs. Setup the TLS
- * layer and do all necessary magic.
- */
-static CURLcode
-wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
+#define QUIC_CIPHERS                                                          \
+  "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_"               \
+  "POLY1305_SHA256:TLS_AES_128_CCM_SHA256"
+#define QUIC_GROUPS "P-256:P-384:P-521"
+
+CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx,
+                            struct Curl_cfilter *cf,
+                            struct Curl_easy *data,
+                            struct ssl_peer *peer,
+                            const struct alpn_spec *alpns_requested,
+                            Curl_wssl_ctx_setup_cb *cb_setup,
+                            void *cb_user_data,
+                            void *ssl_user_data,
+                            Curl_wssl_init_session_reuse_cb *sess_reuse_cb)
 {
+  struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+  struct ssl_primary_config *conn_config;
+  WOLFSSL_METHOD* req_method = NULL;
+  struct alpn_spec alpns;
   int res;
   char *curves;
-  struct ssl_connect_data *connssl = cf->ctx;
-  struct wolfssl_ctx *backend =
-    (struct wolfssl_ctx *)connssl->backend;
-  struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
-  const struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
-  WOLFSSL_METHOD* req_method = NULL;
 #ifdef WOLFSSL_HAVE_KYBER
   word16 pqkem = 0;
   size_t idx = 0;
 #endif
+  CURLcode result = CURLE_FAILED_INIT;
 
-  DEBUGASSERT(backend);
-
-  if(connssl->state == ssl_connection_complete)
-    return CURLE_OK;
+  DEBUGASSERT(!wctx->ssl_ctx);
+  DEBUGASSERT(!wctx->ssl);
+  conn_config = Curl_ssl_cf_get_primary_config(cf);
+  if(!conn_config) {
+    result = CURLE_FAILED_INIT;
+    goto out;
+  }
+  Curl_alpn_copy(&alpns, alpns_requested);
 
 #if LIBWOLFSSL_VERSION_HEX < 0x04002000 /* 4.2.0 (2019) */
   req_method = wolfSSLv23_client_method();
@@ -863,58 +979,62 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
 #endif
   if(!req_method) {
     failf(data, "wolfSSL: could not create a client method");
-    return CURLE_OUT_OF_MEMORY;
+    result = CURLE_OUT_OF_MEMORY;
+    goto out;
   }
 
-  if(backend->ctx)
-    wolfSSL_CTX_free(backend->ctx);
+  if(wctx->ssl_ctx)
+    wolfSSL_CTX_free(wctx->ssl_ctx);
 
-  backend->ctx = wolfSSL_CTX_new(req_method);
-  if(!backend->ctx) {
+  wctx->ssl_ctx = wolfSSL_CTX_new(req_method);
+  if(!wctx->ssl_ctx) {
     failf(data, "wolfSSL: could not create a context");
-    return CURLE_OUT_OF_MEMORY;
+    result = CURLE_OUT_OF_MEMORY;
+    goto out;
   }
 
   switch(conn_config->version) {
   case CURL_SSLVERSION_DEFAULT:
   case CURL_SSLVERSION_TLSv1:
   case CURL_SSLVERSION_TLSv1_0:
-    res = wolfSSL_CTX_set_min_proto_version(backend->ctx, TLS1_VERSION);
+    res = wolfSSL_CTX_set_min_proto_version(wctx->ssl_ctx, TLS1_VERSION);
     break;
   case CURL_SSLVERSION_TLSv1_1:
-    res = wolfSSL_CTX_set_min_proto_version(backend->ctx, TLS1_1_VERSION);
+    res = wolfSSL_CTX_set_min_proto_version(wctx->ssl_ctx, TLS1_1_VERSION);
     break;
   case CURL_SSLVERSION_TLSv1_2:
-    res = wolfSSL_CTX_set_min_proto_version(backend->ctx, TLS1_2_VERSION);
+    res = wolfSSL_CTX_set_min_proto_version(wctx->ssl_ctx, TLS1_2_VERSION);
     break;
 #ifdef WOLFSSL_TLS13
   case CURL_SSLVERSION_TLSv1_3:
-    res = wolfSSL_CTX_set_min_proto_version(backend->ctx, TLS1_3_VERSION);
+    res = wolfSSL_CTX_set_min_proto_version(wctx->ssl_ctx, TLS1_3_VERSION);
     break;
 #endif
   default:
     failf(data, "wolfSSL: unsupported minimum TLS version value");
-    return CURLE_SSL_CONNECT_ERROR;
+    result = CURLE_SSL_CONNECT_ERROR;
+    goto out;
   }
   if(res != WOLFSSL_SUCCESS) {
     failf(data, "wolfSSL: failed set the minimum TLS version");
-    return CURLE_SSL_CONNECT_ERROR;
+    result = CURLE_SSL_CONNECT_ERROR;
+    goto out;
   }
 
   switch(conn_config->version_max) {
 #ifdef WOLFSSL_TLS13
   case CURL_SSLVERSION_MAX_TLSv1_3:
-    res = wolfSSL_CTX_set_max_proto_version(backend->ctx, TLS1_3_VERSION);
+    res = wolfSSL_CTX_set_max_proto_version(wctx->ssl_ctx, TLS1_3_VERSION);
     break;
 #endif
   case CURL_SSLVERSION_MAX_TLSv1_2:
-    res = wolfSSL_CTX_set_max_proto_version(backend->ctx, TLS1_2_VERSION);
+    res = wolfSSL_CTX_set_max_proto_version(wctx->ssl_ctx, TLS1_2_VERSION);
     break;
   case CURL_SSLVERSION_MAX_TLSv1_1:
-    res = wolfSSL_CTX_set_max_proto_version(backend->ctx, TLS1_1_VERSION);
+    res = wolfSSL_CTX_set_max_proto_version(wctx->ssl_ctx, TLS1_1_VERSION);
     break;
   case CURL_SSLVERSION_MAX_TLSv1_0:
-    res = wolfSSL_CTX_set_max_proto_version(backend->ctx, TLS1_VERSION);
+    res = wolfSSL_CTX_set_max_proto_version(wctx->ssl_ctx, TLS1_VERSION);
     break;
   case CURL_SSLVERSION_MAX_DEFAULT:
   case CURL_SSLVERSION_MAX_NONE:
@@ -922,20 +1042,23 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
     break;
   default:
     failf(data, "wolfSSL: unsupported maximum TLS version value");
-    return CURLE_SSL_CONNECT_ERROR;
+    result = CURLE_SSL_CONNECT_ERROR;
+    goto out;
   }
   if(res != WOLFSSL_SUCCESS) {
     failf(data, "wolfSSL: failed set the maximum TLS version");
-    return CURLE_SSL_CONNECT_ERROR;
+    result = CURLE_SSL_CONNECT_ERROR;
+    goto out;
   }
 
 #ifndef WOLFSSL_TLS13
   {
     char *ciphers = conn_config->cipher_list;
     if(ciphers) {
-      if(!SSL_CTX_set_cipher_list(backend->ctx, ciphers)) {
+      if(!SSL_CTX_set_cipher_list(wctx->ssl_ctx, ciphers)) {
         failf(data, "failed setting cipher list: %s", ciphers);
-        return CURLE_SSL_CIPHER;
+        result = CURLE_SSL_CIPHER;
+        goto out;
       }
       infof(data, "Cipher selection: %s", ciphers);
     }
@@ -946,40 +1069,42 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
     const char *ciphers12 = conn_config->cipher_list;
     const char *ciphers13 = conn_config->cipher_list13;
     struct dynbuf c;
-    CURLcode result;
-    Curl_dyn_init(&c, MAX_CIPHER_LEN);
+    curlx_dyn_init(&c, MAX_CIPHER_LEN);
 
     if(ciphers13)
-      result = Curl_dyn_add(&c, ciphers13);
+      result = curlx_dyn_add(&c, ciphers13);
     else
       result = wssl_add_default_ciphers(TRUE, &c);
 
     if(!result) {
       if(ciphers12) {
-        if(Curl_dyn_len(&c))
-          result = Curl_dyn_addn(&c, ":", 1);
+        if(curlx_dyn_len(&c))
+          result = curlx_dyn_addn(&c, ":", 1);
         if(!result)
-          result = Curl_dyn_add(&c, ciphers12);
+          result = curlx_dyn_add(&c, ciphers12);
       }
       else
         result = wssl_add_default_ciphers(FALSE, &c);
     }
     if(result)
-      return result;
+      goto out;
 
-    if(!wolfSSL_CTX_set_cipher_list(backend->ctx, Curl_dyn_ptr(&c))) {
-      failf(data, "failed setting cipher list: %s", Curl_dyn_ptr(&c));
-      Curl_dyn_free(&c);
-      return CURLE_SSL_CIPHER;
+    if(!wolfSSL_CTX_set_cipher_list(wctx->ssl_ctx, curlx_dyn_ptr(&c))) {
+      failf(data, "failed setting cipher list: %s", curlx_dyn_ptr(&c));
+      curlx_dyn_free(&c);
+      result = CURLE_SSL_CIPHER;
+      goto out;
     }
-    infof(data, "Cipher selection: %s", Curl_dyn_ptr(&c));
-    Curl_dyn_free(&c);
+    infof(data, "Cipher selection: %s", curlx_dyn_ptr(&c));
+    curlx_dyn_free(&c);
   }
 #endif
 
   curves = conn_config->curves;
-  if(curves) {
+  if(!curves && cf->conn->transport == TRNSPRT_QUIC)
+    curves = (char *)CURL_UNCONST(QUIC_GROUPS);
 
+  if(curves) {
 #ifdef WOLFSSL_HAVE_KYBER
     for(idx = 0; gnm[idx].name != NULL; idx++) {
       if(strncmp(curves, gnm[idx].name, strlen(gnm[idx].name)) == 0) {
@@ -991,9 +1116,10 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
     if(pqkem == 0)
 #endif
     {
-      if(!wolfSSL_CTX_set1_curves_list(backend->ctx, curves)) {
+      if(!wolfSSL_CTX_set1_curves_list(wctx->ssl_ctx, curves)) {
         failf(data, "failed setting curves list: '%s'", curves);
-        return CURLE_SSL_CIPHER;
+        result = CURLE_SSL_CIPHER;
+        goto out;
       }
     }
   }
@@ -1005,30 +1131,32 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
     const char *key_file = ssl_config->key;
     const struct curl_blob *cert_blob = ssl_config->primary.cert_blob;
     const struct curl_blob *key_blob = ssl_config->key_blob;
-    int file_type = wolfssl_do_file_type(ssl_config->cert_type);
+    int file_type = wssl_do_file_type(ssl_config->cert_type);
     int rc;
 
     switch(file_type) {
     case WOLFSSL_FILETYPE_PEM:
       rc = cert_blob ?
-        wolfSSL_CTX_use_certificate_chain_buffer(backend->ctx,
+        wolfSSL_CTX_use_certificate_chain_buffer(wctx->ssl_ctx,
                                                  cert_blob->data,
                                                  (long)cert_blob->len) :
-        wolfSSL_CTX_use_certificate_chain_file(backend->ctx, cert_file);
+        wolfSSL_CTX_use_certificate_chain_file(wctx->ssl_ctx, cert_file);
       break;
     case WOLFSSL_FILETYPE_ASN1:
       rc = cert_blob ?
-        wolfSSL_CTX_use_certificate_buffer(backend->ctx, cert_blob->data,
+        wolfSSL_CTX_use_certificate_buffer(wctx->ssl_ctx, cert_blob->data,
                                            (long)cert_blob->len, file_type) :
-        wolfSSL_CTX_use_certificate_file(backend->ctx, cert_file, file_type);
+        wolfSSL_CTX_use_certificate_file(wctx->ssl_ctx, cert_file, file_type);
       break;
     default:
       failf(data, "unknown cert type");
-      return CURLE_BAD_FUNCTION_ARGUMENT;
+      result = CURLE_BAD_FUNCTION_ARGUMENT;
+      goto out;
     }
     if(rc != 1) {
       failf(data, "unable to use client certificate");
-      return CURLE_SSL_CONNECT_ERROR;
+      result = CURLE_SSL_CONNECT_ERROR;
+      goto out;
     }
 
     if(!key_blob && !key_file) {
@@ -1036,53 +1164,57 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
       key_file = cert_file;
     }
     else
-      file_type = wolfssl_do_file_type(ssl_config->key_type);
+      file_type = wssl_do_file_type(ssl_config->key_type);
 
     rc = key_blob ?
-      wolfSSL_CTX_use_PrivateKey_buffer(backend->ctx, key_blob->data,
+      wolfSSL_CTX_use_PrivateKey_buffer(wctx->ssl_ctx, key_blob->data,
                                         (long)key_blob->len, file_type) :
-      wolfSSL_CTX_use_PrivateKey_file(backend->ctx, key_file, file_type);
+      wolfSSL_CTX_use_PrivateKey_file(wctx->ssl_ctx, key_file, file_type);
     if(rc != 1) {
       failf(data, "unable to set private key");
-      return CURLE_SSL_CONNECT_ERROR;
+      result = CURLE_SSL_CONNECT_ERROR;
+      goto out;
     }
   }
 #else /* NO_FILESYSTEM */
   if(ssl_config->primary.cert_blob) {
     const struct curl_blob *cert_blob = ssl_config->primary.cert_blob;
     const struct curl_blob *key_blob = ssl_config->key_blob;
-    int file_type = wolfssl_do_file_type(ssl_config->cert_type);
+    int file_type = wssl_do_file_type(ssl_config->cert_type);
     int rc;
 
     switch(file_type) {
     case WOLFSSL_FILETYPE_PEM:
-      rc = wolfSSL_CTX_use_certificate_chain_buffer(backend->ctx,
+      rc = wolfSSL_CTX_use_certificate_chain_buffer(wctx->ssl_ctx,
                                                     cert_blob->data,
                                                     (long)cert_blob->len);
       break;
     case WOLFSSL_FILETYPE_ASN1:
-      rc = wolfSSL_CTX_use_certificate_buffer(backend->ctx, cert_blob->data,
+      rc = wolfSSL_CTX_use_certificate_buffer(wctx->ssl_ctx, cert_blob->data,
                                               (long)cert_blob->len, file_type);
       break;
     default:
       failf(data, "unknown cert type");
-      return CURLE_BAD_FUNCTION_ARGUMENT;
+      result = CURLE_BAD_FUNCTION_ARGUMENT;
+      goto out;
     }
     if(rc != 1) {
       failf(data, "unable to use client certificate");
-      return CURLE_SSL_CONNECT_ERROR;
+      result = CURLE_SSL_CONNECT_ERROR;
+      goto out;
     }
 
     if(!key_blob)
       key_blob = cert_blob;
     else
-      file_type = wolfssl_do_file_type(ssl_config->key_type);
+      file_type = wssl_do_file_type(ssl_config->key_type);
 
-    if(wolfSSL_CTX_use_PrivateKey_buffer(backend->ctx, key_blob->data,
+    if(wolfSSL_CTX_use_PrivateKey_buffer(wctx->ssl_ctx, key_blob->data,
                                          (long)key_blob->len,
                                          file_type) != 1) {
       failf(data, "unable to set private key");
-      return CURLE_SSL_CONNECT_ERROR;
+      result = CURLE_SSL_CONNECT_ERROR;
+      goto out;
     }
   }
 #endif /* !NO_FILESYSTEM */
@@ -1091,37 +1223,49 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
    * fail to connect if the verification fails, or if it should continue
    * anyway. In the latter case the result of the verification is checked with
    * SSL_get_verify_result() below. */
-  wolfSSL_CTX_set_verify(backend->ctx,
+  wolfSSL_CTX_set_verify(wctx->ssl_ctx,
                          conn_config->verifypeer ? WOLFSSL_VERIFY_PEER :
                          WOLFSSL_VERIFY_NONE, NULL);
 
 #ifdef HAVE_SNI
-  if(connssl->peer.sni) {
-    size_t sni_len = strlen(connssl->peer.sni);
+  if(peer->sni) {
+    size_t sni_len = strlen(peer->sni);
     if((sni_len < USHRT_MAX)) {
-      if(wolfSSL_CTX_UseSNI(backend->ctx, WOLFSSL_SNI_HOST_NAME,
-                            connssl->peer.sni,
-                            (unsigned short)sni_len) != 1) {
+      if(wolfSSL_CTX_UseSNI(wctx->ssl_ctx, WOLFSSL_SNI_HOST_NAME,
+                            peer->sni, (unsigned short)sni_len) != 1) {
         failf(data, "Failed to set SNI");
-        return CURLE_SSL_CONNECT_ERROR;
+        result = CURLE_SSL_CONNECT_ERROR;
+        goto out;
       }
+      CURL_TRC_CF(data, cf, "set SNI '%s'", peer->sni);
     }
   }
 #endif
 
+  if(ssl_config->primary.cache_session &&
+     cf->conn->transport != TRNSPRT_QUIC) {
+    /* Register to get notified when a new session is received */
+    wolfSSL_CTX_sess_set_new_cb(wctx->ssl_ctx, wssl_vtls_new_session_cb);
+  }
+
+  if(cb_setup) {
+    result = cb_setup(cf, data, cb_user_data);
+    if(result)
+      goto out;
+  }
+
   /* give application a chance to interfere with SSL set up. */
   if(data->set.ssl.fsslctx) {
-    CURLcode result;
-    if(!backend->x509_store_setup) {
-      result = Curl_wssl_setup_x509_store(cf, data, backend);
+    if(!wctx->x509_store_setup) {
+      result = Curl_wssl_setup_x509_store(cf, data, wctx);
       if(result)
-        return result;
+        goto out;
     }
-    result = (*data->set.ssl.fsslctx)(data, backend->ctx,
+    result = (*data->set.ssl.fsslctx)(data, wctx->ssl_ctx,
                                       data->set.ssl.fsslctxp);
     if(result) {
       failf(data, "error signaled by ssl ctx callback");
-      return result;
+      goto out;
     }
   }
 #ifdef NO_FILESYSTEM
@@ -1130,82 +1274,87 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
           " with \"no filesystem\". Either disable peer verification"
           " (insecure) or if you are building an application with libcurl you"
           " can load certificates via CURLOPT_SSL_CTX_FUNCTION.");
-    return CURLE_SSL_CONNECT_ERROR;
+    result = CURLE_SSL_CONNECT_ERROR;
+    goto out;
   }
 #endif
 
   /* Let's make an SSL structure */
-  if(backend->handle)
-    wolfSSL_free(backend->handle);
-  backend->handle = wolfSSL_new(backend->ctx);
-  if(!backend->handle) {
+  wctx->ssl = wolfSSL_new(wctx->ssl_ctx);
+  if(!wctx->ssl) {
     failf(data, "SSL: could not create a handle");
-    return CURLE_OUT_OF_MEMORY;
+    result = CURLE_OUT_OF_MEMORY;
+    goto out;
   }
 
+  wolfSSL_set_app_data(wctx->ssl, ssl_user_data);
+#ifdef WOLFSSL_QUIC
+  if(cf->conn->transport == TRNSPRT_QUIC)
+    wolfSSL_set_quic_use_legacy_codepoint(wctx->ssl, 0);
+#endif
+
 #ifdef WOLFSSL_HAVE_KYBER
   if(pqkem) {
-    if(wolfSSL_UseKeyShare(backend->handle, pqkem) != WOLFSSL_SUCCESS) {
+    if(wolfSSL_UseKeyShare(wctx->ssl, pqkem) != WOLFSSL_SUCCESS) {
       failf(data, "unable to use PQ KEM");
     }
   }
 #endif
 
+  /* Check if there is a cached ID we can/should use here! */
+  if(ssl_config->primary.cache_session) {
+    /* Set session from cache if there is one */
+    (void)wssl_setup_session(cf, data, wctx, &alpns,
+                             peer->scache_key, sess_reuse_cb);
+  }
+
 #ifdef HAVE_ALPN
-  if(connssl->alpn) {
+  if(alpns.count) {
     struct alpn_proto_buf proto;
-    CURLcode result;
+    memset(&proto, 0, sizeof(proto));
+    Curl_alpn_to_proto_str(&proto, &alpns);
 
-    result = Curl_alpn_to_proto_str(&proto, connssl->alpn);
-    if(result ||
-       wolfSSL_UseALPN(backend->handle,
-                       (char *)proto.data, (unsigned int)proto.len,
+    if(wolfSSL_UseALPN(wctx->ssl, (char *)proto.data,
+                       (unsigned int)proto.len,
                        WOLFSSL_ALPN_CONTINUE_ON_MISMATCH) != WOLFSSL_SUCCESS) {
       failf(data, "SSL: failed setting ALPN protocols");
-      return CURLE_SSL_CONNECT_ERROR;
+      result = CURLE_SSL_CONNECT_ERROR;
+      goto out;
     }
-    infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
+    CURL_TRC_CF(data, cf, "set ALPN: %s", proto.data);
   }
 #endif /* HAVE_ALPN */
 
 #ifdef OPENSSL_EXTRA
   if(Curl_tls_keylog_enabled()) {
     /* Ensure the Client Random is preserved. */
-    wolfSSL_KeepArrays(backend->handle);
+    wolfSSL_KeepArrays(wctx->ssl);
 #if defined(HAVE_SECRET_CALLBACK) && defined(WOLFSSL_TLS13)
-    wolfSSL_set_tls13_secret_cb(backend->handle,
-                                wolfssl_tls13_secret_callback, NULL);
+    wolfSSL_set_tls13_secret_cb(wctx->ssl,
+                                wssl_tls13_secret_callback, NULL);
 #endif
   }
 #endif /* OPENSSL_EXTRA */
 
 #ifdef HAVE_SECURE_RENEGOTIATION
-  if(wolfSSL_UseSecureRenegotiation(backend->handle) != SSL_SUCCESS) {
+  if(wolfSSL_UseSecureRenegotiation(wctx->ssl) != SSL_SUCCESS) {
     failf(data, "SSL: failed setting secure renegotiation");
-    return CURLE_SSL_CONNECT_ERROR;
+    result = CURLE_SSL_CONNECT_ERROR;
+    goto out;
   }
 #endif /* HAVE_SECURE_RENEGOTIATION */
 
-  /* Check if there is a cached ID we can/should use here! */
-  if(ssl_config->primary.cache_session) {
-    /* Set session from cache if there is one */
-    (void)Curl_wssl_setup_session(cf, data, backend, connssl->peer.scache_key);
-    /* Register to get notified when a new session is received */
-    wolfSSL_set_app_data(backend->handle, cf);
-    wolfSSL_CTX_sess_set_new_cb(backend->ctx, wssl_vtls_new_session_cb);
-  }
-
 #ifdef USE_ECH_WOLFSSL
   if(ECH_ENABLED(data)) {
     int trying_ech_now = 0;
 
     if(data->set.str[STRING_ECH_PUBLIC]) {
       infof(data, "ECH: outername not (yet) supported with wolfSSL");
-      return CURLE_SSL_CONNECT_ERROR;
+      result = CURLE_SSL_CONNECT_ERROR;
+      goto out;
     }
     if(data->set.tls_ech == CURLECH_GREASE) {
-      infof(data, "ECH: GREASE'd ECH not yet supported for wolfSSL");
-      return CURLE_SSL_CONNECT_ERROR;
+      infof(data, "ECH: GREASE is done by default by wolfSSL: no need to ask");
     }
     if(data->set.tls_ech & CURLECH_CLA_CFG
        && data->set.str[STRING_ECH_CONFIG]) {
@@ -1214,10 +1363,12 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
 
       b64len = (word32) strlen(b64val);
       if(b64len
-         && wolfSSL_SetEchConfigsBase64(backend->handle, b64val, b64len)
+         && wolfSSL_SetEchConfigsBase64(wctx->ssl, b64val, b64len)
               != WOLFSSL_SUCCESS) {
-        if(data->set.tls_ech & CURLECH_HARD)
-          return CURLE_SSL_CONNECT_ERROR;
+        if(data->set.tls_ech & CURLECH_HARD) {
+          result = CURLE_SSL_CONNECT_ERROR;
+          goto out;
+        }
       }
       else {
        trying_ech_now = 1;
@@ -1225,13 +1376,17 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
       }
     }
     else {
+      struct ssl_connect_data *connssl = cf->ctx;
       struct Curl_dns_entry *dns = NULL;
 
-      dns = Curl_fetch_addr(data, connssl->peer.hostname, connssl->peer.port);
+      dns = Curl_dnscache_get(data, connssl->peer.hostname, connssl->peer.port,
+                              cf->conn->ip_version);
       if(!dns) {
         infof(data, "ECH: requested but no DNS info available");
-        if(data->set.tls_ech & CURLECH_HARD)
-          return CURLE_SSL_CONNECT_ERROR;
+        if(data->set.tls_ech & CURLECH_HARD) {
+          result = CURLE_SSL_CONNECT_ERROR;
+          goto out;
+        }
       }
       else {
         struct Curl_https_rrinfo *rinfo = NULL;
@@ -1242,11 +1397,13 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
           size_t elen = rinfo->echconfiglist_len;
 
           infof(data, "ECH: ECHConfig from DoH HTTPS RR");
-          if(wolfSSL_SetEchConfigs(backend->handle, ecl, (word32) elen) !=
+          if(wolfSSL_SetEchConfigs(wctx->ssl, ecl, (word32) elen) !=
                 WOLFSSL_SUCCESS) {
             infof(data, "ECH: wolfSSL_SetEchConfigs failed");
-            if(data->set.tls_ech & CURLECH_HARD)
-              return CURLE_SSL_CONNECT_ERROR;
+            if(data->set.tls_ech & CURLECH_HARD) {
+              result = CURLE_SSL_CONNECT_ERROR;
+              goto out;
+            }
           }
           else {
             trying_ech_now = 1;
@@ -1255,49 +1412,107 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
         }
         else {
           infof(data, "ECH: requested but no ECHConfig available");
-          if(data->set.tls_ech & CURLECH_HARD)
-            return CURLE_SSL_CONNECT_ERROR;
+          if(data->set.tls_ech & CURLECH_HARD) {
+            result = CURLE_SSL_CONNECT_ERROR;
+            goto out;
+          }
         }
         Curl_resolv_unlink(data, &dns);
       }
     }
 
-    if(trying_ech_now && wolfSSL_set_min_proto_version(backend->handle,
+    if(trying_ech_now && wolfSSL_set_min_proto_version(wctx->ssl,
                                                        TLS1_3_VERSION) != 1) {
       infof(data, "ECH: cannot force TLSv1.3 [ERROR]");
-      return CURLE_SSL_CONNECT_ERROR;
+      result = CURLE_SSL_CONNECT_ERROR;
+      goto out;
     }
 
   }
 #endif  /* USE_ECH_WOLFSSL */
 
+  result = CURLE_OK;
+
+out:
+  if(result && wctx->ssl) {
+    wolfSSL_free(wctx->ssl);
+    wctx->ssl = NULL;
+  }
+  if(result && wctx->ssl_ctx) {
+    wolfSSL_CTX_free(wctx->ssl_ctx);
+    wctx->ssl_ctx = NULL;
+  }
+  return result;
+}
+
+/*
+ * This function loads all the client/CA certificates and CRLs. Setup the TLS
+ * layer and do all necessary magic.
+ */
+static CURLcode
+wssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+  struct ssl_connect_data *connssl = cf->ctx;
+  struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend;
+  struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+  CURLcode result;
+
+  DEBUGASSERT(wssl);
+
+  if(connssl->state == ssl_connection_complete)
+    return CURLE_OK;
+
+  result = Curl_wssl_ctx_init(wssl, cf, data, &connssl->peer,
+                              connssl->alpn, NULL, NULL, cf,
+                              wssl_on_session_reuse);
+  if(result)
+    return result;
+
+#ifdef HAVE_ALPN
+  if(connssl->alpn && (connssl->state != ssl_connection_deferred)) {
+    struct alpn_proto_buf proto;
+    memset(&proto, 0, sizeof(proto));
+    Curl_alpn_to_proto_str(&proto, connssl->alpn);
+    infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
+  }
+#endif
+
+  /* Enable RFC2818 checks */
+  if(conn_config->verifyhost) {
+    char *snihost = connssl->peer.sni ?
+      connssl->peer.sni : connssl->peer.hostname;
+    if(wolfSSL_check_domain_name(wssl->ssl, snihost) !=
+       WOLFSSL_SUCCESS) {
+      return CURLE_SSL_CONNECT_ERROR;
+    }
+  }
+
 #ifdef USE_BIO_CHAIN
   {
     WOLFSSL_BIO *bio;
 
-    bio = wolfSSL_BIO_new(wolfssl_bio_cf_method);
+    bio = wolfSSL_BIO_new(wssl_bio_cf_method);
     if(!bio)
       return CURLE_OUT_OF_MEMORY;
 
     wolfSSL_BIO_set_data(bio, cf);
-    wolfSSL_set_bio(backend->handle, bio, bio);
+    wolfSSL_set_bio(wssl->ssl, bio, bio);
   }
 #else /* USE_BIO_CHAIN */
   /* pass the raw socket into the SSL layer */
-  if(!wolfSSL_set_fd(backend->handle,
+  if(!wolfSSL_set_fd(wssl->ssl,
                      (int)Curl_conn_cf_get_socket(cf, data))) {
-    failf(data, "SSL: SSL_set_fd failed");
+    failf(data, "SSL: wolfSSL_set_fd failed");
     return CURLE_SSL_CONNECT_ERROR;
   }
 #endif /* !USE_BIO_CHAIN */
 
-  connssl->connecting_state = ssl_connect_2;
   return CURLE_OK;
 }
 
 
-static char *wolfssl_strerror(unsigned long error, char *buf,
-                              unsigned long size)
+static char *wssl_strerror(unsigned long error, char *buf,
+                           unsigned long size)
 {
   DEBUGASSERT(size > 40);
   *buf = '\0';
@@ -1313,15 +1528,10 @@ static char *wolfssl_strerror(unsigned long error, char *buf,
   return buf;
 }
 
-
-static CURLcode
-wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
+CURLcode Curl_wssl_verify_pinned(struct Curl_cfilter *cf,
+                                 struct Curl_easy *data,
+                                 struct wssl_ctx *wssl)
 {
-  int ret = -1;
-  struct ssl_connect_data *connssl = cf->ctx;
-  struct wolfssl_ctx *backend =
-    (struct wolfssl_ctx *)connssl->backend;
-  struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
 #ifndef CURL_DISABLE_PROXY
   const char * const pinnedpubkey = Curl_ssl_cf_is_proxy(cf) ?
     data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] :
@@ -1330,29 +1540,143 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
   const char * const pinnedpubkey = data->set.str[STRING_SSL_PINNEDPUBLICKEY];
 #endif
 
-  DEBUGASSERT(backend);
+  if(pinnedpubkey) {
+#ifdef KEEP_PEER_CERT
+    WOLFSSL_X509 *x509;
+    const char *x509_der;
+    int x509_der_len;
+    struct Curl_X509certificate x509_parsed;
+    struct Curl_asn1Element *pubkey;
+    CURLcode result;
 
-  wolfSSL_ERR_clear_error();
+    x509 = wolfSSL_get_peer_certificate(wssl->ssl);
+    if(!x509) {
+      failf(data, "SSL: failed retrieving server certificate");
+      return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+    }
 
-  /* Enable RFC2818 checks */
-  if(conn_config->verifyhost) {
-    char *snihost = connssl->peer.sni ?
-      connssl->peer.sni : connssl->peer.hostname;
-    if(wolfSSL_check_domain_name(backend->handle, snihost) == WOLFSSL_FAILURE)
-      return CURLE_SSL_CONNECT_ERROR;
+    x509_der = (const char *)wolfSSL_X509_get_der(x509, &x509_der_len);
+    if(!x509_der) {
+      failf(data, "SSL: failed retrieving ASN.1 server certificate");
+      return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+    }
+
+    memset(&x509_parsed, 0, sizeof(x509_parsed));
+    if(Curl_parseX509(&x509_parsed, x509_der, x509_der + x509_der_len))
+      return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+
+    pubkey = &x509_parsed.subjectPublicKeyInfo;
+    if(!pubkey->header || pubkey->end <= pubkey->header) {
+      failf(data, "SSL: failed retrieving public key from server certificate");
+      return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+    }
+
+    result = Curl_pin_peer_pubkey(data,
+                                  pinnedpubkey,
+                                  (const unsigned char *)pubkey->header,
+                                  (size_t)(pubkey->end - pubkey->header));
+    wolfSSL_FreeX509(x509);
+    if(result) {
+      failf(data, "SSL: public key does not match pinned public key");
+      return result;
+    }
+#else
+    failf(data, "Library lacks pinning support built-in");
+    return CURLE_NOT_BUILT_IN;
+#endif
   }
+  return CURLE_OK;
+}
 
-  if(!backend->x509_store_setup) {
-    /* After having send off the ClientHello, we prepare the x509
-     * store to verify the coming certificate from the server */
-    CURLcode result;
-    result = Curl_wssl_setup_x509_store(cf, data, backend);
+#ifdef WOLFSSL_EARLY_DATA
+static CURLcode wssl_send_earlydata(struct Curl_cfilter *cf,
+                                    struct Curl_easy *data)
+{
+  struct ssl_connect_data *connssl = cf->ctx;
+  struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend;
+  CURLcode result = CURLE_OK;
+  const unsigned char *buf;
+  size_t blen;
+
+  DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_sending);
+  wssl->io_result = CURLE_OK;
+  while(Curl_bufq_peek(&connssl->earlydata, &buf, &blen)) {
+    int nwritten = 0, rc;
+
+    wolfSSL_ERR_clear_error();
+    rc = wolfSSL_write_early_data(wssl->ssl, buf, (int)blen, &nwritten);
+    CURL_TRC_CF(data, cf, "wolfSSL_write_early_data(len=%zu) -> %d, %d",
+                blen, rc, nwritten);
+    if(rc < 0) {
+      int err = wolfSSL_get_error(wssl->ssl, rc);
+      switch(err) {
+      case WOLFSSL_ERROR_NONE: /* just did not get anything */
+      case WOLFSSL_ERROR_WANT_READ:
+      case WOLFSSL_ERROR_WANT_WRITE:
+        result = CURLE_AGAIN;
+        break;
+      default: {
+        char error_buffer[256];
+        int detail = wolfSSL_get_error(wssl->ssl, err);
+        CURL_TRC_CF(data, cf, "SSL send early data, error: '%s'(%d)",
+                    wssl_strerror((unsigned long)err, error_buffer,
+                                  sizeof(error_buffer)),
+                    detail);
+        result = CURLE_SEND_ERROR;
+        break;
+      }
+      }
+      goto out;
+    }
+
+    Curl_bufq_skip(&connssl->earlydata, (size_t)nwritten);
+  }
+  /* sent everything there was */
+  connssl->earlydata_state = ssl_earlydata_sent;
+  if(!Curl_ssl_cf_is_proxy(cf))
+    Curl_pgrsEarlyData(data, (curl_off_t)connssl->earlydata_skip);
+  infof(data, "SSL sending %zu bytes of early data", connssl->earlydata_skip);
+out:
+  return result;
+}
+#endif /* WOLFSSL_EARLY_DATA */
+
+static CURLcode wssl_handshake(struct Curl_cfilter *cf,
+                               struct Curl_easy *data)
+{
+  struct ssl_connect_data *connssl = cf->ctx;
+  struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend;
+  struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+  int ret = -1, detail;
+  CURLcode result;
+
+  DEBUGASSERT(wssl);
+  connssl->io_need = CURL_SSL_IO_NEED_NONE;
+
+#ifdef WOLFSSL_EARLY_DATA
+  if(connssl->earlydata_state == ssl_earlydata_sending) {
+    result = wssl_send_earlydata(cf, data);
     if(result)
       return result;
   }
+  DEBUGASSERT((connssl->earlydata_state == ssl_earlydata_none) ||
+              (connssl->earlydata_state == ssl_earlydata_sent));
+#else
+  DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_none);
+#endif /* WOLFSSL_EARLY_DATA */
 
-  connssl->io_need = CURL_SSL_IO_NEED_NONE;
-  ret = wolfSSL_connect(backend->handle);
+  wolfSSL_ERR_clear_error();
+  ret = wolfSSL_connect(wssl->ssl);
+
+  if(!wssl->x509_store_setup) {
+    /* After having send off the ClientHello, we prepare the x509
+     * store to verify the coming certificate from the server */
+    result = Curl_wssl_setup_x509_store(cf, data, wssl);
+    if(result) {
+      CURL_TRC_CF(data, cf, "Curl_wssl_setup_x509_store() -> %d", result);
+      return result;
+    }
+  }
 
 #ifdef OPENSSL_EXTRA
   if(Curl_tls_keylog_enabled()) {
@@ -1368,67 +1692,49 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
      * changes, the worst case is that no key is logged on error.
      */
     if(ret == WOLFSSL_SUCCESS ||
-       (!wolfSSL_want_read(backend->handle) &&
-        !wolfSSL_want_write(backend->handle))) {
-      wolfssl_log_tls12_secret(backend->handle);
+       (!wolfSSL_want_read(wssl->ssl) &&
+        !wolfSSL_want_write(wssl->ssl))) {
+      wssl_log_tls12_secret(wssl->ssl);
       /* Client Random and master secrets are no longer needed, erase these.
        * Ignored while the handshake is still in progress. */
-      wolfSSL_FreeArrays(backend->handle);
+      wolfSSL_FreeArrays(wssl->ssl);
     }
   }
 #endif  /* OPENSSL_EXTRA */
 
-  if(ret != 1) {
-    int detail = wolfSSL_get_error(backend->handle, ret);
+  detail = wolfSSL_get_error(wssl->ssl, ret);
+  CURL_TRC_CF(data, cf, "wolfSSL_connect() -> %d, detail=%d", ret, detail);
 
+  if(ret == WOLFSSL_SUCCESS) {
+    return CURLE_OK;
+  }
+  else {
     if(WOLFSSL_ERROR_WANT_READ == detail) {
       connssl->io_need = CURL_SSL_IO_NEED_RECV;
-      return CURLE_OK;
+      return CURLE_AGAIN;
     }
     else if(WOLFSSL_ERROR_WANT_WRITE == detail) {
       connssl->io_need = CURL_SSL_IO_NEED_SEND;
-      return CURLE_OK;
+      return CURLE_AGAIN;
     }
-    /* There is no easy way to override only the CN matching.
-     * This will enable the override of both mismatching SubjectAltNames
-     * as also mismatching CN fields */
     else if(DOMAIN_NAME_MISMATCH == detail) {
-#if 1
+      /* There is no easy way to override only the CN matching.
+       * This will enable the override of both mismatching SubjectAltNames
+       * as also mismatching CN fields */
       failf(data, " subject alt name(s) or common name do not match \"%s\"",
             connssl->peer.dispname);
       return CURLE_PEER_FAILED_VERIFICATION;
-#else
-      /* When the wolfssl_check_domain_name() is used and you desire to
-       * continue on a DOMAIN_NAME_MISMATCH, i.e. 'ssl_config.verifyhost
-       * == 0', CyaSSL version 2.4.0 will fail with an INCOMPLETE_DATA
-       * error. The only way to do this is currently to switch the
-       * Wolfssl_check_domain_name() in and out based on the
-       * 'ssl_config.verifyhost' value. */
-      if(conn_config->verifyhost) {
-        failf(data,
-              " subject alt name(s) or common name do not match \"%s\"\n",
-              connssl->dispname);
-        return CURLE_PEER_FAILED_VERIFICATION;
-      }
-      else {
-        infof(data,
-              " subject alt name(s) and/or common name do not match \"%s\"",
-              connssl->dispname);
-        return CURLE_OK;
-      }
-#endif
     }
     else if(ASN_NO_SIGNER_E == detail) {
       if(conn_config->verifypeer) {
         failf(data, " CA signer not available for verification");
         return CURLE_SSL_CACERT_BADFILE;
       }
-      else {
-        /* Just continue with a warning if no strict certificate
-           verification is required. */
-        infof(data, "CA signer not available for verification, "
-                    "continuing anyway");
-      }
+      /* Just continue with a warning if no strict certificate
+         verification is required. */
+      infof(data, "CA signer not available for verification, "
+                  "continuing anyway");
+      return CURLE_OK;
     }
     else if(ASN_AFTER_DATE_E == detail) {
       failf(data, "server verification failed: certificate has expired.");
@@ -1438,6 +1744,15 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
       failf(data, "server verification failed: certificate not valid yet.");
       return CURLE_PEER_FAILED_VERIFICATION;
     }
+    else if(wssl->io_result) {
+      switch(wssl->io_result) {
+        case CURLE_SEND_ERROR:
+        case CURLE_RECV_ERROR:
+      return CURLE_SSL_CONNECT_ERROR;
+        default:
+          return wssl->io_result;
+      }
+    }
 #ifdef USE_ECH_WOLFSSL
     else if(-1 == detail) {
       /* try access a retry_config ECHConfigList for tracing */
@@ -1446,7 +1761,7 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
       int rv = 0;
 
       /* this currently does not produce the retry_configs */
-      rv = wolfSSL_GetEchConfigs(backend->handle, echConfigs,
+      rv = wolfSSL_GetEchConfigs(wssl->ssl, echConfigs,
                                  &echConfigsLen);
       if(rv != WOLFSSL_SUCCESS) {
         infof(data, "Failed to get ECHConfigs");
@@ -1455,160 +1770,100 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
         char *b64str = NULL;
         size_t blen = 0;
 
-        rv = Curl_base64_encode((const char *)echConfigs, echConfigsLen,
-                                &b64str, &blen);
-        if(!rv && b64str)
+        result = curlx_base64_encode((const char *)echConfigs, echConfigsLen,
+                                     &b64str, &blen);
+        if(!result && b64str)
           infof(data, "ECH: (not yet) retry_configs %s", b64str);
         free(b64str);
       }
+      return CURLE_SSL_CONNECT_ERROR;
     }
 #endif
-    else if(backend->io_result == CURLE_AGAIN) {
-      return CURLE_OK;
-    }
     else {
       char error_buffer[256];
       failf(data, "SSL_connect failed with error %d: %s", detail,
-            wolfssl_strerror((unsigned long)detail, error_buffer,
-                             sizeof(error_buffer)));
-      return CURLE_SSL_CONNECT_ERROR;
-    }
-  }
-
-  if(pinnedpubkey) {
-#ifdef KEEP_PEER_CERT
-    WOLFSSL_X509 *x509;
-    const char *x509_der;
-    int x509_der_len;
-    struct Curl_X509certificate x509_parsed;
-    struct Curl_asn1Element *pubkey;
-    CURLcode result;
-
-    x509 = wolfSSL_get_peer_certificate(backend->handle);
-    if(!x509) {
-      failf(data, "SSL: failed retrieving server certificate");
-      return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
-    }
-
-    x509_der = (const char *)wolfSSL_X509_get_der(x509, &x509_der_len);
-    if(!x509_der) {
-      failf(data, "SSL: failed retrieving ASN.1 server certificate");
-      return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
-    }
-
-    memset(&x509_parsed, 0, sizeof(x509_parsed));
-    if(Curl_parseX509(&x509_parsed, x509_der, x509_der + x509_der_len))
-      return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
-
-    pubkey = &x509_parsed.subjectPublicKeyInfo;
-    if(!pubkey->header || pubkey->end <= pubkey->header) {
-      failf(data, "SSL: failed retrieving public key from server certificate");
-      return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
-    }
-
-    result = Curl_pin_peer_pubkey(data,
-                                  pinnedpubkey,
-                                  (const unsigned char *)pubkey->header,
-                                  (size_t)(pubkey->end - pubkey->header));
-    wolfSSL_FreeX509(x509);
-    if(result) {
-      failf(data, "SSL: public key does not match pinned public key");
-      return result;
-    }
-#else
-    failf(data, "Library lacks pinning support built-in");
-    return CURLE_NOT_BUILT_IN;
-#endif
-  }
-
-#ifdef HAVE_ALPN
-  if(connssl->alpn) {
-    int rc;
-    char *protocol = NULL;
-    unsigned short protocol_len = 0;
-
-    rc = wolfSSL_ALPN_GetProtocol(backend->handle, &protocol, &protocol_len);
-
-    if(rc == WOLFSSL_SUCCESS) {
-      Curl_alpn_set_negotiated(cf, data, connssl,
-                               (const unsigned char *)protocol, protocol_len);
-    }
-    else if(rc == WOLFSSL_ALPN_NOT_FOUND)
-      Curl_alpn_set_negotiated(cf, data, connssl, NULL, 0);
-    else {
-      failf(data, "ALPN, failure getting protocol, error %d", rc);
+            wssl_strerror((unsigned long)detail, error_buffer,
+                          sizeof(error_buffer)));
       return CURLE_SSL_CONNECT_ERROR;
     }
   }
-#endif /* HAVE_ALPN */
-
-  connssl->connecting_state = ssl_connect_3;
-#if (LIBWOLFSSL_VERSION_HEX >= 0x03009010)
-  infof(data, "SSL connection using %s / %s",
-        wolfSSL_get_version(backend->handle),
-        wolfSSL_get_cipher_name(backend->handle));
-#else
-  infof(data, "SSL connected");
-#endif
-
-  return CURLE_OK;
 }
 
-static ssize_t wolfssl_send(struct Curl_cfilter *cf,
-                            struct Curl_easy *data,
-                            const void *mem,
-                            size_t len,
-                            CURLcode *curlcode)
+static ssize_t wssl_send(struct Curl_cfilter *cf,
+                         struct Curl_easy *data,
+                         const void *buf, size_t blen,
+                         CURLcode *curlcode)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct wolfssl_ctx *backend =
-    (struct wolfssl_ctx *)connssl->backend;
-  int memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len;
-  int rc;
-
-  DEBUGASSERT(backend);
+  struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend;
+  size_t total_written = 0;
+  ssize_t nwritten = -1;
+  DEBUGASSERT(wssl);
 
   wolfSSL_ERR_clear_error();
 
-  rc = wolfSSL_write(backend->handle, mem, memlen);
-  if(rc <= 0) {
-    int err = wolfSSL_get_error(backend->handle, rc);
+  if(blen) {
+    int memlen = (blen > (size_t)INT_MAX) ? INT_MAX : (int)blen;
+    int rc;
 
-    switch(err) {
-    case WOLFSSL_ERROR_WANT_READ:
-    case WOLFSSL_ERROR_WANT_WRITE:
-      /* there is data pending, re-invoke SSL_write() */
-      CURL_TRC_CF(data, cf, "wolfssl_send(len=%zu) -> AGAIN", len);
-      *curlcode = CURLE_AGAIN;
-      return -1;
-    default:
-      if(backend->io_result == CURLE_AGAIN) {
-        CURL_TRC_CF(data, cf, "wolfssl_send(len=%zu) -> AGAIN", len);
+    rc = wolfSSL_write(wssl->ssl, buf, memlen);
+    if(rc <= 0) {
+      int err = wolfSSL_get_error(wssl->ssl, rc);
+
+      switch(err) {
+      case WOLFSSL_ERROR_WANT_READ:
+      case WOLFSSL_ERROR_WANT_WRITE:
+        /* there is data pending, re-invoke wolfSSL_write() */
+        if(total_written) {
+          *curlcode = CURLE_OK;
+          nwritten = total_written;
+          goto out;
+        }
         *curlcode = CURLE_AGAIN;
-        return -1;
-      }
-      CURL_TRC_CF(data, cf, "wolfssl_send(len=%zu) -> %d, %d", len, rc, err);
-      {
-        char error_buffer[256];
-        failf(data, "SSL write: %s, errno %d",
-              wolfssl_strerror((unsigned long)err, error_buffer,
-                               sizeof(error_buffer)),
-              SOCKERRNO);
+        nwritten = -1;
+        goto out;
+
+      default:
+        if(wssl->io_result == CURLE_AGAIN) {
+          if(total_written) {
+            *curlcode = CURLE_OK;
+            nwritten = total_written;
+            goto out;
+          }
+          *curlcode = CURLE_AGAIN;
+          nwritten = -1;
+          goto out;
+        }
+        {
+          char error_buffer[256];
+          failf(data, "SSL write: %s, errno %d",
+                wssl_strerror((unsigned long)err, error_buffer,
+                              sizeof(error_buffer)),
+                SOCKERRNO);
+        }
+        *curlcode = CURLE_SEND_ERROR;
+        nwritten = -1;
+        goto out;
       }
-      *curlcode = CURLE_SEND_ERROR;
-      return -1;
     }
+    else
+      total_written += rc;
   }
-  CURL_TRC_CF(data, cf, "wolfssl_send(len=%zu) -> %d", len, rc);
-  return rc;
+
+  *curlcode = CURLE_OK;
+  nwritten = total_written;
+out:
+  CURL_TRC_CF(data, cf, "wssl_send(len=%zu) -> %" FMT_OFF_T ", %d",
+              blen, nwritten, *curlcode);
+  return nwritten;
 }
 
-static CURLcode wolfssl_shutdown(struct Curl_cfilter *cf,
-                                 struct Curl_easy *data,
-                                 bool send_shutdown, bool *done)
+static CURLcode wssl_shutdown(struct Curl_cfilter *cf,
+                              struct Curl_easy *data,
+                              bool send_shutdown, bool *done)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct wolfssl_ctx *wctx = (struct wolfssl_ctx *)connssl->backend;
+  struct wssl_ctx *wctx = (struct wssl_ctx *)connssl->backend;
   CURLcode result = CURLE_OK;
   char buf[1024];
   char error_buffer[256];
@@ -1617,7 +1872,7 @@ static CURLcode wolfssl_shutdown(struct Curl_cfilter *cf,
   int detail;
 
   DEBUGASSERT(wctx);
-  if(!wctx->handle || cf->shutdown) {
+  if(!wctx->ssl || cf->shutdown) {
     *done = TRUE;
     goto out;
   }
@@ -1625,12 +1880,12 @@ static CURLcode wolfssl_shutdown(struct Curl_cfilter *cf,
   wctx->shutting_down = TRUE;
   connssl->io_need = CURL_SSL_IO_NEED_NONE;
   *done = FALSE;
-  if(!(wolfSSL_get_shutdown(wctx->handle) & WOLFSSL_SENT_SHUTDOWN)) {
+  if(!(wolfSSL_get_shutdown(wctx->ssl) & WOLFSSL_SENT_SHUTDOWN)) {
     /* We have not started the shutdown from our side yet. Check
      * if the server already sent us one. */
     wolfSSL_ERR_clear_error();
-    nread = wolfSSL_read(wctx->handle, buf, (int)sizeof(buf));
-    err = wolfSSL_get_error(wctx->handle, nread);
+    nread = wolfSSL_read(wctx->ssl, buf, (int)sizeof(buf));
+    err = wolfSSL_get_error(wctx->ssl, nread);
     CURL_TRC_CF(data, cf, "wolfSSL_read, nread=%d, err=%d", nread, err);
     if(!nread && err == WOLFSSL_ERROR_ZERO_RETURN) {
       bool input_pending;
@@ -1652,16 +1907,16 @@ static CURLcode wolfssl_shutdown(struct Curl_cfilter *cf,
     }
   }
 
-  /* SSL should now have started the shutdown from our side. Since it
+  /* wolfSSL should now have started the shutdown from our side. Since it
    * was not complete, we are lacking the close notify from the server. */
   if(send_shutdown) {
     wolfSSL_ERR_clear_error();
-    if(wolfSSL_shutdown(wctx->handle) == 1) {
+    if(wolfSSL_shutdown(wctx->ssl) == 1) {
       CURL_TRC_CF(data, cf, "SSL shutdown finished");
       *done = TRUE;
       goto out;
     }
-    if(WOLFSSL_ERROR_WANT_WRITE == wolfSSL_get_error(wctx->handle, nread)) {
+    if(WOLFSSL_ERROR_WANT_WRITE == wolfSSL_get_error(wctx->ssl, nread)) {
       CURL_TRC_CF(data, cf, "SSL shutdown still wants to send");
       connssl->io_need = CURL_SSL_IO_NEED_SEND;
       goto out;
@@ -1672,11 +1927,11 @@ static CURLcode wolfssl_shutdown(struct Curl_cfilter *cf,
 
   for(i = 0; i < 10; ++i) {
     wolfSSL_ERR_clear_error();
-    nread = wolfSSL_read(wctx->handle, buf, (int)sizeof(buf));
+    nread = wolfSSL_read(wctx->ssl, buf, (int)sizeof(buf));
     if(nread <= 0)
       break;
   }
-  err = wolfSSL_get_error(wctx->handle, nread);
+  err = wolfSSL_get_error(wctx->ssl, nread);
   switch(err) {
   case WOLFSSL_ERROR_ZERO_RETURN: /* no more data */
     CURL_TRC_CF(data, cf, "SSL shutdown received");
@@ -1684,7 +1939,7 @@ static CURLcode wolfssl_shutdown(struct Curl_cfilter *cf,
     break;
   case WOLFSSL_ERROR_NONE: /* just did not get anything */
   case WOLFSSL_ERROR_WANT_READ:
-    /* SSL has send its notify and now wants to read the reply
+    /* wolfSSL has send its notify and now wants to read the reply
      * from the server. We are not really interested in that. */
     CURL_TRC_CF(data, cf, "SSL shutdown sent, want receive");
     connssl->io_need = CURL_SSL_IO_NEED_RECV;
@@ -1694,10 +1949,10 @@ static CURLcode wolfssl_shutdown(struct Curl_cfilter *cf,
     connssl->io_need = CURL_SSL_IO_NEED_SEND;
     break;
   default:
-    detail = wolfSSL_get_error(wctx->handle, err);
+    detail = wolfSSL_get_error(wctx->ssl, err);
     CURL_TRC_CF(data, cf, "SSL shutdown, error: '%s'(%d)",
-                wolfssl_strerror((unsigned long)err, error_buffer,
-                                 sizeof(error_buffer)),
+                wssl_strerror((unsigned long)err, error_buffer,
+                              sizeof(error_buffer)),
                 detail);
     result = CURLE_RECV_ERROR;
     break;
@@ -1708,91 +1963,89 @@ static CURLcode wolfssl_shutdown(struct Curl_cfilter *cf,
   return result;
 }
 
-static void wolfssl_close(struct Curl_cfilter *cf, struct Curl_easy *data)
+static void wssl_close(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct wolfssl_ctx *backend =
-    (struct wolfssl_ctx *)connssl->backend;
+  struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend;
 
   (void) data;
 
-  DEBUGASSERT(backend);
+  DEBUGASSERT(wssl);
 
-  if(backend->handle) {
-    wolfSSL_free(backend->handle);
-    backend->handle = NULL;
+  if(wssl->ssl) {
+    wolfSSL_free(wssl->ssl);
+    wssl->ssl = NULL;
   }
-  if(backend->ctx) {
-    wolfSSL_CTX_free(backend->ctx);
-    backend->ctx = NULL;
+  if(wssl->ssl_ctx) {
+    wolfSSL_CTX_free(wssl->ssl_ctx);
+    wssl->ssl_ctx = NULL;
   }
 }
 
-static ssize_t wolfssl_recv(struct Curl_cfilter *cf,
-                            struct Curl_easy *data,
-                            char *buf, size_t blen,
-                            CURLcode *curlcode)
+static ssize_t wssl_recv(struct Curl_cfilter *cf,
+                         struct Curl_easy *data,
+                         char *buf, size_t blen,
+                         CURLcode *curlcode)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct wolfssl_ctx *backend =
-    (struct wolfssl_ctx *)connssl->backend;
+  struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend;
   int buffsize = (blen > (size_t)INT_MAX) ? INT_MAX : (int)blen;
   int nread;
 
-  DEBUGASSERT(backend);
+  DEBUGASSERT(wssl);
 
   wolfSSL_ERR_clear_error();
   *curlcode = CURLE_OK;
 
-  nread = wolfSSL_read(backend->handle, buf, buffsize);
+  nread = wolfSSL_read(wssl->ssl, buf, buffsize);
 
   if(nread <= 0) {
-    int err = wolfSSL_get_error(backend->handle, nread);
+    int err = wolfSSL_get_error(wssl->ssl, nread);
 
     switch(err) {
     case WOLFSSL_ERROR_ZERO_RETURN: /* no more data */
-      CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> CLOSED", blen);
+      CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> CLOSED", blen);
       *curlcode = CURLE_OK;
       return 0;
     case WOLFSSL_ERROR_NONE:
     case WOLFSSL_ERROR_WANT_READ:
     case WOLFSSL_ERROR_WANT_WRITE:
-      if(!backend->io_result && connssl->peer_closed) {
-        CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> CLOSED", blen);
+      if(!wssl->io_result && connssl->peer_closed) {
+        CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> CLOSED", blen);
         *curlcode = CURLE_OK;
         return 0;
       }
       /* there is data pending, re-invoke wolfSSL_read() */
-      CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> AGAIN", blen);
+      CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> AGAIN", blen);
       *curlcode = CURLE_AGAIN;
       return -1;
     default:
-      if(backend->io_result == CURLE_AGAIN) {
-        CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> AGAIN", blen);
+      if(wssl->io_result == CURLE_AGAIN) {
+        CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> AGAIN", blen);
         *curlcode = CURLE_AGAIN;
         return -1;
       }
-      else if(!backend->io_result && connssl->peer_closed) {
-        CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> CLOSED", blen);
+      else if(!wssl->io_result && connssl->peer_closed) {
+        CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> CLOSED", blen);
         *curlcode = CURLE_OK;
         return 0;
       }
       else {
         char error_buffer[256];
         failf(data, "SSL read: %s, errno %d",
-              wolfssl_strerror((unsigned long)err, error_buffer,
-                               sizeof(error_buffer)),
+              wssl_strerror((unsigned long)err, error_buffer,
+                            sizeof(error_buffer)),
               SOCKERRNO);
       }
       *curlcode = CURLE_RECV_ERROR;
       return -1;
     }
   }
-  CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> %d", blen, nread);
+
+  CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> %d", blen, nread);
   return nread;
 }
 
-
 size_t Curl_wssl_version(char *buffer, size_t size)
 {
 #if LIBWOLFSSL_VERSION_HEX >= 0x03006000
@@ -1803,7 +2056,7 @@ size_t Curl_wssl_version(char *buffer, size_t size)
 }
 
 
-static int wolfssl_init(void)
+static int wssl_init(void)
 {
   int ret;
 
@@ -1811,14 +2064,14 @@ static int wolfssl_init(void)
   Curl_tls_keylog_open();
 #endif
   ret = (wolfSSL_Init() == WOLFSSL_SUCCESS);
-  wolfssl_bio_cf_init_methods();
+  wssl_bio_cf_init_methods();
   return ret;
 }
 
 
-static void wolfssl_cleanup(void)
+static void wssl_cleanup(void)
 {
-  wolfssl_bio_cf_free_methods();
+  wssl_bio_cf_free_methods();
   wolfSSL_Cleanup();
 #ifdef OPENSSL_EXTRA
   Curl_tls_keylog_close();
@@ -1826,31 +2079,29 @@ static void wolfssl_cleanup(void)
 }
 
 
-static bool wolfssl_data_pending(struct Curl_cfilter *cf,
-                                 const struct Curl_easy *data)
+static bool wssl_data_pending(struct Curl_cfilter *cf,
+                              const struct Curl_easy *data)
 {
   struct ssl_connect_data *ctx = cf->ctx;
-  struct wolfssl_ctx *backend;
+  struct wssl_ctx *wssl;
 
   (void)data;
   DEBUGASSERT(ctx && ctx->backend);
 
-  backend = (struct wolfssl_ctx *)ctx->backend;
-  if(backend->handle)   /* SSL is in use */
-    return wolfSSL_pending(backend->handle);
+  wssl = (struct wssl_ctx *)ctx->backend;
+  if(wssl->ssl)   /* wolfSSL is in use */
+    return wolfSSL_pending(wssl->ssl);
   else
     return FALSE;
 }
 
-static CURLcode
-wolfssl_connect_common(struct Curl_cfilter *cf,
-                       struct Curl_easy *data,
-                       bool nonblocking,
-                       bool *done)
+static CURLcode wssl_connect(struct Curl_cfilter *cf,
+                             struct Curl_easy *data,
+                             bool *done)
 {
-  CURLcode result;
   struct ssl_connect_data *connssl = cf->ctx;
-  curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
+  struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend;
+  CURLcode result = CURLE_OK;
 
   /* check if the connection has already been established */
   if(ssl_connection_complete == connssl->state) {
@@ -1858,116 +2109,108 @@ wolfssl_connect_common(struct Curl_cfilter *cf,
     return CURLE_OK;
   }
 
-  if(ssl_connect_1 == connssl->connecting_state) {
-    /* Find out how much more time we are allowed */
-    const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
-    if(timeout_ms < 0) {
-      /* no need to continue if time already is up */
-      failf(data, "SSL connection timeout");
-      return CURLE_OPERATION_TIMEDOUT;
-    }
+  *done = FALSE;
+  connssl->io_need = CURL_SSL_IO_NEED_NONE;
 
-    result = wolfssl_connect_step1(cf, data);
+  if(ssl_connect_1 == connssl->connecting_state) {
+    result = wssl_connect_step1(cf, data);
     if(result)
       return result;
+    connssl->connecting_state = ssl_connect_2;
   }
 
-  while(ssl_connect_2 == connssl->connecting_state) {
-
-    /* check allowed time left */
-    const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
+  if(ssl_connect_2 == connssl->connecting_state) {
+    if(connssl->earlydata_state == ssl_earlydata_await) {
+      /* We defer the handshake until request data arrives. */
+      DEBUGASSERT(connssl->state == ssl_connection_deferred);
+      goto out;
+    }
+    result = wssl_handshake(cf, data);
+    if(result == CURLE_AGAIN)
+      goto out;
+    wssl->hs_result = result;
+    connssl->connecting_state = ssl_connect_3;
+  }
 
-    if(timeout_ms < 0) {
-      /* no need to continue if time already is up */
-      failf(data, "SSL connection timeout");
-      return CURLE_OPERATION_TIMEDOUT;
+  if(ssl_connect_3 == connssl->connecting_state) {
+    /* Once the handshake has errored, it stays in that state and will
+     * error again on every call. */
+    if(wssl->hs_result) {
+      result = wssl->hs_result;
+      goto out;
     }
+    result = Curl_wssl_verify_pinned(cf, data, wssl);
+    if(result) {
+      wssl->hs_result = result;
+      goto out;
+    }
+    /* handhshake was done without errors */
+#ifdef HAVE_ALPN
+    if(connssl->alpn) {
+      int rc;
+      char *protocol = NULL;
+      unsigned short protocol_len = 0;
+
+      rc = wolfSSL_ALPN_GetProtocol(wssl->ssl, &protocol, &protocol_len);
 
-    /* if ssl is expecting something, check if it is available. */
-    if(connssl->io_need) {
-      curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND) ?
-        sockfd : CURL_SOCKET_BAD;
-      curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV) ?
-        sockfd : CURL_SOCKET_BAD;
-      int what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
-                                   nonblocking ? 0 : timeout_ms);
-      if(what < 0) {
-        /* fatal error */
-        failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
-        return CURLE_SSL_CONNECT_ERROR;
+      if(rc == WOLFSSL_SUCCESS) {
+        Curl_alpn_set_negotiated(cf, data, connssl,
+                                 (const unsigned char *)protocol,
+                                 protocol_len);
       }
-      else if(0 == what) {
-        if(nonblocking) {
-          *done = FALSE;
-          return CURLE_OK;
-        }
-        else {
-          /* timeout */
-          failf(data, "SSL connection timeout");
-          return CURLE_OPERATION_TIMEDOUT;
-        }
+      else if(rc == WOLFSSL_ALPN_NOT_FOUND)
+        Curl_alpn_set_negotiated(cf, data, connssl, NULL, 0);
+      else {
+        failf(data, "ALPN, failure getting protocol, error %d", rc);
+        wssl->hs_result = result = CURLE_SSL_CONNECT_ERROR;
+        goto out;
       }
-      /* socket is readable or writable */
     }
+#endif /* HAVE_ALPN */
 
-    /* Run transaction, and return to the caller if it failed or if
-     * this connection is part of a multi handle and this loop would
-     * execute again. This permits the owner of a multi handle to
-     * abort a connection attempt before step2 has completed while
-     * ensuring that a client using select() or epoll() will always
-     * have a valid fdset to wait on.
-     */
-    result = wolfssl_connect_step2(cf, data);
-    if(result || (nonblocking && (ssl_connect_2 == connssl->connecting_state)))
-      return result;
-  } /* repeat step2 until all transactions are done. */
+#if (LIBWOLFSSL_VERSION_HEX >= 0x03009010)
+    infof(data, "SSL connection using %s / %s",
+          wolfSSL_get_version(wssl->ssl),
+          wolfSSL_get_cipher_name(wssl->ssl));
+#else
+    infof(data, "SSL connected");
+#endif
 
-  if(ssl_connect_3 == connssl->connecting_state) {
-    /* In other backends, this is where we verify the certificate, but
-     * wolfSSL already does that as part of the handshake. */
     connssl->connecting_state = ssl_connect_done;
+    connssl->state = ssl_connection_complete;
+
+#ifdef WOLFSSL_EARLY_DATA
+    if(connssl->earlydata_state > ssl_earlydata_none) {
+      /* We should be in this state by now */
+      DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_sent);
+      connssl->earlydata_state =
+        (wolfSSL_get_early_data_status(wssl->ssl) ==
+         WOLFSSL_EARLY_DATA_REJECTED) ?
+         ssl_earlydata_rejected : ssl_earlydata_accepted;
+    }
+#endif /* WOLFSSL_EARLY_DATA */
   }
 
-  if(ssl_connect_done == connssl->connecting_state) {
-    connssl->state = ssl_connection_complete;
+  if((connssl->connecting_state == ssl_connect_done) ||
+     (connssl->state == ssl_connection_deferred)) {
     *done = TRUE;
   }
-  else
-    *done = FALSE;
-
-  /* Reset our connect state machine */
-  connssl->connecting_state = ssl_connect_1;
-
-  return CURLE_OK;
-}
-
-
-static CURLcode wolfssl_connect_nonblocking(struct Curl_cfilter *cf,
-                                            struct Curl_easy *data,
-                                            bool *done)
-{
-  return wolfssl_connect_common(cf, data, TRUE, done);
-}
-
-
-static CURLcode wolfssl_connect(struct Curl_cfilter *cf,
-                                struct Curl_easy *data)
-{
-  CURLcode result;
-  bool done = FALSE;
 
-  result = wolfssl_connect_common(cf, data, FALSE, &done);
-  if(result)
-    return result;
-
-  DEBUGASSERT(done);
-
-  return CURLE_OK;
+out:
+  if(result) {
+    *done = FALSE;
+    if(result == CURLE_AGAIN)
+      return CURLE_OK;
+  }
+  else if((connssl->connecting_state == ssl_connect_done) ||
+          (connssl->state == ssl_connection_deferred)) {
+    *done = TRUE;
+  }
+  return result;
 }
 
-static CURLcode wolfssl_random(struct Curl_easy *data,
-                               unsigned char *entropy, size_t length)
+static CURLcode wssl_random(struct Curl_easy *data,
+                            unsigned char *entropy, size_t length)
 {
   WC_RNG rng;
   (void)data;
@@ -1982,10 +2225,10 @@ static CURLcode wolfssl_random(struct Curl_easy *data,
   return CURLE_OK;
 }
 
-static CURLcode wolfssl_sha256sum(const unsigned char *tmp, /* input */
-                                  size_t tmplen,
-                                  unsigned char *sha256sum /* output */,
-                                  size_t unused)
+static CURLcode wssl_sha256sum(const unsigned char *tmp, /* input */
+                               size_t tmplen,
+                               unsigned char *sha256sum /* output */,
+                               size_t unused)
 {
   wc_Sha256 SHA256pw;
   (void)unused;
@@ -1996,14 +2239,13 @@ static CURLcode wolfssl_sha256sum(const unsigned char *tmp, /* input */
   return CURLE_OK;
 }
 
-static void *wolfssl_get_internals(struct ssl_connect_data *connssl,
-                                   CURLINFO info UNUSED_PARAM)
+static void *wssl_get_internals(struct ssl_connect_data *connssl,
+                                CURLINFO info UNUSED_PARAM)
 {
-  struct wolfssl_ctx *backend =
-    (struct wolfssl_ctx *)connssl->backend;
+  struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend;
   (void)info;
-  DEBUGASSERT(backend);
-  return backend->handle;
+  DEBUGASSERT(wssl);
+  return wssl->ssl;
 }
 
 const struct Curl_ssl Curl_ssl_wolfssl = {
@@ -2027,28 +2269,27 @@ const struct Curl_ssl Curl_ssl_wolfssl = {
   SSLSUPP_CA_CACHE |
   SSLSUPP_CIPHER_LIST,
 
-  sizeof(struct wolfssl_ctx),
+  sizeof(struct wssl_ctx),
 
-  wolfssl_init,                    /* init */
-  wolfssl_cleanup,                 /* cleanup */
+  wssl_init,                       /* init */
+  wssl_cleanup,                    /* cleanup */
   Curl_wssl_version,               /* version */
-  wolfssl_shutdown,                /* shutdown */
-  wolfssl_data_pending,            /* data_pending */
-  wolfssl_random,                  /* random */
+  wssl_shutdown,                   /* shutdown */
+  wssl_data_pending,               /* data_pending */
+  wssl_random,                     /* random */
   NULL,                            /* cert_status_request */
-  wolfssl_connect,                 /* connect */
-  wolfssl_connect_nonblocking,     /* connect_nonblocking */
+  wssl_connect,                    /* connect */
   Curl_ssl_adjust_pollset,         /* adjust_pollset */
-  wolfssl_get_internals,           /* get_internals */
-  wolfssl_close,                   /* close_one */
+  wssl_get_internals,              /* get_internals */
+  wssl_close,                      /* close_one */
   NULL,                            /* close_all */
   NULL,                            /* set_engine */
   NULL,                            /* set_engine_default */
   NULL,                            /* engines_list */
   NULL,                            /* false_start */
-  wolfssl_sha256sum,               /* sha256sum */
-  wolfssl_recv,                    /* recv decrypted data */
-  wolfssl_send,                    /* send data to encrypt */
+  wssl_sha256sum,                  /* sha256sum */
+  wssl_recv,                       /* recv decrypted data */
+  wssl_send,                       /* send data to encrypt */
   NULL,                            /* get_channel_binding */
 };
 
diff --git a/extra/curl/curl-8.12.1/lib/vtls/wolfssl.h b/extra/curl/curl-8.14.1/lib/vtls/wolfssl.h
similarity index 54%
rename from extra/curl/curl-8.12.1/lib/vtls/wolfssl.h
rename to extra/curl/curl-8.14.1/lib/vtls/wolfssl.h
index 57935b60c714..0ddbee9ed9e6 100644
--- a/extra/curl/curl-8.12.1/lib/vtls/wolfssl.h
+++ b/extra/curl/curl-8.14.1/lib/vtls/wolfssl.h
@@ -23,25 +23,27 @@
  * SPDX-License-Identifier: curl
  *
  ***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #ifdef USE_WOLFSSL
 
-#include "urldata.h"
+#include "../urldata.h"
+
+struct alpn_spec;
+struct ssl_peer;
+struct Curl_ssl_session;
 
 struct WOLFSSL;
-typedef struct WOLFSSL WOLFSSL;
 struct WOLFSSL_CTX;
-typedef struct WOLFSSL_CTX WOLFSSL_CTX;
 struct WOLFSSL_SESSION;
-typedef struct WOLFSSL_SESSION WOLFSSL_SESSION;
 
 extern const struct Curl_ssl Curl_ssl_wolfssl;
 
-struct wolfssl_ctx {
-  WOLFSSL_CTX *ctx;
-  WOLFSSL     *handle;
+struct wssl_ctx {
+  struct WOLFSSL_CTX *ssl_ctx;
+  struct WOLFSSL     *ssl;
   CURLcode    io_result;   /* result of last BIO cfilter operation */
+  CURLcode    hs_result;   /* result of handshake */
   int io_send_blocked_len; /* length of last BIO write that EAGAINed */
   BIT(x509_store_setup);   /* x509 store has been set up */
   BIT(shutting_down);      /* TLS is being shut down */
@@ -49,21 +51,42 @@ struct wolfssl_ctx {
 
 size_t Curl_wssl_version(char *buffer, size_t size);
 
+typedef CURLcode Curl_wssl_ctx_setup_cb(struct Curl_cfilter *cf,
+                                        struct Curl_easy *data,
+                                        void *user_data);
+
+typedef CURLcode Curl_wssl_init_session_reuse_cb(struct Curl_cfilter *cf,
+                                                 struct Curl_easy *data,
+                                                 struct alpn_spec *alpns,
+                                                 struct Curl_ssl_session *scs,
+                                                 bool *do_early_data);
+
+CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx,
+                            struct Curl_cfilter *cf,
+                            struct Curl_easy *data,
+                            struct ssl_peer *peer,
+                            const struct alpn_spec *alpns,
+                            Curl_wssl_ctx_setup_cb *cb_setup,
+                            void *cb_user_data,
+                            void *ssl_user_data,
+                            Curl_wssl_init_session_reuse_cb *sess_reuse_cb);
+
 CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf,
                                     struct Curl_easy *data,
-                                    struct wolfssl_ctx *wssl);
-
-CURLcode Curl_wssl_setup_session(struct Curl_cfilter *cf,
-                                 struct Curl_easy *data,
-                                 struct wolfssl_ctx *wss,
-                                 const char *ssl_peer_key);
+                                    struct wssl_ctx *wssl);
 
 CURLcode Curl_wssl_cache_session(struct Curl_cfilter *cf,
                                  struct Curl_easy *data,
                                  const char *ssl_peer_key,
-                                 WOLFSSL_SESSION *session,
+                                 struct WOLFSSL_SESSION *session,
                                  int ietf_tls_id,
-                                 const char *alpn);
+                                 const char *alpn,
+                                 unsigned char *quic_tp,
+                                 size_t quic_tp_len);
+
+CURLcode Curl_wssl_verify_pinned(struct Curl_cfilter *cf,
+                                 struct Curl_easy *data,
+                                 struct wssl_ctx *wssl);
 
 
 #endif /* USE_WOLFSSL */
diff --git a/extra/curl/curl-8.12.1/lib/vtls/x509asn1.c b/extra/curl/curl-8.14.1/lib/vtls/x509asn1.c
similarity index 88%
rename from extra/curl/curl-8.12.1/lib/vtls/x509asn1.c
rename to extra/curl/curl-8.14.1/lib/vtls/x509asn1.c
index 0bc0a75a4bff..c6246cbd0964 100644
--- a/extra/curl/curl-8.12.1/lib/vtls/x509asn1.c
+++ b/extra/curl/curl-8.14.1/lib/vtls/x509asn1.c
@@ -22,39 +22,39 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #if defined(USE_GNUTLS) || defined(USE_WOLFSSL) ||      \
   defined(USE_SCHANNEL) || defined(USE_SECTRANSP) ||    \
-  defined(USE_MBEDTLS)
+  defined(USE_MBEDTLS) || defined(USE_RUSTLS)
 
-#if defined(USE_WOLFSSL) || defined(USE_SCHANNEL)
+#if defined(USE_GNUTLS) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) || \
+  defined(USE_MBEDTLS) || defined(USE_WOLFSSL) || defined(USE_RUSTLS)
 #define WANT_PARSEX509 /* uses Curl_parseX509() */
 #endif
 
 #if defined(USE_GNUTLS) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) || \
-  defined(USE_MBEDTLS)
+  defined(USE_MBEDTLS) || defined(USE_RUSTLS)
 #define WANT_EXTRACT_CERTINFO /* uses Curl_extract_certinfo() */
-#define WANT_PARSEX509 /* ... uses Curl_parseX509() */
 #endif
 
 #include <curl/curl.h>
-#include "urldata.h"
-#include "strcase.h"
-#include "curl_ctype.h"
+#include "../urldata.h"
+#include "../strcase.h"
+#include "../curl_ctype.h"
 #include "hostcheck.h"
-#include "vtls/vtls.h"
-#include "vtls/vtls_int.h"
-#include "sendf.h"
-#include "inet_pton.h"
-#include "curl_base64.h"
+#include "vtls.h"
+#include "vtls_int.h"
+#include "../sendf.h"
+#include "../curlx/inet_pton.h"
+#include "../curlx/base64.h"
 #include "x509asn1.h"
-#include "dynbuf.h"
+#include "../curlx/dynbuf.h"
 
 /* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_printf.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
 
 /*
  * Constants.
@@ -64,10 +64,10 @@
 #define CURL_ASN1_MAX                   ((size_t) 0x40000)      /* 256K */
 
 /* ASN.1 classes. */
-#define CURL_ASN1_UNIVERSAL             0
-#define CURL_ASN1_APPLICATION           1
-#define CURL_ASN1_CONTEXT_SPECIFIC      2
-#define CURL_ASN1_PRIVATE               3
+/* #define CURL_ASN1_UNIVERSAL             0 */
+/* #define CURL_ASN1_APPLICATION           1 */
+/* #define CURL_ASN1_CONTEXT_SPECIFIC      2 */
+/* #define CURL_ASN1_PRIVATE               3 */
 
 /* ASN.1 types. */
 #define CURL_ASN1_BOOLEAN               1
@@ -76,27 +76,27 @@
 #define CURL_ASN1_OCTET_STRING          4
 #define CURL_ASN1_NULL                  5
 #define CURL_ASN1_OBJECT_IDENTIFIER     6
-#define CURL_ASN1_OBJECT_DESCRIPTOR     7
-#define CURL_ASN1_INSTANCE_OF           8
-#define CURL_ASN1_REAL                  9
+/* #define CURL_ASN1_OBJECT_DESCRIPTOR     7 */
+/* #define CURL_ASN1_INSTANCE_OF           8 */
+/* #define CURL_ASN1_REAL                  9 */
 #define CURL_ASN1_ENUMERATED            10
-#define CURL_ASN1_EMBEDDED              11
+/* #define CURL_ASN1_EMBEDDED              11 */
 #define CURL_ASN1_UTF8_STRING           12
-#define CURL_ASN1_RELATIVE_OID          13
-#define CURL_ASN1_SEQUENCE              16
-#define CURL_ASN1_SET                   17
+/* #define CURL_ASN1_RELATIVE_OID          13 */
+/* #define CURL_ASN1_SEQUENCE              16 */
+/* #define CURL_ASN1_SET                   17 */
 #define CURL_ASN1_NUMERIC_STRING        18
 #define CURL_ASN1_PRINTABLE_STRING      19
 #define CURL_ASN1_TELETEX_STRING        20
-#define CURL_ASN1_VIDEOTEX_STRING       21
+/* #define CURL_ASN1_VIDEOTEX_STRING       21 */
 #define CURL_ASN1_IA5_STRING            22
 #define CURL_ASN1_UTC_TIME              23
 #define CURL_ASN1_GENERALIZED_TIME      24
-#define CURL_ASN1_GRAPHIC_STRING        25
+/* #define CURL_ASN1_GRAPHIC_STRING        25 */
 #define CURL_ASN1_VISIBLE_STRING        26
-#define CURL_ASN1_GENERAL_STRING        27
+/* #define CURL_ASN1_GENERAL_STRING        27 */
 #define CURL_ASN1_UNIVERSAL_STRING      28
-#define CURL_ASN1_CHARACTER_STRING      29
+/* #define CURL_ASN1_CHARACTER_STRING      29 */
 #define CURL_ASN1_BMP_STRING            30
 
 
@@ -256,7 +256,7 @@ static const char *getASN1Element(struct Curl_asn1Element *elem,
 #ifdef WANT_EXTRACT_CERTINFO
 
 /*
- * Search the null terminated OID or OID identifier in local table.
+ * Search the null-terminated OID or OID identifier in local table.
  * Return the table entry pointer or NULL if not found.
  */
 static const struct Curl_OID *searchOID(const char *oid)
@@ -291,7 +291,7 @@ static CURLcode bool2str(struct dynbuf *store,
 {
   if(end - beg != 1)
     return CURLE_BAD_FUNCTION_ARGUMENT;
-  return Curl_dyn_add(store, *beg ? "TRUE": "FALSE");
+  return curlx_dyn_add(store, *beg ? "TRUE": "FALSE");
 }
 
 /*
@@ -305,7 +305,7 @@ static CURLcode octet2str(struct dynbuf *store,
   CURLcode result = CURLE_OK;
 
   while(!result && beg < end)
-    result = Curl_dyn_addf(store, "%02x:", (unsigned char) *beg++);
+    result = curlx_dyn_addf(store, "%02x:", (unsigned char) *beg++);
 
   return result;
 }
@@ -344,7 +344,7 @@ static CURLcode int2str(struct dynbuf *store,
   do
     val = (val << 8) | *(const unsigned char *) beg++;
   while(beg < end);
-  return Curl_dyn_addf(store, "%s%x", val >= 10 ? "0x" : "", val);
+  return curlx_dyn_addf(store, "%s%x", val >= 10 ? "0x" : "", val);
 }
 
 /*
@@ -387,7 +387,7 @@ utf8asn1str(struct dynbuf *to, int type, const char *from, const char *end)
   if(type == CURL_ASN1_UTF8_STRING) {
     /* Just copy. */
     if(inlength)
-      result = Curl_dyn_addn(to, from, inlength);
+      result = curlx_dyn_addn(to, from, inlength);
   }
   else {
     while(!result && (from < end)) {
@@ -426,7 +426,7 @@ utf8asn1str(struct dynbuf *to, int type, const char *from, const char *end)
         charsize++;
       }
       buf[0] = (char) wc;
-      result = Curl_dyn_addn(to, buf, charsize);
+      result = curlx_dyn_addn(to, buf, charsize);
     }
   }
   return result;
@@ -449,7 +449,7 @@ static CURLcode encodeOID(struct dynbuf *store,
   x = y / 40;
   y -= x * 40;
 
-  result = Curl_dyn_addf(store, "%u.%u", x, y);
+  result = curlx_dyn_addf(store, "%u.%u", x, y);
   if(result)
     return result;
 
@@ -462,7 +462,7 @@ static CURLcode encodeOID(struct dynbuf *store,
       y = *(const unsigned char *) beg++;
       x = (x << 7) | (y & 0x7F);
     } while(y & 0x80);
-    result = Curl_dyn_addf(store, ".%u", x);
+    result = curlx_dyn_addf(store, ".%u", x);
   }
   return result;
 }
@@ -480,16 +480,16 @@ static CURLcode OID2str(struct dynbuf *store,
   if(beg < end) {
     if(symbolic) {
       struct dynbuf buf;
-      Curl_dyn_init(&buf, CURL_X509_STR_MAX);
+      curlx_dyn_init(&buf, CURL_X509_STR_MAX);
       result = encodeOID(&buf, beg, end);
 
       if(!result) {
-        const struct Curl_OID *op = searchOID(Curl_dyn_ptr(&buf));
+        const struct Curl_OID *op = searchOID(curlx_dyn_ptr(&buf));
         if(op)
-          result = Curl_dyn_add(store, op->textoid);
+          result = curlx_dyn_add(store, op->textoid);
         else
-          result = Curl_dyn_add(store, Curl_dyn_ptr(&buf));
-        Curl_dyn_free(&buf);
+          result = curlx_dyn_add(store, curlx_dyn_ptr(&buf));
+        curlx_dyn_free(&buf);
       }
     }
     else
@@ -568,12 +568,12 @@ static CURLcode GTime2str(struct dynbuf *store,
     tzl = end - tzp;
   }
 
-  return Curl_dyn_addf(store,
-                       "%.4s-%.2s-%.2s %.2s:%.2s:%c%c%s%.*s%s%.*s",
-                       beg, beg + 4, beg + 6,
-                       beg + 8, beg + 10, sec1, sec2,
-                       fracl ? ".": "", (int)fracl, fracp,
-                       sep, (int)tzl, tzp);
+  return curlx_dyn_addf(store,
+                        "%.4s-%.2s-%.2s %.2s:%.2s:%c%c%s%.*s%s%.*s",
+                        beg, beg + 4, beg + 6,
+                        beg + 8, beg + 10, sec1, sec2,
+                        fracl ? ".": "", (int)fracl, fracp,
+                        sep, (int)tzl, tzp);
 }
 
 #ifdef UNITTESTS
@@ -622,10 +622,10 @@ static CURLcode UTime2str(struct dynbuf *store,
     tzp++;
 
   tzl = end - tzp;
-  return Curl_dyn_addf(store, "%u%.2s-%.2s-%.2s %.2s:%.2s:%.2s %.*s",
-                       20 - (*beg >= '5'), beg, beg + 2, beg + 4,
-                       beg + 6, beg + 8, sec,
-                       (int)tzl, tzp);
+  return curlx_dyn_addf(store, "%u%.2s-%.2s-%.2s %.2s:%.2s:%.2s %.*s",
+                        20 - (*beg >= '5'), beg, beg + 2, beg + 4,
+                        beg + 6, beg + 8, sec,
+                        (int)tzl, tzp);
 }
 
 /*
@@ -658,7 +658,7 @@ static CURLcode ASN1tostr(struct dynbuf *store,
     result = octet2str(store, elem->beg, elem->end);
     break;
   case CURL_ASN1_NULL:
-    result = Curl_dyn_addn(store, "", 1);
+    result = curlx_dyn_addn(store, "", 1);
     break;
   case CURL_ASN1_OBJECT_IDENTIFIER:
     result = OID2str(store, elem->beg, elem->end, TRUE);
@@ -702,7 +702,7 @@ static CURLcode encodeDN(struct dynbuf *store, struct Curl_asn1Element *dn)
   CURLcode result = CURLE_OK;
   bool added = FALSE;
   struct dynbuf temp;
-  Curl_dyn_init(&temp, CURL_X509_STR_MAX);
+  curlx_dyn_init(&temp, CURL_X509_STR_MAX);
 
   for(p1 = dn->beg; p1 < dn->end;) {
     p1 = getASN1Element(&rdn, p1, dn->end);
@@ -725,12 +725,12 @@ static CURLcode encodeDN(struct dynbuf *store, struct Curl_asn1Element *dn)
         result = CURLE_BAD_FUNCTION_ARGUMENT;
         goto error;
       }
-      Curl_dyn_reset(&temp);
+      curlx_dyn_reset(&temp);
       result = ASN1tostr(&temp, &oid, 0);
       if(result)
         goto error;
 
-      str = Curl_dyn_ptr(&temp);
+      str = curlx_dyn_ptr(&temp);
 
       if(!str) {
         result = CURLE_BAD_FUNCTION_ARGUMENT;
@@ -743,20 +743,20 @@ static CURLcode encodeDN(struct dynbuf *store, struct Curl_asn1Element *dn)
         ;
       if(added) {
         if(p3 - str > 2)
-          result = Curl_dyn_addn(store, "/", 1);
+          result = curlx_dyn_addn(store, "/", 1);
         else
-          result = Curl_dyn_addn(store, ", ", 2);
+          result = curlx_dyn_addn(store, ", ", 2);
         if(result)
           goto error;
       }
 
       /* Encode attribute name. */
-      result = Curl_dyn_add(store, str);
+      result = curlx_dyn_add(store, str);
       if(result)
         goto error;
 
       /* Generate equal sign. */
-      result = Curl_dyn_addn(store, "=", 1);
+      result = curlx_dyn_addn(store, "=", 1);
       if(result)
         goto error;
 
@@ -764,12 +764,12 @@ static CURLcode encodeDN(struct dynbuf *store, struct Curl_asn1Element *dn)
       result = ASN1tostr(store, &value, 0);
       if(result)
         goto error;
-      Curl_dyn_reset(&temp);
+      curlx_dyn_reset(&temp);
       added = TRUE; /* use separator for next */
     }
   }
 error:
-  Curl_dyn_free(&temp);
+  curlx_dyn_free(&temp);
 
   return result;
 }
@@ -947,8 +947,8 @@ static CURLcode ssl_push_certinfo_dyn(struct Curl_easy *data,
                                       const char *label,
                                       struct dynbuf *ptr)
 {
-  size_t valuelen = Curl_dyn_len(ptr);
-  char *value = Curl_dyn_ptr(ptr);
+  size_t valuelen = curlx_dyn_len(ptr);
+  char *value = curlx_dyn_ptr(ptr);
 
   CURLcode result = Curl_ssl_push_certinfo_len(data, certnum, label,
                                                value, valuelen);
@@ -966,7 +966,7 @@ static CURLcode do_pubkey_field(struct Curl_easy *data, int certnum,
   CURLcode result;
   struct dynbuf out;
 
-  Curl_dyn_init(&out, CURL_X509_STR_MAX);
+  curlx_dyn_init(&out, CURL_X509_STR_MAX);
 
   /* Generate a certificate information record for the public key. */
 
@@ -974,7 +974,7 @@ static CURLcode do_pubkey_field(struct Curl_easy *data, int certnum,
   if(!result) {
     if(data->set.ssl.certinfo)
       result = ssl_push_certinfo_dyn(data, certnum, label, &out);
-    Curl_dyn_free(&out);
+    curlx_dyn_free(&out);
   }
   return result;
 }
@@ -1026,7 +1026,7 @@ static int do_pubkey(struct Curl_easy *data, int certnum,
     len = ((elem.end - q) * 8);
     if(len) {
       unsigned int i;
-      for(i = *(unsigned char *) q; !(i & 0x80); i <<= 1)
+      for(i = *(const unsigned char *) q; !(i & 0x80); i <<= 1)
         len--;
     }
     if(len > 32)
@@ -1110,7 +1110,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
     if(certnum)
       return CURLE_OK;
 
-  Curl_dyn_init(&out, CURL_X509_STR_MAX);
+  curlx_dyn_init(&out, CURL_X509_STR_MAX);
   /* Prepare the certificate information for curl_easy_getinfo(). */
 
   /* Extract the certificate ASN.1 elements. */
@@ -1126,7 +1126,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
     if(result)
       goto done;
   }
-  Curl_dyn_reset(&out);
+  curlx_dyn_reset(&out);
 
   /* Issuer. */
   result = DNtostr(&out, &cert.issuer);
@@ -1137,20 +1137,20 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
     if(result)
       goto done;
   }
-  Curl_dyn_reset(&out);
+  curlx_dyn_reset(&out);
 
   /* Version (always fits in less than 32 bits). */
   version = 0;
   for(ptr = cert.version.beg; ptr < cert.version.end; ptr++)
     version = (version << 8) | *(const unsigned char *) ptr;
   if(data->set.ssl.certinfo) {
-    result = Curl_dyn_addf(&out, "%x", version);
+    result = curlx_dyn_addf(&out, "%x", version);
     if(result)
       goto done;
     result = ssl_push_certinfo_dyn(data, certnum, "Version", &out);
     if(result)
       goto done;
-    Curl_dyn_reset(&out);
+    curlx_dyn_reset(&out);
   }
 
   /* Serial number. */
@@ -1162,7 +1162,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
     if(result)
       goto done;
   }
-  Curl_dyn_reset(&out);
+  curlx_dyn_reset(&out);
 
   /* Signature algorithm .*/
   result = dumpAlgo(&out, &param, cert.signatureAlgorithm.beg,
@@ -1175,7 +1175,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
     if(result)
       goto done;
   }
-  Curl_dyn_reset(&out);
+  curlx_dyn_reset(&out);
 
   /* Start Date. */
   result = ASN1tostr(&out, &cert.notBefore, 0);
@@ -1186,7 +1186,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
     if(result)
       goto done;
   }
-  Curl_dyn_reset(&out);
+  curlx_dyn_reset(&out);
 
   /* Expire Date. */
   result = ASN1tostr(&out, &cert.notAfter, 0);
@@ -1197,7 +1197,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
     if(result)
       goto done;
   }
-  Curl_dyn_reset(&out);
+  curlx_dyn_reset(&out);
 
   /* Public Key Algorithm. */
   result = dumpAlgo(&out, &param, cert.subjectPublicKeyAlgorithm.beg,
@@ -1211,13 +1211,13 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
       goto done;
   }
 
-  rc = do_pubkey(data, certnum, Curl_dyn_ptr(&out),
+  rc = do_pubkey(data, certnum, curlx_dyn_ptr(&out),
                  &param, &cert.subjectPublicKey);
   if(rc) {
     result = CURLE_OUT_OF_MEMORY; /* the most likely error */
     goto done;
   }
-  Curl_dyn_reset(&out);
+  curlx_dyn_reset(&out);
 
   /* Signature. */
   result = ASN1tostr(&out, &cert.signature, 0);
@@ -1228,12 +1228,12 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
     if(result)
       goto done;
   }
-  Curl_dyn_reset(&out);
+  curlx_dyn_reset(&out);
 
   /* Generate PEM certificate. */
-  result = Curl_base64_encode(cert.certificate.beg,
-                              cert.certificate.end - cert.certificate.beg,
-                              &certptr, &clen);
+  result = curlx_base64_encode(cert.certificate.beg,
+                               cert.certificate.end - cert.certificate.beg,
+                               &certptr, &clen);
   if(result)
     goto done;
 
@@ -1246,22 +1246,22 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
      -----END CERTIFICATE-----\n
    */
 
-  Curl_dyn_reset(&out);
+  curlx_dyn_reset(&out);
 
   /* Build the certificate string. */
-  result = Curl_dyn_add(&out, "-----BEGIN CERTIFICATE-----\n");
+  result = curlx_dyn_add(&out, "-----BEGIN CERTIFICATE-----\n");
   if(!result) {
     size_t j = 0;
 
     while(!result && (j < clen)) {
       size_t chunksize = (clen - j) > 64 ? 64 : (clen - j);
-      result = Curl_dyn_addn(&out, &certptr[j], chunksize);
+      result = curlx_dyn_addn(&out, &certptr[j], chunksize);
       if(!result)
-        result = Curl_dyn_addn(&out, "\n", 1);
+        result = curlx_dyn_addn(&out, "\n", 1);
       j += chunksize;
     }
     if(!result)
-      result = Curl_dyn_add(&out, "-----END CERTIFICATE-----\n");
+      result = curlx_dyn_add(&out, "-----END CERTIFICATE-----\n");
   }
   free(certptr);
   if(!result)
@@ -1271,10 +1271,11 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
 done:
   if(result)
     failf(data, "Failed extracting certificate chain");
-  Curl_dyn_free(&out);
+  curlx_dyn_free(&out);
   return result;
 }
 
 #endif /* WANT_EXTRACT_CERTINFO */
 
-#endif /* USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL or USE_SECTRANSP */
+#endif /* USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL or USE_SECTRANSP
+          or USE_MBEDTLS or USE_RUSTLS */
diff --git a/extra/curl/curl-8.12.1/lib/vtls/x509asn1.h b/extra/curl/curl-8.14.1/lib/vtls/x509asn1.h
similarity index 92%
rename from extra/curl/curl-8.12.1/lib/vtls/x509asn1.h
rename to extra/curl/curl-8.14.1/lib/vtls/x509asn1.h
index 5de8f18e9ca6..1c9c35c3d4ed 100644
--- a/extra/curl/curl-8.12.1/lib/vtls/x509asn1.h
+++ b/extra/curl/curl-8.14.1/lib/vtls/x509asn1.h
@@ -25,14 +25,14 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || \
   defined(USE_SCHANNEL) || defined(USE_SECTRANSP) || \
-  defined(USE_MBEDTLS)
+  defined(USE_MBEDTLS) || defined(USE_RUSTLS)
 
-#include "cfilters.h"
-#include "urldata.h"
+#include "../cfilters.h"
+#include "../urldata.h"
 
 /*
  * Types.
@@ -45,7 +45,7 @@ struct Curl_asn1Element {
   const char *end;            /* Pointer to 1st byte after element. */
   unsigned char class;        /* ASN.1 element class. */
   unsigned char tag;          /* ASN.1 element tag. */
-  bool          constructed;  /* Element is constructed. */
+  BIT(constructed);           /* Element is constructed. */
 };
 
 /* X509 certificate: RFC 5280. */
@@ -80,7 +80,7 @@ CURLcode Curl_verifyhost(struct Curl_cfilter *cf, struct Curl_easy *data,
 
 #ifdef UNITTESTS
 #if defined(USE_GNUTLS) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) || \
-  defined(USE_MBEDTLS)
+  defined(USE_MBEDTLS) || defined(USE_RUSTLS)
 
 /* used by unit1656.c */
 CURLcode Curl_x509_GTime2str(struct dynbuf *store,
@@ -91,5 +91,6 @@ CURLcode Curl_x509_getASN1Element(struct Curl_asn1Element *elem,
 #endif
 #endif
 
-#endif /* USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL or USE_SECTRANSP */
+#endif /* USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL or USE_SECTRANSP
+          or USE_MBEDTLS or USE_RUSTLS */
 #endif /* HEADER_CURL_X509ASN1_H */
diff --git a/extra/curl/curl-8.12.1/lib/ws.c b/extra/curl/curl-8.14.1/lib/ws.c
similarity index 70%
rename from extra/curl/curl-8.12.1/lib/ws.c
rename to extra/curl/curl-8.14.1/lib/ws.c
index 25d19c69726b..fc02025f1d23 100644
--- a/extra/curl/curl-8.12.1/lib/ws.c
+++ b/extra/curl/curl-8.14.1/lib/ws.c
@@ -27,10 +27,11 @@
 #if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP)
 
 #include "urldata.h"
+#include "url.h"
 #include "bufq.h"
-#include "dynbuf.h"
+#include "curlx/dynbuf.h"
 #include "rand.h"
-#include "curl_base64.h"
+#include "curlx/base64.h"
 #include "connect.h"
 #include "sendf.h"
 #include "multiif.h"
@@ -38,7 +39,8 @@
 #include "easyif.h"
 #include "transfer.h"
 #include "select.h"
-#include "nonblock.h"
+#include "curlx/nonblock.h"
+#include "curlx/strparse.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -46,16 +48,26 @@
 #include "memdebug.h"
 
 
-#define WSBIT_FIN 0x80
-#define WSBIT_RSV1 0x40
-#define WSBIT_RSV2 0x20
-#define WSBIT_RSV3 0x10
+/***
+    RFC 6455 Section 5.2
+
+      0 1 2 3 4 5 6 7
+     +-+-+-+-+-------+
+     |F|R|R|R| opcode|
+     |I|S|S|S|  (4)  |
+     |N|V|V|V|       |
+     | |1|2|3|       |
+*/
+#define WSBIT_FIN  (0x80)
+#define WSBIT_RSV1 (0x40)
+#define WSBIT_RSV2 (0x20)
+#define WSBIT_RSV3 (0x10)
 #define WSBIT_RSV_MASK (WSBIT_RSV1 | WSBIT_RSV2 | WSBIT_RSV3)
-#define WSBIT_OPCODE_CONT  0
-#define WSBIT_OPCODE_TEXT  (1)
-#define WSBIT_OPCODE_BIN   (2)
-#define WSBIT_OPCODE_CLOSE (8)
-#define WSBIT_OPCODE_PING  (9)
+#define WSBIT_OPCODE_CONT  (0x0)
+#define WSBIT_OPCODE_TEXT  (0x1)
+#define WSBIT_OPCODE_BIN   (0x2)
+#define WSBIT_OPCODE_CLOSE (0x8)
+#define WSBIT_OPCODE_PING  (0x9)
 #define WSBIT_OPCODE_PONG  (0xa)
 #define WSBIT_OPCODE_MASK  (0xf)
 
@@ -65,58 +77,206 @@
 #define WS_CHUNK_SIZE 65535
 #define WS_CHUNK_COUNT 2
 
-struct ws_frame_meta {
-  char proto_opcode;
-  int flags;
-  const char *name;
+
+/* a client-side WS frame decoder, parsing frame headers and
+ * payload, keeping track of current position and stats */
+enum ws_dec_state {
+  WS_DEC_INIT,
+  WS_DEC_HEAD,
+  WS_DEC_PAYLOAD
 };
 
-static struct ws_frame_meta WS_FRAMES[] = {
-  { WSBIT_OPCODE_CONT,  CURLWS_CONT,   "CONT" },
-  { WSBIT_OPCODE_TEXT,  CURLWS_TEXT,   "TEXT" },
-  { WSBIT_OPCODE_BIN,   CURLWS_BINARY, "BIN" },
-  { WSBIT_OPCODE_CLOSE, CURLWS_CLOSE,  "CLOSE" },
-  { WSBIT_OPCODE_PING,  CURLWS_PING,   "PING" },
-  { WSBIT_OPCODE_PONG,  CURLWS_PONG,   "PONG" },
+struct ws_decoder {
+  int frame_age;        /* zero */
+  int frame_flags;      /* See the CURLWS_* defines */
+  curl_off_t payload_offset;   /* the offset parsing is at */
+  curl_off_t payload_len;
+  unsigned char head[10];
+  int head_len, head_total;
+  enum ws_dec_state state;
+  int cont_flags;
 };
 
-static const char *ws_frame_name_of_op(unsigned char proto_opcode)
-{
-  unsigned char opcode = proto_opcode & WSBIT_OPCODE_MASK;
-  size_t i;
-  for(i = 0; i < sizeof(WS_FRAMES)/sizeof(WS_FRAMES[0]); ++i) {
-    if(WS_FRAMES[i].proto_opcode == opcode)
-      return WS_FRAMES[i].name;
-  }
-  return "???";
-}
+/* a client-side WS frame encoder, generating frame headers and
+ * converting payloads, tracking remaining data in current frame */
+struct ws_encoder {
+  curl_off_t payload_len;  /* payload length of current frame */
+  curl_off_t payload_remain;  /* remaining payload of current */
+  unsigned int xori; /* xor index */
+  unsigned char mask[4]; /* 32-bit mask for this connection */
+  unsigned char firstbyte; /* first byte of frame we encode */
+  BIT(contfragment); /* set TRUE if the previous fragment sent was not final */
+};
 
-static int ws_frame_op2flags(unsigned char proto_opcode)
+/* A websocket connection with en- and decoder that treat frames
+ * and keep track of boundaries. */
+struct websocket {
+  struct Curl_easy *data; /* used for write callback handling */
+  struct ws_decoder dec;  /* decode of we frames */
+  struct ws_encoder enc;  /* decode of we frames */
+  struct bufq recvbuf;    /* raw data from the server */
+  struct bufq sendbuf;    /* raw data to be sent to the server */
+  struct curl_ws_frame frame;  /* the current WS FRAME received */
+  size_t sendbuf_payload; /* number of payload bytes in sendbuf */
+};
+
+
+static const char *ws_frame_name_of_op(unsigned char firstbyte)
 {
-  unsigned char opcode = proto_opcode & WSBIT_OPCODE_MASK;
-  size_t i;
-  for(i = 0; i < sizeof(WS_FRAMES)/sizeof(WS_FRAMES[0]); ++i) {
-    if(WS_FRAMES[i].proto_opcode == opcode)
-      return WS_FRAMES[i].flags;
+  switch(firstbyte & WSBIT_OPCODE_MASK) {
+    case WSBIT_OPCODE_CONT:
+      return "CONT";
+    case WSBIT_OPCODE_TEXT:
+      return "TEXT";
+    case WSBIT_OPCODE_BIN:
+      return "BIN";
+    case WSBIT_OPCODE_CLOSE:
+      return "CLOSE";
+    case WSBIT_OPCODE_PING:
+      return "PING";
+    case WSBIT_OPCODE_PONG:
+      return "PONG";
+    default:
+      return "???";
   }
-  return 0;
 }
 
-static unsigned char ws_frame_flags2op(int flags)
+static int ws_frame_firstbyte2flags(struct Curl_easy *data,
+                                    unsigned char firstbyte, int cont_flags)
 {
-  size_t i;
-  for(i = 0; i < sizeof(WS_FRAMES)/sizeof(WS_FRAMES[0]); ++i) {
-    if(WS_FRAMES[i].flags & flags)
-      return (unsigned char)WS_FRAMES[i].proto_opcode;
+  switch(firstbyte) {
+    /* 0x00 - intermediate TEXT/BINARY fragment */
+    case WSBIT_OPCODE_CONT:
+      if(!(cont_flags & CURLWS_CONT)) {
+        failf(data, "[WS] no ongoing fragmented message to resume");
+        return 0;
+      }
+      return cont_flags | CURLWS_CONT;
+    /* 0x80 - final TEXT/BIN fragment */
+    case (WSBIT_OPCODE_CONT | WSBIT_FIN):
+      if(!(cont_flags & CURLWS_CONT)) {
+        failf(data, "[WS] no ongoing fragmented message to resume");
+        return 0;
+      }
+      return cont_flags & ~CURLWS_CONT;
+    /* 0x01 - first TEXT fragment */
+    case WSBIT_OPCODE_TEXT:
+      if(cont_flags & CURLWS_CONT) {
+        failf(data, "[WS] fragmented message interrupted by new TEXT msg");
+        return 0;
+      }
+      return CURLWS_TEXT | CURLWS_CONT;
+    /* 0x81 - unfragmented TEXT msg */
+    case (WSBIT_OPCODE_TEXT | WSBIT_FIN):
+      if(cont_flags & CURLWS_CONT) {
+        failf(data, "[WS] fragmented message interrupted by new TEXT msg");
+        return 0;
+      }
+      return CURLWS_TEXT;
+    /* 0x02 - first BINARY fragment */
+    case WSBIT_OPCODE_BIN:
+      if(cont_flags & CURLWS_CONT) {
+        failf(data, "[WS] fragmented message interrupted by new BINARY msg");
+        return 0;
+      }
+      return CURLWS_BINARY | CURLWS_CONT;
+    /* 0x82 - unfragmented BINARY msg */
+    case (WSBIT_OPCODE_BIN | WSBIT_FIN):
+      if(cont_flags & CURLWS_CONT) {
+        failf(data, "[WS] fragmented message interrupted by new BINARY msg");
+        return 0;
+      }
+      return CURLWS_BINARY;
+    /* 0x08 - first CLOSE fragment */
+    case WSBIT_OPCODE_CLOSE:
+      failf(data, "[WS] invalid fragmented CLOSE frame");
+      return 0;
+    /* 0x88 - unfragmented CLOSE */
+    case (WSBIT_OPCODE_CLOSE | WSBIT_FIN):
+      return CURLWS_CLOSE;
+    /* 0x09 - first PING fragment */
+    case WSBIT_OPCODE_PING:
+      failf(data, "[WS] invalid fragmented PING frame");
+      return 0;
+    /* 0x89 - unfragmented PING */
+    case (WSBIT_OPCODE_PING | WSBIT_FIN):
+      return CURLWS_PING;
+    /* 0x0a - first PONG fragment */
+    case WSBIT_OPCODE_PONG:
+      failf(data, "[WS] invalid fragmented PONG frame");
+      return 0;
+    /* 0x8a - unfragmented PONG */
+    case (WSBIT_OPCODE_PONG | WSBIT_FIN):
+      return CURLWS_PONG;
+    /* invalid first byte */
+    default:
+      if(firstbyte & WSBIT_RSV_MASK)
+        /* any of the reserved bits 0x40/0x20/0x10 are set */
+        failf(data, "[WS] invalid reserved bits: %02x", firstbyte);
+      else
+        /* any of the reserved opcodes 0x3-0x7 or 0xb-0xf is used */
+        failf(data, "[WS] invalid opcode: %02x", firstbyte);
+      return 0;
   }
-  return 0;
 }
 
-/* No extensions are supported. If any of the RSV bits are set, we must fail */
-static bool ws_frame_rsv_supported(int flags)
+static unsigned char ws_frame_flags2firstbyte(struct Curl_easy *data,
+                                              unsigned int flags,
+                                              bool contfragment,
+                                              CURLcode *err)
 {
-  unsigned char reserved_bits = flags & WSBIT_RSV_MASK;
-  return reserved_bits == 0;
+  switch(flags & ~CURLWS_OFFSET) {
+    case 0:
+      if(contfragment) {
+        infof(data, "[WS] no flags given; interpreting as continuation "
+                    "fragment for compatibility");
+        return (WSBIT_OPCODE_CONT | WSBIT_FIN);
+      }
+      failf(data, "[WS] no flags given");
+      *err = CURLE_BAD_FUNCTION_ARGUMENT;
+      return 0xff;
+    case CURLWS_CONT:
+      if(contfragment) {
+        infof(data, "[WS] setting CURLWS_CONT flag without message type is "
+                    "supported for compatibility but highly discouraged");
+        return WSBIT_OPCODE_CONT;
+      }
+      failf(data, "[WS] No ongoing fragmented message to continue");
+      *err = CURLE_BAD_FUNCTION_ARGUMENT;
+      return 0xff;
+    case CURLWS_TEXT:
+      return contfragment ? (WSBIT_OPCODE_CONT | WSBIT_FIN)
+                          : (WSBIT_OPCODE_TEXT | WSBIT_FIN);
+    case (CURLWS_TEXT | CURLWS_CONT):
+      return contfragment ? WSBIT_OPCODE_CONT : WSBIT_OPCODE_TEXT;
+    case CURLWS_BINARY:
+      return contfragment ? (WSBIT_OPCODE_CONT | WSBIT_FIN)
+                          : (WSBIT_OPCODE_BIN | WSBIT_FIN);
+    case (CURLWS_BINARY | CURLWS_CONT):
+      return contfragment ? WSBIT_OPCODE_CONT : WSBIT_OPCODE_BIN;
+    case CURLWS_CLOSE:
+      return WSBIT_OPCODE_CLOSE | WSBIT_FIN;
+    case (CURLWS_CLOSE | CURLWS_CONT):
+      failf(data, "[WS] CLOSE frame must not be fragmented");
+      *err = CURLE_BAD_FUNCTION_ARGUMENT;
+      return 0xff;
+    case CURLWS_PING:
+      return WSBIT_OPCODE_PING | WSBIT_FIN;
+    case (CURLWS_PING | CURLWS_CONT):
+      failf(data, "[WS] PING frame must not be fragmented");
+      *err = CURLE_BAD_FUNCTION_ARGUMENT;
+      return 0xff;
+    case CURLWS_PONG:
+      return WSBIT_OPCODE_PONG | WSBIT_FIN;
+    case (CURLWS_PONG | CURLWS_CONT):
+      failf(data, "[WS] PONG frame must not be fragmented");
+      *err = CURLE_BAD_FUNCTION_ARGUMENT;
+      return 0xff;
+    default:
+      failf(data, "[WS] unknown flags: %x", flags);
+      *err = CURLE_BAD_FUNCTION_ARGUMENT;
+      return 0xff;
+  }
 }
 
 static void ws_dec_info(struct ws_decoder *dec, struct Curl_easy *data,
@@ -126,23 +286,23 @@ static void ws_dec_info(struct ws_decoder *dec, struct Curl_easy *data,
   case 0:
     break;
   case 1:
-    CURL_TRC_WRITE(data, "websocket, decoded %s [%s%s]", msg,
-                   ws_frame_name_of_op(dec->head[0]),
-                   (dec->head[0] & WSBIT_FIN) ? "" : " NON-FINAL");
+    CURL_TRC_WS(data, "decoded %s [%s%s]", msg,
+                ws_frame_name_of_op(dec->head[0]),
+                (dec->head[0] & WSBIT_FIN) ? "" : " NON-FINAL");
     break;
   default:
     if(dec->head_len < dec->head_total) {
-      CURL_TRC_WRITE(data, "websocket, decoded %s [%s%s](%d/%d)", msg,
-                     ws_frame_name_of_op(dec->head[0]),
-                     (dec->head[0] & WSBIT_FIN) ? "" : " NON-FINAL",
-                     dec->head_len, dec->head_total);
+      CURL_TRC_WS(data, "decoded %s [%s%s](%d/%d)", msg,
+                  ws_frame_name_of_op(dec->head[0]),
+                  (dec->head[0] & WSBIT_FIN) ? "" : " NON-FINAL",
+                  dec->head_len, dec->head_total);
     }
     else {
-      CURL_TRC_WRITE(data, "websocket, decoded %s [%s%s payload=%"
-                     FMT_OFF_T "/%" FMT_OFF_T "]",
-                     msg, ws_frame_name_of_op(dec->head[0]),
-                     (dec->head[0] & WSBIT_FIN) ? "" : " NON-FINAL",
-                     dec->payload_offset, dec->payload_len);
+      CURL_TRC_WS(data, "decoded %s [%s%s payload=%"
+                  FMT_OFF_T "/%" FMT_OFF_T "]",
+                  msg, ws_frame_name_of_op(dec->head[0]),
+                  (dec->head[0] & WSBIT_FIN) ? "" : " NON-FINAL",
+                  dec->payload_offset, dec->payload_len);
     }
     break;
   }
@@ -158,6 +318,16 @@ typedef ssize_t ws_write_payload(const unsigned char *buf, size_t buflen,
                                  void *userp,
                                  CURLcode *err);
 
+static void ws_dec_next_frame(struct ws_decoder *dec)
+{
+  dec->frame_age = 0;
+  dec->frame_flags = 0;
+  dec->payload_offset = 0;
+  dec->payload_len = 0;
+  dec->head_len = dec->head_total = 0;
+  dec->state = WS_DEC_INIT;
+  /* dec->cont_flags must be carried over to next frame */
+}
 
 static void ws_dec_reset(struct ws_decoder *dec)
 {
@@ -167,6 +337,7 @@ static void ws_dec_reset(struct ws_decoder *dec)
   dec->payload_len = 0;
   dec->head_len = dec->head_total = 0;
   dec->state = WS_DEC_INIT;
+  dec->cont_flags = 0;
 }
 
 static void ws_dec_init(struct ws_decoder *dec)
@@ -186,20 +357,19 @@ static CURLcode ws_dec_read_head(struct ws_decoder *dec,
       dec->head[0] = *inbuf;
       Curl_bufq_skip(inraw, 1);
 
-      if(!ws_frame_rsv_supported(dec->head[0])) {
-        failf(data, "WS: unknown reserved bit in frame header: %x",
-              dec->head[0] & WSBIT_RSV_MASK);
+      dec->frame_flags = ws_frame_firstbyte2flags(data, dec->head[0],
+                                                  dec->cont_flags);
+      if(!dec->frame_flags) {
         ws_dec_reset(dec);
         return CURLE_RECV_ERROR;
       }
 
-      dec->frame_flags = ws_frame_op2flags(dec->head[0]);
-      if(!dec->frame_flags) {
-        failf(data, "WS: unknown opcode: %x",
-              dec->head[0] & WSBIT_OPCODE_MASK);
-        ws_dec_reset(dec);
-        return CURLE_RECV_ERROR;
+      /* fragmentation only applies to data frames (text/binary);
+       * control frames (close/ping/pong) do not affect the CONT status */
+      if(dec->frame_flags & (CURLWS_TEXT | CURLWS_BINARY)) {
+        dec->cont_flags = dec->frame_flags;
       }
+
       dec->head_len = 1;
       /* ws_dec_info(dec, data, "seeing opcode"); */
       continue;
@@ -211,10 +381,30 @@ static CURLcode ws_dec_read_head(struct ws_decoder *dec,
 
       if(dec->head[1] & WSBIT_MASK) {
         /* A client MUST close a connection if it detects a masked frame. */
-        failf(data, "WS: masked input frame");
+        failf(data, "[WS] masked input frame");
         ws_dec_reset(dec);
         return CURLE_RECV_ERROR;
       }
+      if(dec->frame_flags & CURLWS_PING && dec->head[1] > 125) {
+        /* The maximum valid size of PING frames is 125 bytes.
+           Accepting overlong pings would mean sending equivalent pongs! */
+        failf(data, "[WS] received PING frame is too big");
+        ws_dec_reset(dec);
+        return CURLE_RECV_ERROR;
+      }
+      if(dec->frame_flags & CURLWS_PONG && dec->head[1] > 125) {
+        /* The maximum valid size of PONG frames is 125 bytes. */
+        failf(data, "[WS] received PONG frame is too big");
+        ws_dec_reset(dec);
+        return CURLE_RECV_ERROR;
+      }
+      if(dec->frame_flags & CURLWS_CLOSE && dec->head[1] > 125) {
+        /* The maximum valid size of CLOSE frames is 125 bytes. */
+        failf(data, "[WS] received CLOSE frame is too big");
+        ws_dec_reset(dec);
+        return CURLE_RECV_ERROR;
+      }
+
       /* How long is the frame head? */
       if(dec->head[1] == 126) {
         dec->head_total = 4;
@@ -249,7 +439,7 @@ static CURLcode ws_dec_read_head(struct ws_decoder *dec,
       break;
     case 10:
       if(dec->head[2] > 127) {
-        failf(data, "WS: frame length longer than 64 signed not supported");
+        failf(data, "[WS] frame length longer than 64 signed not supported");
         return CURLE_RECV_ERROR;
       }
       dec->payload_len = ((curl_off_t)dec->head[2] << 56) |
@@ -264,7 +454,7 @@ static CURLcode ws_dec_read_head(struct ws_decoder *dec,
     default:
       /* this should never happen */
       DEBUGASSERT(0);
-      failf(data, "WS: unexpected frame header length");
+      failf(data, "[WS] unexpected frame header length");
       return CURLE_RECV_ERROR;
     }
 
@@ -300,8 +490,8 @@ static CURLcode ws_dec_pass_payload(struct ws_decoder *dec,
     Curl_bufq_skip(inraw, (size_t)nwritten);
     dec->payload_offset += (curl_off_t)nwritten;
     remain = dec->payload_len - dec->payload_offset;
-    CURL_TRC_WRITE(data, "websocket, passed %zd bytes payload, %"
-                   FMT_OFF_T " remain", nwritten, remain);
+    CURL_TRC_WS(data, "passed %zd bytes payload, %"
+                FMT_OFF_T " remain", nwritten, remain);
   }
 
   return remain ? CURLE_AGAIN : CURLE_OK;
@@ -320,14 +510,14 @@ static CURLcode ws_dec_pass(struct ws_decoder *dec,
 
   switch(dec->state) {
   case WS_DEC_INIT:
-    ws_dec_reset(dec);
+    ws_dec_next_frame(dec);
     dec->state = WS_DEC_HEAD;
     FALLTHROUGH();
   case WS_DEC_HEAD:
     result = ws_dec_read_head(dec, data, inraw);
     if(result) {
       if(result != CURLE_AGAIN) {
-        infof(data, "WS: decode error %d", (int)result);
+        infof(data, "[WS] decode error %d", (int)result);
         break;  /* real error */
       }
       /* incomplete ws frame head */
@@ -369,11 +559,13 @@ static void update_meta(struct websocket *ws,
                         curl_off_t payload_len,
                         size_t cur_len)
 {
+  curl_off_t bytesleft = (payload_len - payload_offset - cur_len);
+
   ws->frame.age = frame_age;
   ws->frame.flags = frame_flags;
   ws->frame.offset = payload_offset;
   ws->frame.len = cur_len;
-  ws->frame.bytesleft = (payload_len - payload_offset - cur_len);
+  ws->frame.bytesleft = bytesleft;
 }
 
 /* WebSockets decoding client writer */
@@ -415,13 +607,15 @@ static ssize_t ws_cw_dec_next(const unsigned char *buf, size_t buflen,
   struct ws_cw_dec_ctx *ctx = user_data;
   struct Curl_easy *data = ctx->data;
   struct websocket *ws = ctx->ws;
+  bool auto_pong = !data->set.ws_no_auto_pong;
   curl_off_t remain = (payload_len - (payload_offset + buflen));
 
   (void)frame_age;
-  if((frame_flags & CURLWS_PING) && !remain) {
+
+  if(auto_pong && (frame_flags & CURLWS_PING) && !remain) {
     /* auto-respond to PINGs, only works for single-frame payloads atm */
     size_t bytes;
-    infof(data, "WS: auto-respond to PING with a PONG");
+    infof(data, "[WS] auto-respond to PING with a PONG");
     /* send back the exact same content as a PONG */
     *err = curl_ws_send(data, buf, buflen, &bytes, 0, CURLWS_PONG);
     if(*err)
@@ -452,9 +646,9 @@ static CURLcode ws_cw_write(struct Curl_easy *data,
   if(!(type & CLIENTWRITE_BODY) || data->set.ws_raw_mode)
     return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
 
-  ws = data->conn->proto.ws;
+  ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN);
   if(!ws) {
-    failf(data, "WS: not a websocket transfer");
+    failf(data, "[WS] not a websocket transfer");
     return CURLE_FAILED_INIT;
   }
 
@@ -463,7 +657,7 @@ static CURLcode ws_cw_write(struct Curl_easy *data,
     nwritten = Curl_bufq_write(&ctx->buf, (const unsigned char *)buf,
                                nbytes, &result);
     if(nwritten < 0) {
-      infof(data, "WS: error adding data to buffer %d", result);
+      infof(data, "[WS] error adding data to buffer %d", result);
       return result;
     }
   }
@@ -479,17 +673,17 @@ static CURLcode ws_cw_write(struct Curl_easy *data,
     if(result == CURLE_AGAIN) {
       /* insufficient amount of data, keep it for later.
        * we pretend to have written all since we have a copy */
-      CURL_TRC_WRITE(data, "websocket, buffered incomplete frame head");
+      CURL_TRC_WS(data, "buffered incomplete frame head");
       return CURLE_OK;
     }
     else if(result) {
-      infof(data, "WS: decode error %d", (int)result);
+      infof(data, "[WS] decode error %d", (int)result);
       return result;
     }
   }
 
   if((type & CLIENTWRITE_EOS) && !Curl_bufq_is_empty(&ctx->buf)) {
-    infof(data, "WS: decode ending with %zd frame bytes remaining",
+    failf(data, "[WS] decode ending with %zd frame bytes remaining",
           Curl_bufq_len(&ctx->buf));
     return CURLE_RECV_ERROR;
   }
@@ -511,12 +705,11 @@ static const struct Curl_cwtype ws_cw_decode = {
 static void ws_enc_info(struct ws_encoder *enc, struct Curl_easy *data,
                         const char *msg)
 {
-  infof(data, "WS-ENC: %s [%s%s%s payload=%" FMT_OFF_T "/%" FMT_OFF_T "]",
-        msg, ws_frame_name_of_op(enc->firstbyte),
-        (enc->firstbyte & WSBIT_OPCODE_MASK) == WSBIT_OPCODE_CONT ?
-        " CONT" : "",
-        (enc->firstbyte & WSBIT_FIN) ? "" : " NON-FIN",
-        enc->payload_len - enc->payload_remain, enc->payload_len);
+  CURL_TRC_WS(data, "WS-ENC: %s [%s%s payload=%"
+              FMT_OFF_T "/%" FMT_OFF_T "]",
+              msg, ws_frame_name_of_op(enc->firstbyte),
+              (enc->firstbyte & WSBIT_FIN) ? "" : " NON-FIN",
+              enc->payload_len - enc->payload_remain, enc->payload_len);
 }
 
 static void ws_enc_reset(struct ws_encoder *enc)
@@ -562,13 +755,12 @@ static ssize_t ws_enc_write_head(struct Curl_easy *data,
                                  CURLcode *err)
 {
   unsigned char firstbyte = 0;
-  unsigned char opcode;
   unsigned char head[14];
   size_t hlen;
   ssize_t n;
 
   if(payload_len < 0) {
-    failf(data, "WS: starting new frame with negative payload length %"
+    failf(data, "[WS] starting new frame with negative payload length %"
                 FMT_OFF_T, payload_len);
     *err = CURLE_SEND_ERROR;
     return -1;
@@ -576,38 +768,40 @@ static ssize_t ws_enc_write_head(struct Curl_easy *data,
 
   if(enc->payload_remain > 0) {
     /* trying to write a new frame before the previous one is finished */
-    failf(data, "WS: starting new frame with %zd bytes from last one "
+    failf(data, "[WS] starting new frame with %zd bytes from last one "
                 "remaining to be sent", (ssize_t)enc->payload_remain);
     *err = CURLE_SEND_ERROR;
     return -1;
   }
 
-  opcode = ws_frame_flags2op((int)flags & ~CURLWS_CONT);
-  if(!opcode) {
-    failf(data, "WS: provided flags not recognized '%x'", flags);
-    *err = CURLE_SEND_ERROR;
+  firstbyte = ws_frame_flags2firstbyte(data, flags, enc->contfragment, err);
+  if(*err) {
     return -1;
   }
 
-  if(!(flags & CURLWS_CONT)) {
-    if(!enc->contfragment)
-      /* not marked as continuing, this is the final fragment */
-      firstbyte |= WSBIT_FIN | opcode;
-    else
-      /* marked as continuing, this is the final fragment; set CONT
-         opcode and FIN bit */
-      firstbyte |= WSBIT_FIN | WSBIT_OPCODE_CONT;
+  /* fragmentation only applies to data frames (text/binary);
+   * control frames (close/ping/pong) do not affect the CONT status */
+  if(flags & (CURLWS_TEXT | CURLWS_BINARY)) {
+    enc->contfragment = (flags & CURLWS_CONT) ? (bit)TRUE : (bit)FALSE;
+  }
 
-    enc->contfragment = FALSE;
+  if(flags & CURLWS_PING && payload_len > 125) {
+    /* The maximum valid size of PING frames is 125 bytes. */
+    failf(data, "[WS] given PING frame is too big");
+    *err = CURLE_TOO_LARGE;
+    return -1;
   }
-  else if(enc->contfragment) {
-    /* the previous fragment was not a final one and this is not either, keep a
-       CONT opcode and no FIN bit */
-    firstbyte |= WSBIT_OPCODE_CONT;
+  if(flags & CURLWS_PONG && payload_len > 125) {
+    /* The maximum valid size of PONG frames is 125 bytes. */
+    failf(data, "[WS] given PONG frame is too big");
+    *err = CURLE_TOO_LARGE;
+    return -1;
   }
-  else {
-    firstbyte = opcode;
-    enc->contfragment = TRUE;
+  if(flags & CURLWS_CLOSE && payload_len > 125) {
+    /* The maximum valid size of CLOSE frames is 125 bytes. */
+    failf(data, "[WS] given CLOSE frame is too big");
+    *err = CURLE_TOO_LARGE;
+    return -1;
   }
 
   head[0] = enc->firstbyte = firstbyte;
@@ -708,18 +902,18 @@ CURLcode Curl_ws_request(struct Curl_easy *data, struct dynbuf *req)
     {
       /* The request MUST contain an |Upgrade| header field whose value
          MUST include the "websocket" keyword. */
-      "Upgrade:", "websocket"
+      "Upgrade", "websocket"
     },
     {
       /* The request MUST contain a |Connection| header field whose value
          MUST include the "Upgrade" token. */
-      "Connection:", "Upgrade",
+      "Connection", "Upgrade",
     },
     {
       /* The request MUST include a header field with the name
          |Sec-WebSocket-Version|. The value of this header field MUST be
          13. */
-      "Sec-WebSocket-Version:", "13",
+      "Sec-WebSocket-Version", "13",
     },
     {
       /* The request MUST include a header field with the name
@@ -727,7 +921,7 @@ CURLcode Curl_ws_request(struct Curl_easy *data, struct dynbuf *req)
          consisting of a randomly selected 16-byte value that has been
          base64-encoded (see Section 4 of [RFC4648]). The nonce MUST be
          selected randomly for each connection. */
-      "Sec-WebSocket-Key:", NULL,
+      "Sec-WebSocket-Key", NULL,
     }
   };
   heads[3].val = &keyval[0];
@@ -736,7 +930,7 @@ CURLcode Curl_ws_request(struct Curl_easy *data, struct dynbuf *req)
   result = Curl_rand(data, (unsigned char *)rand, sizeof(rand));
   if(result)
     return result;
-  result = Curl_base64_encode((char *)rand, sizeof(rand), &randstr, &randlen);
+  result = curlx_base64_encode((char *)rand, sizeof(rand), &randstr, &randlen);
   if(result)
     return result;
   DEBUGASSERT(randlen < sizeof(keyval));
@@ -746,16 +940,26 @@ CURLcode Curl_ws_request(struct Curl_easy *data, struct dynbuf *req)
   }
   strcpy(keyval, randstr);
   free(randstr);
-  for(i = 0; !result && (i < sizeof(heads)/sizeof(heads[0])); i++) {
-    if(!Curl_checkheaders(data, STRCONST(heads[i].name))) {
-      result = Curl_dyn_addf(req, "%s %s\r\n", heads[i].name,
-                             heads[i].val);
+  for(i = 0; !result && (i < CURL_ARRAYSIZE(heads)); i++) {
+    if(!Curl_checkheaders(data, heads[i].name, strlen(heads[i].name))) {
+      result = curlx_dyn_addf(req, "%s: %s\r\n", heads[i].name,
+                              heads[i].val);
     }
   }
   k->upgr101 = UPGR101_WS;
   return result;
 }
 
+static void ws_conn_dtor(void *key, size_t klen, void *entry)
+{
+  struct websocket *ws = entry;
+  (void)key;
+  (void)klen;
+  Curl_bufq_free(&ws->recvbuf);
+  Curl_bufq_free(&ws->sendbuf);
+  free(ws);
+}
+
 /*
  * 'nread' is number of bytes of websocket data already in the buffer at
  * 'mem'.
@@ -769,21 +973,19 @@ CURLcode Curl_ws_accept(struct Curl_easy *data,
   CURLcode result;
 
   DEBUGASSERT(data->conn);
-  ws = data->conn->proto.ws;
+  ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN);
   if(!ws) {
     size_t chunk_size = WS_CHUNK_SIZE;
     ws = calloc(1, sizeof(*ws));
     if(!ws)
       return CURLE_OUT_OF_MEMORY;
-    data->conn->proto.ws = ws;
 #ifdef DEBUGBUILD
     {
-      char *p = getenv("CURL_WS_CHUNK_SIZE");
+      const char *p = getenv("CURL_WS_CHUNK_SIZE");
       if(p) {
-        long l = strtol(p, NULL, 10);
-        if(l > 0 && l <= (1*1024*1024)) {
+        curl_off_t l;
+        if(!curlx_str_number(&p, &l, 1*1024*1024))
           chunk_size = (size_t)l;
-        }
       }
     }
 #endif
@@ -794,6 +996,10 @@ CURLcode Curl_ws_accept(struct Curl_easy *data,
                     BUFQ_OPT_SOFT_LIMIT);
     ws_dec_init(&ws->dec);
     ws_enc_init(&ws->enc);
+    result = Curl_conn_meta_set(data->conn, CURL_META_PROTO_WS_CONN,
+                                ws, ws_conn_dtor);
+    if(result)
+      return result;
   }
   else {
     Curl_bufq_reset(&ws->recvbuf);
@@ -825,7 +1031,14 @@ CURLcode Curl_ws_accept(struct Curl_easy *data,
                      sizeof(ws->enc.mask));
   if(result)
     return result;
-  infof(data, "Received 101, switch to WebSocket; mask %02x%02x%02x%02x",
+
+#ifdef DEBUGBUILD
+  if(getenv("CURL_WS_FORCE_ZERO_MASK"))
+    /* force the bit mask to 0x00000000, effectively disabling masking */
+    memset(ws->enc.mask, 0, sizeof(ws->enc.mask));
+#endif
+
+  infof(data, "[WS] Received 101, switch to WebSocket; mask %02x%02x%02x%02x",
         ws->enc.mask[0], ws->enc.mask[1], ws->enc.mask[2], ws->enc.mask[3]);
 
   /* Install our client writer that decodes WS frames payload */
@@ -849,12 +1062,12 @@ CURLcode Curl_ws_accept(struct Curl_easy *data,
                                nread, &result);
     if(nwritten < 0)
       return result;
-    infof(data, "%zu bytes websocket payload", nread);
+    CURL_TRC_WS(data, "%zu bytes payload", nread);
   }
   else { /* !connect_only */
     /* And pass any additional data to the writers */
     if(nread) {
-      result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)mem, nread);
+      result = Curl_client_write(data, CLIENTWRITE_BODY, mem, nread);
     }
   }
   k->upgr101 = UPGR101_RECEIVED;
@@ -882,6 +1095,8 @@ static ssize_t ws_client_collect(const unsigned char *buf, size_t buflen,
                                  CURLcode *err)
 {
   struct ws_collect *ctx = userp;
+  struct Curl_easy *data = ctx->data;
+  bool auto_pong = !data->set.ws_no_auto_pong;
   size_t nwritten;
   curl_off_t remain = (payload_len - (payload_offset + buflen));
 
@@ -893,10 +1108,10 @@ static ssize_t ws_client_collect(const unsigned char *buf, size_t buflen,
     ctx->payload_len = payload_len;
   }
 
-  if((frame_flags & CURLWS_PING) && !remain) {
+  if(auto_pong && (frame_flags & CURLWS_PING) && !remain) {
     /* auto-respond to PINGs, only works for single-frame payloads atm */
     size_t bytes;
-    infof(ctx->data, "WS: auto-respond to PING with a PONG");
+    infof(ctx->data, "[WS] auto-respond to PING with a PONG");
     /* send back the exact same content as a PONG */
     *err = curl_ws_send(ctx->data, buf, buflen, &bytes, 0, CURLWS_PONG);
     if(*err)
@@ -950,19 +1165,19 @@ CURL_EXTERN CURLcode curl_ws_recv(CURL *d, void *buffer,
   if(!conn) {
     /* Unhappy hack with lifetimes of transfers and connection */
     if(!data->set.connect_only) {
-      failf(data, "CONNECT_ONLY is required");
+      failf(data, "[WS] CONNECT_ONLY is required");
       return CURLE_UNSUPPORTED_PROTOCOL;
     }
 
     Curl_getconnectinfo(data, &conn);
     if(!conn) {
-      failf(data, "connection not found");
+      failf(data, "[WS] connection not found");
       return CURLE_BAD_FUNCTION_ARGUMENT;
     }
   }
-  ws = conn->proto.ws;
+  ws = Curl_conn_meta_get(conn, CURL_META_PROTO_WS_CONN);
   if(!ws) {
-    failf(data, "connection is not setup for websocket");
+    failf(data, "[WS] connection is not setup for websocket");
     return CURLE_BAD_FUNCTION_ARGUMENT;
   }
 
@@ -983,7 +1198,7 @@ CURL_EXTERN CURLcode curl_ws_recv(CURL *d, void *buffer,
       }
       else if(n == 0) {
         /* connection closed */
-        infof(data, "connection expectedly closed?");
+        infof(data, "[WS] connection expectedly closed?");
         return CURLE_GOT_NOTHING;
       }
       CURL_TRC_WS(data, "curl_ws_recv, added %zu bytes from network",
@@ -1032,12 +1247,11 @@ static CURLcode ws_flush(struct Curl_easy *data, struct websocket *ws,
     /* Simulate a blocking send after this chunk has been sent */
     bool eagain_next = FALSE;
     size_t chunk_egain = 0;
-    char *p = getenv("CURL_WS_CHUNK_EAGAIN");
+    const char *p = getenv("CURL_WS_CHUNK_EAGAIN");
     if(p) {
-      long l = strtol(p, NULL, 10);
-      if(l > 0 && l <= (1*1024*1024)) {
+      curl_off_t l;
+      if(!curlx_str_number(&p, &l, 1*1024*1024))
         chunk_egain = (size_t)l;
-      }
     }
 #endif
 
@@ -1051,7 +1265,7 @@ static CURLcode ws_flush(struct Curl_easy *data, struct websocket *ws,
       }
 #endif
       if(blocking) {
-        result = ws_send_raw_blocking(data, ws, (char *)out, outlen);
+        result = ws_send_raw_blocking(data, ws, (const char *)out, outlen);
         n = result ? 0 : outlen;
       }
       else if(data->set.connect_only || Curl_is_in_callback(data))
@@ -1068,11 +1282,11 @@ static CURLcode ws_flush(struct Curl_easy *data, struct websocket *ws,
         return result;
       }
       else if(result) {
-        failf(data, "WS: flush, write error %d", result);
+        failf(data, "[WS] flush, write error %d", result);
         return result;
       }
       else {
-        infof(data, "WS: flushed %zu bytes", n);
+        CURL_TRC_WS(data, "flushed %zu bytes", n);
         Curl_bufq_skip(&ws->sendbuf, n);
       }
     }
@@ -1104,7 +1318,7 @@ static CURLcode ws_send_raw_blocking(CURL *d, struct websocket *ws,
                   buflen);
       left_ms = Curl_timeleft(data, NULL, FALSE);
       if(left_ms < 0) {
-        failf(data, "Timeout waiting for socket becoming writable");
+        failf(data, "[WS] Timeout waiting for socket becoming writable");
         return CURLE_SEND_ERROR;
       }
 
@@ -1114,7 +1328,7 @@ static CURLcode ws_send_raw_blocking(CURL *d, struct websocket *ws,
       ev = Curl_socket_check(CURL_SOCKET_BAD, CURL_SOCKET_BAD, sock,
                              left_ms ? left_ms : 500);
       if(ev < 0) {
-        failf(data, "Error while waiting for socket becoming writable");
+        failf(data, "[WS] Error while waiting for socket becoming writable");
         return CURLE_SEND_ERROR;
       }
     }
@@ -1125,11 +1339,12 @@ static CURLcode ws_send_raw_blocking(CURL *d, struct websocket *ws,
 static CURLcode ws_send_raw(struct Curl_easy *data, const void *buffer,
                             size_t buflen, size_t *pnwritten)
 {
-  struct websocket *ws = data->conn->proto.ws;
+  struct websocket *ws;
   CURLcode result;
 
+  ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN);
   if(!ws) {
-    failf(data, "Not a websocket transfer");
+    failf(data, "[WS] Not a websocket transfer");
     return CURLE_SEND_ERROR;
   }
   if(!buflen)
@@ -1179,16 +1394,16 @@ CURL_EXTERN CURLcode curl_ws_send(CURL *d, const void *buffer_arg,
       goto out;
   }
   if(!data->conn) {
-    failf(data, "No associated connection");
+    failf(data, "[WS] No associated connection");
     result = CURLE_SEND_ERROR;
     goto out;
   }
-  if(!data->conn->proto.ws) {
-    failf(data, "Not a websocket transfer");
+  ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN);
+  if(!ws) {
+    failf(data, "[WS] Not a websocket transfer");
     result = CURLE_SEND_ERROR;
     goto out;
   }
-  ws = data->conn->proto.ws;
 
   if(data->set.ws_raw_mode) {
     /* In raw mode, we write directly to the connection */
@@ -1198,7 +1413,7 @@ CURL_EXTERN CURLcode curl_ws_send(CURL *d, const void *buffer_arg,
       goto out;
 
     if(fragsize || flags) {
-      failf(data, "ws_send, raw mode: fragsize and flags cannot be non-zero");
+      failf(data, "[WS] fragsize and flags must be zero in raw mode");
       return CURLE_BAD_FUNCTION_ARGUMENT;
     }
     result = ws_send_raw(data, buffer, buflen, sent);
@@ -1213,7 +1428,7 @@ CURL_EXTERN CURLcode curl_ws_send(CURL *d, const void *buffer_arg,
     if(buflen < ws->sendbuf_payload) {
       /* We have been called with LESS buffer data than before. This
        * is not how it's supposed too work. */
-      failf(data, "curl_ws_send() called with smaller 'buflen' than "
+      failf(data, "[WS] curl_ws_send() called with smaller 'buflen' than "
             "bytes already buffered in previous call, %zu vs %zu",
             buflen, ws->sendbuf_payload);
       result = CURLE_BAD_FUNCTION_ARGUMENT;
@@ -1222,7 +1437,7 @@ CURL_EXTERN CURLcode curl_ws_send(CURL *d, const void *buffer_arg,
     if((curl_off_t)buflen >
        (ws->enc.payload_remain + (curl_off_t)ws->sendbuf_payload)) {
       /* too large buflen beyond payload length of frame */
-      infof(data, "WS: unaligned frame size (sending %zu instead of %"
+      failf(data, "[WS] unaligned frame size (sending %zu instead of %"
                   FMT_OFF_T ")",
             buflen, ws->enc.payload_remain + ws->sendbuf_payload);
       result = CURLE_BAD_FUNCTION_ARGUMENT;
@@ -1255,11 +1470,15 @@ CURL_EXTERN CURLcode curl_ws_send(CURL *d, const void *buffer_arg,
       if(n < 0 && (result != CURLE_AGAIN))
         goto out;
       ws->sendbuf_payload += Curl_bufq_len(&ws->sendbuf) - prev_len;
+      if(!ws->sendbuf_payload) {
+        result = CURLE_AGAIN;
+        goto out;
+      }
     }
 
     /* flush, blocking when in callback */
     result = ws_flush(data, ws, Curl_is_in_callback(data));
-    if(!result) {
+    if(!result && ws->sendbuf_payload > 0) {
       *sent += ws->sendbuf_payload;
       buffer += ws->sendbuf_payload;
       buflen -= ws->sendbuf_payload;
@@ -1296,42 +1515,31 @@ CURL_EXTERN CURLcode curl_ws_send(CURL *d, const void *buffer_arg,
   return result;
 }
 
-static void ws_free(struct connectdata *conn)
-{
-  if(conn && conn->proto.ws) {
-    Curl_bufq_free(&conn->proto.ws->recvbuf);
-    Curl_bufq_free(&conn->proto.ws->sendbuf);
-    Curl_safefree(conn->proto.ws);
-  }
-}
-
 static CURLcode ws_setup_conn(struct Curl_easy *data,
                               struct connectdata *conn)
 {
   /* WebSockets is 1.1 only (for now) */
-  data->state.httpwant = CURL_HTTP_VERSION_1_1;
+  data->state.http_neg.accept_09 = FALSE;
+  data->state.http_neg.only_10 = FALSE;
+  data->state.http_neg.wanted = CURL_HTTP_V1x;
+  data->state.http_neg.allowed = CURL_HTTP_V1x;
   return Curl_http_setup_conn(data, conn);
 }
 
 
-static CURLcode ws_disconnect(struct Curl_easy *data,
-                              struct connectdata *conn,
-                              bool dead_connection)
-{
-  (void)data;
-  (void)dead_connection;
-  ws_free(conn);
-  return CURLE_OK;
-}
-
 CURL_EXTERN const struct curl_ws_frame *curl_ws_meta(CURL *d)
 {
   /* we only return something for websocket, called from within the callback
      when not using raw mode */
   struct Curl_easy *data = d;
-  if(GOOD_EASY_HANDLE(data) && Curl_is_in_callback(data) && data->conn &&
-     data->conn->proto.ws && !data->set.ws_raw_mode)
-    return &data->conn->proto.ws->frame;
+  if(GOOD_EASY_HANDLE(data) && Curl_is_in_callback(data) &&
+     data->conn && !data->set.ws_raw_mode) {
+    struct websocket *ws;
+    ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN);
+    if(ws)
+      return &ws->frame;
+
+  }
   return NULL;
 }
 
@@ -1348,7 +1556,7 @@ const struct Curl_handler Curl_handler_ws = {
   Curl_http_getsock_do,                 /* doing_getsock */
   ZERO_NULL,                            /* domore_getsock */
   ZERO_NULL,                            /* perform_getsock */
-  ws_disconnect,                        /* disconnect */
+  ZERO_NULL,                            /* disconnect */
   Curl_http_write_resp,                 /* write_resp */
   Curl_http_write_resp_hd,              /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
@@ -1375,7 +1583,7 @@ const struct Curl_handler Curl_handler_wss = {
   Curl_http_getsock_do,                 /* doing_getsock */
   ZERO_NULL,                            /* domore_getsock */
   ZERO_NULL,                            /* perform_getsock */
-  ws_disconnect,                        /* disconnect */
+  ZERO_NULL,                            /* disconnect */
   Curl_http_write_resp,                 /* write_resp */
   Curl_http_write_resp_hd,              /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
diff --git a/extra/curl/curl-8.14.1/lib/ws.h b/extra/curl/curl-8.14.1/lib/ws.h
new file mode 100644
index 000000000000..b7655abbce74
--- /dev/null
+++ b/extra/curl/curl-8.14.1/lib/ws.h
@@ -0,0 +1,47 @@
+#ifndef HEADER_CURL_WS_H
+#define HEADER_CURL_WS_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "curl_setup.h"
+
+#if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP)
+
+/* meta key for storing protocol meta at connection */
+#define CURL_META_PROTO_WS_CONN   "meta:proto:ws:conn"
+
+CURLcode Curl_ws_request(struct Curl_easy *data, struct dynbuf *req);
+CURLcode Curl_ws_accept(struct Curl_easy *data, const char *mem, size_t len);
+
+extern const struct Curl_handler Curl_handler_ws;
+#ifdef USE_SSL
+extern const struct Curl_handler Curl_handler_wss;
+#endif
+
+
+#else
+#define Curl_ws_request(x,y) CURLE_OK
+#define Curl_ws_free(x) Curl_nop_stmt
+#endif
+
+#endif /* HEADER_CURL_WS_H */
diff --git a/extra/curl/curl-8.12.1/libcurl.pc.in b/extra/curl/curl-8.14.1/libcurl.pc.in
similarity index 100%
rename from extra/curl/curl-8.12.1/libcurl.pc.in
rename to extra/curl/curl-8.14.1/libcurl.pc.in
diff --git a/extra/curl/curl-8.12.1/missing b/extra/curl/curl-8.14.1/missing
similarity index 100%
rename from extra/curl/curl-8.12.1/missing
rename to extra/curl/curl-8.14.1/missing
diff --git a/extra/curl/curl-8.14.1/scripts/CMakeLists.txt b/extra/curl/curl-8.14.1/scripts/CMakeLists.txt
new file mode 100644
index 000000000000..dd590c910fc7
--- /dev/null
+++ b/extra/curl/curl-8.14.1/scripts/CMakeLists.txt
@@ -0,0 +1,78 @@
+#***************************************************************************
+#                                  _   _ ____  _
+#  Project                     ___| | | |  _ \| |
+#                             / __| | | | |_) | |
+#                            | (__| |_| |  _ <| |___
+#                             \___|\___/|_| \_\_____|
+#
+# Copyright (C) Viktor Szakats, et al.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at https://curl.se/docs/copyright.html.
+#
+# You may opt to use, copy, modify, merge, publish, distribute and/or sell
+# copies of the Software, and permit persons to whom the Software is
+# furnished to do so, under the terms of the COPYING file.
+#
+# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+# KIND, either express or implied.
+#
+# SPDX-License-Identifier: curl
+#
+###########################################################################
+
+option(CURL_COMPLETION_FISH "Install fish completions" OFF)
+option(CURL_COMPLETION_ZSH "Install zsh completions" OFF)
+
+if(CURL_COMPLETION_FISH OR
+   CURL_COMPLETION_ZSH)
+  if(PERL_FOUND)
+    if(CURL_COMPLETION_FISH)
+      set(_completion_fish "${CMAKE_CURRENT_BINARY_DIR}/curl.fish")
+      add_custom_command(OUTPUT "${_completion_fish}"
+        COMMAND "${PERL_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/completion.pl"
+          --opts-dir "${PROJECT_SOURCE_DIR}/docs/cmdline-opts" --shell fish > "${_completion_fish}"
+        DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/completion.pl"
+        VERBATIM
+      )
+      add_custom_target("curl-completion-fish" ALL DEPENDS "${_completion_fish}")
+    endif()
+
+    if(CURL_COMPLETION_ZSH)
+      set(_completion_zsh "${CMAKE_CURRENT_BINARY_DIR}/_curl")
+      add_custom_command(OUTPUT "${_completion_zsh}"
+        COMMAND "${PERL_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/completion.pl"
+          --opts-dir "${PROJECT_SOURCE_DIR}/docs/cmdline-opts" --shell zsh > "${_completion_zsh}"
+        DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/completion.pl"
+        VERBATIM
+      )
+      add_custom_target("curl-completion-zsh" ALL DEPENDS "${_completion_zsh}")
+    endif()
+
+    if(NOT CURL_DISABLE_INSTALL)
+      if(CURL_COMPLETION_FISH)
+        if(NOT CURL_COMPLETION_FISH_DIR AND NOT CMAKE_CROSSCOMPILING AND NOT DEFINED CMAKE_INSTALL_PREFIX)
+          find_package(PkgConfig QUIET)
+          pkg_get_variable(CURL_COMPLETION_FISH_DIR "fish" "completionsdir")
+        endif()
+        if(NOT CURL_COMPLETION_FISH_DIR AND CMAKE_INSTALL_DATAROOTDIR)
+          set(CURL_COMPLETION_FISH_DIR "${CMAKE_INSTALL_DATAROOTDIR}/fish/vendor_completions.d")
+        endif()
+        if(CURL_COMPLETION_FISH_DIR)
+          install(FILES "${_completion_fish}" DESTINATION "${CURL_COMPLETION_FISH_DIR}")
+        endif()
+      endif()
+      if(CURL_COMPLETION_ZSH)
+        if(NOT CURL_COMPLETION_ZSH_DIR AND CMAKE_INSTALL_DATAROOTDIR)
+          set(CURL_COMPLETION_ZSH_DIR "${CMAKE_INSTALL_DATAROOTDIR}/zsh/site-functions")
+        endif()
+        if(CURL_COMPLETION_ZSH_DIR)
+          install(FILES "${_completion_zsh}" DESTINATION "${CURL_COMPLETION_ZSH_DIR}")
+        endif()
+      endif()
+    endif()
+  else()
+    message(WARNING "No perl: cannot generate completion script")
+  endif()
+endif()
diff --git a/extra/curl/curl-8.12.1/scripts/Makefile.am b/extra/curl/curl-8.14.1/scripts/Makefile.am
similarity index 66%
rename from extra/curl/curl-8.12.1/scripts/Makefile.am
rename to extra/curl/curl-8.14.1/scripts/Makefile.am
index b29c99d89268..0b278e62b01f 100644
--- a/extra/curl/curl-8.12.1/scripts/Makefile.am
+++ b/extra/curl/curl-8.14.1/scripts/Makefile.am
@@ -22,9 +22,12 @@
 #
 ###########################################################################
 
-EXTRA_DIST = coverage.sh completion.pl firefox-db2pem.sh checksrc.pl    \
- mk-ca-bundle.pl mk-unity.pl schemetable.c cd2nroff nroff2cd cdall cd2cd managen \
- dmaketgz maketgz release-tools.sh verify-release cmakelint.sh mdlinkcheck
+EXTRA_DIST = coverage.sh completion.pl firefox-db2pem.sh checksrc.pl checksrc-all.sh \
+  mk-ca-bundle.pl mk-unity.pl schemetable.c cd2nroff nroff2cd cdall cd2cd managen    \
+  dmaketgz maketgz release-tools.sh verify-release cmakelint.sh mdlinkcheck          \
+  CMakeLists.txt pythonlint.sh randdisable wcurl top-complexity
+
+dist_bin_SCRIPTS = wcurl
 
 ZSH_FUNCTIONS_DIR = @ZSH_FUNCTIONS_DIR@
 FISH_FUNCTIONS_DIR = @FISH_FUNCTIONS_DIR@
@@ -43,41 +46,29 @@ all-local: $(ZSH_COMPLETION_FUNCTION_FILENAME) $(FISH_COMPLETION_FUNCTION_FILENA
 
 if USE_ZSH_COMPLETION
 $(ZSH_COMPLETION_FUNCTION_FILENAME): completion.pl
-if CROSSCOMPILING
-	@echo "NOTICE: we can't generate zsh completion when cross-compiling!"
-else # if not cross-compiling:
-	if test -z "$(PERL)"; then echo "No perl: can't install completion script"; else \
-	$(PERL) $(srcdir)/completion.pl --curl $(top_builddir)/src/curl$(EXEEXT) --shell zsh > $@ ; fi
-endif
+	if test -z "$(PERL)"; then echo 'No perl: cannot generate completion script'; else \
+	$(PERL) $(srcdir)/completion.pl --opts-dir $(top_srcdir)/docs/cmdline-opts --shell zsh > $@; fi
 endif
 
 if USE_FISH_COMPLETION
 $(FISH_COMPLETION_FUNCTION_FILENAME): completion.pl
-if CROSSCOMPILING
-	@echo "NOTICE: we can't generate fish completion when cross-compiling!"
-else # if not cross-compiling:
-	if test -z "$(PERL)"; then echo "No perl: can't install completion script"; else \
-	$(PERL) $(srcdir)/completion.pl --curl $(top_builddir)/src/curl$(EXEEXT) --shell fish > $@ ; fi
-endif
+	if test -z "$(PERL)"; then echo 'No perl: cannot generate completion script'; else \
+	$(PERL) $(srcdir)/completion.pl --opts-dir $(top_srcdir)/docs/cmdline-opts --shell fish > $@; fi
 endif
 
 install-data-local:
-if CROSSCOMPILING
-	@echo "NOTICE: we can't install completion scripts when cross-compiling!"
-else # if not cross-compiling:
 if USE_ZSH_COMPLETION
 	if test -n "$(PERL)"; then \
 	  $(MKDIR_P) $(DESTDIR)$(ZSH_FUNCTIONS_DIR); \
-	  $(INSTALL_DATA) $(ZSH_COMPLETION_FUNCTION_FILENAME) $(DESTDIR)$(ZSH_FUNCTIONS_DIR)/$(ZSH_COMPLETION_FUNCTION_FILENAME) ; \
+	  $(INSTALL_DATA) $(ZSH_COMPLETION_FUNCTION_FILENAME) $(DESTDIR)$(ZSH_FUNCTIONS_DIR)/$(ZSH_COMPLETION_FUNCTION_FILENAME); \
 	fi
 endif
 if USE_FISH_COMPLETION
 	if test -n "$(PERL)"; then \
 	  $(MKDIR_P) $(DESTDIR)$(FISH_FUNCTIONS_DIR); \
-	  $(INSTALL_DATA) $(FISH_COMPLETION_FUNCTION_FILENAME) $(DESTDIR)$(FISH_FUNCTIONS_DIR)/$(FISH_COMPLETION_FUNCTION_FILENAME) ; \
+	  $(INSTALL_DATA) $(FISH_COMPLETION_FUNCTION_FILENAME) $(DESTDIR)$(FISH_FUNCTIONS_DIR)/$(FISH_COMPLETION_FUNCTION_FILENAME); \
 	fi
 endif
-endif
 
 distclean:
 	rm -f $(CLEANFILES)
diff --git a/extra/curl/curl-8.12.1/scripts/Makefile.in b/extra/curl/curl-8.14.1/scripts/Makefile.in
similarity index 76%
rename from extra/curl/curl-8.12.1/scripts/Makefile.in
rename to extra/curl/curl-8.14.1/scripts/Makefile.in
index 68615119d209..cee04b022e13 100644
--- a/extra/curl/curl-8.12.1/scripts/Makefile.in
+++ b/extra/curl/curl-8.14.1/scripts/Makefile.in
@@ -37,6 +37,7 @@
 # SPDX-License-Identifier: curl
 #
 ###########################################################################
+
 VPATH = @srcdir@
 am__is_gnu_make = { \
   if test -z '$(MAKELEVEL)'; then \
@@ -140,11 +141,41 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/curl-amissl.m4 \
 	$(top_srcdir)/configure.ac
 am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
 	$(ACLOCAL_M4)
-DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+DIST_COMMON = $(srcdir)/Makefile.am $(dist_bin_SCRIPTS) \
+	$(am__DIST_COMMON)
 mkinstalldirs = $(install_sh) -d
 CONFIG_HEADER = $(top_builddir)/lib/curl_config.h
 CONFIG_CLEAN_FILES =
 CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+    $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+    *) f=$$p;; \
+  esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+  srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+  for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+  for p in $$list; do echo "$$p $$p"; done | \
+  sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+  $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+    if (++n[$$2] == $(am__install_max)) \
+      { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+    END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+  sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+  sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+  test -z "$$files" \
+    || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+    || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+         $(am__cd) "$$dir" && rm -f $$files; }; \
+  }
+am__installdirs = "$(DESTDIR)$(bindir)"
+SCRIPTS = $(dist_bin_SCRIPTS)
 AM_V_P = $(am__v_P_@AM_V@)
 am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
 am__v_P_0 = false
@@ -330,10 +361,12 @@ target_alias = @target_alias@
 top_build_prefix = @top_build_prefix@
 top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
-EXTRA_DIST = coverage.sh completion.pl firefox-db2pem.sh checksrc.pl    \
- mk-ca-bundle.pl mk-unity.pl schemetable.c cd2nroff nroff2cd cdall cd2cd managen \
- dmaketgz maketgz release-tools.sh verify-release cmakelint.sh mdlinkcheck
+EXTRA_DIST = coverage.sh completion.pl firefox-db2pem.sh checksrc.pl checksrc-all.sh \
+  mk-ca-bundle.pl mk-unity.pl schemetable.c cd2nroff nroff2cd cdall cd2cd managen    \
+  dmaketgz maketgz release-tools.sh verify-release cmakelint.sh mdlinkcheck          \
+  CMakeLists.txt pythonlint.sh randdisable wcurl top-complexity
 
+dist_bin_SCRIPTS = wcurl
 @USE_ZSH_COMPLETION_TRUE@ZSH_COMPLETION_FUNCTION_FILENAME = _curl
 @USE_FISH_COMPLETION_TRUE@FISH_COMPLETION_FUNCTION_FILENAME = curl.fish
 CLEANFILES = $(ZSH_COMPLETION_FUNCTION_FILENAME) $(FISH_COMPLETION_FUNCTION_FILENAME)
@@ -369,6 +402,41 @@ $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
 $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
 	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
 $(am__aclocal_m4_deps):
+install-dist_binSCRIPTS: $(dist_bin_SCRIPTS)
+	@$(NORMAL_INSTALL)
+	@list='$(dist_bin_SCRIPTS)'; test -n "$(bindir)" || list=; \
+	if test -n "$$list"; then \
+	  echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \
+	  $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \
+	fi; \
+	for p in $$list; do \
+	  if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+	  if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \
+	done | \
+	sed -e 'p;s,.*/,,;n' \
+	    -e 'h;s|.*|.|' \
+	    -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \
+	$(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \
+	  { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+	    if ($$2 == $$4) { files[d] = files[d] " " $$1; \
+	      if (++n[d] == $(am__install_max)) { \
+		print "f", d, files[d]; n[d] = 0; files[d] = "" } } \
+	    else { print "f", d "/" $$4, $$1 } } \
+	  END { for (d in files) print "f", d, files[d] }' | \
+	while read type dir files; do \
+	     if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+	     test -z "$$files" || { \
+	       echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(bindir)$$dir'"; \
+	       $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
+	     } \
+	; done
+
+uninstall-dist_binSCRIPTS:
+	@$(NORMAL_UNINSTALL)
+	@list='$(dist_bin_SCRIPTS)'; test -n "$(bindir)" || exit 0; \
+	files=`for p in $$list; do echo "$$p"; done | \
+	       sed -e 's,.*/,,;$(transform)'`; \
+	dir='$(DESTDIR)$(bindir)'; $(am__uninstall_files_from_dir)
 
 mostlyclean-libtool:
 	-rm -f *.lo
@@ -416,8 +484,11 @@ distdir-am: $(DISTFILES)
 	done
 check-am: all-am
 check: check-am
-all-am: Makefile all-local
+all-am: Makefile $(SCRIPTS) all-local
 installdirs:
+	for dir in "$(DESTDIR)$(bindir)"; do \
+	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+	done
 install: install-am
 install-exec: install-exec-am
 install-data: install-data-am
@@ -473,7 +544,7 @@ install-dvi: install-dvi-am
 
 install-dvi-am:
 
-install-exec-am:
+install-exec-am: install-dist_binSCRIPTS
 
 install-html: install-html-am
 
@@ -511,7 +582,7 @@ ps: ps-am
 
 ps-am:
 
-uninstall-am:
+uninstall-am: uninstall-dist_binSCRIPTS
 
 .MAKE: install-am install-strip
 
@@ -519,14 +590,15 @@ uninstall-am:
 	clean-libtool cscopelist-am ctags-am distclean \
 	distclean-generic distclean-libtool distdir dvi dvi-am html \
 	html-am info info-am install install-am install-data \
-	install-data-am install-data-local install-dvi install-dvi-am \
-	install-exec install-exec-am install-html install-html-am \
-	install-info install-info-am install-man install-pdf \
-	install-pdf-am install-ps install-ps-am install-strip \
-	installcheck installcheck-am installdirs maintainer-clean \
-	maintainer-clean-generic mostlyclean mostlyclean-generic \
-	mostlyclean-libtool pdf pdf-am ps ps-am tags-am uninstall \
-	uninstall-am
+	install-data-am install-data-local install-dist_binSCRIPTS \
+	install-dvi install-dvi-am install-exec install-exec-am \
+	install-html install-html-am install-info install-info-am \
+	install-man install-pdf install-pdf-am install-ps \
+	install-ps-am install-strip installcheck installcheck-am \
+	installdirs maintainer-clean maintainer-clean-generic \
+	mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \
+	ps ps-am tags-am uninstall uninstall-am \
+	uninstall-dist_binSCRIPTS
 
 .PRECIOUS: Makefile
 
@@ -534,25 +606,22 @@ uninstall-am:
 all-local: $(ZSH_COMPLETION_FUNCTION_FILENAME) $(FISH_COMPLETION_FUNCTION_FILENAME)
 
 @USE_ZSH_COMPLETION_TRUE@$(ZSH_COMPLETION_FUNCTION_FILENAME): completion.pl
-@CROSSCOMPILING_TRUE@@USE_ZSH_COMPLETION_TRUE@	@echo "NOTICE: we can't generate zsh completion when cross-compiling!"
-@CROSSCOMPILING_FALSE@@USE_ZSH_COMPLETION_TRUE@	if test -z "$(PERL)"; then echo "No perl: can't install completion script"; else \
-@CROSSCOMPILING_FALSE@@USE_ZSH_COMPLETION_TRUE@	$(PERL) $(srcdir)/completion.pl --curl $(top_builddir)/src/curl$(EXEEXT) --shell zsh > $@ ; fi
+@USE_ZSH_COMPLETION_TRUE@	if test -z "$(PERL)"; then echo 'No perl: cannot generate completion script'; else \
+@USE_ZSH_COMPLETION_TRUE@	$(PERL) $(srcdir)/completion.pl --opts-dir $(top_srcdir)/docs/cmdline-opts --shell zsh > $@; fi
 
 @USE_FISH_COMPLETION_TRUE@$(FISH_COMPLETION_FUNCTION_FILENAME): completion.pl
-@CROSSCOMPILING_TRUE@@USE_FISH_COMPLETION_TRUE@	@echo "NOTICE: we can't generate fish completion when cross-compiling!"
-@CROSSCOMPILING_FALSE@@USE_FISH_COMPLETION_TRUE@	if test -z "$(PERL)"; then echo "No perl: can't install completion script"; else \
-@CROSSCOMPILING_FALSE@@USE_FISH_COMPLETION_TRUE@	$(PERL) $(srcdir)/completion.pl --curl $(top_builddir)/src/curl$(EXEEXT) --shell fish > $@ ; fi
+@USE_FISH_COMPLETION_TRUE@	if test -z "$(PERL)"; then echo 'No perl: cannot generate completion script'; else \
+@USE_FISH_COMPLETION_TRUE@	$(PERL) $(srcdir)/completion.pl --opts-dir $(top_srcdir)/docs/cmdline-opts --shell fish > $@; fi
 
 install-data-local:
-@CROSSCOMPILING_TRUE@	@echo "NOTICE: we can't install completion scripts when cross-compiling!"
-@CROSSCOMPILING_FALSE@@USE_ZSH_COMPLETION_TRUE@	if test -n "$(PERL)"; then \
-@CROSSCOMPILING_FALSE@@USE_ZSH_COMPLETION_TRUE@	  $(MKDIR_P) $(DESTDIR)$(ZSH_FUNCTIONS_DIR); \
-@CROSSCOMPILING_FALSE@@USE_ZSH_COMPLETION_TRUE@	  $(INSTALL_DATA) $(ZSH_COMPLETION_FUNCTION_FILENAME) $(DESTDIR)$(ZSH_FUNCTIONS_DIR)/$(ZSH_COMPLETION_FUNCTION_FILENAME) ; \
-@CROSSCOMPILING_FALSE@@USE_ZSH_COMPLETION_TRUE@	fi
-@CROSSCOMPILING_FALSE@@USE_FISH_COMPLETION_TRUE@	if test -n "$(PERL)"; then \
-@CROSSCOMPILING_FALSE@@USE_FISH_COMPLETION_TRUE@	  $(MKDIR_P) $(DESTDIR)$(FISH_FUNCTIONS_DIR); \
-@CROSSCOMPILING_FALSE@@USE_FISH_COMPLETION_TRUE@	  $(INSTALL_DATA) $(FISH_COMPLETION_FUNCTION_FILENAME) $(DESTDIR)$(FISH_FUNCTIONS_DIR)/$(FISH_COMPLETION_FUNCTION_FILENAME) ; \
-@CROSSCOMPILING_FALSE@@USE_FISH_COMPLETION_TRUE@	fi
+@USE_ZSH_COMPLETION_TRUE@	if test -n "$(PERL)"; then \
+@USE_ZSH_COMPLETION_TRUE@	  $(MKDIR_P) $(DESTDIR)$(ZSH_FUNCTIONS_DIR); \
+@USE_ZSH_COMPLETION_TRUE@	  $(INSTALL_DATA) $(ZSH_COMPLETION_FUNCTION_FILENAME) $(DESTDIR)$(ZSH_FUNCTIONS_DIR)/$(ZSH_COMPLETION_FUNCTION_FILENAME); \
+@USE_ZSH_COMPLETION_TRUE@	fi
+@USE_FISH_COMPLETION_TRUE@	if test -n "$(PERL)"; then \
+@USE_FISH_COMPLETION_TRUE@	  $(MKDIR_P) $(DESTDIR)$(FISH_FUNCTIONS_DIR); \
+@USE_FISH_COMPLETION_TRUE@	  $(INSTALL_DATA) $(FISH_COMPLETION_FUNCTION_FILENAME) $(DESTDIR)$(FISH_FUNCTIONS_DIR)/$(FISH_COMPLETION_FUNCTION_FILENAME); \
+@USE_FISH_COMPLETION_TRUE@	fi
 
 distclean:
 	rm -f $(CLEANFILES)
diff --git a/extra/curl/curl-8.12.1/scripts/cd2cd b/extra/curl/curl-8.14.1/scripts/cd2cd
similarity index 100%
rename from extra/curl/curl-8.12.1/scripts/cd2cd
rename to extra/curl/curl-8.14.1/scripts/cd2cd
diff --git a/extra/curl/curl-8.12.1/scripts/cd2nroff b/extra/curl/curl-8.14.1/scripts/cd2nroff
similarity index 100%
rename from extra/curl/curl-8.12.1/scripts/cd2nroff
rename to extra/curl/curl-8.14.1/scripts/cd2nroff
diff --git a/extra/curl/curl-8.12.1/scripts/cdall b/extra/curl/curl-8.14.1/scripts/cdall
similarity index 100%
rename from extra/curl/curl-8.12.1/scripts/cdall
rename to extra/curl/curl-8.14.1/scripts/cdall
diff --git a/extra/curl/curl-8.14.1/scripts/checksrc-all.sh b/extra/curl/curl-8.14.1/scripts/checksrc-all.sh
new file mode 100755
index 000000000000..2c4b3c5e28e9
--- /dev/null
+++ b/extra/curl/curl-8.14.1/scripts/checksrc-all.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+# Copyright (C) Viktor Szakats
+#
+# SPDX-License-Identifier: curl
+
+set -eu
+
+anyfailed=0
+
+for dir in $({
+    if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
+      git ls-files '*.[ch]'
+    else
+      find . -name '*.[ch]'
+    fi
+    [ -n "${1:-}" ] && find "$@" -name '*.[ch]'
+  } | grep -v -F '/CMakeFiles/' | sed -E 's|/[^/]+$||' | sort -u); do
+  if ! ./scripts/checksrc.pl "${dir}"/*.[ch]; then
+    anyfailed=1
+  fi
+done
+
+exit "${anyfailed}"
diff --git a/extra/curl/curl-8.12.1/scripts/checksrc.pl b/extra/curl/curl-8.14.1/scripts/checksrc.pl
similarity index 96%
rename from extra/curl/curl-8.12.1/scripts/checksrc.pl
rename to extra/curl/curl-8.14.1/scripts/checksrc.pl
index 8289c14c5c6e..f62962ff1aca 100755
--- a/extra/curl/curl-8.12.1/scripts/checksrc.pl
+++ b/extra/curl/curl-8.14.1/scripts/checksrc.pl
@@ -39,7 +39,7 @@
 my $wlist="";
 my @alist;
 my $windows_os = $^O eq 'MSWin32' || $^O eq 'cygwin' || $^O eq 'msys';
-my $verbose;
+my $verbose = 0;
 my %skiplist;
 
 my %ignore;
@@ -97,6 +97,7 @@
     'EMPTYLINEBRACE'        => 'Empty line before the open brace',
     'EQUALSNOSPACE'         => 'equals sign without following space',
     'EQUALSNULL'            => 'if/while comparison with == NULL',
+    'ERRNOVAR'              => 'use of bare errno define',
     'EXCLAMATIONSPACE'      => 'Whitespace after exclamation mark in expression',
     'FOPENMODE'             => 'fopen needs a macro for the mode string',
     'INCLUDEDUP',           => 'same file is included again',
@@ -287,6 +288,11 @@ sub checkwarn {
         $file = shift @ARGV;
         next;
     }
+    elsif($file =~ /^-v/) {
+        $verbose = 1;
+        $file = shift @ARGV;
+        next;
+    }
     elsif($file =~ /^(-h|--help)/) {
         undef $file;
         last;
@@ -306,6 +312,7 @@ sub checkwarn {
     print "  -W[file]  Skip the given file - ignore all its flaws\n";
     print "  -i<n>     Indent spaces. Default: 2\n";
     print "  -m<n>     Maximum line length. Default: 79\n";
+    print "  -v        Verbose\n";
     print "\nDetects and warns for these problems:\n";
     my @allw = keys %warnings;
     push @allw, keys %warnings_extended;
@@ -447,6 +454,11 @@ sub scanfile {
     my $l = "";
     my $prep = 0;
     my $prevp = 0;
+
+    if($verbose) {
+        printf "Checking file: $file\n";
+    }
+
     open(my $R, '<', $file) || die "failed to open $file";
 
     my $incomment=0;
@@ -479,10 +491,10 @@ sub scanfile {
             my $count = 0;
             while($l =~ /([\d]{4})/g) {
                 push @copyright, {
-                  year => $1,
-                  line => $line,
-                  col => index($l, $1),
-                  code => $l
+                    year => $1,
+                    line => $line,
+                    col => index($l, $1),
+                    code => $l
                 };
                 $count++;
             }
@@ -873,7 +885,7 @@ sub scanfile {
             $suff =~ s/\(/\\(/;
             $l =~ s/$prefix$bad$suff/$prefix$replace/;
             goto again;
-      }
+        }
         $l = $bl; # restore to pre-bannedfunc content
 
         if($warnings{"STDERR"}) {
@@ -1022,6 +1034,12 @@ sub scanfile {
                       "space after exclamation mark");
         }
 
+        if($nostr =~ /(.*)\b(EACCES|EADDRINUSE|EADDRNOTAVAIL|EAFNOSUPPORT|EBADF|ECONNREFUSED|ECONNRESET|EINPROGRESS|EINTR|EINVAL|EISCONN|EMSGSIZE|ENOMEM|ETIMEDOUT|EWOULDBLOCK)\b/) {
+            checkwarn("ERRNOVAR",
+                      $line, length($1), $file, $ol,
+                      "use of bare errno define $2, use SOCK$2");
+        }
+
         # check for more than one consecutive space before open brace or
         # question mark. Skip lines containing strings since they make it hard
         # due to artificially getting multiple spaces
@@ -1033,16 +1051,16 @@ sub scanfile {
         }
       preproc:
         if($prep) {
-          # scan for use of banned symbols on a preprocessor line
-          if($l =~ /^(^|.*\W)
-                     (WIN32)
-                     (\W|$)
-                   /x) {
-              checkwarn("BANNEDPREPROC",
-                        $line, length($1), $file, $ol,
-                        "use of $2 is banned from preprocessor lines" .
-                        (($2 eq "WIN32") ? ", use _WIN32 instead" : ""));
-          }
+            # scan for use of banned symbols on a preprocessor line
+            if($l =~ /^(^|.*\W)
+                       (WIN32)
+                       (\W|$)
+                     /x) {
+                checkwarn("BANNEDPREPROC",
+                          $line, length($1), $file, $ol,
+                          "use of $2 is banned from preprocessor lines" .
+                          (($2 eq "WIN32") ? ", use _WIN32 instead" : ""));
+            }
         }
         $line++;
         $prevp = $prep;
@@ -1116,5 +1134,7 @@ sub scanfile {
         $serrors,
         $swarnings;
     }
-    exit 5; # return failure
+    if($errors || $warnings) {
+        exit 5; # return failure
+    }
 }
diff --git a/extra/curl/curl-8.12.1/scripts/cmakelint.sh b/extra/curl/curl-8.14.1/scripts/cmakelint.sh
similarity index 100%
rename from extra/curl/curl-8.12.1/scripts/cmakelint.sh
rename to extra/curl/curl-8.14.1/scripts/cmakelint.sh
diff --git a/extra/curl/curl-8.12.1/scripts/completion.pl b/extra/curl/curl-8.14.1/scripts/completion.pl
similarity index 69%
rename from extra/curl/curl-8.12.1/scripts/completion.pl
rename to extra/curl/curl-8.14.1/scripts/completion.pl
index dd0c81d35b80..a7cf97bdad91 100755
--- a/extra/curl/curl-8.12.1/scripts/completion.pl
+++ b/extra/curl/curl-8.14.1/scripts/completion.pl
@@ -28,18 +28,17 @@
 use Getopt::Long();
 use Pod::Usage();
 
-my $curl = 'curl';
+my $opts_dir = '../docs/cmdline-opts';
 my $shell = 'zsh';
 my $help = 0;
 Getopt::Long::GetOptions(
-    'curl=s' => \$curl,
+    'opts-dir=s' => \$opts_dir,
     'shell=s' => \$shell,
     'help' => \$help,
 ) or Pod::Usage::pod2usage();
 Pod::Usage::pod2usage() if $help;
 
-my $regex = '\s+(?:(-[^\s]+),\s)?(--[^\s]+)\s*(\<.+?\>)?\s+(.*)';
-my @opts = parse_main_opts('--help all', $regex);
+my @opts = parse_main_opts($opts_dir);
 
 if ($shell eq 'fish') {
     print "# curl fish completion\n\n";
@@ -53,7 +52,7 @@
     $opts_str .= qq{  $_ \\\n} foreach (@opts);
     chomp $opts_str;
 
-my $tmpl = <<"EOS";
+    my $tmpl = <<"EOS";
 #compdef curl
 
 # curl zsh completion
@@ -76,23 +75,43 @@
 }
 
 sub parse_main_opts {
-    my ($cmd, $regex) = @_;
+    my ($opts_dir) = @_;
 
-    my @list;
-    my @lines = call_curl($cmd);
+    my (@files, @list);
+    my ($dir_handle, $file_content);
 
-    foreach my $line (@lines) {
-        my ($short, $long, $arg, $desc) = ($line =~ /^$regex/) or next;
+    opendir($dir_handle, $opts_dir) || die "Unable to open dir: $opts_dir due to error: $!";
+    @files = readdir($dir_handle);
+    closedir($dir_handle) || die "Unable to close handle on dir: $opts_dir due to error: $!";
 
-        my $option = '';
+    # We want regular files that end with .md and don't start with an underscore
+    # Edge case: MANPAGE.md doesn't start with an underscore but also isn't documentation for an option
+    @files = grep { $_ =~ /\.md$/i && !/^_/ && -f "$opts_dir/$_" && $_ ne "MANPAGE.md" } @files;
 
-        $arg =~ s/\:/\\\:/g if defined $arg;
+    for my $file (@files) {
+        open(my $doc_handle, '<', "$opts_dir/$file") || die "Unable to open file: $file due to error: $!";
+        $file_content = join('', <$doc_handle>);
+        close($doc_handle) || die "Unable to close file: $file due to error: $!";
+
+        # Extract the curldown header section demarcated by ---
+        $file_content =~ /^---\s*\n(.*?)\n---\s*\n/s || die "Unable to parse file $file";
 
+        $file_content = $1;
+        my ($short, $long, $arg, $desc);
+
+        if ($file_content =~ /^Short:\s+(.*)\s*$/im) {$short = "-$1";}
+        if ($file_content =~ /^Long:\s+(.*)\s*$/im) {$long = "--$1";}
+        if ($file_content =~ /^Arg:\s+(.*)\s*$/im) {$arg = $1;}
+        if ($file_content =~ /^Help:\s+(.*)\s*$/im) {$desc = $1;}
+
+        $arg =~ s/\:/\\\:/g if defined $arg;
         $desc =~ s/'/'\\''/g if defined $desc;
         $desc =~ s/\[/\\\[/g if defined $desc;
         $desc =~ s/\]/\\\]/g if defined $desc;
         $desc =~ s/\:/\\\:/g if defined $desc;
 
+        my $option = '';
+
         if ($shell eq 'fish') {
             $option .= "complete --command curl";
             $option .= " --short-option '" . strip_dash(trim($short)) . "'"
@@ -123,17 +142,18 @@ sub parse_main_opts {
             }
         }
 
-        push @list, $option;
+        push(@list, $option);
     }
 
     # Sort longest first, because zsh won't complete an option listed
-    # after one that's a prefix of it.
+    # after one that's a prefix of it. When length is equal, fall back
+    # to stringwise cmp.
     @list = sort {
         $a =~ /([^=]*)/; my $ma = $1;
         $b =~ /([^=]*)/; my $mb = $1;
 
-        length($mb) <=> length($ma)
-    } @list if $shell eq 'zsh';
+        length($mb) <=> length($ma) || $ma cmp $mb
+    } @list;
 
     return @list;
 }
@@ -141,17 +161,6 @@ sub parse_main_opts {
 sub trim { my $s = shift; $s =~ s/^\s+|\s+$//g; return $s };
 sub strip_dash { my $s = shift; $s =~ s/^-+//g; return $s };
 
-sub call_curl {
-    my ($cmd) = @_;
-    my $output = `"$curl" $cmd`;
-    if ($? == -1) {
-        die "Could not run curl: $!";
-    } elsif ((my $exit_code = $? >> 8) != 0) {
-        die "curl returned $exit_code with output:\n$output";
-    }
-    return split /\n/, $output;
-}
-
 __END__
 
 =head1 NAME
@@ -162,8 +171,8 @@ =head1 SYNOPSIS
 
 completion.pl [options...]
 
-    --curl   path to curl executable
-    --shell  zsh/fish
-    --help   prints this help
+    --opts-dir path to cmdline-opts directory
+    --shell    zsh/fish
+    --help     prints this help
 
 =cut
diff --git a/extra/curl/curl-8.12.1/scripts/coverage.sh b/extra/curl/curl-8.14.1/scripts/coverage.sh
similarity index 100%
rename from extra/curl/curl-8.12.1/scripts/coverage.sh
rename to extra/curl/curl-8.14.1/scripts/coverage.sh
diff --git a/extra/curl/curl-8.12.1/scripts/dmaketgz b/extra/curl/curl-8.14.1/scripts/dmaketgz
similarity index 100%
rename from extra/curl/curl-8.12.1/scripts/dmaketgz
rename to extra/curl/curl-8.14.1/scripts/dmaketgz
diff --git a/extra/curl/curl-8.12.1/scripts/firefox-db2pem.sh b/extra/curl/curl-8.14.1/scripts/firefox-db2pem.sh
similarity index 100%
rename from extra/curl/curl-8.12.1/scripts/firefox-db2pem.sh
rename to extra/curl/curl-8.14.1/scripts/firefox-db2pem.sh
diff --git a/extra/curl/curl-8.12.1/scripts/maketgz b/extra/curl/curl-8.14.1/scripts/maketgz
similarity index 100%
rename from extra/curl/curl-8.12.1/scripts/maketgz
rename to extra/curl/curl-8.14.1/scripts/maketgz
diff --git a/extra/curl/curl-8.12.1/scripts/managen b/extra/curl/curl-8.14.1/scripts/managen
similarity index 97%
rename from extra/curl/curl-8.12.1/scripts/managen
rename to extra/curl/curl-8.14.1/scripts/managen
index 20abfc12efef..5e03180144b7 100755
--- a/extra/curl/curl-8.12.1/scripts/managen
+++ b/extra/curl/curl-8.14.1/scripts/managen
@@ -302,7 +302,46 @@ sub render {
             # skip leading blank lines
             next;
         }
+
         $start = 1;
+
+        if(/^[ \t]*\n/) {
+            # count and ignore blank lines
+            $blankline++;
+            next;
+        }
+        elsif($d =~ /^    (.*)/) {
+            my $word = $1;
+            if(!$quote && $manpage) {
+                push @desc, "\n" if($blankline);
+                push @desc, ".nf\n";
+                $blankline = 0;
+            }
+            $quote = 1;
+            $d = "$word\n";
+        }
+        elsif($d =~ /^(```|~~~)/) {
+            if(!$quote) {
+                if($manpage) {
+                    push @desc, "\n" if($blankline);
+                    push @desc, ".nf\n";
+                    $blankline = 0;
+                }
+                $quote = 2;
+            }
+            else {
+                # end of quote
+                push @desc, ".fi\n" if($manpage);
+                $quote = 0;
+            }
+            next;
+        }
+        elsif($quote == 1) {
+            # end of quote
+            push @desc, ".fi\n" if($manpage);
+            $quote = 0;
+        }
+
         if(/^# (.*)/) {
             $header = 1;
             if($top != 1) {
@@ -366,31 +405,28 @@ sub render {
             print STDERR "$f:$line:1:ERROR: $cmd detected, use ##-style\n";
             return 3;
         }
-        elsif(/^[ \t]*\n/) {
-            # count and ignore blank lines
-            $blankline++;
-            next;
-        }
-        elsif($d =~ /^    (.*)/) {
-            my $word = $1;
-            if(!$quote && $manpage) {
-                push @desc, "\n" if($blankline);
-                push @desc, ".nf\n";
-                $blankline = 0;
-            }
-            $quote = 1;
-            $d = "$word\n";
-        }
-        elsif($quote && ($d !~ /^    (.*)/)) {
-            # end of quote
-            push @desc, ".fi\n" if($manpage);
-            $quote = 0;
-        }
 
         $d =~ s/`%DATE`/$date/g;
         $d =~ s/`%VERSION`/$version/g;
         $d =~ s/`%GLOBALS`/$globals/g;
 
+        if(!$quote) {
+            if($d =~ /^(.*)  /) {
+                printf STDERR "$f:$line:%d:ERROR: 2 spaces detected\n",
+                    length($1);
+                return 3;
+            }
+            my $back = $d;
+
+            # remove all backticked pieces
+            $back =~ s/\`(.*?)\`//g;
+
+            if($back =~ /[^\\][\<\>]/) {
+                print STDERR "$f:$line:1:WARN: un-escaped < or > used: $back\n";
+                return 3;
+            }
+        }
+
         # convert backticks to double quotes
         $d =~ s/\`/\"/g;
 
@@ -410,17 +446,6 @@ sub render {
             }
         }
 
-        if(!$quote) {
-            if($d =~ /^(.*)  /) {
-                printf STDERR "$f:$line:%d:ERROR: 2 spaces detected\n",
-                    length($1);
-                return 3;
-            }
-            elsif($d =~ /[^\\][\<\>]/) {
-                print STDERR "$f:$line:1:WARN: un-escaped < or > used\n";
-                return 3;
-            }
-        }
         # convert backslash-'<' or '> to just the second character
         $d =~ s/\\([><])/$1/g;
         # convert single backslash to double-backslash
@@ -1176,8 +1201,8 @@ HEADER
 }
 
 sub showonly {
-    my ($f) = @_;
-    if(single($f, 1)) {
+    my ($dir, $f) = @_;
+    if(single($dir, 1, $f, 1)) {
         print STDERR "$f: failed\n";
     }
 }
@@ -1212,7 +1237,7 @@ sub getargs {
         return;
     }
     elsif($f eq "single") {
-        showonly($s[0]);
+        showonly($dir, $s[0]);
         return;
     }
     elsif($f eq "protos") {
diff --git a/extra/curl/curl-8.12.1/scripts/mdlinkcheck b/extra/curl/curl-8.14.1/scripts/mdlinkcheck
similarity index 100%
rename from extra/curl/curl-8.12.1/scripts/mdlinkcheck
rename to extra/curl/curl-8.14.1/scripts/mdlinkcheck
diff --git a/extra/curl/curl-8.14.1/scripts/mk-ca-bundle.pl b/extra/curl/curl-8.14.1/scripts/mk-ca-bundle.pl
new file mode 100755
index 000000000000..79c78eb85b19
--- /dev/null
+++ b/extra/curl/curl-8.14.1/scripts/mk-ca-bundle.pl
@@ -0,0 +1,667 @@
+#!/usr/bin/env perl
+# ***************************************************************************
+# *                                  _   _ ____  _
+# *  Project                     ___| | | |  _ \| |
+# *                             / __| | | | |_) | |
+# *                            | (__| |_| |  _ <| |___
+# *                             \___|\___/|_| \_\_____|
+# *
+# * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+# *
+# * This software is licensed as described in the file COPYING, which
+# * you should have received as part of this distribution. The terms
+# * are also available at https://curl.se/docs/copyright.html.
+# *
+# * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+# * copies of the Software, and permit persons to whom the Software is
+# * furnished to do so, under the terms of the COPYING file.
+# *
+# * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+# * KIND, either express or implied.
+# *
+# * SPDX-License-Identifier: curl
+# *
+# ***************************************************************************
+# This Perl script creates a fresh ca-bundle.crt file for use with libcurl.
+# It downloads certdata.txt from Mozilla's source tree (see URL below),
+# then parses certdata.txt and extracts CA Root Certificates into PEM format.
+# These are then processed with the OpenSSL commandline tool to produce the
+# final ca-bundle.crt file.
+# The script is based on the parse-certs script written by Roland Krikava.
+# This Perl script works on almost any platform since its only external
+# dependency is the OpenSSL commandline tool for optional text listing.
+# Hacked by Guenter Knauf.
+#
+use Encode;
+use Getopt::Std;
+use MIME::Base64;
+use strict;
+use warnings;
+use vars qw($opt_b $opt_d $opt_f $opt_h $opt_i $opt_k $opt_l $opt_m $opt_n $opt_p $opt_q $opt_s $opt_t $opt_u $opt_v $opt_w);
+use List::Util;
+use Text::Wrap;
+use Time::Local;
+my $MOD_SHA = "Digest::SHA";
+eval "require $MOD_SHA";
+if($@) {
+    $MOD_SHA = "Digest::SHA::PurePerl";
+    eval "require $MOD_SHA";
+}
+eval "require LWP::UserAgent";
+
+my %urls = (
+    'autoland' => 'https://raw.githubusercontent.com/mozilla-firefox/firefox/refs/heads/autoland/security/nss/lib/ckfw/builtins/certdata.txt',
+    'beta'    => 'https://raw.githubusercontent.com/mozilla-firefox/firefox/refs/heads/beta/security/nss/lib/ckfw/builtins/certdata.txt',
+    'release' => 'https://raw.githubusercontent.com/mozilla-firefox/firefox/refs/heads/release/security/nss/lib/ckfw/builtins/certdata.txt',
+);
+
+$opt_d = 'release';
+
+# If the OpenSSL commandline is not in search path you can configure it here!
+my $openssl = 'openssl';
+
+my $version = '1.29';
+
+$opt_w = 76; # default base64 encoded lines length
+
+# default cert types to include in the output (default is to include CAs which
+# may issue SSL server certs)
+my $default_mozilla_trust_purposes = "SERVER_AUTH";
+my $default_mozilla_trust_levels = "TRUSTED_DELEGATOR";
+$opt_p = $default_mozilla_trust_purposes . ":" . $default_mozilla_trust_levels;
+
+my @valid_mozilla_trust_purposes = (
+    "DIGITAL_SIGNATURE",
+    "NON_REPUDIATION",
+    "KEY_ENCIPHERMENT",
+    "DATA_ENCIPHERMENT",
+    "KEY_AGREEMENT",
+    "KEY_CERT_SIGN",
+    "CRL_SIGN",
+    "SERVER_AUTH",
+    "CLIENT_AUTH",
+    "CODE_SIGNING",
+    "EMAIL_PROTECTION",
+    "IPSEC_END_SYSTEM",
+    "IPSEC_TUNNEL",
+    "IPSEC_USER",
+    "TIME_STAMPING",
+    "STEP_UP_APPROVED"
+);
+
+my @valid_mozilla_trust_levels = (
+    "TRUSTED_DELEGATOR",    # CAs
+    "NOT_TRUSTED",          # Don't trust these certs.
+    "MUST_VERIFY_TRUST",    # This explicitly tells us that it ISN'T a CA but is
+                            # otherwise ok. In other words, this should tell the
+                            # app to ignore any other sources that claim this is
+                            # a CA.
+    "TRUSTED"               # This cert is trusted, but only for itself and not
+                            # for delegates (i.e. it is not a CA).
+);
+
+my $default_signature_algorithms = $opt_s = "MD5";
+
+my @valid_signature_algorithms = (
+    "MD5",
+    "SHA1",
+    "SHA256",
+    "SHA384",
+    "SHA512"
+);
+
+$0 =~ s@.*(/|\\)@@;
+$Getopt::Std::STANDARD_HELP_VERSION = 1;
+getopts('bd:fhiklmnp:qs:tuvw:');
+
+if(!defined($opt_d)) {
+    # to make plain "-d" use not cause warnings, and actually still work
+    $opt_d = 'release';
+}
+
+# Use predefined URL or else custom URL specified on command line.
+my $url;
+if(defined($urls{$opt_d})) {
+    $url = $urls{$opt_d};
+    if(!$opt_k && $url !~ /^https:\/\//i) {
+        die "The URL for '$opt_d' is not HTTPS. Use -k to override (insecure).\n";
+    }
+}
+else {
+  $url = $opt_d;
+}
+
+if($opt_i) {
+    print ("=" x 78 . "\n");
+    print "Script Version                   : $version\n";
+    print "Perl Version                     : $]\n";
+    print "Operating System Name            : $^O\n";
+    print "Getopt::Std.pm Version           : ${Getopt::Std::VERSION}\n";
+    print "Encode::Encoding.pm Version      : ${Encode::Encoding::VERSION}\n";
+    print "MIME::Base64.pm Version          : ${MIME::Base64::VERSION}\n";
+    print "LWP::UserAgent.pm Version        : ${LWP::UserAgent::VERSION}\n" if($LWP::UserAgent::VERSION);
+    print "LWP.pm Version                   : ${LWP::VERSION}\n" if($LWP::VERSION);
+    print "Digest::SHA.pm Version           : ${Digest::SHA::VERSION}\n" if($Digest::SHA::VERSION);
+    print "Digest::SHA::PurePerl.pm Version : ${Digest::SHA::PurePerl::VERSION}\n" if($Digest::SHA::PurePerl::VERSION);
+    print ("=" x 78 . "\n");
+}
+
+sub warning_message() {
+    if($opt_d =~ m/^risk$/i) { # Long Form Warning and Exit
+        print "Warning: Use of this script may pose some risk:\n";
+        print "\n";
+        print "  1) If you use HTTP URLs they are subject to a man in the middle attack\n";
+        print "  2) Default to 'release', but more recent updates may be found in other trees\n";
+        print "  3) certdata.txt file format may change, lag time to update this script\n";
+        print "  4) Generally unwise to blindly trust CAs without manual review & verification\n";
+        print "  5) Mozilla apps use additional security checks aren't represented in certdata\n";
+        print "  6) Use of this script will make a security engineer grind his teeth and\n";
+        print "     swear at you.  ;)\n";
+        exit;
+    } else { # Short Form Warning
+        print "Warning: Use of this script may pose some risk, -d risk for more details.\n";
+    }
+}
+
+sub HELP_MESSAGE() {
+    print "Usage:\t${0} [-b] [-d<certdata>] [-f] [-i] [-k] [-l] [-n] [-p<purposes:levels>] [-q] [-s<algorithms>] [-t] [-u] [-v] [-w<l>] [<outputfile>]\n";
+    print "\t-b\tbackup an existing version of ca-bundle.crt\n";
+    print "\t-d\tspecify Mozilla tree to pull certdata.txt or custom URL\n";
+    print "\t\t  Valid names are:\n";
+    print "\t\t    ", join( ", ", map { ( $_ =~ m/$opt_d/ ) ? "$_ (default)" : "$_" } sort keys %urls ), "\n";
+    print "\t-f\tforce rebuild even if certdata.txt is current\n";
+    print "\t-i\tprint version info about used modules\n";
+    print "\t-k\tallow URLs other than HTTPS, enable HTTP fallback (insecure)\n";
+    print "\t-l\tprint license info about certdata.txt\n";
+    print "\t-m\tinclude meta data in output\n";
+    print "\t-n\tno download of certdata.txt (to use existing)\n";
+    print wrap("\t","\t\t", "-p\tlist of Mozilla trust purposes and levels for certificates to include in output. Takes the form of a comma separated list of purposes, a colon, and a comma separated list of levels. (default: $default_mozilla_trust_purposes:$default_mozilla_trust_levels)"), "\n";
+    print "\t\t  Valid purposes are:\n";
+    print wrap("\t\t    ","\t\t    ", join( ", ", "ALL", @valid_mozilla_trust_purposes ) ), "\n";
+    print "\t\t  Valid levels are:\n";
+    print wrap("\t\t    ","\t\t    ", join( ", ", "ALL", @valid_mozilla_trust_levels ) ), "\n";
+    print "\t-q\tbe really quiet (no progress output at all)\n";
+    print wrap("\t","\t\t", "-s\tcomma separated list of certificate signatures/hashes to output in plain text mode. (default: $default_signature_algorithms)\n");
+    print "\t\t  Valid signature algorithms are:\n";
+    print wrap("\t\t    ","\t\t    ", join( ", ", "ALL", @valid_signature_algorithms ) ), "\n";
+    print "\t-t\tinclude plain text listing of certificates\n";
+    print "\t-u\tunlink (remove) certdata.txt after processing\n";
+    print "\t-v\tbe verbose and print out processed CAs\n";
+    print "\t-w <l>\twrap base64 output lines after <l> chars (default: ${opt_w})\n";
+    exit;
+}
+
+sub VERSION_MESSAGE() {
+    print "${0} version ${version} running Perl ${]} on ${^O}\n";
+}
+
+warning_message() unless ($opt_q || $url =~ m/^(ht|f)tps:/i );
+HELP_MESSAGE() if($opt_h);
+
+sub report($@) {
+    my $output = shift;
+
+    print STDERR $output . "\n" unless $opt_q;
+}
+
+sub is_in_list($@) {
+    my $target = shift;
+
+    return defined(List::Util::first { $target eq $_ } @_);
+}
+
+# Parses $param_string as a case insensitive comma separated list with optional
+# whitespace validates that only allowed parameters are supplied
+sub parse_csv_param($$@) {
+    my $description = shift;
+    my $param_string = shift;
+    my @valid_values = @_;
+
+    my @values = map {
+        s/^\s+//;  # strip leading spaces
+        s/\s+$//;  # strip trailing spaces
+        uc $_      # return the modified string as upper case
+    } split( ',', $param_string );
+
+    # Find all values which are not in the list of valid values or "ALL"
+    my @invalid = grep { !is_in_list($_,"ALL",@valid_values) } @values;
+
+    if(scalar(@invalid) > 0) {
+        # Tell the user which parameters were invalid and print the standard help
+        # message which will exit
+        print "Error: Invalid ", $description, scalar(@invalid) == 1 ? ": " : "s: ", join( ", ", map { "\"$_\"" } @invalid ), "\n";
+        HELP_MESSAGE();
+    }
+
+    @values = @valid_values if(is_in_list("ALL",@values));
+
+    return @values;
+}
+
+sub sha256 {
+    my $result;
+    if($Digest::SHA::VERSION || $Digest::SHA::PurePerl::VERSION) {
+        open(FILE, $_[0]) or die "Can't open '$_[0]': $!";
+        binmode(FILE);
+        $result = $MOD_SHA->new(256)->addfile(*FILE)->hexdigest;
+        close(FILE);
+    } else {
+        # Use OpenSSL command if Perl Digest::SHA modules not available
+        $result = `"$openssl" dgst -r -sha256 "$_[0]"`;
+        $result =~ s/^([0-9a-f]{64}) .+/$1/is;
+    }
+    return $result;
+}
+
+
+sub oldhash {
+    my $hash = "";
+    open(C, "<$_[0]") || return 0;
+    while(<C>) {
+        chomp;
+        if($_ =~ /^\#\# SHA256: (.*)/) {
+            $hash = $1;
+            last;
+        }
+    }
+    close(C);
+    return $hash;
+}
+
+if( $opt_p !~ m/:/ ) {
+    print "Error: Mozilla trust identifier list must include both purposes and levels\n";
+    HELP_MESSAGE();
+}
+
+(my $included_mozilla_trust_purposes_string, my $included_mozilla_trust_levels_string) = split( ':', $opt_p );
+my @included_mozilla_trust_purposes = parse_csv_param( "trust purpose", $included_mozilla_trust_purposes_string, @valid_mozilla_trust_purposes );
+my @included_mozilla_trust_levels = parse_csv_param( "trust level", $included_mozilla_trust_levels_string, @valid_mozilla_trust_levels );
+
+my @included_signature_algorithms = parse_csv_param( "signature algorithm", $opt_s, @valid_signature_algorithms );
+
+sub should_output_cert(%) {
+    my %trust_purposes_by_level = @_;
+
+    foreach my $level (@included_mozilla_trust_levels) {
+        # for each level we want to output, see if any of our desired purposes are
+        # included
+        return 1 if( defined( List::Util::first { is_in_list( $_, @included_mozilla_trust_purposes ) } @{$trust_purposes_by_level{$level}} ) );
+    }
+
+    return 0;
+}
+
+my $crt = $ARGV[0] || 'ca-bundle.crt';
+(my $txt = $url) =~ s@(.*/|\?.*)@@g;
+
+my $stdout = $crt eq '-';
+my $resp;
+my $fetched;
+
+my $oldhash = oldhash($crt);
+
+report "SHA256 of old file: $oldhash";
+
+if(!$opt_n) {
+    report "Downloading $txt ...";
+
+    # If we have an HTTPS URL then use curl
+    if($url =~ /^https:\/\//i) {
+        my $curl = `curl -V`;
+        if($curl) {
+            if($curl =~ /^Protocols:.* https( |$)/m) {
+                report "Get certdata with curl!";
+                my $proto = !$opt_k ? "--proto =https" : "";
+                my $quiet = $opt_q ? "-s" : "";
+                my @out = `curl -Lw %{response_code} $proto $quiet -o "$txt" "$url"`;
+                if(!$? && @out && $out[0] == 200) {
+                    $fetched = 1;
+                    report "Downloaded $txt";
+                }
+                else {
+                    report "Failed downloading via HTTPS with curl";
+                    if(-e $txt && !unlink($txt)) {
+                        report "Failed to remove '$txt': $!";
+                    }
+                }
+            }
+            else {
+                report "curl lacks https support";
+            }
+        }
+        else {
+            report "curl not found";
+        }
+    }
+
+    # If nothing was fetched then use LWP
+    if(!$fetched) {
+        if($url =~ /^https:\/\//i) {
+            report "Falling back to HTTP";
+            $url =~ s/^https:\/\//http:\/\//i;
+        }
+        if(!$opt_k) {
+            report "URLs other than HTTPS are disabled by default, to enable use -k";
+            exit 1;
+        }
+        report "Get certdata with LWP!";
+        if(!defined(${LWP::UserAgent::VERSION})) {
+            report "LWP is not available (LWP::UserAgent not found)";
+            exit 1;
+        }
+        my $ua  = new LWP::UserAgent(agent => "$0/$version");
+        $ua->env_proxy();
+        $resp = $ua->mirror($url, $txt);
+        if($resp && $resp->code eq '304') {
+            report "Not modified";
+            exit 0 if -e $crt && !$opt_f;
+        }
+        else {
+            $fetched = 1;
+            report "Downloaded $txt";
+        }
+        if(!$resp || $resp->code !~ /^(?:200|304)$/) {
+            report "Unable to download latest data: "
+              . ($resp? $resp->code . ' - ' . $resp->message : "LWP failed");
+            exit 1 if -e $crt || ! -r $txt;
+        }
+    }
+}
+
+my $filedate = $resp ? $resp->last_modified : (stat($txt))[9];
+my $datesrc = "as of";
+if(!$filedate) {
+    # mxr.mozilla.org gave us a time, hg.mozilla.org does not!
+    $filedate = time();
+    $datesrc="downloaded on";
+}
+
+# get the hash from the download file
+my $newhash= sha256($txt);
+
+if(!$opt_f && $oldhash eq $newhash) {
+    report "Downloaded file identical to previous run\'s source file. Exiting";
+    if($opt_u && -e $txt && !unlink($txt)) {
+        report "Failed to remove $txt: $!\n";
+    }
+    exit;
+}
+
+report "SHA256 of new file: $newhash";
+
+my $currentdate = scalar gmtime($filedate);
+
+my $format = $opt_t ? "plain text and " : "";
+if($stdout) {
+    open(CRT, '> -') or die "Couldn't open STDOUT: $!\n";
+} else {
+    open(CRT,">$crt.~") or die "Couldn't open $crt.~: $!\n";
+}
+print CRT <<EOT;
+##
+## Bundle of CA Root Certificates
+##
+## Certificate data from Mozilla ${datesrc}: ${currentdate} GMT
+##
+## Find updated versions here: https://curl.se/docs/caextract.html
+##
+## This is a bundle of X.509 certificates of public Certificate Authorities
+## (CA). These were automatically extracted from Mozilla's root certificates
+## file (certdata.txt).  This file can be found in the mozilla source tree:
+## ${url}
+##
+## It contains the certificates in ${format}PEM format and therefore
+## can be directly used with curl / libcurl / php_curl, or with
+## an Apache+mod_ssl webserver for SSL client authentication.
+## Just configure this file as the SSLCACertificateFile.
+##
+## Conversion done with mk-ca-bundle.pl version $version.
+## SHA256: $newhash
+##
+
+EOT
+
+report "Processing  '$txt' ...";
+my $caname;
+my $certnum = 0;
+my $skipnum = 0;
+my $start_of_cert = 0;
+my $main_block = 0;
+my $main_block_name;
+my $trust_block = 0;
+my $trust_block_name;
+my @precert;
+my $cka_value;
+my $valid = 0;
+
+open(TXT,"$txt") or die "Couldn't open $txt: $!\n";
+while(<TXT>) {
+    if(/\*\*\*\*\* BEGIN LICENSE BLOCK \*\*\*\*\*/) {
+        print CRT;
+        print if($opt_l);
+        while(<TXT>) {
+            print CRT;
+            print if($opt_l);
+            last if(/\*\*\*\*\* END LICENSE BLOCK \*\*\*\*\*/);
+        }
+        next;
+    }
+    # The input file format consists of blocks of Mozilla objects.
+    # The blocks are separated by blank lines but may be related.
+    elsif(/^\s*$/) {
+        $main_block = 0;
+        $trust_block = 0;
+        next;
+    }
+    # Each certificate has a main block.
+    elsif(/^# Certificate "(.*)"/) {
+        (!$main_block && !$trust_block) or die "Unexpected certificate block";
+        $main_block = 1;
+        $main_block_name = $1;
+        # Reset all other certificate variables.
+        $trust_block = 0;
+        $trust_block_name = "";
+        $valid = 0;
+        $start_of_cert = 0;
+        $caname = "";
+        $cka_value = "";
+        undef @precert;
+        next;
+    }
+    # Each certificate's main block is followed by a trust block.
+    elsif(/^# Trust for (?:Certificate )?"(.*)"/) {
+        (!$main_block && !$trust_block) or die "Unexpected trust block";
+        $trust_block = 1;
+        $trust_block_name = $1;
+        if($main_block_name ne $trust_block_name) {
+            die "cert name \"$main_block_name\" != trust name \"$trust_block_name\"";
+        }
+        next;
+    }
+    # Ignore other blocks.
+    #
+    # There is a documentation comment block, a BEGINDATA block, and a bunch of
+    # blocks starting with "# Explicitly Distrust <certname>".
+    #
+    # The latter is for certificates that have already been removed and are not
+    # included. Not all explicitly distrusted certificates are ignored at this
+    # point, just those without an actual certificate.
+    elsif(!$main_block && !$trust_block) {
+        next;
+    }
+    elsif(/^#/) {
+        # The commented lines in a main block are plaintext metadata that describes
+        # the certificate. Issuer, Subject, Fingerprint, etc.
+        if($main_block) {
+            push @precert, $_ if not /^#$/;
+            if(/^# Not Valid After : (.*)/) {
+                my $stamp = $1;
+                use Time::Piece;
+                # Not Valid After : Thu Sep 30 14:01:15 2021
+                my $t = Time::Piece->strptime($stamp, "%a %b %d %H:%M:%S %Y");
+                my $delta = ($t->epoch - time()); # negative means no longer valid
+                if($delta < 0) {
+                    $skipnum++;
+                    report "Skipping: $main_block_name is not valid anymore" if($opt_v);
+                    $valid = 0;
+                }
+                else {
+                    $valid = 1;
+                }
+            }
+        }
+        next;
+    }
+    elsif(!$valid) {
+        next;
+    }
+
+    chomp;
+
+    if($main_block) {
+        if(/^CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE/) {
+            !$start_of_cert or die "Duplicate CKO_CERTIFICATE object";
+            $start_of_cert = 1;
+            next;
+        }
+        elsif(!$start_of_cert) {
+            next;
+        }
+        elsif(/^CKA_LABEL UTF8 \"(.*)\"/) {
+            ($caname eq "") or die "Duplicate CKA_LABEL attribute";
+            $caname = $1;
+            if($caname ne $main_block_name) {
+                die "caname \"$caname\" != cert name \"$main_block_name\"";
+            }
+            next;
+        }
+        elsif(/^CKA_VALUE MULTILINE_OCTAL/) {
+            ($cka_value eq "") or die "Duplicate CKA_VALUE attribute";
+            while(<TXT>) {
+                last if(/^END/);
+                chomp;
+                my @octets = split(/\\/);
+                shift @octets;
+                for(@octets) {
+                    $cka_value .= chr(oct);
+                }
+            }
+            next;
+        }
+        else {
+            next;
+        }
+    }
+
+    if(!$trust_block || !$start_of_cert || $caname eq "" || $cka_value eq "") {
+        die "Certificate extraction failed";
+    }
+
+    my %trust_purposes_by_level;
+
+    if(/^CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST/) {
+        # now scan the trust part to determine how we should trust this cert
+        while(<TXT>) {
+            if(/^\s*$/) {
+                $trust_block = 0;
+                last;
+            }
+            if(/^CKA_TRUST_([A-Z_]+)\s+CK_TRUST\s+CKT_NSS_([A-Z_]+)\s*$/) {
+                if(!is_in_list($1,@valid_mozilla_trust_purposes)) {
+                    report "Warning: Unrecognized trust purpose for cert: $caname. Trust purpose: $1. Trust Level: $2";
+                } elsif(!is_in_list($2,@valid_mozilla_trust_levels)) {
+                    report "Warning: Unrecognized trust level for cert: $caname. Trust purpose: $1. Trust Level: $2";
+                } else {
+                    push @{$trust_purposes_by_level{$2}}, $1;
+                }
+            }
+        }
+
+        # Sanity check that an explicitly distrusted certificate only has trust
+        # purposes with a trust level of NOT_TRUSTED.
+        #
+        # Certificate objects that are explicitly distrusted are in a certificate
+        # block that starts # Certificate "Explicitly Distrust(ed) <certname>",
+        # where "Explicitly Distrust(ed) " was prepended to the original cert name.
+        if($caname =~ /distrust/i ||
+             $main_block_name =~ /distrust/i ||
+             $trust_block_name =~ /distrust/i) {
+            my @levels = keys %trust_purposes_by_level;
+            if(scalar(@levels) != 1 || $levels[0] ne "NOT_TRUSTED") {
+                die "\"$caname\" must have all trust purposes at level NOT_TRUSTED.";
+            }
+        }
+
+        if(!should_output_cert(%trust_purposes_by_level)) {
+            $skipnum ++;
+            report "Skipping: $caname lacks acceptable trust level" if($opt_v);
+        } else {
+            my $encoded = MIME::Base64::encode_base64($cka_value, '');
+            $encoded =~ s/(.{1,${opt_w}})/$1\n/g;
+            my $pem = "-----BEGIN CERTIFICATE-----\n"
+                    . $encoded
+                    . "-----END CERTIFICATE-----\n";
+            print CRT "\n$caname\n";
+            my $maxStringLength = length(decode('UTF-8', $caname, Encode::FB_CROAK | Encode::LEAVE_SRC));
+            print CRT ("=" x $maxStringLength . "\n");
+            if($opt_t) {
+                foreach my $key (sort keys %trust_purposes_by_level) {
+                    my $string = $key . ": " . join(", ", @{$trust_purposes_by_level{$key}});
+                    print CRT $string . "\n";
+                }
+            }
+            if($opt_m) {
+                print CRT for @precert;
+            }
+            if(!$opt_t) {
+                print CRT $pem;
+            } else {
+                my $pipe = "";
+                foreach my $hash (@included_signature_algorithms) {
+                    $pipe = "|$openssl x509 -" . $hash . " -fingerprint -noout -inform PEM";
+                    if(!$stdout) {
+                        $pipe .= " >> $crt.~";
+                        close(CRT) or die "Couldn't close $crt.~: $!";
+                    }
+                    open(TMP, $pipe) or die "Couldn't open openssl pipe: $!";
+                    print TMP $pem;
+                    close(TMP) or die "Couldn't close openssl pipe: $!";
+                    if(!$stdout) {
+                        open(CRT, ">>$crt.~") or die "Couldn't open $crt.~: $!";
+                    }
+                }
+                $pipe = "|$openssl x509 -text -inform PEM";
+                if(!$stdout) {
+                    $pipe .= " >> $crt.~";
+                    close(CRT) or die "Couldn't close $crt.~: $!";
+                }
+                open(TMP, $pipe) or die "Couldn't open openssl pipe: $!";
+                print TMP $pem;
+                close(TMP) or die "Couldn't close openssl pipe: $!";
+                if(!$stdout) {
+                    open(CRT, ">>$crt.~") or die "Couldn't open $crt.~: $!";
+                }
+            }
+            report "Processed: $caname" if($opt_v);
+            $certnum++;
+        }
+    }
+}
+close(TXT) or die "Couldn't close $txt: $!\n";
+close(CRT) or die "Couldn't close $crt.~: $!\n";
+unless($stdout) {
+    if($opt_b && -e $crt) {
+        my $bk = 1;
+        while(-e "$crt.~${bk}~") {
+            $bk++;
+        }
+        rename $crt, "$crt.~${bk}~" or die "Failed to create backup $crt.~$bk}~: $!\n";
+    } elsif( -e $crt ) {
+        unlink( $crt ) or die "Failed to remove $crt: $!\n";
+    }
+    rename "$crt.~", $crt or die "Failed to rename $crt.~ to $crt: $!\n";
+}
+if($opt_u && -e $txt && !unlink($txt)) {
+    report "Failed to remove $txt: $!\n";
+}
+report "Done ($certnum CA certs processed, $skipnum skipped).";
diff --git a/extra/curl/curl-8.12.1/scripts/mk-unity.pl b/extra/curl/curl-8.14.1/scripts/mk-unity.pl
similarity index 91%
rename from extra/curl/curl-8.12.1/scripts/mk-unity.pl
rename to extra/curl/curl-8.14.1/scripts/mk-unity.pl
index f9ebf2524ce0..4ad86d1c7744 100755
--- a/extra/curl/curl-8.12.1/scripts/mk-unity.pl
+++ b/extra/curl/curl-8.14.1/scripts/mk-unity.pl
@@ -35,6 +35,8 @@
     die "Usage: $0 [<c-sources>] [--exclude <exclude-c-sources>]\n";
 }
 
+my $srcdir = shift @ARGV;
+
 # Specific sources to exclude or add as an extra source file
 my @src;
 my %exclude;
@@ -58,6 +60,11 @@
 
 foreach my $src (@src) {
     if($src =~ /\.c$/g && !exists $exclude{$src}) {
-        print "#include \"$src\"\n";
+        if(-e "$srcdir/$src") {
+            print "#include \"$srcdir/$src\"\n";
+        }
+        else {
+            print "#include \"$src\"\n";
+        }
     }
 }
diff --git a/extra/curl/curl-8.12.1/scripts/nroff2cd b/extra/curl/curl-8.14.1/scripts/nroff2cd
similarity index 100%
rename from extra/curl/curl-8.12.1/scripts/nroff2cd
rename to extra/curl/curl-8.14.1/scripts/nroff2cd
diff --git a/extra/curl/curl-8.14.1/scripts/pythonlint.sh b/extra/curl/curl-8.14.1/scripts/pythonlint.sh
new file mode 100755
index 000000000000..b7dc95633b4c
--- /dev/null
+++ b/extra/curl/curl-8.14.1/scripts/pythonlint.sh
@@ -0,0 +1,30 @@
+#!/bin/sh
+#***************************************************************************
+#                                  _   _ ____  _
+#  Project                     ___| | | |  _ \| |
+#                             / __| | | | |_) | |
+#                            | (__| |_| |  _ <| |___
+#                             \___|\___/|_| \_\_____|
+#
+# Copyright (C) Dan Fandrich, <dan@coneharvesters.com>, et al.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at https://curl.se/docs/copyright.html.
+#
+# You may opt to use, copy, modify, merge, publish, distribute and/or sell
+# copies of the Software, and permit persons to whom the Software is
+# furnished to do so, under the terms of the COPYING file.
+#
+# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+# KIND, either express or implied.
+#
+# SPDX-License-Identifier: curl
+#
+###########################################################################
+
+# Use ruff to perform a style analysis on all Python code in the supplied
+# locations, or all Python files found in the current directory tree by
+# default.
+
+ruff check --extend-select=B007,B016,C405,C416,COM818,D200,D213,D204,D401,D415,FURB129,N818,PERF401,PERF403,PIE790,PIE808,PLW0127,Q004,RUF010,SIM101,SIM117,SIM118,TRY400,TRY401 "$@"
diff --git a/extra/curl/curl-8.14.1/scripts/randdisable b/extra/curl/curl-8.14.1/scripts/randdisable
new file mode 100755
index 000000000000..f8a7a147ed14
--- /dev/null
+++ b/extra/curl/curl-8.14.1/scripts/randdisable
@@ -0,0 +1,78 @@
+#!/usr/bin/env perl
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+#
+# SPDX-License-Identifier: curl
+#
+# This script makes a "random" build using configure and verifies that it
+# builds curl correctly. It randomly adds a number of the available
+# --disable-* flags to configure. When it detects a problem the script stops,
+# otherwise it continues trying more combinations.
+#
+# 1. Figure out all existing configure --disable-* options
+# 2. Generate random command line using supported options + random TLS
+# 3. Run configure (exit if problem)
+# 4. run "make -sj10" to build (exit if problem)
+# 5. run curl -V (exit if problem)
+# 6. GOTO 2
+#
+# Tips:
+#
+# - edit the @tls array to include all TLS backends you can build with
+# - do a checkout in a ram-based filesystem
+#
+use List::Util qw/shuffle/;
+
+sub getoptions {
+    my @all = `./configure --help`;
+    for my $o (@all) {
+        chomp $o;
+        if($o =~ /(--disable-[^ ]*)/) {
+            if($1 !~ /FEATURE/) {
+                push @disable, $1;
+            }
+        }
+    }
+}
+
+getoptions();
+
+# options to select a TLS
+my @tls = ("--with-openssl",
+           "--with-wolfssl=/home/daniel/build-wolfssl",
+           "--with-gnutls",
+           "--with-mbedtls");
+
+do {
+
+    # get a random number of disable options
+    my $num = rand(scalar(@disable) - 2) + 2;
+
+    my $c = 0;
+    my $arg;
+    for my $d (shuffle @disable) {
+        $arg .= " $d";
+        if(++$c >= $num) {
+            last;
+        }
+    }
+
+    my @stls = shuffle @tls;
+    $arg.= " ".$stls[0];
+
+    system("make clean");
+    if(system("./configure $arg")) {
+        print STDERR "configure problem\n";
+        print STDERR "./configure $arg\n";
+        exit 1;
+    }
+    if(system("make -sj10")) {
+        print STDERR "Build problem\n";
+        print STDERR "./configure $arg\n";
+        exit 1;
+    }
+    if(system("./src/curl -V 2>/dev/null")) {
+        print STDERR "Running problem\n";
+        print STDERR "./configure $arg\n";
+        exit 1;
+    }
+} while(1);
diff --git a/extra/curl/curl-8.12.1/scripts/release-tools.sh b/extra/curl/curl-8.14.1/scripts/release-tools.sh
similarity index 100%
rename from extra/curl/curl-8.12.1/scripts/release-tools.sh
rename to extra/curl/curl-8.14.1/scripts/release-tools.sh
diff --git a/extra/curl/curl-8.12.1/scripts/schemetable.c b/extra/curl/curl-8.14.1/scripts/schemetable.c
similarity index 100%
rename from extra/curl/curl-8.12.1/scripts/schemetable.c
rename to extra/curl/curl-8.14.1/scripts/schemetable.c
diff --git a/extra/curl/curl-8.14.1/scripts/top-complexity b/extra/curl/curl-8.14.1/scripts/top-complexity
new file mode 100755
index 000000000000..9bd771048f21
--- /dev/null
+++ b/extra/curl/curl-8.14.1/scripts/top-complexity
@@ -0,0 +1,124 @@
+#!/usr/bin/env perl
+#***************************************************************************
+#                                  _   _ ____  _
+#  Project                     ___| | | |  _ \| |
+#                             / __| | | | |_) | |
+#                            | (__| |_| |  _ <| |___
+#                             \___|\___/|_| \_\_____|
+#
+# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at https://curl.se/docs/copyright.html.
+#
+# You may opt to use, copy, modify, merge, publish, distribute and/or sell
+# copies of the Software, and permit persons to whom the Software is
+# furnished to do so, under the terms of the COPYING file.
+#
+# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+# KIND, either express or implied.
+#
+# SPDX-License-Identifier: curl
+#
+###########################################################################
+
+use strict;
+use warnings;
+
+#######################################################################
+# Check for a command in the PATH of the test server.
+#
+sub checkcmd {
+    my ($cmd)=@_;
+    my @paths;
+    if ($^O eq 'MSWin32' || $^O eq 'dos' || $^O eq 'os2') {
+        # PATH separator is different
+        @paths=(split(';', $ENV{'PATH'}));
+    }
+    else {
+        @paths=(split(':', $ENV{'PATH'}), "/usr/sbin", "/usr/local/sbin",
+                "/sbin", "/usr/bin", "/usr/local/bin");
+    }
+    for(@paths) {
+        if( -x "$_/$cmd" && ! -d "$_/$cmd" ) {
+            # executable bit but not a directory!
+            return "$_/$cmd";
+        }
+    }
+    return "";
+}
+
+my $pmccabe = checkcmd("pmccabe");
+if(!$pmccabe) {
+    print "Make sure 'pmccabe' exists in your PATH\n";
+    exit 1;
+}
+if(! -r "lib/url.c" || ! -r "lib/urldata.h") {
+    print "Invoke this script in the curl source tree root\n";
+    exit 1;
+}
+
+my @files;
+open(F, "git ls-files '*.c'|");
+while(<F>) {
+    chomp $_;
+    my $file = $_;
+    # we can't filter these with git so do it here
+    if($file =~ /^(lib|src)/) {
+        push @files, $file;
+    }
+}
+
+my $cmd = "$pmccabe ".join(" ", @files);
+my @output=`$cmd`;
+
+# these functions can have these scores, but not higher
+my %whitelist = (
+
+    );
+
+# functions with complexity above this level causes the function to return error
+my $cutoff = 100;
+
+# functions above this complexity level are shown
+my $show = 70;
+
+my $error = 0;
+my %where;
+my %perm;
+# each line starts with the complexity score
+# 142     417     809     1677    1305    src/tool_getparam.c(1677): getparameter
+for my $l (@output) {
+    chomp $l;
+    if($l =~/^(\d+)\t\d+\t\d+\t\d+\t\d+\t([^\(]+).*: ([^ ]*)/) {
+        my ($score, $path, $func)=($1, $2, $3);
+
+        if($score > $show) {
+            my $allow = 0;
+            if($whitelist{$func} &&
+               ($score <= $whitelist{$func})) {
+                $allow = 1;
+            }
+            $where{"$path:$func"}=$score;
+            $perm{"$path:$func"}=$allow;
+            if(($score > $cutoff) && !$allow) {
+                $error++;
+            }
+        }
+    }
+
+}
+
+my $showncutoff;
+for my $e (sort {$where{$b} <=> $where{$a}} keys %where) {
+    if(!$showncutoff &&
+       ($where{$e} <= $cutoff)) {
+        print "\n---- threshold: $cutoff ----\n\n";
+        $showncutoff = 1;
+    }
+    printf "%-5d %s%s\n", $where{$e}, $e,
+        $perm{$e} ? " [ALLOWED]": "";
+}
+
+exit $error;
diff --git a/extra/curl/curl-8.12.1/scripts/verify-release b/extra/curl/curl-8.14.1/scripts/verify-release
similarity index 100%
rename from extra/curl/curl-8.12.1/scripts/verify-release
rename to extra/curl/curl-8.14.1/scripts/verify-release
diff --git a/extra/curl/curl-8.14.1/scripts/wcurl b/extra/curl/curl-8.14.1/scripts/wcurl
new file mode 100755
index 000000000000..af66ae7fcac7
--- /dev/null
+++ b/extra/curl/curl-8.14.1/scripts/wcurl
@@ -0,0 +1,323 @@
+#!/bin/sh
+
+# wcurl - a simple wrapper around curl to easily download files.
+#
+# Requires curl >= 7.46.0 (2015)
+#
+# Copyright (C) Samuel Henrique <samueloph@debian.org>, Sergio Durigan
+# Junior <sergiodj@debian.org> and many contributors, see the AUTHORS
+# file.
+#
+# Permission to use, copy, modify, and distribute this software for any purpose
+# with or without fee is hereby granted, provided that the above copyright
+# notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN
+# NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+# OR OTHER DEALINGS IN THE SOFTWARE.
+#
+# Except as contained in this notice, the name of a copyright holder shall not be
+# used in advertising or otherwise to promote the sale, use or other dealings in
+# this Software without prior written authorization of the copyright holder.
+#
+# SPDX-License-Identifier: curl
+
+# Stop on errors and on usage of unset variables.
+set -eu
+
+VERSION="2025.05.26"
+
+PROGRAM_NAME="$(basename "$0")"
+readonly PROGRAM_NAME
+
+# Display the version.
+print_version()
+{
+    cat << _EOF_
+${VERSION}
+_EOF_
+}
+
+# Display the program usage.
+usage()
+{
+    cat << _EOF_
+${PROGRAM_NAME} -- a simple wrapper around curl to easily download files.
+
+Usage: ${PROGRAM_NAME} <URL>...
+       ${PROGRAM_NAME} [--curl-options <CURL_OPTIONS>]... [--no-decode-filename] [-o|-O|--output <PATH>] [--dry-run] [--] <URL>...
+       ${PROGRAM_NAME} [--curl-options=<CURL_OPTIONS>]... [--no-decode-filename] [--output=<PATH>] [--dry-run] [--] <URL>...
+       ${PROGRAM_NAME} -h|--help
+       ${PROGRAM_NAME} -V|--version
+
+Options:
+
+  --curl-options <CURL_OPTIONS>: Specify extra options to be passed when invoking curl. May be
+                                 specified more than once.
+
+  -o, -O, --output <PATH>: Use the provided output path instead of getting it from the URL. If
+                           multiple URLs are provided, resulting files share the same name with a
+                           number appended to the end (curl >= 7.83.0). If this option is provided
+                           multiple times, only the last value is considered.
+
+  --no-decode-filename: Don't percent-decode the output filename, even if the percent-encoding in
+                        the URL was done by wcurl, e.g.: The URL contained whitespaces.
+
+  --dry-run: Don't actually execute curl, just print what would be invoked.
+
+  -V, --version: Print version information.
+
+  -h, --help: Print this usage message.
+
+  <CURL_OPTIONS>: Any option supported by curl can be set here. This is not used by wcurl; it is
+                 instead forwarded to the curl invocation.
+
+  <URL>: URL to be downloaded. Anything that is not a parameter is considered
+         an URL. Whitespaces are percent-encoded and the URL is passed to curl, which
+         then performs the parsing. May be specified more than once.
+_EOF_
+}
+
+# Display an error message and bail out.
+error()
+{
+    printf "%s\n" "$*" > /dev/stderr
+    exit 1
+}
+
+# Extra curl options provided by the user.
+# This is set per-URL for every URL provided.
+# Some options are global, but we are erroring on the side of needlesly setting
+# them multiple times instead of causing issues with parameters that needs to
+# be set per-URL.
+CURL_OPTIONS=""
+
+# The URLs to be downloaded.
+URLS=""
+
+# Variable used to be set to the percent-decoded filename parsed from the URL, unless
+# --output or --no-decode-filename are used.
+OUTPUT_PATH=""
+HAS_USER_SET_OUTPUT="false"
+
+# The parameters that are passed per-URL to curl.
+readonly PER_URL_PARAMETERS="\
+    --fail \
+    --globoff \
+    --location \
+    --proto-default https \
+    --remote-time \
+    --retry 5 "
+
+# Whether to invoke curl or not.
+DRY_RUN="false"
+
+# Sanitize parameters.
+sanitize()
+{
+    if [ -z "${URLS}" ]; then
+        error "You must provide at least one URL to download."
+    fi
+
+    readonly CURL_OPTIONS URLS DRY_RUN HAS_USER_SET_OUTPUT
+}
+
+# Indicate via exit code whether the string given in the first parameter
+# consists solely of characters from the string given in the second parameter.
+# In other words, it returns 0 if the first parameter only contains characters
+# from the second parameter, e.g.: Are $1 characters a subset of $2 characters?
+is_subset_of()
+{
+    case "${1}" in
+        *[!${2}]*|'') return 1;;
+    esac
+}
+
+# Print the given string percent-decoded.
+percent_decode()
+{
+    # Encodings of control characters (00-1F) are passed through without decoding.
+    # Iterate on the input character-by-character, decoding it.
+    printf "%s\n" "${1}" | fold -w1 | while IFS= read -r decode_out; do
+        # If character is a "%", read the next character as decode_hex1.
+        if [ "${decode_out}" = % ] && IFS= read -r decode_hex1; then
+            decode_out="${decode_out}${decode_hex1}"
+            # If there's one more character, read it as decode_hex2.
+            if IFS= read -r decode_hex2; then
+                decode_out="${decode_out}${decode_hex2}"
+                # Skip decoding if this is a control character (00-1F).
+                # Skip decoding if DECODE_FILENAME is not "true".
+                if is_subset_of "${decode_hex1}" "23456789abcdefABCDEF" && \
+                    is_subset_of "${decode_hex2}" "0123456789abcdefABCDEF" && \
+                    [ "${DECODE_FILENAME}" = "true" ]; then
+                    # Use printf to decode it into octal and then decode it to the final format.
+                    decode_out="$(printf "%b" "\\$(printf %o "0x${decode_hex1}${decode_hex2}")")"
+                fi
+            fi
+        fi
+        printf %s "${decode_out}"
+    done
+}
+
+# Print the percent-decoded filename portion of the given URL.
+get_url_filename()
+{
+    # Remove protocol and query string if present.
+    hostname_and_path="$(printf %s "${1}" | sed -e 's,^[^/]*//,,' -e 's,?.*$,,')"
+    # If what remains contains a slash, there's a path; return it percent-decoded.
+    case "${hostname_and_path}" in
+        # sed to remove everything preceding the last '/', e.g.: "example/something" becomes "something"
+        */*) percent_decode "$(printf %s "${hostname_and_path}" | sed -e 's,^.*/,,')";;
+    esac
+    # No slash means there was just a hostname and no path; return empty string.
+}
+
+# Execute curl with the list of URLs provided by the user.
+exec_curl()
+{
+    CMD="curl "
+
+    # Store version to check if it supports --no-clobber and --parallel.
+    curl_version=$($CMD --version | cut -f2 -d' ' | head -n1)
+    curl_version_major=$(echo "$curl_version" | cut -f1 -d.)
+    curl_version_minor=$(echo "$curl_version" | cut -f2 -d.)
+
+    CURL_HAS_NO_CLOBBER=""
+    CURL_HAS_PARALLEL=""
+    # --no-clobber is only supported since 7.83.0.
+    # --parallel is only supported since 7.66.0.
+    if [ "${curl_version_major}" -ge 8 ]; then
+        CURL_HAS_NO_CLOBBER="--no-clobber"
+        CURL_HAS_PARALLEL="--parallel"
+    elif [ "${curl_version_major}" -eq 7 ];then
+        if [ "${curl_version_minor}" -ge 83 ]; then
+            CURL_HAS_NO_CLOBBER="--no-clobber"
+        fi
+        if [ "${curl_version_minor}" -ge 66 ]; then
+            CURL_HAS_PARALLEL="--parallel"
+        fi
+    fi
+
+    # Detecting whether we need --parallel.  It's easier to rely on
+    # the shell's argument parsing.
+    # shellcheck disable=SC2086
+    set -- $URLS
+
+    if [ "$#" -gt 1 ]; then
+        CURL_PARALLEL="$CURL_HAS_PARALLEL"
+    else
+        CURL_PARALLEL=""
+    fi
+
+    # Start assembling the command.
+    #
+    # We use 'set --' here (again) because (a) we don't have arrays on
+    # POSIX shell, and (b) we need better control over the way we
+    # split arguments.
+    #
+    # shellcheck disable=SC2086
+    set -- ${CMD} ${CURL_PARALLEL}
+
+    NEXT_PARAMETER=""
+    for url in ${URLS}; do
+        # If the user did not provide an output path, define one.
+        if [ "${HAS_USER_SET_OUTPUT}" = "false" ]; then
+            OUTPUT_PATH="$(get_url_filename "${url}")"
+            # If we could not get a path from the URL, use the default: index.html.
+            [ -z "${OUTPUT_PATH}" ] && OUTPUT_PATH=index.html
+        fi
+        # shellcheck disable=SC2086
+        set -- "$@" ${NEXT_PARAMETER} ${PER_URL_PARAMETERS} ${CURL_HAS_NO_CLOBBER} ${CURL_OPTIONS} --output "${OUTPUT_PATH}" "${url}"
+        NEXT_PARAMETER="--next"
+    done
+
+    if [ "${DRY_RUN}" = "false" ]; then
+        exec "$@"
+    else
+        printf "%s\n" "$@"
+    fi
+}
+
+# Default to decoding the output filename
+DECODE_FILENAME="true"
+
+# Use "${1-}" in order to avoid errors because of 'set -u'.
+while [ -n "${1-}" ]; do
+    case "${1}" in
+        --curl-options=*)
+            opt=$(printf "%s\n" "${1}" | sed 's/^--curl-options=//')
+            CURL_OPTIONS="${CURL_OPTIONS} ${opt}"
+            ;;
+
+        --curl-options)
+            shift
+            CURL_OPTIONS="${CURL_OPTIONS} ${1}"
+            ;;
+
+        --dry-run)
+            DRY_RUN="true"
+            ;;
+
+        --output=*)
+            opt=$(printf "%s\n" "${1}" | sed 's/^--output=//')
+            HAS_USER_SET_OUTPUT="true"
+            OUTPUT_PATH="${opt}"
+            ;;
+
+        -o|-O|--output)
+            shift
+            HAS_USER_SET_OUTPUT="true"
+            OUTPUT_PATH="${1}"
+            ;;
+
+        -o*|-O*)
+            opt=$(printf "%s\n" "${1}" | sed 's/^-[oO]//')
+            HAS_USER_SET_OUTPUT="true"
+            OUTPUT_PATH="${opt}"
+            ;;
+
+        --no-decode-filename)
+            DECODE_FILENAME="false"
+            ;;
+
+        -h|--help)
+            usage
+            exit 0
+            ;;
+
+        -V|--version)
+            print_version
+            exit 0
+            ;;
+
+        --)
+            # This is the start of the list of URLs.
+            shift
+            for url in "$@"; do
+                # Encode whitespaces into %20, since wget supports those URLs.
+                newurl=$(printf "%s\n" "${url}" | sed 's/ /%20/g')
+                URLS="${URLS} ${newurl}"
+            done
+            break
+            ;;
+
+        -*)
+            error "Unknown option: '$1'."
+            ;;
+
+        *)
+            # This must be a URL.
+            # Encode whitespaces into %20, since wget supports those URLs.
+            newurl=$(printf "%s\n" "${1}" | sed 's/ /%20/g')
+            URLS="${URLS} ${newurl}"
+            ;;
+    esac
+    shift
+done
+
+sanitize
+exec_curl
diff --git a/extra/protobuf/protobuf-24.4/third_party/utf8_range/CMakeLists.txt b/extra/protobuf/protobuf-24.4/third_party/utf8_range/CMakeLists.txt
index 344952d38cf5..18bc5dc3b9f9 100644
--- a/extra/protobuf/protobuf-24.4/third_party/utf8_range/CMakeLists.txt
+++ b/extra/protobuf/protobuf-24.4/third_party/utf8_range/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required (VERSION 3.5)
+# cmake_minimum_required (VERSION 3.5)
 project (utf8_range C CXX)
 
 # option() honor variables
diff --git a/man/comp_err.1 b/man/comp_err.1
index e4f400d00970..e75d6de29a0b 100644
--- a/man/comp_err.1
+++ b/man/comp_err.1
@@ -2,12 +2,12 @@
 .\"     Title: comp_err
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "COMP_ERR" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "COMP_ERR" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/ibd2sdi.1 b/man/ibd2sdi.1
index 619f446b9a4d..302948ff5afc 100644
--- a/man/ibd2sdi.1
+++ b/man/ibd2sdi.1
@@ -2,12 +2,12 @@
 .\"     Title: ibd2sdi
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "IBD2SDI" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "IBD2SDI" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
@@ -221,7 +221,7 @@ Display version information and exit\&. For example:
 .RS 4
 .\}
 .nf
-ibd2sdi  Ver 8\&.4\&.4 for Linux on x86_64 (Source distribution)
+ibd2sdi  Ver 8\&.4\&.5 for Linux on x86_64 (Source distribution)
 .fi
 .if n \{\
 .RE
diff --git a/man/innochecksum.1 b/man/innochecksum.1
index 7b661e3d5f8d..74db3603d7d8 100644
--- a/man/innochecksum.1
+++ b/man/innochecksum.1
@@ -2,12 +2,12 @@
 .\"     Title: innochecksum
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "INNOCHECKSUM" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "INNOCHECKSUM" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/my_print_defaults.1 b/man/my_print_defaults.1
index 4f46f3483b23..9b923b1df021 100644
--- a/man/my_print_defaults.1
+++ b/man/my_print_defaults.1
@@ -2,12 +2,12 @@
 .\"     Title: my_print_defaults
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "MY_PRINT_DEFAULTS" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "MY_PRINT_DEFAULTS" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/myisam_ftdump.1 b/man/myisam_ftdump.1
index a40f4ead9fee..7e7126df10ad 100644
--- a/man/myisam_ftdump.1
+++ b/man/myisam_ftdump.1
@@ -2,12 +2,12 @@
 .\"     Title: myisam_ftdump
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "MYISAM_FTDUMP" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "MYISAM_FTDUMP" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/myisamchk.1 b/man/myisamchk.1
index 5258f55f9b84..ad601acd6844 100644
--- a/man/myisamchk.1
+++ b/man/myisamchk.1
@@ -2,12 +2,12 @@
 .\"     Title: myisamchk
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "MYISAMCHK" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "MYISAMCHK" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/myisamlog.1 b/man/myisamlog.1
index acaa27d27e7a..bc722d55d462 100644
--- a/man/myisamlog.1
+++ b/man/myisamlog.1
@@ -2,12 +2,12 @@
 .\"     Title: myisamlog
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "MYISAMLOG" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "MYISAMLOG" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/myisampack.1 b/man/myisampack.1
index eed75591f280..80592399a395 100644
--- a/man/myisampack.1
+++ b/man/myisampack.1
@@ -2,12 +2,12 @@
 .\"     Title: myisampack
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "MYISAMPACK" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "MYISAMPACK" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/mysql.1 b/man/mysql.1
index 2401a1ed439d..f7a96d433823 100644
--- a/man/mysql.1
+++ b/man/mysql.1
@@ -2,12 +2,12 @@
 .\"     Title: mysql
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "MYSQL" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "MYSQL" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/mysql.server.1 b/man/mysql.server.1
index ef0a8d0092c7..36498006c34f 100644
--- a/man/mysql.server.1
+++ b/man/mysql.server.1
@@ -2,12 +2,12 @@
 .\"     Title: mysql.server
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "MYSQL\&.SERVER" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "MYSQL\&.SERVER" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/mysql_config.1 b/man/mysql_config.1
index 9d9cdf06d264..ab1c6e5d60b0 100644
--- a/man/mysql_config.1
+++ b/man/mysql_config.1
@@ -2,12 +2,12 @@
 .\"     Title: mysql_config
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "MYSQL_CONFIG" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "MYSQL_CONFIG" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/mysql_config_editor.1 b/man/mysql_config_editor.1
index afce6acfa710..516bb1541cba 100644
--- a/man/mysql_config_editor.1
+++ b/man/mysql_config_editor.1
@@ -2,12 +2,12 @@
 .\"     Title: mysql_config_editor
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "MYSQL_CONFIG_EDITOR" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "MYSQL_CONFIG_EDITOR" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/mysql_migrate_keyring.1 b/man/mysql_migrate_keyring.1
index 1d3c6a2c30d0..ccc3cc8d3b9c 100644
--- a/man/mysql_migrate_keyring.1
+++ b/man/mysql_migrate_keyring.1
@@ -2,12 +2,12 @@
 .\"     Title: mysql_migrate_keyring
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "MYSQL_MIGRATE_KEYRING" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "MYSQL_MIGRATE_KEYRING" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/mysql_secure_installation.1 b/man/mysql_secure_installation.1
index 3a879a83d6f8..5d8b18a53b8e 100644
--- a/man/mysql_secure_installation.1
+++ b/man/mysql_secure_installation.1
@@ -2,12 +2,12 @@
 .\"     Title: mysql_secure_installation
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "MYSQL_SECURE_INSTALLATION" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "MYSQL_SECURE_INSTALLATION" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/mysql_tzinfo_to_sql.1 b/man/mysql_tzinfo_to_sql.1
index 2611e96e28fc..c4a1bdc90ff0 100644
--- a/man/mysql_tzinfo_to_sql.1
+++ b/man/mysql_tzinfo_to_sql.1
@@ -2,12 +2,12 @@
 .\"     Title: mysql_tzinfo_to_sql
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "MYSQL_TZINFO_TO_SQL" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "MYSQL_TZINFO_TO_SQL" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/mysqladmin.1 b/man/mysqladmin.1
index 1eefc555a278..7109bbc97a4a 100644
--- a/man/mysqladmin.1
+++ b/man/mysqladmin.1
@@ -2,12 +2,12 @@
 .\"     Title: mysqladmin
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "MYSQLADMIN" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "MYSQLADMIN" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/mysqlbinlog.1 b/man/mysqlbinlog.1
index a63e6af3fdda..073a0ab7231b 100644
--- a/man/mysqlbinlog.1
+++ b/man/mysqlbinlog.1
@@ -2,12 +2,12 @@
 .\"     Title: mysqlbinlog
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "MYSQLBINLOG" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "MYSQLBINLOG" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/mysqlcheck.1 b/man/mysqlcheck.1
index 35db7339ca73..d8a72973c58c 100644
--- a/man/mysqlcheck.1
+++ b/man/mysqlcheck.1
@@ -2,12 +2,12 @@
 .\"     Title: mysqlcheck
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "MYSQLCHECK" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "MYSQLCHECK" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/mysqld.8 b/man/mysqld.8
index 634acd5ea75f..8b537dc078f3 100644
--- a/man/mysqld.8
+++ b/man/mysqld.8
@@ -2,12 +2,12 @@
 .\"     Title: mysqld
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "MYSQLD" "8" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "MYSQLD" "8" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/mysqld_multi.1 b/man/mysqld_multi.1
index 9fa62e1820df..ea180b82406e 100644
--- a/man/mysqld_multi.1
+++ b/man/mysqld_multi.1
@@ -2,12 +2,12 @@
 .\"     Title: mysqld_multi
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "MYSQLD_MULTI" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "MYSQLD_MULTI" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/mysqld_safe.1 b/man/mysqld_safe.1
index 7077c9b903ec..625988e50d48 100644
--- a/man/mysqld_safe.1
+++ b/man/mysqld_safe.1
@@ -2,12 +2,12 @@
 .\"     Title: mysqld_safe
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "MYSQLD_SAFE" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "MYSQLD_SAFE" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/mysqldump.1 b/man/mysqldump.1
index 2d648ebe4fa3..c854b3573611 100644
--- a/man/mysqldump.1
+++ b/man/mysqldump.1
@@ -2,12 +2,12 @@
 .\"     Title: mysqldump
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "MYSQLDUMP" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "MYSQLDUMP" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/mysqldumpslow.1 b/man/mysqldumpslow.1
index 1fbb3c2298bb..20d7f519e325 100644
--- a/man/mysqldumpslow.1
+++ b/man/mysqldumpslow.1
@@ -2,12 +2,12 @@
 .\"     Title: mysqldumpslow
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "MYSQLDUMPSLOW" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "MYSQLDUMPSLOW" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/mysqlimport.1 b/man/mysqlimport.1
index 69bb196db625..29e98cb58411 100644
--- a/man/mysqlimport.1
+++ b/man/mysqlimport.1
@@ -2,12 +2,12 @@
 .\"     Title: mysqlimport
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "MYSQLIMPORT" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "MYSQLIMPORT" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/mysqlrouter.1 b/man/mysqlrouter.1
index 8cf46b8d6202..421f854d6cc6 100644
--- a/man/mysqlrouter.1
+++ b/man/mysqlrouter.1
@@ -2,12 +2,12 @@
 .\"     Title: mysqlrouter
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Router
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "MYSQLROUTER" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Router"
+.TH "MYSQLROUTER" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Router"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
@@ -96,7 +96,7 @@ Displays the version number and related information of the application, and exit
 .\}
 .nf
 $> \fBmysqlrouter \-\-version\fR
-MySQL Router v8\&.4\&.4 on Linux (64\-bit) (GPL community edition)
+MySQL Router v8\&.4\&.5 on Linux (64\-bit) (GPL community edition)
 .fi
 .if n \{\
 .RE
diff --git a/man/mysqlrouter_keyring.1 b/man/mysqlrouter_keyring.1
index b7dd89bfbef4..f05d7ed3c194 100644
--- a/man/mysqlrouter_keyring.1
+++ b/man/mysqlrouter_keyring.1
@@ -2,12 +2,12 @@
 .\"     Title: mysqlrouter_keyring
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Router
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "MYSQLROUTER_KEYRING" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Router"
+.TH "MYSQLROUTER_KEYRING" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Router"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/mysqlrouter_passwd.1 b/man/mysqlrouter_passwd.1
index e0cb891513fa..cf0202bd9c95 100644
--- a/man/mysqlrouter_passwd.1
+++ b/man/mysqlrouter_passwd.1
@@ -2,12 +2,12 @@
 .\"     Title: mysqlrouter_passwd
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Router
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "MYSQLROUTER_PASSWD" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Router"
+.TH "MYSQLROUTER_PASSWD" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Router"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/mysqlrouter_plugin_info.1 b/man/mysqlrouter_plugin_info.1
index 1cddc2f42e00..94fa5d10ce83 100644
--- a/man/mysqlrouter_plugin_info.1
+++ b/man/mysqlrouter_plugin_info.1
@@ -2,12 +2,12 @@
 .\"     Title: mysqlrouter_plugin_info
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Router
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "MYSQLROUTER_PLUGIN_INFO" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Router"
+.TH "MYSQLROUTER_PLUGIN_INFO" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Router"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
@@ -54,7 +54,7 @@ To print help information:
 To print application version:
   \&./mysqlrouter_plugin_info \-\-version
 $> \&./bin/mysqlrouter_plugin_info \-\-version
-MySQLRouter Plugin Info App 8\&.0\&.3
+MySQLRouter Plugin Info App 8\&.4\&.3
 .fi
 .if n \{\
 .RE
diff --git a/man/mysqlshow.1 b/man/mysqlshow.1
index b325f4dcee49..105227530ecf 100644
--- a/man/mysqlshow.1
+++ b/man/mysqlshow.1
@@ -2,12 +2,12 @@
 .\"     Title: mysqlshow
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "MYSQLSHOW" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "MYSQLSHOW" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/mysqlslap.1 b/man/mysqlslap.1
index b48427cd4608..5971b6991968 100644
--- a/man/mysqlslap.1
+++ b/man/mysqlslap.1
@@ -2,12 +2,12 @@
 .\"     Title: mysqlslap
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "MYSQLSLAP" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "MYSQLSLAP" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/ndb_blob_tool.1 b/man/ndb_blob_tool.1
index 27238c7238eb..bf45a3d83d58 100644
--- a/man/ndb_blob_tool.1
+++ b/man/ndb_blob_tool.1
@@ -2,12 +2,12 @@
 .\"     Title: ndb_blob_tool
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "NDB_BLOB_TOOL" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "NDB_BLOB_TOOL" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/ndb_config.1 b/man/ndb_config.1
index 624bd8c88a3b..b512c0efef8e 100644
--- a/man/ndb_config.1
+++ b/man/ndb_config.1
@@ -2,12 +2,12 @@
 .\"     Title: ndb_config
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "NDB_CONFIG" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "NDB_CONFIG" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/ndb_delete_all.1 b/man/ndb_delete_all.1
index 8a94184c7064..890a59bc5a0a 100644
--- a/man/ndb_delete_all.1
+++ b/man/ndb_delete_all.1
@@ -2,12 +2,12 @@
 .\"     Title: ndb_delete_all
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "NDB_DELETE_ALL" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "NDB_DELETE_ALL" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/ndb_desc.1 b/man/ndb_desc.1
index 8b80a59d28d4..ad8be9f80650 100644
--- a/man/ndb_desc.1
+++ b/man/ndb_desc.1
@@ -2,12 +2,12 @@
 .\"     Title: ndb_desc
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "NDB_DESC" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "NDB_DESC" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/ndb_drop_index.1 b/man/ndb_drop_index.1
index c0a3f0b1d364..0b509cf8d50b 100644
--- a/man/ndb_drop_index.1
+++ b/man/ndb_drop_index.1
@@ -2,12 +2,12 @@
 .\"     Title: ndb_drop_index
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "NDB_DROP_INDEX" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "NDB_DROP_INDEX" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/ndb_drop_table.1 b/man/ndb_drop_table.1
index c878237c225d..5d59be7838e9 100644
--- a/man/ndb_drop_table.1
+++ b/man/ndb_drop_table.1
@@ -2,12 +2,12 @@
 .\"     Title: ndb_drop_table
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "NDB_DROP_TABLE" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "NDB_DROP_TABLE" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/ndb_error_reporter.1 b/man/ndb_error_reporter.1
index 15c267f8995b..80ac588b265b 100644
--- a/man/ndb_error_reporter.1
+++ b/man/ndb_error_reporter.1
@@ -2,12 +2,12 @@
 .\"     Title: ndb_error_reporter
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "NDB_ERROR_REPORTER" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "NDB_ERROR_REPORTER" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/ndb_import.1 b/man/ndb_import.1
index 0f0518cd4b27..0c1d5719e77e 100644
--- a/man/ndb_import.1
+++ b/man/ndb_import.1
@@ -2,12 +2,12 @@
 .\"     Title: ndb_import
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "NDB_IMPORT" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "NDB_IMPORT" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/ndb_index_stat.1 b/man/ndb_index_stat.1
index 671a066ca0bd..a59adf178687 100644
--- a/man/ndb_index_stat.1
+++ b/man/ndb_index_stat.1
@@ -2,12 +2,12 @@
 .\"     Title: ndb_index_stat
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "NDB_INDEX_STAT" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "NDB_INDEX_STAT" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/ndb_mgm.1 b/man/ndb_mgm.1
index 100b75be3757..c2a3095b8d52 100644
--- a/man/ndb_mgm.1
+++ b/man/ndb_mgm.1
@@ -2,12 +2,12 @@
 .\"     Title: ndb_mgm
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "NDB_MGM" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "NDB_MGM" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/ndb_mgmd.8 b/man/ndb_mgmd.8
index ebfa8a5292ba..1e87f139d007 100644
--- a/man/ndb_mgmd.8
+++ b/man/ndb_mgmd.8
@@ -2,12 +2,12 @@
 .\"     Title: ndb_mgmd
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "NDB_MGMD" "8" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "NDB_MGMD" "8" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/ndb_move_data.1 b/man/ndb_move_data.1
index d3f481a97343..43a9ded07d00 100644
--- a/man/ndb_move_data.1
+++ b/man/ndb_move_data.1
@@ -2,12 +2,12 @@
 .\"     Title: ndb_move_data
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "NDB_MOVE_DATA" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "NDB_MOVE_DATA" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/ndb_perror.1 b/man/ndb_perror.1
index d5f7e8f0953a..fa7ba8eb97d2 100644
--- a/man/ndb_perror.1
+++ b/man/ndb_perror.1
@@ -2,12 +2,12 @@
 .\"     Title: ndb_perror
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "NDB_PERROR" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "NDB_PERROR" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/ndb_print_backup_file.1 b/man/ndb_print_backup_file.1
index 3861c26ab7c0..f5a6f993b22f 100644
--- a/man/ndb_print_backup_file.1
+++ b/man/ndb_print_backup_file.1
@@ -2,12 +2,12 @@
 .\"     Title: ndb_print_backup_file
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "NDB_PRINT_BACKUP_FILE" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "NDB_PRINT_BACKUP_FILE" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/ndb_print_file.1 b/man/ndb_print_file.1
index abee9cc55e8c..f0715a54a387 100644
--- a/man/ndb_print_file.1
+++ b/man/ndb_print_file.1
@@ -2,12 +2,12 @@
 .\"     Title: ndb_print_file
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "NDB_PRINT_FILE" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "NDB_PRINT_FILE" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/ndb_print_frag_file.1 b/man/ndb_print_frag_file.1
index 13a9c48fc7e5..3c71615fd8ae 100644
--- a/man/ndb_print_frag_file.1
+++ b/man/ndb_print_frag_file.1
@@ -2,12 +2,12 @@
 .\"     Title: ndb_print_frag_file
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "NDB_PRINT_FRAG_FILE" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "NDB_PRINT_FRAG_FILE" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/ndb_print_schema_file.1 b/man/ndb_print_schema_file.1
index f53555c58a00..5efccfc6d3d7 100644
--- a/man/ndb_print_schema_file.1
+++ b/man/ndb_print_schema_file.1
@@ -2,12 +2,12 @@
 .\"     Title: ndb_print_schema_file
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "NDB_PRINT_SCHEMA_FILE" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "NDB_PRINT_SCHEMA_FILE" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/ndb_print_sys_file.1 b/man/ndb_print_sys_file.1
index ead824db7ecd..36aa00009042 100644
--- a/man/ndb_print_sys_file.1
+++ b/man/ndb_print_sys_file.1
@@ -2,12 +2,12 @@
 .\"     Title: ndb_print_sys_file
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "NDB_PRINT_SYS_FILE" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "NDB_PRINT_SYS_FILE" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/ndb_redo_log_reader.1 b/man/ndb_redo_log_reader.1
index 94f825d80675..7b07fd0435ce 100644
--- a/man/ndb_redo_log_reader.1
+++ b/man/ndb_redo_log_reader.1
@@ -2,12 +2,12 @@
 .\"     Title: ndb_redo_log_reader
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "NDB_REDO_LOG_READER" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "NDB_REDO_LOG_READER" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/ndb_restore.1 b/man/ndb_restore.1
index 03d36630411b..2ccbd6657c3a 100644
--- a/man/ndb_restore.1
+++ b/man/ndb_restore.1
@@ -2,12 +2,12 @@
 .\"     Title: ndb_restore
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "NDB_RESTORE" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "NDB_RESTORE" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/ndb_secretsfile_reader.1 b/man/ndb_secretsfile_reader.1
index 8c380fb7f1f2..f4897e408504 100644
--- a/man/ndb_secretsfile_reader.1
+++ b/man/ndb_secretsfile_reader.1
@@ -2,12 +2,12 @@
 .\"     Title: ndb_secretsfile_reader
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "NDB_SECRETSFILE_READER" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "NDB_SECRETSFILE_READER" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/ndb_select_all.1 b/man/ndb_select_all.1
index e5aa362460b2..1c2790306710 100644
--- a/man/ndb_select_all.1
+++ b/man/ndb_select_all.1
@@ -2,12 +2,12 @@
 .\"     Title: ndb_select_all
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "NDB_SELECT_ALL" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "NDB_SELECT_ALL" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/ndb_select_count.1 b/man/ndb_select_count.1
index d57fda11e3f6..f1f23c1b6163 100644
--- a/man/ndb_select_count.1
+++ b/man/ndb_select_count.1
@@ -2,12 +2,12 @@
 .\"     Title: ndb_select_count
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "NDB_SELECT_COUNT" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "NDB_SELECT_COUNT" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/ndb_show_tables.1 b/man/ndb_show_tables.1
index dee17848bdb8..7734910f655d 100644
--- a/man/ndb_show_tables.1
+++ b/man/ndb_show_tables.1
@@ -2,12 +2,12 @@
 .\"     Title: ndb_show_tables
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "NDB_SHOW_TABLES" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "NDB_SHOW_TABLES" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/ndb_sign_keys.1 b/man/ndb_sign_keys.1
index 37939f1e8b34..1b30a68bfd05 100644
--- a/man/ndb_sign_keys.1
+++ b/man/ndb_sign_keys.1
@@ -2,12 +2,12 @@
 .\"     Title: ndb_sign_keys
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "NDB_SIGN_KEYS" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "NDB_SIGN_KEYS" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/ndb_size.pl.1 b/man/ndb_size.pl.1
index fe115c51e545..0d891072b020 100644
--- a/man/ndb_size.pl.1
+++ b/man/ndb_size.pl.1
@@ -2,12 +2,12 @@
 .\"     Title: ndb_size.pl
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "NDB_SIZE\&.PL" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "NDB_SIZE\&.PL" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/ndb_top.1 b/man/ndb_top.1
index 0a11649878dc..7f5d16927203 100644
--- a/man/ndb_top.1
+++ b/man/ndb_top.1
@@ -2,12 +2,12 @@
 .\"     Title: ndb_top
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "NDB_TOP" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "NDB_TOP" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/ndb_waiter.1 b/man/ndb_waiter.1
index 52d1b0a51803..0e4b7e5c93a4 100644
--- a/man/ndb_waiter.1
+++ b/man/ndb_waiter.1
@@ -2,12 +2,12 @@
 .\"     Title: ndb_waiter
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "NDB_WAITER" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "NDB_WAITER" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/ndbd.8 b/man/ndbd.8
index 066e2d261d29..77f71593c2fb 100644
--- a/man/ndbd.8
+++ b/man/ndbd.8
@@ -2,12 +2,12 @@
 .\"     Title: ndbd
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "NDBD" "8" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "NDBD" "8" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/ndbinfo_select_all.1 b/man/ndbinfo_select_all.1
index cad143918a28..b2268e28bf0d 100644
--- a/man/ndbinfo_select_all.1
+++ b/man/ndbinfo_select_all.1
@@ -2,12 +2,12 @@
 .\"     Title: ndbinfo_select_all
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "NDBINFO_SELECT_ALL" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "NDBINFO_SELECT_ALL" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/ndbmtd.8 b/man/ndbmtd.8
index f5c37f6b0926..5ecc33812d8f 100644
--- a/man/ndbmtd.8
+++ b/man/ndbmtd.8
@@ -2,12 +2,12 @@
 .\"     Title: ndbmtd
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "NDBMTD" "8" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "NDBMTD" "8" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/ndbxfrm.1 b/man/ndbxfrm.1
index f7bdfc56f0a0..a118f7db4305 100644
--- a/man/ndbxfrm.1
+++ b/man/ndbxfrm.1
@@ -2,12 +2,12 @@
 .\"     Title: ndbxfrm
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "NDBXFRM" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "NDBXFRM" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/man/perror.1 b/man/perror.1
index 0874343891a9..0611e1935352 100644
--- a/man/perror.1
+++ b/man/perror.1
@@ -2,12 +2,12 @@
 .\"     Title: perror
 .\"    Author: [FIXME: author] [see http://docbook.sf.net/el/author]
 .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
-.\"      Date: 03/13/2025
+.\"      Date: 06/13/2025
 .\"    Manual: MySQL Database System
 .\"    Source: MySQL 8.4
 .\"  Language: English
 .\"
-.TH "PERROR" "1" "03/13/2025" "MySQL 8\&.4" "MySQL Database System"
+.TH "PERROR" "1" "06/13/2025" "MySQL 8\&.4" "MySQL Database System"
 .\" -----------------------------------------------------------------
 .\" * Define some portability stuff
 .\" -----------------------------------------------------------------
diff --git a/mysql-test/r/alter_table_partition_id_int_max.result b/mysql-test/r/alter_table_partition_id_int_max.result
new file mode 100644
index 000000000000..7b69d3f787cb
--- /dev/null
+++ b/mysql-test/r/alter_table_partition_id_int_max.result
@@ -0,0 +1,14 @@
+# Adding debug point 'skip_dd_table_access_check' to @@SESSION.debug
+ALTER TABLE mysql.table_partitions AUTO_INCREMENT=2147483646;
+CREATE TABLE `t1` (
+`id` bigint NOT NULL AUTO_INCREMENT,
+`dtstat` bigint NOT NULL,
+PRIMARY KEY (`id`,`dtstat`)
+) ENGINE=InnoDB AUTO_INCREMENT=5808007 PARTITION BY LIST (`dtstat`)
+(PARTITION p_20230601 VALUES IN (20230601) ENGINE = InnoDB,
+PARTITION P_20230201 VALUES IN (20230201) ENGINE = InnoDB);
+ALTER TABLE t1 ADD PARTITION
+(PARTITION P_20240101 VALUES IN (20240101) ENGINE = InnoDB);
+ALTER TABLE t1 TRUNCATE PARTITION P_20240101;
+DROP TABLE t1;
+# Removing debug point 'skip_dd_table_access_check' from @@SESSION.debug
diff --git a/mysql-test/r/mysql_commands.result b/mysql-test/r/mysql_commands.result
new file mode 100644
index 000000000000..79abf59cde2c
--- /dev/null
+++ b/mysql-test/r/mysql_commands.result
@@ -0,0 +1,102 @@
+#
+# WL#16949: A mysql command line option to disable all built-in commands
+#
+# FR1: --commands boolean option added
+1
+1
+2
+2
+3
+3
+4
+4
+5
+5
+6
+6
+# FR1.1: check the default value of --commands
+# test: must not fail
+include/assert_grep.inc [FR1.1: checking the default --commands value]
+include/assert_grep.inc [FR1.1: checking the updated --commands value]
+# FR1.2.1: help short disabled
+ERROR at line 1: Unknown command '\?'.
+# FR1.2.2: clear disabled
+ERROR 1064 (42000) at line 1: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'clear' at line 1
+# FR1.2.3: connect disabled
+ERROR 1064 (42000) at line 1: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'connect' at line 1
+# FR1.2.4: delimier enabled
+# FR1.2.5: edit disabled
+ERROR 1064 (42000) at line 1: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'edit' at line 1
+# FR1.2.6: ego disabled
+ERROR at line 1: Unknown command '\G'.
+# FR1.2.7: exit disabled
+1
+1
+ERROR 1064 (42000) at line 2: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'exit' at line 1
+# FR1.2.8: go enabled
+go
+1
+# FR1.2.9: help disabled
+ERROR 1064 (42000) at line 1: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1
+# FR1.2.10: nopager disabled
+ERROR 1064 (42000) at line 1: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'nopager' at line 1
+# FR1.2.11: notee disabled
+ERROR 1064 (42000) at line 1: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'notee' at line 1
+# FR1.2.12: pager disabled
+ERROR 1064 (42000) at line 1: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'pager more' at line 1
+# FR1.2.13: print disabled
+ERROR 1064 (42000) at line 1: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'print' at line 1
+# FR1.2.14: prompt disabled
+ERROR 1064 (42000) at line 1: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'prompt foo' at line 1
+# FR1.2.15: quit disabled
+ERROR 1064 (42000) at line 1: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'quit' at line 1
+# FR1.2.16: rehash disabled
+ERROR 1064 (42000) at line 1: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'rehash' at line 1
+# FR1.2.17: source disabled
+ERROR 1064 (42000) at line 1: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'source gogo.text' at line 1
+# FR1.2.18: status disabled
+ERROR 1064 (42000) at line 1: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'status' at line 1
+# FR1.2.19: system disabled
+ERROR 1064 (42000) at line 1: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'system echo' at line 1
+# FR1.2.20: tee disabled
+ERROR 1064 (42000) at line 1: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'tee foo' at line 1
+# FR1.2.21: use disabled
+ERROR at line 1: Unknown command '\u'.
+# FR1.2.22: charset disabled
+ERROR 1064 (42000) at line 1: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'charset latin1' at line 1
+# FR1.2.22.1: \c enabled
+# FR1.2.23: warnings disabled
+ERROR 1064 (42000) at line 1: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'warnings' at line 1
+# FR1.2.24: nowarning disabled
+ERROR 1064 (42000) at line 1: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'nowarning' at line 1
+# FR1.2.25: resetconnection disabled
+ERROR 1064 (42000) at line 1: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'resetconnection' at line 1
+# FR1.2.26: query_attributes disabled
+ERROR 1064 (42000) at line 1: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'query_attributes a b' at line 1
+# FR1.2.27: ssl_session_data_print disabled
+ERROR 1064 (42000) at line 1: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'ssl_session_data_print foo' at line 1
+# FR1.3: system-command ignored when commands=off
+# must fail with syntax error
+ERROR 1064 (42000) at line 1: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'system cd foo' at line 1
+# FR1.3: system-command ignored when commands=off in reverse
+# must fail with syntax error
+ERROR 1064 (42000) at line 1: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'system cd foo' at line 1
+# FR1.4: resetconnection enabled when commands is ON
+# FR1.5: system-command=off in effect when commands=on
+# must fail with specific error
+ERROR at line 1: 'system' command received, but the --system-command option is off. Skipping.
+# FR1.5: system-command=off in effect when commands=on in reverse
+# must fail with specific error
+ERROR at line 1: 'system' command received, but the --system-command option is off. Skipping.
+# FR1.6: commands in .cnf files work
+include/assert_grep.inc [FR1.1: the --commands value must be true]
+# FR1.7: --binary-mode=1 overrides --commands
+# must fail with a syntax error
+ERROR 1064 (42000) at line 1: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'system echo foo' at line 1
+trying in reverse
+# must fail with a syntax error
+ERROR 1064 (42000) at line 1: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'system echo foo' at line 1
+# FR1.8: --binary-mode=0 enables --commands
+# must fail with a syntax error
+ERROR 1064 (42000) at line 1: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'system echo foo' at line 1
+# End of tests
diff --git a/mysql-test/suite/ndb/include/get_ndb_connection_id.inc b/mysql-test/suite/ndb/include/get_ndb_connection_id.inc
new file mode 100644
index 000000000000..e61a1c4cd46d
--- /dev/null
+++ b/mysql-test/suite/ndb/include/get_ndb_connection_id.inc
@@ -0,0 +1,20 @@
+#
+# get_ndb_connection_id.inc - get API node id used by this session
+#
+# Usage
+# --source suite/ndb/include/get_ndb_connection_id.inc
+# 
+# $ndb_connection_id will be set to node id of current 
+# session's API connection
+
+--disable_query_log
+--disable_result_log
+create table get_ndb_connection_id(a int primary key) engine=ndb;
+begin;
+insert into get_ndb_connection_id values (1);
+
+let $ndb_connection_id = query_get_value(select client_node_id from ndbinfo.cluster_operations where tableid = (select id from ndbinfo.dict_obj_info where fq_name like '%get_ndb_connection_id%') limit 1, client_node_id, 1);
+rollback;
+drop table get_ndb_connection_id;
+--enable_result_log
+--enable_query_log
diff --git a/mysql-test/suite/ndb/r/ndb_scan_protocol_timeout.result b/mysql-test/suite/ndb/r/ndb_scan_protocol_timeout.result
index 0d86c588bab8..7bc20f2b90a2 100644
--- a/mysql-test/suite/ndb/r/ndb_scan_protocol_timeout.result
+++ b/mysql-test/suite/ndb/r/ndb_scan_protocol_timeout.result
@@ -533,3 +533,25 @@ ops
 0
 drop table test.t1;
 drop table test.t2;
+set @save_debug = @@session.debug;
+SET SESSION debug="+d,ndb_reduced_api_protocol_timeout";
+Determine our NdbApi connection id
+Determine row ids used to control data nodes involved
+Drop down to just the row on node 1
+Block send from node 1 to our API
+Hint transaction to node 2
+a	nodeid
+Send select of all - will hang
+Now cause restart of node 1
+Node 1 is being restarted
+
+Wait for data node to restart
+Reap query - will get timeout error
+ERROR HY000: Got error 4008 'Receive from NDB failed' from NDBCLUSTER
+Issue more queries to check all is well
+select * from test.t1;
+select * from test.t1;
+select * from test.t1;
+Cleanup
+drop table test.t1;
+SET SESSION debug=@save_debug;
diff --git a/mysql-test/suite/ndb/t/ndb_scan_protocol_timeout.test b/mysql-test/suite/ndb/t/ndb_scan_protocol_timeout.test
index 8a20d514c444..4b4928e9b9e9 100644
--- a/mysql-test/suite/ndb/t/ndb_scan_protocol_timeout.test
+++ b/mysql-test/suite/ndb/t/ndb_scan_protocol_timeout.test
@@ -638,3 +638,94 @@ select count(1) as ops from ndbinfo.cluster_operations;
 drop table test.t1;
 drop table test.t2;
 
+
+
+# Cause reduced API timeout
+set @save_debug = @@session.debug;
+SET SESSION debug="+d,ndb_reduced_api_protocol_timeout";
+
+--echo Determine our NdbApi connection id
+--source suite/ndb/include/get_ndb_connection_id.inc
+#--echo My cluster connection is $ndb_connection_id;
+
+--echo Determine row ids used to control data nodes involved
+
+# Put 100 rows into a table
+--disable_query_log
+--disable_result_log
+--let $rows = 100
+create table test.t1 (a int primary key, nodeid int) engine=ndb comment="NDB_TABLE=READ_BACKUP=0";
+
+while ($rows)
+{
+  --eval insert into test.t1 values ($rows, 0)
+  --dec $rows
+}
+
+# Iterate over all rows, determining which data node holds
+# the primary replica + storing that in the row's nodeid column
+#
+--let $rows = query_get_value('select count(1) as c from test.t1', c, 1)
+while ($rows)
+{
+  begin;
+  --eval select * from test.t1 where a = $rows lock in share mode;
+  
+  let $nodeid = query_get_value(select node_id from ndbinfo.cluster_operations where tableid = (select id from ndbinfo.dict_obj_info where fq_name like '%t1%') limit 1, node_id, 1);
+  --eval update test.t1 set nodeid = $nodeid where a=$rows;
+  commit;
+  --dec $rows
+}
+--enable_result_log
+--enable_query_log
+
+# Choose two semi-arbitrary key values, one on each data node
+
+let $node1_row_key = query_get_value('select a from test.t1 where nodeid=1 limit 1', a, 1);
+let $node2_row_key = query_get_value('select a from test.t1 where nodeid=2 limit 1', a, 1);
+
+#--echo Node1 row : $node1_row_key
+#--echo Node2 row : $node2_row_key
+
+--echo Drop down to just the row on node 1
+--disable_query_log
+--eval delete from test.t1 where a != $node1_row_key
+--enable_query_log
+
+--echo Block send from node 1 to our API
+
+--exec $NDB_MGM -e "1 DUMP 9988 $ndb_connection_id" > /dev/null
+
+--echo Hint transaction to node 2
+--disable_query_log
+begin;
+--eval select * from test.t1 where a = $node2_row_key;
+
+--echo Send select of all - will hang
+--send select * from test.t1;
+
+--echo Now cause restart of node 1
+
+--exec $NDB_MGM -e "1 RESTART -a -f"
+
+--echo Wait for data node to restart
+--exec $NDB_WAITER > /dev/null
+
+--echo Reap query - will get timeout error
+--error 1296
+--reap
+--enable_query_log
+
+--echo Issue more queries to check all is well
+--disable_result_log
+select * from test.t1;
+select * from test.t1;
+select * from test.t1;
+--enable_result_log
+
+--echo Cleanup
+
+drop table test.t1;
+
+SET SESSION debug=@save_debug;
+
diff --git a/mysql-test/suite/rpl/t/rpl_channel_creation_under_replica_load.test b/mysql-test/suite/rpl/t/rpl_channel_creation_under_replica_load.test
index 1045a4bf24ad..45344d4429f9 100644
--- a/mysql-test/suite/rpl/t/rpl_channel_creation_under_replica_load.test
+++ b/mysql-test/suite/rpl/t/rpl_channel_creation_under_replica_load.test
@@ -15,6 +15,9 @@
 # Bug#33090128 RANDOM ERROR 1201: COULD NOT INITIALIZE MASTER INFO STRUCTURE; ON CHANGE MASTER
 #
 
+--source include/not_valgrind.inc
+--source include/have_nodebug.inc
+--source include/big_test.inc
 --source include/have_binlog_format_row.inc
 --source include/rpl/init_source_replica.inc
 
diff --git a/mysql-test/t/alter_table_partition_id_int_max.test b/mysql-test/t/alter_table_partition_id_int_max.test
new file mode 100644
index 000000000000..4b785913c9e5
--- /dev/null
+++ b/mysql-test/t/alter_table_partition_id_int_max.test
@@ -0,0 +1,70 @@
+########################################################################
+# ==== Purpose ====
+#
+# This test ensures that ALTER TABLE TRUNCATE PARTITION is
+# successful when the `partition_id` in the `mysql.table_partitions`
+# DD table exceeds the INT_MAX value
+#
+# ==== Implementation ====
+#
+# 1. In Set the AUTO_INCREMENT value in the mysql.table_partitions to
+#    INT_MAX - 1.
+# 2. Create a partitioned table with two partitions.
+# 3. Add a third partition in the table, this should make the
+#    partition_id exceed INT_MAX
+# 4. Ensure that TRUNCATE PARTITION operation is successful without
+#    any errors.
+# 5. Cleanup
+#
+# ==== References ====
+# Bug#35912852: cant truncate partition when partition_id exceed INT_MAX
+########################################################################
+
+# This test needs debug binaries
+--source include/have_debug.inc
+# This test alters the system table so restart is needed
+--source include/force_restart.inc
+
+# Enable the skip_dd_table_access_check debug_point
+--let $debug_point= skip_dd_table_access_check
+--let $debug_type= SESSION
+--source include/add_debug_point.inc
+
+# Before the test ensure that there are no entries in the
+# mysql.table_partitions table
+--assert(`SELECT COUNT(*) = 0 FROM mysql.table_partitions`)
+
+# 1. Set the AUTO_INCREMENT value in the mysql.table_partitions to
+#    INT_MAX - 1.
+ALTER TABLE mysql.table_partitions AUTO_INCREMENT=2147483646;
+
+# 2. Create a partitioned table with two partitions.
+CREATE TABLE `t1` (
+    `id` bigint NOT NULL AUTO_INCREMENT,
+    `dtstat` bigint NOT NULL,
+    PRIMARY KEY (`id`,`dtstat`)
+    ) ENGINE=InnoDB AUTO_INCREMENT=5808007 PARTITION BY LIST (`dtstat`)
+        (PARTITION p_20230601 VALUES IN (20230601) ENGINE = InnoDB,
+         PARTITION P_20230201 VALUES IN (20230201) ENGINE = InnoDB);
+
+# Assert that there are two partitions and the partition_id has reached INT_MAX
+--assert(`SELECT COUNT(*) = 2 FROM mysql.table_partitions`)
+--assert(`SELECT id = 2147483646 FROM mysql.table_partitions WHERE name = "p_20230601"`)
+--assert(`SELECT id = 2147483647 FROM mysql.table_partitions WHERE name = "p_20230201"`)
+
+# 3. Add a third partition in the table, this should make the
+#    partition_id exceed INT_MAX
+ALTER TABLE t1 ADD PARTITION
+    (PARTITION P_20240101 VALUES IN (20240101) ENGINE = InnoDB);
+--assert(`SELECT id = 2147483648 FROM mysql.table_partitions WHERE name = "p_20230601"`)
+--assert(`SELECT id = 2147483649 FROM mysql.table_partitions WHERE name = "p_20230201"`)
+--assert(`SELECT id = 2147483650 FROM mysql.table_partitions WHERE name = "p_20240101"`)
+
+# 4. Ensure that TRUNCATE PARTITION operation is successful without
+#    any errors.
+ALTER TABLE t1 TRUNCATE PARTITION P_20240101;
+
+# 5. Cleanup
+DROP TABLE t1;
+--assert(`SELECT COUNT(*) = 0 FROM mysql.table_partitions`)
+--source include/remove_debug_point.inc
diff --git a/mysql-test/t/mysql_commands.test b/mysql-test/t/mysql_commands.test
new file mode 100644
index 000000000000..a531c3687daa
--- /dev/null
+++ b/mysql-test/t/mysql_commands.test
@@ -0,0 +1,388 @@
+--echo #
+--echo # WL#16949: A mysql command line option to disable all built-in commands
+--echo #
+
+--echo # FR1: --commands boolean option added
+--exec $MYSQL --commands -e "SELECT 1"
+--exec $MYSQL --disable-commands -e "SELECT 2"
+--exec $MYSQL --skip-commands -e "SELECT 3"
+--exec $MYSQL --enable-commands -e "SELECT 4"
+--exec $MYSQL --commands=0 -e "SELECT 5"
+--exec $MYSQL --commands=1 -e "SELECT 6"
+
+--echo # FR1.1: check the default value of --commands
+
+--write_file $MYSQLTEST_VARDIR/tmp/wl16949_fr1.sql
+help;
+EOF
+--echo # test: must not fail
+--exec $MYSQL < $MYSQLTEST_VARDIR/tmp/wl16949_fr1.sql > $MYSQLTEST_VARDIR/tmp/wl16949_fr1.out 2>&1
+--remove_file $MYSQLTEST_VARDIR/tmp/wl16949_fr1.sql
+--remove_file $MYSQLTEST_VARDIR/tmp/wl16949_fr1.out
+
+--exec $MYSQL --help > $MYSQLTEST_VARDIR/tmp/wl16949_fr1_1.out 2>&1
+--let $assert_text=FR1.1: checking the default --commands value
+--let $assert_file=$MYSQLTEST_VARDIR/tmp/wl16949_fr1_1.out
+--let $assert_select=^commands[\\ \\t]*TRUE\$
+--let $assert_count=1
+--source include/assert_grep.inc
+--remove_file $MYSQLTEST_VARDIR/tmp/wl16949_fr1_1.out
+
+--exec $MYSQL --commands=0 --help > $MYSQLTEST_VARDIR/tmp/wl16949_fr1_1.out 2>&1
+--let $assert_text=FR1.1: checking the updated --commands value
+--let $assert_file=$MYSQLTEST_VARDIR/tmp/wl16949_fr1_1.out
+--let $assert_select=^commands[\\ \\t]*FALSE\$
+--let $assert_count=1
+--source include/assert_grep.inc
+--remove_file $MYSQLTEST_VARDIR/tmp/wl16949_fr1_1.out
+
+--echo # FR1.2.1: help short disabled
+--let $filename=$MYSQLTEST_VARDIR/tmp/wl16949_fr1_2_help_short.sql
+--write_file $filename
+\?
+EOF
+--error 1
+--exec $MYSQL --commands=0 < $filename 2>&1
+--remove_file $filename
+
+--echo # FR1.2.2: clear disabled
+--let $filename=$MYSQLTEST_VARDIR/tmp/wl16949_fr1_2_clear.sql
+--write_file $filename
+clear;
+EOF
+--error 1
+--exec $MYSQL --commands=0 < $filename 2>&1
+--remove_file $filename
+
+--echo # FR1.2.3: connect disabled
+--let $filename=$MYSQLTEST_VARDIR/tmp/wl16949_fr1_2_connect.sql
+--write_file $filename
+connect;
+EOF
+--error 1
+--exec $MYSQL --commands=0 < $filename 2>&1
+--remove_file $filename
+
+--echo # FR1.2.4: delimier enabled
+--let $filename=$MYSQLTEST_VARDIR/tmp/wl16949_fr1_2_delimier.sql
+--write_file $filename
+delimiter ;
+EOF
+--exec $MYSQL --commands=0 < $filename 2>&1
+--remove_file $filename
+
+--echo # FR1.2.5: edit disabled
+--let $filename=$MYSQLTEST_VARDIR/tmp/wl16949_fr1_2_edit.sql
+--write_file $filename
+edit;
+EOF
+--error 1
+--exec $MYSQL --commands=0 < $filename 2>&1
+--remove_file $filename
+
+--echo # FR1.2.6: ego disabled
+--let $filename=$MYSQLTEST_VARDIR/tmp/wl16949_fr1_2_ego.sql
+--write_file $filename
+SELECT 1\G
+EOF
+--error 1
+--exec $MYSQL --commands=0 < $filename 2>&1
+--remove_file $filename
+
+--echo # FR1.2.7: exit disabled
+--let $filename=$MYSQLTEST_VARDIR/tmp/wl16949_fr1_2_exit.sql
+--write_file $filename
+SELECT 1;
+exit;
+SELECT 2;
+EOF
+--error 1
+--exec $MYSQL --commands=0 < $filename 2>&1
+--remove_file $filename
+
+--echo # FR1.2.8: go enabled
+--let $filename=$MYSQLTEST_VARDIR/tmp/wl16949_fr1_2_go.sql
+--write_file $filename
+SELECT 1
+go;
+EOF
+--exec $MYSQL --commands=0 < $filename 2>&1
+--remove_file $filename
+
+--echo # FR1.2.9: help disabled
+--let $filename=$MYSQLTEST_VARDIR/tmp/wl16949_fr1_2_help.sql
+--write_file $filename
+help;
+EOF
+--error 1
+--exec $MYSQL --commands=0 < $filename 2>&1
+--remove_file $filename
+
+--echo # FR1.2.10: nopager disabled
+--let $filename=$MYSQLTEST_VARDIR/tmp/wl16949_fr1_2_nopager.sql
+--write_file $filename
+nopager;
+EOF
+--error 1
+--exec $MYSQL --commands=0 < $filename 2>&1
+--remove_file $filename
+
+--echo # FR1.2.11: notee disabled
+--let $filename=$MYSQLTEST_VARDIR/tmp/wl16949_fr1_2_notee.sql
+--write_file $filename
+notee;
+EOF
+--error 1
+--exec $MYSQL --commands=0 < $filename 2>&1
+--remove_file $filename
+
+--echo # FR1.2.12: pager disabled
+--let $filename=$MYSQLTEST_VARDIR/tmp/wl16949_fr1_2_pager.sql
+--write_file $filename
+pager more;
+EOF
+--error 1
+--exec $MYSQL --commands=0 < $filename 2>&1
+--remove_file $filename
+
+--echo # FR1.2.13: print disabled
+--let $filename=$MYSQLTEST_VARDIR/tmp/wl16949_fr1_2_print.sql
+--write_file $filename
+print;
+EOF
+--error 1
+--exec $MYSQL --commands=0 < $filename 2>&1
+--remove_file $filename
+
+--echo # FR1.2.14: prompt disabled
+--let $filename=$MYSQLTEST_VARDIR/tmp/wl16949_fr1_2_prompt.sql
+--write_file $filename
+prompt foo;
+EOF
+--error 1
+--exec $MYSQL --commands=0 < $filename 2>&1
+--remove_file $filename
+
+--echo # FR1.2.15: quit disabled
+--let $filename=$MYSQLTEST_VARDIR/tmp/wl16949_fr1_2_quit.sql
+--write_file $filename
+quit;
+SELECT 2;
+EOF
+--error 1
+--exec $MYSQL --commands=0 < $filename 2>&1
+--remove_file $filename
+
+--echo # FR1.2.16: rehash disabled
+--let $filename=$MYSQLTEST_VARDIR/tmp/wl16949_fr1_2_rehash.sql
+--write_file $filename
+rehash;
+EOF
+--error 1
+--exec $MYSQL --commands=0 < $filename 2>&1
+--remove_file $filename
+
+--echo # FR1.2.17: source disabled
+--let $filename=$MYSQLTEST_VARDIR/tmp/wl16949_fr1_2_source.sql
+--write_file $filename
+source gogo.text;
+EOF
+--error 1
+--exec $MYSQL --commands=0 < $filename 2>&1
+--remove_file $filename
+
+--echo # FR1.2.18: status disabled
+--let $filename=$MYSQLTEST_VARDIR/tmp/wl16949_fr1_2_status.sql
+--write_file $filename
+status;
+EOF
+--error 1
+--exec $MYSQL --commands=0 < $filename 2>&1
+--remove_file $filename
+
+--echo # FR1.2.19: system disabled
+--let $filename=$MYSQLTEST_VARDIR/tmp/wl16949_fr1_2_system.sql
+--write_file $filename
+system echo;
+EOF
+--error 1
+--exec $MYSQL --commands=0 < $filename 2>&1
+--remove_file $filename
+
+--echo # FR1.2.20: tee disabled
+--let $filename=$MYSQLTEST_VARDIR/tmp/wl16949_fr1_2_tee.sql
+--write_file $filename
+tee foo;
+EOF
+--error 1
+--exec $MYSQL --commands=0 < $filename 2>&1
+--remove_file $filename
+
+--echo # FR1.2.21: use disabled
+--let $filename=$MYSQLTEST_VARDIR/tmp/wl16949_fr1_2_use.sql
+--write_file $filename
+\u test;
+EOF
+--error 1
+--exec $MYSQL --commands=0 < $filename 2>&1
+--remove_file $filename
+
+--echo # FR1.2.22: charset disabled
+--let $filename=$MYSQLTEST_VARDIR/tmp/wl16949_fr1_2_charset.sql
+--write_file $filename
+charset latin1;
+EOF
+--error 1
+--exec $MYSQL --commands=0 < $filename 2>&1
+--remove_file $filename
+
+--echo # FR1.2.22.1: \c enabled
+--let $filename=$MYSQLTEST_VARDIR/tmp/wl16949_fr1_2_uscore_c.sql
+--write_file $filename
+\C latin1;
+EOF
+--exec $MYSQL < $filename 2>$filename.2
+--remove_file $filename
+--remove_file $filename.2
+
+
+--echo # FR1.2.23: warnings disabled
+--let $filename=$MYSQLTEST_VARDIR/tmp/wl16949_fr1_2_warnings.sql
+--write_file $filename
+warnings;
+EOF
+--error 1
+--exec $MYSQL --commands=0 < $filename 2>&1
+--remove_file $filename
+
+--echo # FR1.2.24: nowarning disabled
+--let $filename=$MYSQLTEST_VARDIR/tmp/wl16949_fr1_2_nowarning.sql
+--write_file $filename
+nowarning;
+EOF
+--error 1
+--exec $MYSQL --commands=0 < $filename 2>&1
+--remove_file $filename
+
+--echo # FR1.2.25: resetconnection disabled
+--let $filename=$MYSQLTEST_VARDIR/tmp/wl16949_fr1_2_resetconnection.sql
+--write_file $filename
+resetconnection;
+EOF
+--error 1
+--exec $MYSQL --commands=0 < $filename 2>&1
+--remove_file $filename
+
+--echo # FR1.2.26: query_attributes disabled
+--let $filename=$MYSQLTEST_VARDIR/tmp/wl16949_fr1_2_query_attributes.sql
+--write_file $filename
+query_attributes a b;
+EOF
+--error 1
+--exec $MYSQL --commands=0 < $filename 2>&1
+--remove_file $filename
+
+--echo # FR1.2.27: ssl_session_data_print disabled
+--let $filename=$MYSQLTEST_VARDIR/tmp/wl16949_fr1_2_ssl_session_data_print.sql
+--write_file $filename
+ssl_session_data_print foo;
+EOF
+--error 1
+--exec $MYSQL --commands=0 < $filename 2>&1
+--remove_file $filename
+
+
+--echo # FR1.3: system-command ignored when commands=off
+--let $filename=$MYSQLTEST_VARDIR/tmp/wl16949_fr1_3.sql
+--write_file $filename
+system cd foo;
+EOF
+--echo # must fail with syntax error
+--error 1
+--exec $MYSQL --commands=0 --system-command=1 < $filename 2>&1
+--remove_file $filename
+
+--echo # FR1.3: system-command ignored when commands=off in reverse
+--let $filename=$MYSQLTEST_VARDIR/tmp/wl16949_fr1_3_reverse.sql
+--write_file $filename
+system cd foo;
+EOF
+--echo # must fail with syntax error
+--error 1
+--exec $MYSQL --system-command=1 --commands=0 < $filename 2>&1
+--remove_file $filename
+
+--echo # FR1.4: resetconnection enabled when commands is ON
+--let $filename=$MYSQLTEST_VARDIR/tmp/wl16949_fr1_4_resetconnection.sql
+--write_file $filename
+resetconnection;
+EOF
+--exec $MYSQL --commands=1 < $filename 2>&1
+--remove_file $filename
+
+--echo # FR1.5: system-command=off in effect when commands=on
+--let $filename=$MYSQLTEST_VARDIR/tmp/wl16949_fr1_5.sql
+--write_file $filename
+system echo foo;
+EOF
+--echo # must fail with specific error
+--error 1
+--exec $MYSQL --commands=1 --system-command=0 < $filename 2>&1
+--remove_file $filename
+
+--echo # FR1.5: system-command=off in effect when commands=on in reverse
+--let $filename=$MYSQLTEST_VARDIR/tmp/wl16949_fr1_5_reverse.sql
+--write_file $filename
+system echo foo;
+EOF
+--echo # must fail with specific error
+--error 1
+--exec $MYSQL --system-command=0 --commands=1 < $filename 2>&1
+--remove_file $filename
+
+--echo # FR1.6: commands in .cnf files work
+--let $filename=$MYSQLTEST_VARDIR/tmp/wl16949_fr1_6.cnf
+--write_file $filename
+[mysql]
+commands=1
+EOF
+--exec $EXE_MYSQL --defaults-file=$filename --help > $filename.out 2>&1
+--let $assert_text=FR1.1: the --commands value must be true
+--let $assert_file=$filename.out
+--let $assert_select=^commands[\\ \\t]*TRUE\$
+--let $assert_count=1
+--source include/assert_grep.inc
+--remove_file $filename.out
+--remove_file $filename
+
+--echo # FR1.7: --binary-mode=1 overrides --commands
+--let $filename=$MYSQLTEST_VARDIR/tmp/wl16949_fr1_7.sql
+--write_file $filename
+system echo foo;
+EOF
+--echo # must fail with a syntax error
+--error 1
+--exec $MYSQL --commands=1 --binary-mode=1 < $filename 2>&1
+--remove_file $filename
+
+--echo trying in reverse
+--let $filename=$MYSQLTEST_VARDIR/tmp/wl16949_fr1_7_reverse.sql
+--write_file $filename
+system echo foo;
+EOF
+--echo # must fail with a syntax error
+--error 1
+--exec $MYSQL --binary-mode=1 --commands=1 < $filename 2>&1
+--remove_file $filename
+
+--echo # FR1.8: --binary-mode=0 enables --commands
+--let $filename=$MYSQLTEST_VARDIR/tmp/wl16949_fr1_8.sql
+--write_file $filename
+system echo foo;
+EOF
+--echo # must fail with a syntax error
+--error 1
+--exec $MYSQL --binary-mode=0 --commands=0 < $filename 2>&1
+--remove_file $filename
+
+--echo # End of tests
diff --git a/mysql-test/valgrind.supp b/mysql-test/valgrind.supp
index 8bd24ee03e11..ccd353d6816c 100644
--- a/mysql-test/valgrind.supp
+++ b/mysql-test/valgrind.supp
@@ -47,6 +47,18 @@
 
 }
 
+{
+   pthread allocate_tls timer memory loss
+   Memcheck:Leak
+   match-leak-kinds: possible
+   fun:calloc
+   ...
+   fun:_dl_allocate_tls
+   fun:pthread_create*
+   ...
+   fun:timer_create*
+}
+
 {
    semi_sync_master_plugin_init
    Memcheck:Leak
@@ -2732,3 +2744,14 @@
    fun:_dl_catch_exception
    ...
 }
+
+{
+   mysqltest_do_shutdown_timeout
+   Memcheck:Leak
+   match-leak-kinds: reachable
+   ...
+   fun:allocate
+   ...
+   fun:_ZL18do_shutdown_serverP10st_command
+   fun:main
+}
diff --git a/plugin/group_replication/libmysqlgcs/src/bindings/xcom/xcom/xcom_base.cc b/plugin/group_replication/libmysqlgcs/src/bindings/xcom/xcom/xcom_base.cc
index 0a6054f97ea4..21f9b6944c40 100644
--- a/plugin/group_replication/libmysqlgcs/src/bindings/xcom/xcom/xcom_base.cc
+++ b/plugin/group_replication/libmysqlgcs/src/bindings/xcom/xcom/xcom_base.cc
@@ -2329,8 +2329,10 @@ static int reserve_synode_number(synode_allocation_type *synode_allocation,
     *site = proposer_site = find_site_def_rw(*msgno);
 
     // Update node set get the latest state
-    if (is_view(a->body.c_t))
+    if (is_view(a->body.c_t)) {
+      free_node_set(&a->body.app_u_u.present);
       a->body.app_u_u.present = detector_node_set(*site);
+    }
 
     // Set the global current message for all number allocators
     set_current_message(incr_synode(*msgno));
diff --git a/router/LICENSE.router b/router/LICENSE.router
index 5e32d23ec6ec..ecf35932bbb0 100644
--- a/router/LICENSE.router
+++ b/router/LICENSE.router
@@ -1,6 +1,6 @@
 Licensing Information User Manual
 
-MySQL Router 8.4.5 Community
+MySQL Router 8.4.6 Community
      __________________________________________________________________
 
 Introduction
@@ -8,18 +8,18 @@ Introduction
    This License Information User Manual contains Oracle's product license
    and other licensing information, including licensing information for
    third-party software which may be included in this distribution of
-   MySQL Router 8.4.5 Community.
+   MySQL Router 8.4.6 Community.
 
-   Last updated: March 2025
+   Last updated: June 2025
 
 Licensing Information
 
-   This release of MySQL Router 8.4.5 Community is brought to you by the
+   This release of MySQL Router 8.4.6 Community is brought to you by the
    MySQL team at Oracle. This software is released under version 2 of the
    GNU General Public License (GPLv2), as set forth below, with the
    following additional permissions:
 
-   This distribution of MySQL Router 8.4.5 Community is designed to work
+   This distribution of MySQL Router 8.4.6 Community is designed to work
    with certain software (including but not limited to OpenSSL) that is
    licensed under separate terms, as designated in a particular file or
    component or in the license documentation. Without limiting your rights
diff --git a/router/src/connection_pool/src/connection_pool.cc b/router/src/connection_pool/src/connection_pool.cc
index 07cb93b7631a..43ca5230ac0d 100644
--- a/router/src/connection_pool/src/connection_pool.cc
+++ b/router/src/connection_pool/src/connection_pool.cc
@@ -192,8 +192,9 @@ ConnectionPool::add_if_not_full(ConnectionPool::ServerSideConnection conn) {
 
         conn.prepare_for_pool();
 
+        auto endpoint = conn.endpoint();
         auto it = pool.emplace(
-            conn.endpoint(),
+            std::move(endpoint),
             std::make_shared<PooledConnection<ServerSideConnection>>(
                 std::move(conn)));
 
diff --git a/router/src/connection_pool/tests/test_connection_pool_concurrency.cc b/router/src/connection_pool/tests/test_connection_pool_concurrency.cc
index fc1153c797fb..90d80baafd1b 100644
--- a/router/src/connection_pool/tests/test_connection_pool_concurrency.cc
+++ b/router/src/connection_pool/tests/test_connection_pool_concurrency.cc
@@ -29,6 +29,7 @@
 #include "mysql/harness/net_ts/socket.h"
 #include "mysql/harness/net_ts/timer.h"
 #include "mysqlrouter/connection_pool.h"
+#include "router_test_helpers.h"  // init_windows_sockets
 
 #include <gtest/gtest.h>
 
@@ -85,7 +86,9 @@ class PoolAdder {
 
     if (!connect_res) {
       auto ec = connect_res.error();
-      ASSERT_EQ(ec, std::errc::operation_in_progress) << ec.message();
+      ASSERT_TRUE(ec == std::errc::operation_in_progress ||
+                  ec == std::errc::operation_would_block)
+          << ec.message();
 
       auto wait_res = sock.wait(net::socket_base::wait_write);
       ASSERT_NO_ERROR(wait_res);
@@ -148,7 +151,9 @@ class StashAdder {
 
     if (!connect_res) {
       auto ec = connect_res.error();
-      ASSERT_EQ(ec, std::errc::operation_in_progress) << ec.message();
+      ASSERT_TRUE(ec == std::errc::operation_in_progress ||
+                  ec == std::errc::operation_would_block)
+          << ec.message();
 
       auto wait_res = sock.wait(net::socket_base::wait_write);
       ASSERT_NO_ERROR(wait_res);
@@ -248,9 +253,7 @@ TEST(ConnectionPoolTest, concurrent_pool_access) {
       // cleanup the closed client connections.
 
       auto cur = client_sockets.begin();
-      auto end = client_sockets.end();
-
-      for (; cur != end;) {
+      for (; cur != client_sockets.end();) {
         if (socket_is_alive(cur->native_handle())) {
           ++cur;
         } else {
@@ -365,9 +368,8 @@ TEST(ConnectionPoolTest, concurrent_stash_access) {
       // cleanup the closed client connections.
 
       auto cur = client_sockets.begin();
-      auto end = client_sockets.end();
 
-      for (; cur != end;) {
+      for (; cur != client_sockets.end();) {
         if (socket_is_alive(cur->native_handle())) {
           ++cur;
         } else {
@@ -439,6 +441,7 @@ TEST(ConnectionPoolTest, concurrent_stash_access) {
 
 int main(int argc, char *argv[]) {
   TlsLibraryContext lib_ctx;
+  init_windows_sockets();
 
   ::testing::InitGoogleTest(&argc, argv);
 
diff --git a/router/src/http/include/http/base/connection.h b/router/src/http/include/http/base/connection.h
index 38d3add17452..6a1a5a62b66a 100644
--- a/router/src/http/include/http/base/connection.h
+++ b/router/src/http/include/http/base/connection.h
@@ -29,6 +29,7 @@
 #include <algorithm>
 #include <atomic>
 #include <bitset>
+#include <cctype>
 #include <list>
 #include <map>
 #include <sstream>
@@ -50,6 +51,7 @@
 #include "http/cno/string.h"
 
 #include "mysql/harness/net_ts/internet.h"
+#include "mysql/harness/string_utils.h"
 #include "mysqlrouter/http_common_export.h"
 
 namespace http {
@@ -122,6 +124,7 @@ class Connection : public base::ConnectionInterface, public cno::CnoInterface {
     socket_.set_option(net::ip::tcp::no_delay{true});
     impl::set_socket_parent(&socket_, ss.str().c_str());
     cno_init(&cno_, kind);
+    cno_.disallow_h2_prior_knowledge = 1;
     cno::callback_init(&cno_, this);
     output_buffers_.emplace_back(4096);
     cno_begin(&cno_, version);
@@ -142,13 +145,25 @@ class Connection : public base::ConnectionInterface, public cno::CnoInterface {
             const Headers &headers, const IOBuffer &data) override {
     cno_message_t message;
     std::vector<cno_header_t> cno_header(headers.size(), cno_header_t());
+    std::vector<std::string> http2_headers_names;
     const bool only_header = 0 == data.length();
 
+    if (CNO_HTTP2 == cno_.mode) {
+      http2_headers_names.reserve(headers.size());
+    }
+
     auto output = cno_header.data();
 
     for (const auto &entry : headers) {
-      output->name.size = entry.first.length();
-      output->name.data = entry.first.c_str();
+      if (CNO_HTTP2 == cno_.mode) {
+        auto &header_name =
+            http2_headers_names.emplace_back(make_lower(entry.first));
+        output->name.size = header_name.length();
+        output->name.data = header_name.c_str();
+      } else {
+        output->name.size = entry.first.length();
+        output->name.data = entry.first.c_str();
+      }
 
       output->value.size = entry.second.length();
       output->value.data = entry.second.c_str();
@@ -200,6 +215,11 @@ class Connection : public base::ConnectionInterface, public cno::CnoInterface {
   void start() override { do_net_recv(); }
 
  protected:
+  std::string make_lower(std::string s) {
+    std::transform(s.begin(), s.end(), s.begin(), ::tolower);
+    return s;
+  }
+
   void do_net_send() {
     socket_.async_send(ref_buffers(output_buffers_),
                        [this](std::error_code error, auto size) {
diff --git a/router/src/http/include/http/client/client.h b/router/src/http/include/http/client/client.h
index 2299195e2e0a..72e0849faf9e 100644
--- a/router/src/http/include/http/client/client.h
+++ b/router/src/http/include/http/client/client.h
@@ -80,11 +80,11 @@ class HTTP_CLIENT_EXPORT Client {
   int error_code() const;
   std::string error_message() const;
   const Statistics &statistics() const;
+  class CallbacksPrivateImpl;
 
  private:
   void start_http_flow();
 
-  class CallbacksPrivateImpl;
   bool is_connected_{false};
   std::error_code error_code_;
   Endpoint connected_endpoint_;
diff --git a/router/src/http/src/http/client/client.cc b/router/src/http/src/http/client/client.cc
index 4c364ed8a246..f112af1b563b 100644
--- a/router/src/http/src/http/client/client.cc
+++ b/router/src/http/src/http/client/client.cc
@@ -214,16 +214,92 @@ class Client::CallbacksPrivateImpl
   Client *parent_;
 };
 
+#define PRINT_AND_CALL(X) \
+  print(#X);              \
+  X
+
+template <typename Callbacks>
+class TraceCallbacks : public Callbacks {
+ public:
+  template <typename... Args>
+  TraceCallbacks(Args &&...args)  // NOLINT(runtime/explicit)
+      : Callbacks{std::forward<Args>(args)...}, out_{&std::cout} {
+    print("ctor");
+  }
+
+  void on_connection_ready() override {
+    PRINT_AND_CALL(Callbacks::on_connection_ready());
+  }
+
+  void on_input_payload(const char *data, size_t size) override {
+    PRINT_AND_CALL(Callbacks::on_input_payload(data, size));
+  }
+  void on_input_begin(int status_code,
+                      const std::string &status_text) override {
+    PRINT_AND_CALL(Callbacks::on_input_begin(status_code, status_text));
+  }
+  void on_input_end() override { PRINT_AND_CALL(Callbacks::on_input_end()); }
+  void on_input_header(std::string &&key, std::string &&value) override {
+    PRINT_AND_CALL(
+        Callbacks::on_input_header(std::move(key), std::move(value)));
+  }
+  void on_output_end_payload() override {
+    PRINT_AND_CALL(Callbacks::on_output_end_payload());
+  }
+
+ public:  // ConnectionTls::ConnectionStatusCallbacks
+  void on_connection_close(ConnectionTls::Parent *connection) override {
+    PRINT_AND_CALL(Callbacks::on_connection_close(connection));
+  }
+  void on_connection_io_error(ConnectionTls::Parent *connection,
+                              const std::error_code &ec) override {
+    PRINT_AND_CALL(Callbacks::on_connection_io_error(connection, ec));
+  }
+
+ public:  // ConnectionRaw::ConnectionStatusCallbacks
+  void on_connection_close(ConnectionRaw::Parent *connection) override {
+    PRINT_AND_CALL(Callbacks::on_connection_close(connection));
+  }
+  void on_connection_io_error(ConnectionRaw::Parent *connection,
+                              const std::error_code &ec) override {
+    PRINT_AND_CALL(Callbacks::on_connection_io_error(connection, ec));
+  }
+
+  void print_single() {}
+
+  template <typename Arg, typename... Args>
+  void print_single(const Arg &arg, const Args... args) {
+    *out_ << arg;
+    print_single(args...);
+  }
+
+  std::ostream *out_;
+  template <typename... Args>
+  void print(const Args &...args) {
+    *out_ << "this:" << this << ", thread:" << std::this_thread::get_id()
+          << ": ";
+    print_single(args...);
+    *out_ << std::endl;
+    out_->flush();
+  }
+};
+
+#undef PRINT_AND_CALL
+
+// Choose one of following.
+using UsedCallbackImpl = Client::CallbacksPrivateImpl;
+// TraceCallbacks<Client::CallbacksPrivateImpl>;
+
 Client::Client(io_context &io_context, TlsClientContext &&tls_context,
                bool use_http2)
     : io_context_{io_context},
       tls_context_{std::move(tls_context)},
-      callbacks_{std::make_unique<CallbacksPrivateImpl>(this)},
+      callbacks_{std::make_unique<UsedCallbackImpl>(this)},
       use_http2_{use_http2} {}
 
 Client::Client(io_context &io_context, const bool use_http2)
     : io_context_{io_context},
-      callbacks_{std::make_unique<CallbacksPrivateImpl>(this)},
+      callbacks_{std::make_unique<UsedCallbackImpl>(this)},
       use_http2_{use_http2} {}
 
 Client::~Client() = default;
diff --git a/router/src/openssl/include/tls/trace_stream.h b/router/src/openssl/include/tls/trace_stream.h
index e1a04a9c7d39..38775204112c 100644
--- a/router/src/openssl/include/tls/trace_stream.h
+++ b/router/src/openssl/include/tls/trace_stream.h
@@ -56,6 +56,7 @@ class TraceStream : Mutex_static_holder<Trace_stream_static_holder> {
   using VectorOfBuffers = std::vector<net::mutable_buffer>;
   using VectorOfConstBuffers = std::vector<net::const_buffer>;
 
+ public:
   TraceStream(TraceStream &&other)
       : recv_buffer_{other.recv_buffer_},
         send_buffer_{other.send_buffer_},
@@ -117,6 +118,11 @@ class TraceStream : Mutex_static_holder<Trace_stream_static_holder> {
     return lower_layer_.cancel();
   }
 
+  void shutdown(net::socket_base::shutdown_type s) {
+    print("shutdown");
+    lower_layer_.shutdown(s);
+  }
+
   auto close() {
     print("close");
     return lower_layer_.close();
diff --git a/router/src/router/src/common/log_filter.cc b/router/src/router/src/common/log_filter.cc
index a964b1fc9d38..cb4fce3c3ac1 100644
--- a/router/src/router/src/common/log_filter.cc
+++ b/router/src/router/src/common/log_filter.cc
@@ -49,7 +49,8 @@ void LogFilter::add_pattern(const std::string &pattern,
 
 void SQLLogFilter::add_default_sql_patterns() {
   // Add pattern for replacing passwords in 'CREATE USER [IF NOT EXISTS] ...'.
-  // Works for both mysql_native_password and plaintext authentication methods.
+  // Works for mysql_native_password, plaintext authentication and other
+  // auth_plugin methods.
   //
   // Below example showcases mysql_native_password method; lines are wrapped
   // for easier viewing (in real life they're a single line).
@@ -66,7 +67,7 @@ void SQLLogFilter::add_default_sql_patterns() {
   //     'some_user'@'h2' IDENTIFIED WITH mysql_native_password AS ***,
   //     'some_user'@'h3' IDENTIFIED WITH mysql_native_password AS ***
   // clang-format on
-  add_pattern("(IDENTIFIED\\s+(WITH\\s+[a-z_]+\\s+)?(BY|AS))\\s+'[^']*'",
+  add_pattern("(IDENTIFIED\\s+(WITH\\s+[a-z0-9_`]+\\s+)?(BY|AS))\\s+'[^']*'",
               "$1 ***");
 }
 
diff --git a/router/src/routing/src/classic_connect.cc b/router/src/routing/src/classic_connect.cc
index 739418ed8886..880ae2d6a948 100644
--- a/router/src/routing/src/classic_connect.cc
+++ b/router/src/routing/src/classic_connect.cc
@@ -283,6 +283,8 @@ stdx::expected<Processor::Result, std::error_code> ConnectProcessor::resolve() {
 
     destination_ec_ = ec;
 
+    destination->connect_status(destination_ec_);
+
     // resolve(...) failed, move host:port to the quarantine to monitor the
     // solve to come back.
 
diff --git a/router/src/routing/src/dest_metadata_cache.cc b/router/src/routing/src/dest_metadata_cache.cc
index 5736c999aadc..202a4f91b5eb 100644
--- a/router/src/routing/src/dest_metadata_cache.cc
+++ b/router/src/routing/src/dest_metadata_cache.cc
@@ -438,7 +438,7 @@ std::optional<Destinations> DestMetadataCacheGroup::refresh_destinations(
                 err.message().c_str());
 
       if (err == make_error_condition(std::errc::timed_out) ||
-          err == make_error_condition(std::errc::no_such_file_or_directory)) {
+          err.category() == net::ip::resolver_category()) {
         return std::nullopt;
       }
 
diff --git a/router/tests/component/test_bootstrap.cc b/router/tests/component/test_bootstrap.cc
index 80b4b49b278b..09cea164bbb0 100644
--- a/router/tests/component/test_bootstrap.cc
+++ b/router/tests/component/test_bootstrap.cc
@@ -182,7 +182,20 @@ auto get_test_description(
 
 class RouterBootstrapOkTest
     : public RouterComponentBootstrapWithDefaultCertsTest,
-      public ::testing::WithParamInterface<BootstrapTestParam> {};
+      public ::testing::WithParamInterface<BootstrapTestParam> {
+ public:
+  void verify_password_not_logged(const std::string &console_output) {
+    const auto lines = mysql_harness::split_string(console_output, '\n');
+    size_t found_create_statements{0};
+    for (const auto &line : lines) {
+      if (line.find("CREATE USER") != std::string::npos) {
+        EXPECT_THAT(line, ::testing::HasSubstr("***"));
+        ++found_create_statements;
+      }
+    }
+    EXPECT_GT(found_create_statements, 0);
+  }
+};
 
 /**
  * @test
@@ -217,7 +230,7 @@ TEST_P(RouterBootstrapOkTest, BootstrapOk) {
 
   std::vector<std::string> bootstrap_params{
       "--bootstrap=127.0.0.1:" + std::to_string(server_port), "-d",
-      bootstrap_dir.name()};
+      bootstrap_dir.name(), "--logger.level=debug"};
 
   auto &router = launch_router_for_bootstrap(bootstrap_params);
 
@@ -233,6 +246,8 @@ TEST_P(RouterBootstrapOkTest, BootstrapOk) {
         << router_console_output;
   }
 
+  verify_password_not_logged(router_console_output);
+
   const std::string conf_file = bootstrap_dir.name() + "/mysqlrouter.conf";
   // 'config_file' is set as side-effect of bootstrap_failover()
   ASSERT_THAT(conf_file, ::testing::Not(::testing::IsEmpty()));
@@ -3260,7 +3275,7 @@ INSTANTIATE_TEST_SUITE_P(
              "Failed changing the authentication plugin for account "
              "'.*'@'localhost': Error executing MySQL query \"alter user "
              "'.*'@'localhost' identified with `caching_sha2_password` by "
-             "'.*'\": Unexpected error .*"},
+             "\\*\\*\\*\": Unexpected error .*"},
             /*unexpected_output_strings*/
             {"Successfully changed the authentication plugin for .*"},
             /*test_description*/
diff --git a/router/tests/component/test_http_server.cc b/router/tests/component/test_http_server.cc
index bf444bd1d769..792f0c8485b4 100644
--- a/router/tests/component/test_http_server.cc
+++ b/router/tests/component/test_http_server.cc
@@ -309,11 +309,6 @@ TEST_P(HttpServerPlainTest, ensure_http11) {
   ASSERT_NO_FATAL_FAILURE(testcase_ensure());
 }
 
-TEST_P(HttpServerPlainTest, ensure_http2) {
-  const bool k_use_http2 = true;
-  ASSERT_NO_FATAL_FAILURE(testcase_ensure(k_use_http2));
-}
-
 const std::string localhost_ipv4("127.0.0.1");
 const std::string localhost_ipv6("::1");
 
@@ -1294,11 +1289,6 @@ TEST_P(HttpServerSecureTest, ensure_http11) {
   ASSERT_NO_FATAL_FAILURE(this->testcase_ensure());
 }
 
-TEST_P(HttpServerSecureTest, ensure_http2) {
-  const bool k_use_http2 = true;
-  ASSERT_NO_FATAL_FAILURE(this->testcase_ensure(k_use_http2));
-}
-
 constexpr const char kErrmsgRegexNoSslCertKey[]{
     "if ssl=1 is set, ssl_cert and ssl_key must be set too"};
 
diff --git a/share/messages_to_error_log.txt b/share/messages_to_error_log.txt
index d5201026e6d9..38fe453d4e54 100644
--- a/share/messages_to_error_log.txt
+++ b/share/messages_to_error_log.txt
@@ -12802,6 +12802,9 @@ ER_RPL_KILL_OLD_DUMP_THREAD_ENCOUNTERED
 ER_RPL_MTA_ALLOW_COMMIT_OUT_OF_ORDER
   eng "The transaction '%-.192s' will commit out of order with respect to its source to follow the group global order."
 
+ER_TEMPTABLE_ENGINE_ERROR
+  eng "Temptable: %s"
+
 # DO NOT add server-to-client messages here;
 # they go in messages_to_clients.txt
 # in the same directory as this file.
diff --git a/sql/dd/impl/raw/object_keys.h b/sql/dd/impl/raw/object_keys.h
index 1ea718922f8a..060c6189ee3c 100644
--- a/sql/dd/impl/raw/object_keys.h
+++ b/sql/dd/impl/raw/object_keys.h
@@ -266,8 +266,8 @@ class Se_private_id_key : public Object_key {
 
 class Composite_pk : public Object_key {
  public:
-  Composite_pk(int index_no, uint first_column_no, ulonglong first_id,
-               uint second_column_no, ulonglong second_id)
+  Composite_pk(int index_no, uint first_column_no, Object_id first_id,
+               uint second_column_no, Object_id second_id)
       : m_index_no(index_no),
         m_first_column_no(first_column_no),
         m_first_id(first_id),
@@ -283,10 +283,10 @@ class Composite_pk : public Object_key {
   int m_index_no;
 
   int m_first_column_no;
-  ulonglong m_first_id;
+  Object_id m_first_id;
 
   int m_second_column_no;
-  ulonglong m_second_id;
+  Object_id m_second_id;
 };
 
 ///////////////////////////////////////////////////////////////////////////
diff --git a/sql/dd/impl/tables/table_partition_values.cc b/sql/dd/impl/tables/table_partition_values.cc
index 903bd0305cc0..237c6759f901 100644
--- a/sql/dd/impl/tables/table_partition_values.cc
+++ b/sql/dd/impl/tables/table_partition_values.cc
@@ -78,7 +78,8 @@ Table_partition_values::Table_partition_values() {
 
 class Table_partition_values_pk : public Object_key {
  public:
-  Table_partition_values_pk(int partition_id, int list_num, int column_num)
+  Table_partition_values_pk(Object_id partition_id, int list_num,
+                            int column_num)
       : m_partition_id(partition_id),
         m_list_num(list_num),
         m_column_num(column_num) {}
@@ -89,7 +90,7 @@ class Table_partition_values_pk : public Object_key {
   String_type str() const override;
 
  private:
-  int m_partition_id;
+  Object_id m_partition_id;
   int m_list_num;
   int m_column_num;
 };
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 306b85215c1b..c17cd28bfa20 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -4852,6 +4852,31 @@ class THD : public MDL_context_owner,
   /// Flag indicating whether this session incremented the number of sessions
   /// with GTID_NEXT set to AUTOMATIC:tag
   bool has_incremented_gtid_automatic_count;
+
+  /// Increment the owned temptable counter. This is used to find leaked
+  /// temptable handles during close_connection calls
+  void increment_temptable_count() { ++m_opened_temptable_count; }
+  /// Decrement the owned temptable counter.
+  void decrement_temptable_count() { --m_opened_temptable_count; }
+  /// Return currently owned temptable count.
+  size_t get_temptable_count() const { return m_opened_temptable_count; }
+
+ private:
+  /** Each THD can open multiple temptables, we create these temptable objects
+    in the temptable engine. These objects have their lifetime controlled with
+    create and delete_table calls in the temptable handler. The sql layer is
+    responsible for calling the create and delete_table functions. We increment
+    this counter in create, and decrement it in delete_table. This allows us to
+    detect any situation where we call close_connection which releases the
+    underlying memory in the temptable engine, before all temptable objects
+    have been deleted. This way we can react and clean up any temptable objects
+    before we free the underlying memory. So we can think of the THD as owning
+    multiple temptable objects in the temptable engine, and we tie the lifetime
+    of these objects to the lifetime of the THD. In normal operation they
+    should be deleted before the close_connection call but this enforces
+    defined behaviour when they aren't.
+  */
+  size_t m_opened_temptable_count{};
 };
 
 /**
diff --git a/sql/sql_resolver.cc b/sql/sql_resolver.cc
index 2322685e4819..2a377140e888 100644
--- a/sql/sql_resolver.cc
+++ b/sql/sql_resolver.cc
@@ -2244,23 +2244,22 @@ void Query_block::clear_sj_expressions(NESTED_JOIN *nested_join) {
 }
 
 /**
-  Build equality conditions using outer expressions and inner
-  expressions. If the equality condition is not constant, add
-  it to the semi-join condition. Otherwise, evaluate it and
-  remove the constant expressions from the
-  outer/inner expressions list if the result is true. If the
-  result is false, remove all the expressions in outer/inner
-  expression list and attach an always false condition
-  to semijoin condition.
-
-  @param thd            Thread context
-  @param nested_join    Join nest
-  @param subq_query_block    Query block for the subquery
-  @param outer_tables_map Map of tables from original outer query block
+  Build equality predicates using outer expressions and inner expressions.
+  If an equality predicate is not constant, add it to the semi-join condition.
+  Otherwise, evaluate the predicate. If the result of the predicate is true,
+  remove the expressions of the constant predicate from the outer/inner
+  expressions list. If the result is false, remove all the expressions in
+  outer/inner expression list and attach an always false condition to
+  semijoin condition.
+
+  @param thd               Thread context
+  @param nested_join       Join nest
+  @param subq_query_block  Query block for the subquery
+  @param outer_tables_map  Map of tables from original outer query block
   @param[in,out] sj_cond   Semi-join condition to be constructed
                            Contains non-equalities on input.
-  @param[out]    simple_const true if the returned semi-join condition is
-                              a simple true or false predicate, false otherwise.
+  @param[out] simple_const true if the returned semi-join condition is
+                           a simple true or false predicate, false otherwise.
 
   @return false if success, true if error
 */
@@ -2271,12 +2270,13 @@ bool Query_block::build_sj_cond(THD *thd, NESTED_JOIN *nested_join,
   *simple_const = false;
 
   Item *new_cond = nullptr;
+  bool remove_condition = false;
 
   auto ii = nested_join->sj_inner_exprs.begin();
   auto oi = nested_join->sj_outer_exprs.begin();
   while (ii != nested_join->sj_inner_exprs.end() &&
          oi != nested_join->sj_outer_exprs.end()) {
-    bool should_remove = false;
+    bool remove_predicate = false;
     Item *inner = *ii;
     Item *outer = *oi;
     /*
@@ -2291,7 +2291,7 @@ bool Query_block::build_sj_cond(THD *thd, NESTED_JOIN *nested_join,
     Item *predicate = item_eq;
     if (!item_eq->fixed && item_eq->fix_fields(thd, &predicate)) return true;
 
-    // Evaluate if the condition is on const expressions
+    // Evaluate if the predicate is a const value:
     if (predicate->const_item() &&
         !(predicate)->walk(&Item::is_non_const_over_literals,
                            enum_walk::POSTFIX, nullptr)) {
@@ -2318,20 +2318,14 @@ bool Query_block::build_sj_cond(THD *thd, NESTED_JOIN *nested_join,
           const condition evaluates to true as Item_cond::fix_fields will
           remove the condition later.
         */
-        should_remove = true;
+        remove_predicate = true;
       } else {
         /*
-          Remove all the expressions in inner/outer expression list if
-          one of condition evaluates to always false. Add an always false
-          condition to semi-join condition.
+          Predicate is false, and thus condition is false. However, generate
+          the full condition so that it can be removed completely when all
+          predicates have been processed.
         */
-        nested_join->sj_inner_exprs.clear();
-        nested_join->sj_outer_exprs.clear();
-        Item *new_item = new Item_func_false();
-        if (new_item == nullptr) return true;
-        (*sj_cond) = new_item;
-        *simple_const = true;
-        return false;
+        remove_condition = true;
       }
     }
     /*
@@ -2349,7 +2343,7 @@ bool Query_block::build_sj_cond(THD *thd, NESTED_JOIN *nested_join,
     */
     nested_join->sj_corr_tables |= inner->used_tables() & outer_tables_map;
 
-    if (should_remove) {
+    if (remove_predicate) {
       ii = nested_join->sj_inner_exprs.erase(ii);
       oi = nested_join->sj_outer_exprs.erase(oi);
     } else {
@@ -2359,6 +2353,25 @@ bool Query_block::build_sj_cond(THD *thd, NESTED_JOIN *nested_join,
       ++ii, ++oi;
     }
   }
+  if (remove_condition) {
+    /*
+      Condition is false.
+      Clean up the synthesized condition.
+      Remove all the expressions in inner/outer expression list.
+      Add an always false predicate to semi-join condition.
+    */
+    Item::Cleanup_after_removal_context ctx(this);
+    new_cond->walk(&Item::clean_up_after_removal, walk_options,
+                   pointer_cast<uchar *>(&ctx));
+
+    nested_join->sj_inner_exprs.clear();
+    nested_join->sj_outer_exprs.clear();
+    Item *new_item = new Item_func_false();
+    if (new_item == nullptr) return true;
+    (*sj_cond) = new_item;
+    *simple_const = true;
+    return false;
+  }
   /*
     Semijoin processing expects at least one inner/outer expression
     in the list if there is a sj_nest present. This is required for semi-join
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 3d73c28f0523..39f251ed9a52 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -2310,11 +2310,24 @@ bool store_create_info(THD *thd, Table_ref *table_list, String *packet,
   packet->append(STRING_WITH_LEN("\n)"));
 
   /**
-    Append START TRANSACTION for CREATE SELECT on SE supporting atomic DDL.
-    This is done only while binlogging CREATE TABLE AS SELECT.
+    Append START TRANSACTION clause for:
+    a. CREATE TABLE AS SELECT if the SE supports atomic DDL.
+    b. CREATE TABLE ... START TRANSACTION
+      - HA_CREATE_INFO::m_transactional_ddl indicates if the table should be
+        created and not committed at the end of the statement. This flag is
+        set for CREATE TABLE ... START TRANSACTION statement only.
+
+    These checks also ensure we do not append 'START TRANSACTION' clause for
+    'SHOW CREATE TABLE' query or the normal 'CREATE TABLE' query without the
+    'START TRANSACTION' clause where the query needs to be rewritten (for eg:
+    adding GIPK information on a table without primary key when
+    sql_generate_invisible_primary_key is enabled).
   */
-  if (!thd->lex->query_block->field_list_is_empty() &&
-      (create_info_arg->db_type->flags & HTON_SUPPORTS_ATOMIC_DDL)) {
+  if ((!thd->lex->query_block->field_list_is_empty()  // contains SELECT clause
+       &&
+       (create_info_arg->db_type->flags & HTON_SUPPORTS_ATOMIC_DDL))  // case a
+      || (create_info_arg && create_info_arg->m_transactional_ddl)    // case b
+  ) {
     packet->append(STRING_WITH_LEN(" START TRANSACTION"));
   }
 
diff --git a/sql/sql_union.cc b/sql/sql_union.cc
index 393def6beaba..e0b6c6cd2ea8 100644
--- a/sql/sql_union.cc
+++ b/sql/sql_union.cc
@@ -2132,11 +2132,11 @@ void Query_block::destroy() {
     unit->destroy();
     unit = next;
   }
-
   List_iterator<Window> li(m_windows);
   Window *w;
-  while ((w = li++)) w->destroy();
-
+  while ((w = li++)) {
+    w->destroy();
+  }
   // Destroy allocated derived tables
   destroy_tmp_tables(get_table_list());
 
diff --git a/storage/innobase/ddl/ddl0builder.cc b/storage/innobase/ddl/ddl0builder.cc
index 0ac9dca7823c..4eb093065551 100644
--- a/storage/innobase/ddl/ddl0builder.cc
+++ b/storage/innobase/ddl/ddl0builder.cc
@@ -363,10 +363,6 @@ bool Merge_cursor::Compare::operator()(const File_cursor *lhs,
 dberr_t Merge_cursor::add_file(const ddl::file_t &file, size_t buffer_size,
                                const Range &range) noexcept {
   ut_a(file.m_file.is_open());
-  /* Keep the buffer size as much required to avoid the overlapping reads from
-  the subsequent ranges. In this iteration, buffer size would remain same for
-  subsequent reads */
-  buffer_size = std::min(size_t(range.second - range.first), buffer_size);
   auto cursor = ut::new_withkey<File_cursor>(
       ut::make_psi_memory_key(mem_key_ddl), m_builder, file.m_file, buffer_size,
       range, m_stage);
diff --git a/storage/innobase/ddl/ddl0file-reader.cc b/storage/innobase/ddl/ddl0file-reader.cc
index 3581b1c88682..8d6268419082 100644
--- a/storage/innobase/ddl/ddl0file-reader.cc
+++ b/storage/innobase/ddl/ddl0file-reader.cc
@@ -79,11 +79,12 @@ dberr_t File_reader::prepare() noexcept {
     return DB_OUT_OF_MEMORY;
   }
 
+  /* Position the m_ptr to the first record. */
   if (const auto err = seek(); err != DB_SUCCESS) {
     return err;
   }
 
-  /* Position m_mrec on the first record. */
+  /* Position m_mrec on the first record and advance to next record. */
   return next();
 }
 
@@ -95,7 +96,7 @@ dberr_t File_reader::seek() noexcept {
       ddl::pread(m_file.get(), m_io_buffer.first, len, m_range.first);
 
   if (err == DB_SUCCESS) {
-    /* Fetch and advance to the next record. */
+    /* Fetch the record. */
     m_ptr = m_io_buffer.first;
   }
 
diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc
index 3854811857fe..5b6199d33b5f 100644
--- a/storage/innobase/handler/ha_innodb.cc
+++ b/storage/innobase/handler/ha_innodb.cc
@@ -23799,18 +23799,11 @@ number.
 @return the field's new value if it's updated, otherwise nullptr */
 static const dfield_t *innobase_get_field_from_update_vector(
     const upd_t *update, uint32_t col_no) {
-  const dict_table_t *const table = update->table;
-  const dict_index_t *const clustered_index = table->first_index();
+  const dict_index_t *const clustered_index = update->table->first_index();
   const auto index_field_pos = clustered_index->get_col_pos(col_no);
-
-  for (uint32_t j = 0; j < update->n_fields; j++) {
-    const upd_field_t *const ufield = &update->fields[j];
-    if (ufield->field_no == index_field_pos) {
-      return &ufield->new_val;
-    }
-  }
-
-  return (nullptr);
+  const upd_field_t *const upd_field =
+      upd_get_field_by_field_no(update, index_field_pos, false);
+  return upd_field ? &upd_field->new_val : nullptr;
 }
 
 dfield_t *innobase_get_computed_value(
diff --git a/storage/innobase/row/row0import.cc b/storage/innobase/row/row0import.cc
index e1f4902a3e2f..d2b06d01b6ea 100644
--- a/storage/innobase/row/row0import.cc
+++ b/storage/innobase/row/row0import.cc
@@ -1768,8 +1768,39 @@ dberr_t row_import::add_instant_dropped_columns(dict_table_t *target_table) {
       index->get_field(i)->col = target_table->get_col(mapping[i]);
     }
   }
+
   ut::delete_arr(mapping);
 
+  /* Correct field mapping for secondary indexes too. */
+  for (auto *secondary_index : m_table->indexes) {
+    if (secondary_index == index) continue;
+
+    auto secondary_mapping =
+        std::make_unique<uint16_t[]>(secondary_index->n_fields);
+
+    for (size_t i = 0; i < secondary_index->n_fields; i++) {
+      secondary_mapping[i] = secondary_index->get_field(i)->col->ind;
+    }
+
+    /* Allocate memory for n_fields (secondary_index->n_fields). No need to
+     allocate for n_dropped_cols, because instant column drop is not supported
+     for columns that are part of secondary index. */
+    {
+      dict_field_t *fields = secondary_index->fields;
+      secondary_index->fields = (dict_field_t *)mem_heap_alloc(
+          secondary_index->heap,
+          1 + secondary_index->n_fields * sizeof(dict_field_t));
+      memcpy(secondary_index->fields, fields,
+             1 + secondary_index->n_fields * sizeof(dict_field_t));
+
+      /* Fix field->col pointers with the mapping created. */
+      for (size_t i = 0; i < secondary_index->n_fields; i++) {
+        secondary_index->get_field(i)->col =
+            target_table->get_col(secondary_mapping[i]);
+      }
+    }
+  }
+
   /* Set initial/current/total_col_count for table */
   target_table->initial_col_count = m_initial_column_count;
   target_table->current_col_count = m_current_column_count;
@@ -1974,13 +2005,22 @@ dberr_t row_import::adjust_instant_metadata_in_taregt_table(
   m_table->current_row_version = m_current_row_version;
 
   ut_ad(m_table->has_row_versions());
-  dict_index_t &first_index = *m_table->first_index();
-  first_index.row_versions = true;
-  first_index.rec_cache.offsets = nullptr;
-  first_index.rec_cache.nullable_cols = 0;
-  /* Recreate fields array for clustered index */
-  first_index.create_fields_array();
-  first_index.create_nullables(m_table->current_row_version);
+
+  /* Reset field cache for every index, because it becomes invalid in case
+   of instant column add or drop. */
+  for (auto *index : m_table->indexes) {
+    if (index == m_table->first_index()) {
+      ut_ad(index->is_clustered());
+
+      index->row_versions = true;
+
+      /* Recreate fields array for clustered index */
+      index->create_fields_array();
+      index->create_nullables(m_table->current_row_version);
+    }
+    index->rec_cache.offsets = nullptr;
+    index->rec_cache.nullable_cols = 0;
+  }
 
   /* FIXME: Force to discard the table, in case of any rollback later. */
   //    m_table->discard_after_ddl = true;
@@ -2109,11 +2149,18 @@ dberr_t row_import::set_instant_info(THD *thd,
   ut_ad(m_table->has_instant_cols());
   m_table->set_upgraded_instant();
 
-  dict_index_t &first_index = *m_table->first_index();
-  first_index.instant_cols = true;
-  first_index.rec_cache.offsets = nullptr;
-  first_index.rec_cache.nullable_cols = 0;
-  first_index.set_instant_nullable(m_n_instant_nullable);
+  /* Reset field cache for every index, because it becomes invalid in case
+   of instant columns. */
+  for (auto *index : m_table->indexes) {
+    if (index == m_table->first_index()) {
+      ut_ad(index->is_clustered());
+      index->instant_cols = true;
+      index->set_instant_nullable(m_n_instant_nullable);
+    }
+    index->rec_cache.offsets = nullptr;
+    index->rec_cache.nullable_cols = 0;
+  }
+
   /* FIXME: Force to discard the table, in case of any rollback later. */
   //    m_table->discard_after_ddl = true;
 
diff --git a/storage/ndb/include/logger/BufferedLogHandler.hpp b/storage/ndb/include/logger/BufferedLogHandler.hpp
index c7adea5ff49a..d6af3360e79d 100644
--- a/storage/ndb/include/logger/BufferedLogHandler.hpp
+++ b/storage/ndb/include/logger/BufferedLogHandler.hpp
@@ -72,7 +72,7 @@ class BufferedLogHandler : public LogHandler {
 
   struct LogMessageFixedPart {
     Logger::LoggerLevel level;
-    time_t log_timestamp;
+    std::timespec log_timestamp;
     size_t varpart_length[2];  // 0: length of category, 1: length of message
   };
   static constexpr Uint32 MAX_VARPART_SIZE =
@@ -80,7 +80,7 @@ class BufferedLogHandler : public LogHandler {
 
  protected:
   void writeHeader(const char *pCategory, Logger::LoggerLevel level,
-                   time_t now) override;
+                   const std::timespec *now) override;
   void writeMessage(const char *pMsg) override;
   void writeFooter() override;
 
diff --git a/storage/ndb/include/logger/ConsoleLogHandler.hpp b/storage/ndb/include/logger/ConsoleLogHandler.hpp
index 88abf847d421..a394f1365952 100644
--- a/storage/ndb/include/logger/ConsoleLogHandler.hpp
+++ b/storage/ndb/include/logger/ConsoleLogHandler.hpp
@@ -58,7 +58,7 @@ class ConsoleLogHandler : public LogHandler {
 
  protected:
   void writeHeader(const char *pCategory, Logger::LoggerLevel level,
-                   time_t now) override;
+                   const std::timespec *now) override;
   void writeMessage(const char *pMsg) override;
   void writeFooter() override;
   NdbOut &_out;
diff --git a/storage/ndb/include/logger/FileLogHandler.hpp b/storage/ndb/include/logger/FileLogHandler.hpp
index 6706e1902abd..9e0ef7980c41 100644
--- a/storage/ndb/include/logger/FileLogHandler.hpp
+++ b/storage/ndb/include/logger/FileLogHandler.hpp
@@ -82,7 +82,7 @@ class FileLogHandler : public LogHandler {
 
  protected:
   void writeHeader(const char *pCategory, Logger::LoggerLevel level,
-                   time_t now) override;
+                   const std::timespec *now) override;
   void writeMessage(const char *pMsg) override;
   void writeFooter() override;
 
diff --git a/storage/ndb/include/logger/LogHandler.hpp b/storage/ndb/include/logger/LogHandler.hpp
index 00c0cd28ebd8..84df27212be3 100644
--- a/storage/ndb/include/logger/LogHandler.hpp
+++ b/storage/ndb/include/logger/LogHandler.hpp
@@ -94,9 +94,9 @@ class LogHandler {
    * @param pMsg the log message.
    */
   virtual void append(const char *pCategory, Logger::LoggerLevel level,
-                      const char *pMsg, time_t now);
+                      const char *pMsg, const std::timespec *now);
   void append_impl(const char *pCategory, Logger::LoggerLevel level,
-                   const char *pMsg, time_t now);
+                   const char *pMsg, const std::timespec *now);
 
   /**
    * Returns a default formatted header. It currently has the
@@ -108,7 +108,8 @@ class LogHandler {
    * @return the header.
    */
   const char *getDefaultHeader(char *pStr, const char *pCategory,
-                               Logger::LoggerLevel level, time_t now) const;
+                               Logger::LoggerLevel level,
+                               const std::timespec *now) const;
 
   /**
    * Returns a default formatted footer. Currently only returns a newline.
@@ -196,7 +197,7 @@ class LogHandler {
    * @param level the log level.
    */
   virtual void writeHeader(const char *pCategory, Logger::LoggerLevel level,
-                           time_t now) = 0;
+                           const std::timespec *now) = 0;
 
   /**
    * Write the message to the log.
@@ -223,7 +224,7 @@ class LogHandler {
   // for handling repeated messages
   unsigned m_count_repeated_messages;
   unsigned m_max_repeat_frequency;
-  time_t m_last_log_time;
+  std::timespec m_last_log_time;
   char m_last_category[MAX_HEADER_LENGTH];
   char m_last_message[MAX_LOG_MESSAGE_SIZE];
   Logger::LoggerLevel m_last_level;
diff --git a/storage/ndb/include/logger/Logger.hpp b/storage/ndb/include/logger/Logger.hpp
index 740756fcd811..3dd0317f4cd2 100644
--- a/storage/ndb/include/logger/Logger.hpp
+++ b/storage/ndb/include/logger/Logger.hpp
@@ -27,9 +27,9 @@
 #define Logger_H
 
 #include <ndb_global.h>
-#include <time.h>
 #include <BaseString.hpp>
 #include <NdbOut.hpp>
+#include <ctime>
 #include "portlib/ndb_compiler.h"
 
 #define MAX_LOG_MESSAGE_SIZE 1024
@@ -121,7 +121,8 @@ class Logger {
       @len max length of result buffer
 
   */
-  static void format_timestamp(const time_t epoch, char *str, size_t len);
+  static void format_timestamp(const std::timespec *epoch, char *str,
+                               size_t len);
 
   // Timestamp - handy class for getting a timestamp string
   class Timestamp {
diff --git a/storage/ndb/include/logger/SysLogHandler.hpp b/storage/ndb/include/logger/SysLogHandler.hpp
index 55494bcab4e6..ff67e3b5b6ea 100644
--- a/storage/ndb/include/logger/SysLogHandler.hpp
+++ b/storage/ndb/include/logger/SysLogHandler.hpp
@@ -88,7 +88,7 @@ class SysLogHandler : public LogHandler {
 
  protected:
   void writeHeader(const char *pCategory, Logger::LoggerLevel level,
-                   time_t now) override;
+                   const std::timespec *now) override;
   void writeMessage(const char *pMsg) override;
   void writeFooter() override;
 
diff --git a/storage/ndb/include/ndb_config.h.in b/storage/ndb/include/ndb_config.h.in
index d3fdb4c2d27e..e3d103d09f5f 100644
--- a/storage/ndb/include/ndb_config.h.in
+++ b/storage/ndb/include/ndb_config.h.in
@@ -69,3 +69,4 @@
 #cmakedefine HAVE_EXPLICIT_BZERO 1
 #cmakedefine HAVE_MEMSET_S 1
 #cmakedefine HAVE_POLL_H 1
+#cmakedefine HAVE_TM_GMTOFF 1
diff --git a/storage/ndb/include/portlib/NdbTimestamp.h b/storage/ndb/include/portlib/NdbTimestamp.h
new file mode 100644
index 000000000000..3f4a97c07c0f
--- /dev/null
+++ b/storage/ndb/include/portlib/NdbTimestamp.h
@@ -0,0 +1,66 @@
+/*
+   Copyright (c) 2025, Oracle and/or its affiliates.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License, version 2.0,
+   as published by the Free Software Foundation.
+
+   This program is designed to work with certain software (including
+   but not limited to OpenSSL) that is licensed under separate terms,
+   as designated in a particular file or component or in included license
+   documentation.  The authors of MySQL hereby grant you an additional
+   permission to link the program and your derivative works with the
+   separately licensed software that they have either included with
+   the program or referenced in the documentation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License, version 2.0, for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+*/
+
+#ifndef NDB_TIMESTAMP_H
+#define NDB_TIMESTAMP_H
+
+#include <ctime>
+#include "ndb_types.h"
+#include "util/span.h"
+
+struct NdbTimestampComponents {
+  Int16 year;
+  Int8 mon;
+  Int8 mday;
+  Int8 hour;
+  Int8 min;
+  Int8 sec;
+  Int32 gmtoff;
+  Int32 nsec;
+};
+
+enum class NdbTimestampStringFormat {
+  DefaultFormat,
+  LegacyFormat,       // YYYY-MM-DD HH:MM:SS
+  Iso8601Utc,         // YYYY-MM-DDTHH:MM:SS.ssssssZ
+  Iso8601SystemTime,  // YYYY-MM-DDTHH:MM:SS.ssssss±HH:MM
+};
+
+// Reset internal state after environment variable TZ has changed
+void NdbTimestamp_Reset();
+
+std::timespec NdbTimestamp_GetCurrentTime();
+int NdbTimestamp_GetAsString(ndb::span<char> buf,
+                             NdbTimestampStringFormat format,
+                             const std::timespec *t = nullptr,
+                             const NdbTimestampComponents *tm = nullptr);
+int NdbTimestamp_SetDefaultStringFormat(NdbTimestampStringFormat format);
+int NdbTimestamp_GetDefaultStringFormatLength();
+int NdbTimestamp_GetUtcComponents(const std::timespec *t,
+                                  NdbTimestampComponents *tm);
+int NdbTimestamp_GetLocalComponents(const std::timespec *t,
+                                    NdbTimestampComponents *tm);
+
+#endif
diff --git a/storage/ndb/include/util/ndb_opts.h b/storage/ndb/include/util/ndb_opts.h
index a3ddb7b309b7..f58b0b79859c 100644
--- a/storage/ndb/include/util/ndb_opts.h
+++ b/storage/ndb/include/util/ndb_opts.h
@@ -51,6 +51,7 @@ OPT_EXTERN(int, opt_connect_retries, NONE);
 OPT_EXTERN(const char *, opt_charsets_dir, = nullptr);
 OPT_EXTERN(const char *, opt_tls_search_path, = NDB_TLS_SEARCH_PATH);
 OPT_EXTERN(unsigned long long, opt_mgm_tls, = 0);
+OPT_EXTERN(unsigned long long, opt_ndb_log_timestamps, = 0);
 
 #ifndef NDEBUG
 OPT_EXTERN(const char *, opt_debug, = nullptr);
@@ -80,6 +81,10 @@ namespace NdbStdOpt {
 
 static const char *tls_names[] = {"relaxed", "strict", nullptr};
 static TYPELIB mgm_tls_typelib = {2, "TLS requirement", tls_names, nullptr};
+static const char *timestamps_names[] = {"legacy", "utc", "system", nullptr};
+static TYPELIB timestamps_typelib = {std::size(timestamps_names) - 1,
+                                     "log timestamps format", timestamps_names,
+                                     nullptr};
 
 static constexpr struct my_option usage = {
     "usage",    '?',     "Display this help and exit.",
@@ -266,6 +271,22 @@ static constexpr struct my_option mgm_tls = {"ndb-mgm-tls",
                                              0,
                                              nullptr};
 
+static constexpr struct my_option log_timestamps = {
+    "ndb-log-timestamps",
+    NDB_OPT_NOSHORT,
+    "Log timestamps format (legacy, utc, system)",
+    &opt_ndb_log_timestamps,
+    nullptr,
+    &timestamps_typelib,
+    GET_ENUM,
+    REQUIRED_ARG,
+    0 /* default */,
+    0 /*min*/,
+    2 /*max*/,
+    nullptr,
+    0,
+    nullptr};
+
 #ifndef NDEBUG
 static constexpr struct my_option debug = {
     "debug",    '#',     "Output debug log. Often this is 'd:t:o,filename'.",
diff --git a/storage/ndb/plugin/ha_ndbcluster.cc b/storage/ndb/plugin/ha_ndbcluster.cc
index 3f4f763e8c7d..fbc0b3d067c3 100644
--- a/storage/ndb/plugin/ha_ndbcluster.cc
+++ b/storage/ndb/plugin/ha_ndbcluster.cc
@@ -110,6 +110,7 @@
 #include "storage/ndb/src/common/util/parse_mask.hpp"
 #include "storage/ndb/src/ndbapi/NdbQueryBuilder.hpp"
 #include "storage/ndb/src/ndbapi/NdbQueryOperation.hpp"
+#include "storage/ndb/src/ndbapi/ndb_internal.hpp"
 #include "string_with_len.h"
 #include "strxnmov.h"
 #include "template_utils.h"
@@ -12715,6 +12716,10 @@ static int ndbcluster_init(void *handlerton_ptr) {
 
   // Initialize NdbApi
   ndb_init_internal(1);
+  Ndb_internal::set_log_timestamp_format(
+      opt_log_timestamps
+          ? Ndb_internal::log_timestamp_format::iso8601_system_time
+          : Ndb_internal::log_timestamp_format::iso8601_utc);
 
   if (!ndb_server_hooks.register_server_hooks(ndb_wait_setup_server_startup,
                                               ndb_dd_upgrade_hook)) {
diff --git a/storage/ndb/plugin/ndb_table_stats.cc b/storage/ndb/plugin/ndb_table_stats.cc
index 36d248d29669..39a6d1512e83 100644
--- a/storage/ndb/plugin/ndb_table_stats.cc
+++ b/storage/ndb/plugin/ndb_table_stats.cc
@@ -197,7 +197,7 @@ bool ndb_get_table_commit_count(Ndb *ndb, const NdbDictionary::Table *ndbtab,
     return true;  // Error
   }
 
-  int retries = 100;
+  int retries = 10;
   NdbTransaction *trans;
   do {
     /**
diff --git a/storage/ndb/src/common/logger/BufferedLogHandler.cpp b/storage/ndb/src/common/logger/BufferedLogHandler.cpp
index 3b2c04f84814..96a9a665a379 100644
--- a/storage/ndb/src/common/logger/BufferedLogHandler.cpp
+++ b/storage/ndb/src/common/logger/BufferedLogHandler.cpp
@@ -24,6 +24,7 @@
 */
 
 #include "BufferedLogHandler.hpp"
+#include "portlib/NdbTimestamp.h"
 #include "util/cstrbuf.h"
 #include "util/require.h"
 
@@ -104,13 +105,14 @@ bool BufferedLogHandler::is_open() {
 // PROTECTED
 //
 void BufferedLogHandler::writeHeader(const char *pCategory,
-                                     Logger::LoggerLevel level, time_t now) {
+                                     Logger::LoggerLevel level,
+                                     const std::timespec *now) {
   /**
    * Add log level, timestamp, category length to m_log_fixedpart and
    * category to m_log_varpart.
    */
   m_log_fixedpart.level = level;
-  m_log_fixedpart.log_timestamp = now;
+  m_log_fixedpart.log_timestamp = *now;
 
   size_t pCategory_len = strlen(pCategory);
   m_log_fixedpart.varpart_length[0] = pCategory_len;
@@ -164,7 +166,7 @@ bool BufferedLogHandler::writeToDestLogHandler() {
     msg[log_fixed_part.varpart_length[1]] = '\0';
 
     m_dest_loghandler->append(category, log_fixed_part.level, msg,
-                              log_fixed_part.log_timestamp);
+                              &log_fixed_part.log_timestamp);
     return true;
   }
   return false;
@@ -177,9 +179,9 @@ void BufferedLogHandler::writeLostMsgDestLogHandler() {
     cstrbuf<LostMsgHandler::MAX_LOST_MESSAGE_SIZE> msg;
     require(msg.appendf(LostMsgHandler::LOST_MESSAGES_FMT, lost_count) != -1);
     assert(!msg.is_truncated());
-    const time_t now = ::time(nullptr);
+    std::timespec now = NdbTimestamp_GetCurrentTime();
     m_dest_loghandler->append(m_buffer_msg_category, Logger::LL_INFO,
-                              msg.c_str(), now);
+                              msg.c_str(), &now);
   }
 }
 
@@ -198,7 +200,7 @@ bool MessageStreamLostMsgHandler::writeLostMsg(char *buf, size_t buf_size,
                                                size_t lost_msgs) {
   BufferedLogHandler::LogMessageFixedPart lost_message_fixedpart;
   lost_message_fixedpart.level = Logger::LL_WARNING;
-  lost_message_fixedpart.log_timestamp = time((time_t *)nullptr);
+  lost_message_fixedpart.log_timestamp = NdbTimestamp_GetCurrentTime();
 
   const size_t sz_fixedpart = sizeof(lost_message_fixedpart);
   require(sz_fixedpart <= buf_size);
diff --git a/storage/ndb/src/common/logger/ConsoleLogHandler.cpp b/storage/ndb/src/common/logger/ConsoleLogHandler.cpp
index 8b88e5dfb895..b4dbd00d84fa 100644
--- a/storage/ndb/src/common/logger/ConsoleLogHandler.cpp
+++ b/storage/ndb/src/common/logger/ConsoleLogHandler.cpp
@@ -41,7 +41,8 @@ bool ConsoleLogHandler::is_open() { return true; }
 // PROTECTED
 //
 void ConsoleLogHandler::writeHeader(const char *pCategory,
-                                    Logger::LoggerLevel level, time_t now) {
+                                    Logger::LoggerLevel level,
+                                    const std::timespec *now) {
   char str[MAX_HEADER_LENGTH];
   _out << getDefaultHeader(str, pCategory, level, now);
 }
diff --git a/storage/ndb/src/common/logger/EventLogHandler.cpp b/storage/ndb/src/common/logger/EventLogHandler.cpp
index c6dc2b42cd6d..e80b192c9709 100644
--- a/storage/ndb/src/common/logger/EventLogHandler.cpp
+++ b/storage/ndb/src/common/logger/EventLogHandler.cpp
@@ -27,7 +27,7 @@
 
 #include "EventLogHandler.hpp"
 
-#include <time.h>
+#include <ctime>
 
 #include "message.h"
 
@@ -164,7 +164,7 @@ bool EventLogHandler::close() {
 bool EventLogHandler::is_open() { return (m_event_source != nullptr); }
 
 void EventLogHandler::writeHeader(const char *, Logger::LoggerLevel level,
-                                  time_t) {
+                                  const std::timespec *) {
   m_level = level;
 }
 
diff --git a/storage/ndb/src/common/logger/EventLogHandler.hpp b/storage/ndb/src/common/logger/EventLogHandler.hpp
index 39d6a098fa20..c4671692640f 100644
--- a/storage/ndb/src/common/logger/EventLogHandler.hpp
+++ b/storage/ndb/src/common/logger/EventLogHandler.hpp
@@ -29,7 +29,7 @@
 #include "LogHandler.hpp"
 #include "portlib/ndb_compiler.h"
 
-#include <time.h>
+#include <ctime>
 
 /**
  * Log messages to the Windows event log
@@ -60,7 +60,7 @@ class EventLogHandler : public LogHandler {
 
  private:
   virtual void writeHeader(const char *pCategory, Logger::LoggerLevel level,
-                           time_t now);
+                           const std::timespec *now);
   virtual void writeMessage(const char *pMsg);
   virtual void writeFooter();
 
diff --git a/storage/ndb/src/common/logger/FileLogHandler.cpp b/storage/ndb/src/common/logger/FileLogHandler.cpp
index 2fb8ffca5696..cf1a51a0161c 100644
--- a/storage/ndb/src/common/logger/FileLogHandler.cpp
+++ b/storage/ndb/src/common/logger/FileLogHandler.cpp
@@ -75,7 +75,8 @@ bool FileLogHandler::close() {
 }
 
 void FileLogHandler::writeHeader(const char *pCategory,
-                                 Logger::LoggerLevel level, time_t now) {
+                                 Logger::LoggerLevel level,
+                                 const std::timespec *now) {
   char str[MAX_HEADER_LENGTH];
   m_pLogFile->writeChar(getDefaultHeader(str, pCategory, level, now));
 }
diff --git a/storage/ndb/src/common/logger/LogHandler.cpp b/storage/ndb/src/common/logger/LogHandler.cpp
index 8d934daeb31d..a9d97e53be63 100644
--- a/storage/ndb/src/common/logger/LogHandler.cpp
+++ b/storage/ndb/src/common/logger/LogHandler.cpp
@@ -33,19 +33,19 @@
 //
 // PUBLIC
 //
-LogHandler::LogHandler() : m_errorCode(0), m_errorStr(nullptr) {
+LogHandler::LogHandler()
+    : m_errorCode(0), m_errorStr(nullptr), m_last_log_time() {
   m_max_repeat_frequency = 3;  // repeat messages maximum every 3 seconds
   m_count_repeated_messages = 0;
   m_last_category[0] = 0;
   m_last_message[0] = 0;
-  m_last_log_time = 0;
   m_last_level = Logger::LL_UNDEFINED_LEVEL;
 }
 
 LogHandler::~LogHandler() {}
 
 void LogHandler::append(const char *pCategory, Logger::LoggerLevel level,
-                        const char *pMsg, time_t now) {
+                        const char *pMsg, const std::timespec *now) {
   if (m_max_repeat_frequency == 0 || level != m_last_level ||
       strcmp(pCategory, m_last_category) || strcmp(pMsg, m_last_message)) {
     if (m_count_repeated_messages > 0)  // print that message
@@ -60,18 +60,25 @@ void LogHandler::append(const char *pCategory, Logger::LoggerLevel level,
     }
   } else  // repeated message
   {
-    if (now < (time_t)(m_last_log_time + m_max_repeat_frequency)) {
+    Int64 diff_sec = now->tv_sec - m_last_log_time.tv_sec;
+    Int32 diff_nsec = now->tv_nsec - m_last_log_time.tv_nsec;
+    if (diff_nsec < 0) {
+      diff_sec--;
+      diff_nsec += 1000'000'000;
+    }
+
+    if (diff_sec < m_max_repeat_frequency) {
       m_count_repeated_messages++;
       return;
     }
   }
 
   append_impl(pCategory, level, pMsg, now);
-  m_last_log_time = now;
+  m_last_log_time = *now;
 }
 
 void LogHandler::append_impl(const char *pCategory, Logger::LoggerLevel level,
-                             const char *pMsg, time_t now) {
+                             const char *pMsg, const std::timespec *now) {
   writeHeader(pCategory, level, now);
   if (m_count_repeated_messages <= 1)
     writeMessage(pMsg);
@@ -86,7 +93,7 @@ void LogHandler::append_impl(const char *pCategory, Logger::LoggerLevel level,
 
 const char *LogHandler::getDefaultHeader(char *pStr, const char *pCategory,
                                          Logger::LoggerLevel level,
-                                         time_t now) const {
+                                         const std::timespec *now) const {
   char timestamp[64];
   Logger::format_timestamp(now, timestamp, sizeof(timestamp));
 
diff --git a/storage/ndb/src/common/logger/Logger.cpp b/storage/ndb/src/common/logger/Logger.cpp
index 7f6ba0a04ca9..769d9a332ae2 100644
--- a/storage/ndb/src/common/logger/Logger.cpp
+++ b/storage/ndb/src/common/logger/Logger.cpp
@@ -42,7 +42,8 @@
 
 #include <BufferedLogHandler.hpp>
 
-#include <portlib/ndb_localtime.h>
+#include "portlib/NdbTimestamp.h"
+#include "util/require.h"
 
 const char *Logger::LoggerLevelNames[] = {"ON      ", "DEBUG   ", "INFO    ",
                                           "WARNING ", "ERROR   ", "CRITICAL",
@@ -77,7 +78,7 @@ class InternalLogListHandler : public LogHandler {
    * Here we pass the message to all bound in LogHandlers
    */
   void append(const char *pCategory, Logger::LoggerLevel level,
-              const char *pMsg, time_t now) override {
+              const char *pMsg, const std::timespec *now) override {
     Guard g(m_listMutex);
 
     LogHandler *pHandler = nullptr;
@@ -120,7 +121,7 @@ class InternalLogListHandler : public LogHandler {
 
  protected:
   void writeHeader(const char * /*pCategory*/, Logger::LoggerLevel /*level*/,
-                   time_t /*now*/) override {}
+                   const std::timespec * /*now*/) override {}
   void writeMessage(const char * /*pMsg*/) override {}
   void writeFooter() override {}
 
@@ -385,8 +386,8 @@ void Logger::log(LoggerLevel logLevel, const char *pMsg, va_list ap) const {
   if (m_logLevels[LL_ON] && m_logLevels[logLevel]) {
     char buf[MAX_LOG_MESSAGE_SIZE];
     BaseString::vsnprintf(buf, sizeof(buf), pMsg, ap);
-    time_t now = ::time((time_t *)nullptr);
-    m_logHandler->append(m_pCategory, logLevel, buf, now);
+    std::timespec now = NdbTimestamp_GetCurrentTime();
+    m_logHandler->append(m_pCategory, logLevel, buf, &now);
   }
 }
 
@@ -396,31 +397,18 @@ void Logger::setRepeatFrequency(unsigned val) {
 }
 
 Logger::Timestamp::Timestamp() {
-  Logger::format_timestamp(time(nullptr), buff, TsLen);
+  std::timespec now = NdbTimestamp_GetCurrentTime();
+  Logger::format_timestamp(&now, buff, TsLen);
 }
 
-void Logger::format_timestamp(const time_t epoch, char *str, size_t len) {
+void Logger::format_timestamp(const std::timespec *epoch, char *str,
+                              size_t len) {
   assert(len > 0);  // Assume buffer has size
 
   // convert to local timezone
-  tm tm_buf;
-  if (ndb_localtime_r(&epoch, &tm_buf) == nullptr) {
-    // Failed to convert to local timezone.
-    // Fill with bogus time stamp value in order
-    // to ensure buffer can be safely printed
-    strncpy(str, "2001-01-01 00:00:00", len);
-    str[len - 1] = 0;
-    return;
-  }
-
-  // Print the broken down time in timestamp format
-  // to the string buffer
-  BaseString::snprintf(
-      str, len, "%d-%.2d-%.2d %.2d:%.2d:%.2d", tm_buf.tm_year + 1900,
-      tm_buf.tm_mon + 1,  // month is [0,11]. +1 -> [1,12]
-      tm_buf.tm_mday, tm_buf.tm_hour, tm_buf.tm_min, tm_buf.tm_sec);
-  str[len - 1] = 0;
-  return;
+  int nb = NdbTimestamp_GetAsString(
+      {str, len}, NdbTimestampStringFormat::DefaultFormat, epoch, nullptr);
+  require(nb > 0);
 }
 
 #ifdef TEST_LOGGER
@@ -449,7 +437,8 @@ class Ndb_log_consumer : public NdbApiLogConsumer {
   void log(LogLevel level, const char *category, const char *message) override {
     char timestamp[64];
     std::ofstream ofs(filename);
-    Logger::format_timestamp(time(nullptr), timestamp, sizeof(timestamp));
+    std::timespec now = NdbTimestamp_GetCurrentTime();
+    Logger::format_timestamp(&now, timestamp, sizeof(timestamp));
     ofs << timestamp << " [" << category << "]"
         << " [" << level << "] " << message << std::endl;
     ofs.close();
@@ -465,11 +454,13 @@ TAPTEST(logger) {
       ofs.close();
     };
 
+    const int timestamp_length = NdbTimestamp_GetDefaultStringFormatLength();
+    constexpr int category_length = 13;  // [EventLogger]
     auto getLastLogMsg = [&](const std::string &fileName, bool clear = true) {
       std::ifstream ifs(fileName);
       std::stringstream ss;
       // Remove date + Category
-      ifs.seekg(34);
+      ifs.seekg(timestamp_length + 1 + category_length + 1);
       ss << ifs.rdbuf();
       ifs.close();
       if (clear) {
@@ -507,7 +498,8 @@ TAPTEST(logger) {
       g_eventLogger->info("[SYNC] message #%d", i);
       const std::string expected_msg =
           "INFO     -- [SYNC] message #" + std::to_string(i);
-      OK(getLastLogMsg(file) == expected_msg);
+      const std::string last_msg = getLastLogMsg(file);
+      OK(last_msg == expected_msg);
     }
 
     // Async
diff --git a/storage/ndb/src/common/logger/SysLogHandler.cpp b/storage/ndb/src/common/logger/SysLogHandler.cpp
index 951650169b5e..e6e61233ac3e 100644
--- a/storage/ndb/src/common/logger/SysLogHandler.cpp
+++ b/storage/ndb/src/common/logger/SysLogHandler.cpp
@@ -66,7 +66,8 @@ bool SysLogHandler::close() {
 bool SysLogHandler::is_open() { return m_open; }
 
 void SysLogHandler::writeHeader(const char *pCategory,
-                                Logger::LoggerLevel level, time_t /*now*/) {
+                                Logger::LoggerLevel level,
+                                const std::timespec * /*now*/) {
   // Save category to be used by writeMessage...
   m_pCategory = pCategory;
   // Map LogLevel to syslog severity
diff --git a/storage/ndb/src/common/portlib/CMakeLists.txt b/storage/ndb/src/common/portlib/CMakeLists.txt
index a661efa8ec50..4c0c033fb071 100644
--- a/storage/ndb/src/common/portlib/CMakeLists.txt
+++ b/storage/ndb/src/common/portlib/CMakeLists.txt
@@ -72,6 +72,7 @@ ADD_CONVENIENCE_LIBRARY(ndbportlib
   NdbGetRUsage.cpp
   NdbLockCpuUtil.cpp
   NdbSpin.cpp
+  NdbTimestamp.cpp
   LINK_LIBRARIES mysys ${LIBSOCKET}
   )
 
@@ -85,3 +86,4 @@ NDB_ADD_TEST(NdbDir-t NdbDir.cpp LIBS ndbmgmapi ndbgeneral ndbportlib)
 
 NDB_ADD_TEST(NdbGetInAddr-t NdbTCP.cpp LIBS ndbgeneral ${LIBSOCKET} ${LIBNSL})
 NDB_ADD_TEST(NdbProcess-t NdbProcess.cpp LIBS ndbmgmapi ndbgeneral ndbportlib)
+NDB_ADD_TEST(NdbTimestamp-t NdbTimestamp.cpp LIBS ndbgeneral ndbportlib)
diff --git a/storage/ndb/src/common/portlib/NdbTimestamp.cpp b/storage/ndb/src/common/portlib/NdbTimestamp.cpp
new file mode 100644
index 000000000000..aad5abfff431
--- /dev/null
+++ b/storage/ndb/src/common/portlib/NdbTimestamp.cpp
@@ -0,0 +1,580 @@
+/*
+   Copyright (c) 2025, Oracle and/or its affiliates.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License, version 2.0,
+   as published by the Free Software Foundation.
+
+   This program is designed to work with certain software (including
+   but not limited to OpenSSL) that is licensed under separate terms,
+   as designated in a particular file or component or in included license
+   documentation.  The authors of MySQL hereby grant you an additional
+   permission to link the program and your derivative works with the
+   separately licensed software that they have either included with
+   the program or referenced in the documentation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License, version 2.0, for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
+*/
+
+#include <atomic>
+#include <cassert>
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <ctime>
+
+#include "ndb_config.h"
+#include "portlib/NdbTimestamp.h"
+#include "util/require.h"
+#include "util/span.h"
+
+#ifdef _WIN32
+static inline struct tm *gmtime_r(const time_t *pt, struct tm *ptm) {
+  return gmtime_s(ptm, pt) == 0 ? ptm : nullptr;
+}
+
+static inline struct tm *localtime_r(const time_t *pt, struct tm *ptm) {
+  return localtime_s(ptm, pt) == 0 ? ptm : nullptr;
+}
+#endif
+
+#if !defined(NDEBUG) || !defined(HAVE_TM_GMTOFF)
+
+/*
+ * On platforms where struct tm does not have tm_gmtoff field (Windows and
+ * Solaris) the below code will calculate tm_gmtoff if needed and caches it.
+ *
+ * The cache can contain offset for standard time and dayligt saving time for
+ * one odd and one even year, this to avoid potentially repeatable invalidation
+ * and recalculation around new year where timestamps from old year may mix with
+ * timestamps from new year.
+ *
+ * Normally these functions are only used for current time and needing cache for
+ * two years should be rare. Also timezones normally use same GMT offset from
+ * year to year, but some day some timezone may announce that they will change
+ * for example their use of daylight saving time.
+ *
+ * The calculation of GMT offset is done by comparing the time part of time
+ * (HH:MM:SS) component from system timezone and GMT. It assumes that the date
+ * part may be off by at most one day. That is true as long as GMT offset is
+ * less than the shortest day. Today all known timezones that uses daylight
+ * saving time adjusts time with one hour and shortest day may be 23 hours. And
+ * timezones are between -11:00 (Pacific/Pago_Pago) and +14:00
+ * (Pacific/Kiritimati).
+ */
+
+static int calculate_gmtoff(const time_t *pt, const struct tm *ptm) {
+  struct tm gtm;
+  gmtime_r(pt, &gtm);
+  if (ptm->tm_year == gtm.tm_year && ptm->tm_yday == gtm.tm_yday) {
+    // no adjustment needed
+  } else if (ptm->tm_year == gtm.tm_year) {
+    int yday_diff = ptm->tm_yday - gtm.tm_yday;
+    // Only adjusting hours since date will not be used below
+    gtm.tm_hour -= yday_diff * 24;
+  } else {
+    // Assume only one year difference, and one is new years eve and the other
+    // day after.
+    int yday_diff = ptm->tm_year - gtm.tm_year;
+    // Only adjusting hours since date will not be used below
+    gtm.tm_hour -= yday_diff * 24;
+#if !defined(NDEBUG)
+    /* Check that assumptions are correct:
+     * - year differ by one
+     * - the newer year has yday=1 (Jan 1)
+     * - the older year is on its last day (Dec 31)
+     */
+    if (gtm.tm_year < ptm->tm_year) {
+      require(gtm.tm_year + 1 == ptm->tm_year);
+      require(ptm->tm_yday == 0);
+      require(gtm.tm_mon == 11);
+      require(gtm.tm_mday == 31);
+    } else {
+      require(ptm->tm_year + 1 == gtm.tm_year);
+      require(gtm.tm_yday == 0);
+      require(ptm->tm_mon == 11);
+      require(ptm->tm_mday == 31);
+    }
+#endif
+  }
+  int gmtoff = (ptm->tm_sec - gtm.tm_sec) + 60 * (ptm->tm_min - gtm.tm_min) +
+               3600 * (ptm->tm_hour - gtm.tm_hour);
+#ifdef HAVE_TM_GMTOFF
+  require(gmtoff == ptm->tm_gmtoff);
+#endif
+  return gmtoff;
+}
+
+#ifndef HAVE_TM_GMTOFF
+struct year_gmtoff {
+  int32_t year;
+  int32_t gmtoff;
+};
+static_assert(std::atomic<year_gmtoff>::is_always_lock_free);
+static constexpr int32_t bad_gmtoff = UINT32_MAX;
+
+static std::atomic<year_gmtoff> year_gmtoff_arr[2 /* year&1 */][2 /* isdst */];
+
+static void init_cached_gmtoff() {
+  for (auto &year_entry : year_gmtoff_arr)
+    for (auto &dst_entry : year_entry)
+      dst_entry.store({0, 0}, std::memory_order_relaxed);
+}
+
+static int get_cached_gmtoff(int year, int isdst) {
+  year_gmtoff v =
+      year_gmtoff_arr[year & 1][isdst].load(std::memory_order_relaxed);
+  if (v.year != year) return bad_gmtoff;
+  return v.gmtoff;
+}
+
+static void set_cached_gmtoff(int year, int isdst, int gmtoff) {
+  year_gmtoff v = {year, gmtoff};
+  year_gmtoff_arr[year & 1][isdst].store(v, std::memory_order_relaxed);
+}
+#endif
+
+static int get_gmtoff(const time_t *pt [[maybe_unused]], const struct tm *ptm) {
+#ifdef HAVE_TM_GMTOFF
+  assert(ptm->tm_gmtoff == calculate_gmtoff(pt, ptm));
+  return ptm->tm_gmtoff;
+#else
+  int gmtoff = get_cached_gmtoff(ptm->tm_year, ptm->tm_isdst);
+  if (gmtoff != bad_gmtoff) {
+    assert(gmtoff == calculate_gmtoff(pt, ptm));
+    return gmtoff;
+  }
+  gmtoff = calculate_gmtoff(pt, ptm);
+  set_cached_gmtoff(ptm->tm_year, ptm->tm_isdst, gmtoff);
+  return gmtoff;
+#endif
+}
+
+#endif
+
+void NdbTimestamp_Reset() {
+  tzset();
+#ifndef HAVE_TM_GMTOFF
+  init_cached_gmtoff();
+#endif
+}
+
+std::timespec NdbTimestamp_GetCurrentTime() {
+  std::timespec ts;
+  require(std::timespec_get(&ts, TIME_UTC) == TIME_UTC);
+  return ts;
+}
+
+int NdbTimestamp_GetUtcComponents(const std::timespec *t,
+                                  NdbTimestampComponents *tm) {
+  time_t sec = t->tv_sec;
+  struct tm tmbuf;
+  if (gmtime_r(&sec, &tmbuf) == nullptr) return -1;
+  tm->year = tmbuf.tm_year + 1900;
+  tm->mon = tmbuf.tm_mon + 1;
+  tm->mday = tmbuf.tm_mday;
+  tm->hour = tmbuf.tm_hour;
+  tm->min = tmbuf.tm_min;
+  tm->sec = tmbuf.tm_sec;
+  tm->nsec = t->tv_nsec;
+  tm->gmtoff = 0;
+  return 0;
+}
+
+int NdbTimestamp_GetLocalComponents(const std::timespec *t,
+                                    NdbTimestampComponents *tm) {
+  time_t sec = t->tv_sec;
+  struct tm tmbuf;
+  if (localtime_r(&sec, &tmbuf) == nullptr) return -1;
+  tm->year = tmbuf.tm_year + 1900;
+  tm->mon = tmbuf.tm_mon + 1;
+  tm->mday = tmbuf.tm_mday;
+  tm->hour = tmbuf.tm_hour;
+  tm->min = tmbuf.tm_min;
+  tm->sec = tmbuf.tm_sec;
+  tm->nsec = t->tv_nsec;
+#ifdef HAVE_TM_GMTOFF
+  tm->gmtoff = tmbuf.tm_gmtoff;
+  assert(tm->gmtoff == get_gmtoff(&sec, &tmbuf));
+#else
+  tm->gmtoff = get_gmtoff(&sec, &tmbuf);
+#endif
+  return 0;
+}
+
+static NdbTimestampStringFormat default_format =
+    NdbTimestampStringFormat::LegacyFormat;
+
+/*
+ * unsigned_integer_to_zero_padded_string
+ *
+ * Writes decimal representation of unsigned integer, x, into given buffer, zero
+ * padded prefix to full buffer.
+ *
+ * Note, given integer value does not fit in buffer function abort program.
+ *
+ * Note, no null termination. Returns buffer end (next position after last
+ * digit).
+ *
+ */
+static char *unsigned_integer_to_zero_padded_string(ndb::span<char> buf,
+                                                    unsigned x) {
+  size_t i = buf.size();
+  while (x > 0 && i > 0) {
+    i--;
+    buf[i] = '0' + (x % 10);
+    x /= 10;
+  }
+  require(x == 0);
+  while (i > 0) {
+    i--;
+    buf[i] = '0';
+  }
+  return buf.end();
+}
+
+static int NdbTimestamp_FormatString(ndb::span<char> buf,
+                                     NdbTimestampStringFormat format,
+                                     const NdbTimestampComponents *tm) {
+  if (format == NdbTimestampStringFormat::DefaultFormat)
+    format = default_format;
+  int timesep;
+  int usec;
+  int timezone;
+  size_t length;
+  switch (format) {
+    case NdbTimestampStringFormat::LegacyFormat:
+      timesep = 1;
+      usec = 0;
+      timezone = 0;
+      length = 19;  // 10 + 1 + 8;
+      break;
+    case NdbTimestampStringFormat::Iso8601Utc:
+      require(tm->gmtoff == 0);
+      timesep = 2;
+      usec = 1;
+      timezone = 1;
+      length = 27;  // 10 + 1 + 8 + 7 + 1;
+      break;
+    case NdbTimestampStringFormat::Iso8601SystemTime:
+      timesep = 2;
+      usec = 1;
+      timezone = 2;
+      length = 32;  // 10 + 1 + 8 + 7 + 6;
+      break;
+    default:
+      // unreachable
+      abort();
+  }
+  if (length > buf.size()) {
+    // Too small buffer
+    return -1;
+  }
+  char *p = buf.data();
+  // date
+  {
+    p = unsigned_integer_to_zero_padded_string({p, 4}, tm->year);
+    *p++ = '-';
+    p = unsigned_integer_to_zero_padded_string({p, 2}, tm->mon);
+    *p++ = '-';
+    p = unsigned_integer_to_zero_padded_string({p, 2}, tm->mday);
+  }
+  // time separator
+  switch (timesep) {
+    case 0:
+      break;
+    case 1:
+      *p++ = ' ';
+      break;
+    case 2:
+      *p++ = 'T';
+      break;
+  }
+  // time
+  {
+    p = unsigned_integer_to_zero_padded_string({p, 2}, tm->hour);
+    *p++ = ':';
+    p = unsigned_integer_to_zero_padded_string({p, 2}, tm->min);
+    *p++ = ':';
+    p = unsigned_integer_to_zero_padded_string({p, 2}, tm->sec);
+  }
+  // usec
+  if (usec == 1) {
+    *p++ = '.';
+    p = unsigned_integer_to_zero_padded_string({p, 6}, tm->nsec / 1000);
+  }
+  // timezone
+  switch (timezone) {
+    case 0:
+      break;
+    case 1:
+      *p++ = 'Z';
+      break;
+    case 2: {
+      int gmtoff = tm->gmtoff;
+      if (gmtoff < 0) {
+        *p++ = '-';
+        gmtoff = -gmtoff;
+      } else {
+        *p++ = '+';
+      }
+      int h = gmtoff / 3600;
+      int m = gmtoff / 60 % 60;
+      p = unsigned_integer_to_zero_padded_string({p, 2}, h);
+      *p++ = ':';
+      p = unsigned_integer_to_zero_padded_string({p, 2}, m);
+    } break;
+    default:
+      // unreachable
+      abort();
+  }
+  *p = 0;
+  assert(p == buf.begin() + length);
+  return length;
+}
+
+int NdbTimestamp_GetAsString(ndb::span<char> buf,
+                             NdbTimestampStringFormat format,
+                             const std::timespec *pt,
+                             const NdbTimestampComponents *ptm) {
+  std::timespec t;
+  NdbTimestampComponents tm;
+  int ok = 0;
+  if (ptm != nullptr)
+    tm = *ptm;
+  else {
+    if (pt != nullptr)
+      t = *pt;
+    else
+      t = NdbTimestamp_GetCurrentTime();
+    if (format == NdbTimestampStringFormat::DefaultFormat)
+      format = default_format;
+    switch (format) {
+      case NdbTimestampStringFormat::LegacyFormat:
+      case NdbTimestampStringFormat::Iso8601SystemTime:
+        ok = NdbTimestamp_GetLocalComponents(&t, &tm);
+        break;
+      case NdbTimestampStringFormat::Iso8601Utc:
+        ok = NdbTimestamp_GetUtcComponents(&t, &tm);
+        break;
+      default:
+        abort();
+    }
+  }
+  if (ok == -1) return -1;
+  ok = NdbTimestamp_FormatString(buf, format, &tm);
+  return ok;
+}
+
+int NdbTimestamp_SetDefaultStringFormat(NdbTimestampStringFormat format) {
+  switch (format) {
+    case NdbTimestampStringFormat::DefaultFormat:
+      default_format = NdbTimestampStringFormat::LegacyFormat;
+      return 0;
+    case NdbTimestampStringFormat::LegacyFormat:
+    case NdbTimestampStringFormat::Iso8601Utc:
+    case NdbTimestampStringFormat::Iso8601SystemTime:
+      default_format = format;
+      return 0;
+    default:
+      abort();
+  }
+  return -1;
+}
+
+int NdbTimestamp_GetDefaultStringFormatLength() {
+  switch (default_format) {
+    case NdbTimestampStringFormat::DefaultFormat:
+      // default_format should never be DefaultFormat
+      assert(false);
+      return -1;
+    case NdbTimestampStringFormat::LegacyFormat:
+      return 19;
+    case NdbTimestampStringFormat::Iso8601Utc:
+      return 27;
+    case NdbTimestampStringFormat::Iso8601SystemTime:
+      return 32;
+  }
+  return -1;
+}
+
+#ifdef TEST_NDBTIMESTAMP
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include "unittest/mytap/tap.h"
+
+/*
+ * On Windows setting timezone with TZ environment variable and tzset funtion
+ * will not work as for POSIX-like systems.
+ *   - TZ variable only support the "std offset dst" format, not the POSIX
+ *     ":[filespec]" format, nor the
+ *     "std offset[dst[offset][,start[/time],end[/time]]]" format that can be
+ *     used to define the DST rules.
+ *   - From MSDN article for _tzset: "The C run-time library assumes the
+ *     United States' rules for implementing the calculation of daylight
+ *     saving time (DST)."
+ * Skipping check of Stockholm timezone on Windows.
+ * Also since Windows only supports three letter timezone in TZ UTC-14 will be
+ * used instead of LINT-14.
+ *
+ *   - timezones[][0] is used on POSIX-like systems
+ *   - timezones[][1] is used on Windows
+ */
+static const char *timezones[5][2] = {
+    {"TZ=:Etc/UTC" /* "TZ=UTC" */, "TZ=UTC"},
+    {"TZ=:Europe/Stockholm" /* "TZ=CET-01CEST-02,M3.5.0,M10.5.0" */, nullptr},
+    {"TZ=:America/Los_Angeles" /* "TZ=PST+08PDT+07,M3.3.0,M11.1.0" */,
+     "TZ=PST+08PDT"},
+    {"TZ=:Pacific/Pago_Pago" /* "TZ=SST+11" */, "TZ=SST+11"},
+    {"TZ=:Pacific/Kiritimati" /* "TZ=LINT-14" */, "TZ=UTC-14"}};
+
+static struct {
+  time_t t;
+  const char *s[5];  // In same order as timezones above
+} times[5] = {
+    {1735691400,
+     {"2025-01-01T00:30:00.012345Z", "2025-01-01T01:30:00.012345+01:00",
+      "2024-12-31T16:30:00.012345-08:00", "2024-12-31T13:30:00.012345-11:00",
+      "2025-01-01T14:30:00.012345+14:00"}},
+    {1747701000,
+     {"2025-05-20T00:30:00.012345Z", "2025-05-20T02:30:00.012345+02:00",
+      "2025-05-19T17:30:00.012345-07:00", "2025-05-19T13:30:00.012345-11:00",
+      "2025-05-20T14:30:00.012345+14:00"}},
+    {1750379400,
+     {"2025-06-20T00:30:00.012345Z", "2025-06-20T02:30:00.012345+02:00",
+      "2025-06-19T17:30:00.012345-07:00", "2025-06-19T13:30:00.012345-11:00",
+      "2025-06-20T14:30:00.012345+14:00"}},
+    {1761953400,
+     {"2025-10-31T23:30:00.012345Z", "2025-11-01T00:30:00.012345+01:00",
+      "2025-10-31T16:30:00.012345-07:00", "2025-10-31T12:30:00.012345-11:00",
+      "2025-11-01T13:30:00.012345+14:00"}},
+    {1767223800,
+     {"2025-12-31T23:30:00.012345Z", "2026-01-01T00:30:00.012345+01:00",
+      "2025-12-31T15:30:00.012345-08:00", "2025-12-31T12:30:00.012345-11:00",
+      "2026-01-01T13:30:00.012345+14:00"}}};
+
+static void test_UTC() {
+  std::timespec t = NdbTimestamp_GetCurrentTime();
+  time_t ut = t.tv_sec;
+
+  NdbTimestampComponents tc;
+  struct tm tm;
+  NdbTimestamp_GetUtcComponents(&t, &tc);
+  gmtime_r(&ut, &tm);
+  ok1(tc.year == tm.tm_year + 1900);
+  ok1(tc.mon == tm.tm_mon + 1);
+  ok1(tc.mday == tm.tm_mday);
+  ok1(tc.hour == tm.tm_hour);
+  ok1(tc.min == tm.tm_min);
+  ok1(tc.sec == tm.tm_sec);
+  ok1(tc.nsec == t.tv_nsec);
+#ifdef HAVE_TM_GMTOFF
+  ok1(tc.gmtoff == tm.tm_gmtoff);
+#else
+  skip(1, "No tm.tm_gmtoff on this platform");
+#endif
+
+  char buf[2][100];
+
+  // Adjust nano seconds to match expected result
+  tc.nsec = 12345678;
+  t.tv_nsec = 12345678;
+
+  NdbTimestamp_GetAsString({buf[0], sizeof(buf[0])},
+                           NdbTimestampStringFormat::Iso8601Utc, nullptr, &tc);
+  strftime(buf[1], sizeof(buf[1]), "%FT%T.012345Z", &tm);
+  ok1(strcmp(buf[0], buf[1]) == 0);
+
+  constexpr int iutc = 0;  // First timezone is UTC
+  for (size_t i = 0; i < std::size(times); i++) {
+    t.tv_sec = times[i].t;
+    NdbTimestamp_GetAsString({buf[0], sizeof(buf[0])},
+                             NdbTimestampStringFormat::Iso8601Utc, &t);
+    ok1(strcmp(buf[0], times[i].s[iutc]) == 0);
+  }
+}
+
+static void test_TZ(int itz) {
+#ifndef _WIN32
+  const char *tzenv = timezones[itz][0];
+#else
+  const char *tzenv = timezones[itz][1];
+  if (tzenv == nullptr) {
+    skip(16, "Timezone '%s' not supported on Windows.", timezones[itz][0]);
+    return;
+  }
+#endif
+  putenv(strdup(tzenv));
+  printf("%s\n", tzenv);
+  NdbTimestamp_Reset();
+
+  std::timespec t = NdbTimestamp_GetCurrentTime();
+  time_t ut = t.tv_sec;
+
+  NdbTimestampComponents tc;
+  struct tm tm;
+  NdbTimestamp_GetLocalComponents(&t, &tc);
+  localtime_r(&ut, &tm);
+  ok1(tc.year == tm.tm_year + 1900);
+  ok1(tc.mon == tm.tm_mon + 1);
+  ok1(tc.mday == tm.tm_mday);
+  ok1(tc.hour == tm.tm_hour);
+  ok1(tc.min == tm.tm_min);
+  ok1(tc.sec == tm.tm_sec);
+  ok1(tc.nsec == t.tv_nsec);
+#ifdef HAVE_TM_GMTOFF
+  ok1(tc.gmtoff == tm.tm_gmtoff);
+#else
+  skip(1, "No tm.tm_gmtoff on this platform");
+#endif
+
+  char buf[2][100];
+
+  NdbTimestamp_GetAsString({buf[0], sizeof(buf[0])},
+                           NdbTimestampStringFormat::LegacyFormat, nullptr,
+                           &tc);
+  strftime(buf[1], sizeof(buf[1]), "%F %T", &tm);
+  ok1(strcmp(buf[0], buf[1]) == 0);
+
+  // Only compare up to GMT hour offset since format of GMT offset may differ
+  // (+12:34 vs +1234)
+  ok1(strncmp(buf[0], buf[1], 29) == 0);
+
+  // Adjust nano seconds to match expected result
+  tc.nsec = 12345678;
+  t.tv_nsec = 12345678;
+
+  NdbTimestamp_GetAsString({buf[0], sizeof(buf[0])},
+                           NdbTimestampStringFormat::Iso8601SystemTime, nullptr,
+                           &tc);
+  strftime(buf[1], sizeof(buf[1]), "%FT%T.012345%z", &tm);
+  // Only compare up to GMT hour offset since format of GMT offset may differ
+  // (+12:34 vs +1234)
+  ok1(strncmp(buf[0], buf[1], 29) == 0);
+
+  for (size_t i = 0; i < std::size(times); i++) {
+    t.tv_sec = times[i].t;
+    NdbTimestamp_GetAsString({buf[0], sizeof(buf[0])},
+                             NdbTimestampStringFormat::Iso8601SystemTime, &t);
+    int eq = (strcmp(buf[0], times[i].s[itz]) == 0);
+    if (!eq) diag("ERROR: '%s' != '%s'", buf[0], times[i].s[itz]);
+    ok1(eq);
+  }
+}
+
+int main() {
+  test_UTC();
+  for (size_t i = 1; i < std::size(timezones); i++) test_TZ(i);
+  return exit_status();
+}
+
+#endif
diff --git a/storage/ndb/src/common/util/ndb_opts.cpp b/storage/ndb/src/common/util/ndb_opts.cpp
index cfb18541a30a..c0d5f90861c2 100644
--- a/storage/ndb/src/common/util/ndb_opts.cpp
+++ b/storage/ndb/src/common/util/ndb_opts.cpp
@@ -46,12 +46,19 @@ void ndb_usage(usage_fn, const char *load_default_groups[],
 
 static void default_ndb_opt_usage(void) {
   struct my_option my_long_options[] = {
-      NdbStdOpt::usage,           NdbStdOpt::help,
-      NdbStdOpt::version,         NdbStdOpt::ndb_connectstring,
-      NdbStdOpt::mgmd_host,       NdbStdOpt::connectstring,
-      NdbStdOpt::ndb_nodeid,      NdbStdOpt::optimized_node_selection,
-      NdbStdOpt::charsets_dir,    NdbStdOpt::connect_retry_delay,
-      NdbStdOpt::connect_retries, NDB_STD_OPT_DEBUG NdbStdOpt::end_of_options,
+      NdbStdOpt::usage,
+      NdbStdOpt::help,
+      NdbStdOpt::version,
+      NdbStdOpt::ndb_connectstring,
+      NdbStdOpt::mgmd_host,
+      NdbStdOpt::connectstring,
+      NdbStdOpt::ndb_nodeid,
+      NdbStdOpt::optimized_node_selection,
+      NdbStdOpt::charsets_dir,
+      NdbStdOpt::connect_retry_delay,
+      NdbStdOpt::connect_retries,
+      NdbStdOpt::log_timestamps,
+      NDB_STD_OPT_DEBUG NdbStdOpt::end_of_options,
   };
 
   ndb_usage(default_ndb_opt_short, load_default_groups, my_long_options);
diff --git a/storage/ndb/src/kernel/angel.cpp b/storage/ndb/src/kernel/angel.cpp
index 03f63c247954..5f21050ae29a 100644
--- a/storage/ndb/src/kernel/angel.cpp
+++ b/storage/ndb/src/kernel/angel.cpp
@@ -672,6 +672,12 @@ void angel_run(const char *progname, const Vector<BaseString> &original_args,
     one_arg.assfmt("--angel-pid=%d", getpid());
     args.push_back(one_arg);
 
+    if (opt_ndb_log_timestamps < std::size(NdbStdOpt::timestamps_names) - 1) {
+      one_arg.assfmt("--ndb-log-timestamps=%s",
+                     NdbStdOpt::timestamps_names[opt_ndb_log_timestamps]);
+      args.push_back(one_arg);
+    }
+
     if (have_password_option) {
       /**
        * Removes all password opts and adds a new one
diff --git a/storage/ndb/src/kernel/blocks/cmvmi/Cmvmi.cpp b/storage/ndb/src/kernel/blocks/cmvmi/Cmvmi.cpp
index 72a478c2996d..3c1dd9822e3c 100644
--- a/storage/ndb/src/kernel/blocks/cmvmi/Cmvmi.cpp
+++ b/storage/ndb/src/kernel/blocks/cmvmi/Cmvmi.cpp
@@ -29,10 +29,12 @@
 #include <NdbTick.h>
 #include <kernel_types.h>
 #include <portlib/NdbMem.h>
+#include <portlib/NdbTimestamp.h>
 #include <signal.h>
 #include <Configuration.hpp>
 #include <NdbOut.hpp>
 #include <cstring>
+#include <ctime>
 #include <util/ConfigValues.hpp>
 
 #include <FastScheduler.hpp>
@@ -423,10 +425,11 @@ void SavedEventBuffer::save(const Uint32 *theData, Uint32 len) {
   Uint32 total = len + SavedEvent::HeaderLength;
   alloc(total);
 
+  std::timespec now = NdbTimestamp_GetCurrentTime();
   SavedEvent s;
   s.m_len = len;  // size of SavedEvent
   s.m_seq = m_saved_event_sequence++;
-  s.m_time = (Uint32)time(0);
+  s.m_time = (Uint32)now.tv_sec;
   const Uint32 *src = (const Uint32 *)&s;
   Uint32 *dst = m_data + m_write_pos;
 
diff --git a/storage/ndb/src/kernel/blocks/dbtc/Dbtc.hpp b/storage/ndb/src/kernel/blocks/dbtc/Dbtc.hpp
index 17e040267286..2190bc85bdc3 100644
--- a/storage/ndb/src/kernel/blocks/dbtc/Dbtc.hpp
+++ b/storage/ndb/src/kernel/blocks/dbtc/Dbtc.hpp
@@ -2339,8 +2339,6 @@ class Dbtc : public SimulatedBlock {
 
   void checkScanActiveInFailedLqh(Signal *signal, Uint32 scanPtrI,
                                   Uint32 failedNodeId);
-  void checkScanFragList(Signal *, Uint32 failedNodeId, ScanRecord *scanP,
-                         Local_ScanFragRec_dllist::Head &);
 
   void nodeFailCheckTransactions(Signal *, Uint32 transPtrI,
                                  Uint32 failedNodeId);
@@ -2826,6 +2824,7 @@ class Dbtc : public SimulatedBlock {
   bool m_dbinfo_full_apiconnectrecord;
 
   void dump_trans(ApiConnectRecordPtr transPtr);
+  void dump_scan_state(ApiConnectRecordPtr scanTransPtr);
   bool hasOp(ApiConnectRecordPtr transPtr, Uint32 op);
 
  public:
diff --git a/storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp b/storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp
index f46e6601e1fe..70bdb6551293 100644
--- a/storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp
+++ b/storage/ndb/src/kernel/blocks/dbtc/DbtcMain.cpp
@@ -2982,6 +2982,76 @@ void Dbtc::dump_trans(ApiConnectRecordPtr transPtr) {
     } while (tcConList.next(tcConnectptr));
 }
 
+void Dbtc::dump_scan_state(ApiConnectRecordPtr scanTransPtr) {
+  jam();
+  if (scanTransPtr.p->apiScanRec != RNIL) {
+    ScanRecordPtr scanRecPtr;
+    scanRecPtr.i = scanTransPtr.p->apiScanRec;
+    scanRecordPool.getPtr(scanRecPtr);
+
+    g_eventLogger->info(
+        "TC %u ACR %u scan record %u state %u noFrag %u booked %u "
+        "parallel %u table %u batch rows %u bytes %u block %x ri %x",
+        instance(), scanTransPtr.i, scanTransPtr.p->apiScanRec,
+        scanRecPtr.p->scanState, scanRecPtr.p->scanNoFrag,
+        scanRecPtr.p->m_booked_fragments_count, scanRecPtr.p->scanParallel,
+        scanRecPtr.p->scanTableref, scanRecPtr.p->first_batch_size_rows,
+        scanRecPtr.p->batch_byte_size, scanRecPtr.p->m_scan_block_no,
+        scanRecPtr.p->scanRequestInfo);
+
+    {
+      ScanFragLocationPtr sflPtr;
+      Local_ScanFragLocation_list fragLocationRecs(
+          m_fragLocationPool, scanRecPtr.p->m_fragLocations);
+      for (fragLocationRecs.first(sflPtr); !sflPtr.isNull();
+           fragLocationRecs.next(sflPtr)) {
+        for (Uint32 idx = sflPtr.p->m_first_index; idx < sflPtr.p->m_next_index;
+             idx++) {
+          const ScanFragLocation *sfl = &sflPtr.p->m_frag_location_array[idx];
+          g_eventLogger->info("TC %u Frag %u location preferred %x primary %x",
+                              instance(), sfl->fragId, sfl->preferredBlockRef,
+                              sfl->primaryBlockRef);
+        }
+      }
+    }
+
+    {
+      ScanFragRecPtr scanFragPtr;
+      Local_ScanFragRec_dllist running(c_scan_frag_pool,
+                                       scanRecPtr.p->m_running_scan_frags);
+      Local_ScanFragRec_dllist delivered(c_scan_frag_pool,
+                                         scanRecPtr.p->m_delivered_scan_frags);
+      Local_ScanFragRec_dllist queued(c_scan_frag_pool,
+                                      scanRecPtr.p->m_queued_scan_frags);
+
+      for (running.first(scanFragPtr); !scanFragPtr.isNull();
+           running.next(scanFragPtr)) {
+        g_eventLogger->info(
+            "TC %u   Running   scan frag %u state %u timer %u lqh %x conf %u",
+            instance(), scanFragPtr.i, scanFragPtr.p->scanFragState,
+            scanFragPtr.p->scanFragTimer, scanFragPtr.p->lqhBlockref,
+            scanFragPtr.p->m_scan_frag_conf_status);
+      }
+      for (delivered.first(scanFragPtr); !scanFragPtr.isNull();
+           delivered.next(scanFragPtr)) {
+        g_eventLogger->info(
+            "TC %u   Delivered scan frag %u state %u timer %u lqh %x conf %u",
+            instance(), scanFragPtr.i, scanFragPtr.p->scanFragState,
+            scanFragPtr.p->scanFragTimer, scanFragPtr.p->lqhBlockref,
+            scanFragPtr.p->m_scan_frag_conf_status);
+      }
+      for (queued.first(scanFragPtr); !scanFragPtr.isNull();
+           queued.next(scanFragPtr)) {
+        g_eventLogger->info(
+            "TC %u   Queued    scan frag %u state %u timer %u lqh %x conf %u",
+            instance(), scanFragPtr.i, scanFragPtr.p->scanFragState,
+            scanFragPtr.p->scanFragTimer, scanFragPtr.p->lqhBlockref,
+            scanFragPtr.p->m_scan_frag_conf_status);
+      }
+    }
+  }
+}
+
 bool Dbtc::hasOp(ApiConnectRecordPtr transPtr, Uint32 opPtrI) {
   TcConnectRecordPtr tcPtr;
   LocalTcConnectRecord_fifo tcConList(tcConnectRecord, transPtr.p->tcConnect);
@@ -10186,12 +10256,6 @@ void Dbtc::apiFailBlockCleanupCallback(Signal *signal, Uint32 failedNodeId,
   sendSignal(capiFailRef, GSN_API_FAILCONF, signal, 2, JBB);
 }
 
-void Dbtc::checkScanFragList(Signal *signal, Uint32 failedNodeId,
-                             ScanRecord *scanP,
-                             Local_ScanFragRec_dllist::Head &head) {
-  DEBUG("checkScanActiveInFailedLqh: scanFragError");
-}
-
 void Dbtc::execTAKE_OVERTCCONF(Signal *signal) {
   jamEntry();
 
@@ -13945,24 +14009,50 @@ void Dbtc::execSCAN_NEXTREQ(Signal *signal) {
   if (unlikely(apiConnectptr.p->apiConnectstate != CS_START_SCAN)) {
     jam();
     releaseSections(handle);
-    if (apiConnectptr.p->apiConnectstate == CS_CONNECTED) {
-      jam();
-      /*********************************************************************
-       * The application sends a SCAN_NEXTREQ after experiencing a time-out.
-       *  We will send a SCAN_TABREF to indicate a time-out occurred.
-       *********************************************************************/
-      DEBUG("scanTabRefLab: ZSCANTIME_OUT_ERROR2");
-      g_eventLogger->info("apiConnectptr(%d) -> abort", apiConnectptr.i);
-      ndbabort();  // B2 indication of strange things going on
-      scanTabRefLab(signal, ZSCANTIME_OUT_ERROR2, apiConnectptr.p);
+    g_eventLogger->warning(
+        "TC %u : SCAN_NEXTREQ(stop=%u) from node %u to ACR %u "
+        "finds unexpected ACR state %u",
+        instance(), stopScan, refToNode(signal->senderBlockRef()),
+        apiConnectptr.i, apiConnectptr.p->apiConnectstate);
+    if (apiConnectptr.p->apiConnectstate != CS_DISCONNECTED) {
+      jam();
+      g_eventLogger->warning("TC %u : ACR seized by node %u, scanRec %u",
+                             instance(),
+                             refToNode(apiConnectptr.p->ndbapiBlockref),
+                             apiConnectptr.p->apiScanRec);
+      if (apiConnectptr.p->apiScanRec != RNIL) {
+        jam();
+        /* Dump scan state */
+        dump_scan_state(apiConnectptr);
+      }
+    }
+
+    if (apiConnectptr.p->apiConnectstate == CS_CONNECTED &&
+        apiConnectptr.p->ndbapiBlockref == signal->senderBlockRef() &&
+        stopScan) {
+      jam();
+
+      g_eventLogger->warning("TC %u : Confirming scan close", instance());
+
+      /* Case where client owns ApiConnectRecord, sent close and it is already
+       * closed.
+       * Acknowledge close as this is not a state problem from our pov.
+       */
+      ScanTabConf *conf = (ScanTabConf *)&signal->theData[0];
+      conf->apiConnectPtr = apiConnectptr.p->ndbapiConnect;
+      conf->requestInfo = ScanTabConf::EndOfData;
+      conf->transId1 = apiConnectptr.p->transid[0];
+      conf->transId2 = apiConnectptr.p->transid[1];
+      sendSignal(signal->senderBlockRef(), GSN_SCAN_TABCONF, signal,
+                 ScanTabConf::SignalLength, JBB);
       return;
     }
-    DEBUG("scanTabRefLab: ZSTATE_ERROR");
-    DEBUG("  apiConnectstate=" << apiConnectptr.p->apiConnectstate);
-    ndbabort();  // B2 indication of strange things going on
-    scanTabRefLab(signal, ZSTATE_ERROR, apiConnectptr.p);
+
+    /* Disconnect sender, and record owner, if different */
+    handleSignalStateProblem(signal, apiConnectptr,
+                             refToNode(signal->senderBlockRef()), 4);
     return;
-  }  // if
+  }
 
   /*******************************************************
    * START THE ACTUAL LOGIC OF SCAN_NEXTREQ.
diff --git a/storage/ndb/src/kernel/main.cpp b/storage/ndb/src/kernel/main.cpp
index 4af922e22283..1bdaac96d831 100644
--- a/storage/ndb/src/kernel/main.cpp
+++ b/storage/ndb/src/kernel/main.cpp
@@ -27,6 +27,7 @@
 #include <ndb_opts.h>
 #include <portlib/ndb_daemon.h>
 #include <kernel/NodeBitmask.hpp>
+#include "portlib/NdbTimestamp.h"
 #include "portlib/ssl_applink.h"
 #include "util/ndb_openssl_evp.h"
 
@@ -79,6 +80,7 @@ static struct my_option my_long_options[] = {
     NdbStdOpt::connect_retries,      // used
     NdbStdOpt::tls_search_path,
     NdbStdOpt::mgm_tls,
+    NdbStdOpt::log_timestamps,
     NDB_STD_OPT_DEBUG{"core-file", NDB_OPT_NOSHORT, "Write core on errors.",
                       &opt_core, nullptr, nullptr, GET_BOOL, NO_ARG,
                       OPT_WANT_CORE_DEFAULT, 0, 0, nullptr, 0, nullptr},
@@ -159,19 +161,6 @@ int real_main(int argc, char **argv) {
   Ndb_opts::release();  // because ndbd can fork and call real_main() again
   Ndb_opts opts(argc, argv, my_long_options, load_default_groups);
 
-  // Print to stdout/console
-  g_eventLogger->createConsoleHandler();
-
-#ifdef _WIN32
-  /* Output to Windows event log */
-  g_eventLogger->createEventLogHandler("MySQL Cluster Data Node Daemon");
-#endif
-
-  g_eventLogger->setCategory("ndbd");
-
-  // Turn on max loglevel for startup messages
-  g_eventLogger->m_logLevel.setLogLevel(LogLevel::llStartUp, 15);
-
   opts.set_usage_funcs(short_usage_sub);
 
 #ifndef NDEBUG
@@ -201,6 +190,35 @@ int real_main(int argc, char **argv) {
     exit(ho_error);
   }
 
+  switch (opt_ndb_log_timestamps) {
+    case 0: /* legacy */
+      NdbTimestamp_SetDefaultStringFormat(
+          NdbTimestampStringFormat::LegacyFormat);
+      break;
+    case 1: /* utc */
+      NdbTimestamp_SetDefaultStringFormat(NdbTimestampStringFormat::Iso8601Utc);
+      break;
+    case 2: /* system */
+      NdbTimestamp_SetDefaultStringFormat(
+          NdbTimestampStringFormat::Iso8601SystemTime);
+      break;
+    default:
+      abort();  // unreachable
+  }
+
+  // Print to stdout/console
+  g_eventLogger->createConsoleHandler();
+
+#ifdef _WIN32
+  /* Output to Windows event log */
+  g_eventLogger->createEventLogHandler("MySQL Cluster Data Node Daemon");
+#endif
+
+  g_eventLogger->setCategory("ndbd");
+
+  // Turn on max loglevel for startup messages
+  g_eventLogger->m_logLevel.setLogLevel(LogLevel::llStartUp, 15);
+
   if (opt_no_daemon || opt_foreground) {
     // --nodaemon or --forground implies --daemon=0
     opt_daemon = 0;
diff --git a/storage/ndb/src/mgmclient/CommandInterpreter.cpp b/storage/ndb/src/mgmclient/CommandInterpreter.cpp
index 07d00a68bbab..c2860a06320b 100644
--- a/storage/ndb/src/mgmclient/CommandInterpreter.cpp
+++ b/storage/ndb/src/mgmclient/CommandInterpreter.cpp
@@ -24,6 +24,7 @@
 */
 
 #include <atomic>
+#include <ctime>
 
 #include <ndb_global.h>
 
@@ -2552,8 +2553,8 @@ static void report_events(const ndb_logevent &event) {
         event.SavedEvent.len);
 
   char timestamp_str[64];
-  Logger::format_timestamp(event.SavedEvent.time, timestamp_str,
-                           sizeof(timestamp_str));
+  std::timespec t = {event.SavedEvent.time, 0};
+  Logger::format_timestamp(&t, timestamp_str, sizeof(timestamp_str));
 
   ndbout_c("%s %s", timestamp_str, out);
 }
diff --git a/storage/ndb/src/mgmsrv/main.cpp b/storage/ndb/src/mgmsrv/main.cpp
index 122b1ad2351c..aa9bb0f7f703 100644
--- a/storage/ndb/src/mgmsrv/main.cpp
+++ b/storage/ndb/src/mgmsrv/main.cpp
@@ -36,6 +36,7 @@
 #include <kernel_types.h>
 #include <mgmapi_config_parameters.h>
 #include <ndb_version.h>
+#include <portlib/NdbTimestamp.h>
 #include <portlib/ndb_daemon.h>
 #include <version.h>
 #include <NdbAutoPtr.hpp>
@@ -112,6 +113,7 @@ static struct my_option my_long_options[] = {
     NdbStdOpt::connectstring,
     NdbStdOpt::tls_search_path,
     NdbStdOpt::mgm_tls,
+    NdbStdOpt::log_timestamps,
     NDB_STD_OPT_DEBUG{"config-file", 'f', "Specify cluster configuration file",
                       &opts.config_filename, nullptr, nullptr, GET_STR,
                       REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
@@ -323,6 +325,22 @@ static int mgmd_main(int argc, char **argv) {
 
   if ((ho_error = ndb_opts.handle_options())) mgmd_exit(ho_error);
 
+  switch (opt_ndb_log_timestamps) {
+    case 0: /* legacy */
+      NdbTimestamp_SetDefaultStringFormat(
+          NdbTimestampStringFormat::LegacyFormat);
+      break;
+    case 1: /* utc */
+      NdbTimestamp_SetDefaultStringFormat(NdbTimestampStringFormat::Iso8601Utc);
+      break;
+    case 2: /* system */
+      NdbTimestamp_SetDefaultStringFormat(
+          NdbTimestampStringFormat::Iso8601SystemTime);
+      break;
+    default:
+      abort();  // unreachable
+  }
+
   if (argc > 0) {
     std::string invalid_args;
     for (int i = 0; i < argc; i++) invalid_args += ' ' + std::string(argv[i]);
diff --git a/storage/ndb/src/ndbapi/NdbDictionaryImpl.cpp b/storage/ndb/src/ndbapi/NdbDictionaryImpl.cpp
index 9815c2c68b99..013e3f65a7ac 100644
--- a/storage/ndb/src/ndbapi/NdbDictionaryImpl.cpp
+++ b/storage/ndb/src/ndbapi/NdbDictionaryImpl.cpp
@@ -5604,8 +5604,11 @@ NdbEventImpl *NdbDictionaryImpl::getEvent(const char *eventName,
     DBUG_PRINT("error", ("Unexpected number of blob events "
                          "present Expect : %d Actual : %d",
                          blob_count, blob_event_count));
-    m_error.code = 241; /* Invalid schema object version */
-    DBUG_RETURN(nullptr);
+    if (ndb_dictionary_is_mysqld) {
+      m_error.code = 241; /* Invalid schema object version */
+      DBUG_RETURN(nullptr);
+    }
+    DBUG_PRINT("error", ("Blob event mismatch, but not MySQLD so ignoring"));
   }
 
   // Return the successfully created event
diff --git a/storage/ndb/src/ndbapi/ndb_cluster_connection.cpp b/storage/ndb/src/ndbapi/ndb_cluster_connection.cpp
index 52288f40aa24..14337909d66a 100644
--- a/storage/ndb/src/ndbapi/ndb_cluster_connection.cpp
+++ b/storage/ndb/src/ndbapi/ndb_cluster_connection.cpp
@@ -23,6 +23,8 @@
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
 */
 
+#include <ctime>
+
 #include <ndb_global.h>
 #include "util/require.h"
 
@@ -434,8 +436,8 @@ class NdbApiInternalLogHandler : public LogHandler {
     g_api_internal_log_handler = nullptr;
   }
 
-  virtual void append(const char *pCategory, Logger::LoggerLevel level,
-                      const char *pMsg, time_t now) override {
+  void append(const char *pCategory, Logger::LoggerLevel level,
+              const char *pMsg, const std::timespec *now) override {
     {
       if (m_userConsumer) {
         Guard g(m_consumer_mutex);
@@ -469,11 +471,10 @@ class NdbApiInternalLogHandler : public LogHandler {
     return true;
   }
   bool checkParams() override { return true; }
-  void writeHeader(const char *, Logger::LoggerLevel, time_t) override {
-    return;
-  }
-  void writeMessage(const char *) override { return; }
-  void writeFooter() override { return; }
+  void writeHeader(const char *, Logger::LoggerLevel,
+                   const std::timespec *) override {}
+  void writeMessage(const char *) override {}
+  void writeFooter() override {}
   void setRepeatFrequency(unsigned val) override {
     m_defaultHandler->setRepeatFrequency(val);
   }
diff --git a/storage/ndb/src/ndbapi/ndb_internal.cpp b/storage/ndb/src/ndbapi/ndb_internal.cpp
index 7ad94114711b..8c127cc8ea5f 100644
--- a/storage/ndb/src/ndbapi/ndb_internal.cpp
+++ b/storage/ndb/src/ndbapi/ndb_internal.cpp
@@ -25,6 +25,7 @@
 
 #include "ndb_internal.hpp"
 #include "API.hpp"
+#include "portlib/NdbTimestamp.h"
 
 int Ndb_internal::send_event_report(bool is_poll_owner, Ndb *ndb, Uint32 *data,
                                     Uint32 length) {
@@ -43,3 +44,26 @@ int Ndb_internal::send_dump_state_all(Ndb *ndb, Uint32 *dumpStateCodeArray,
                                       Uint32 len) {
   return ndb->theImpl->send_dump_state_all(dumpStateCodeArray, len);
 }
+
+int Ndb_internal::set_log_timestamp_format(log_timestamp_format format) {
+  switch (format) {
+    case log_timestamp_format::default_format:
+      NdbTimestamp_SetDefaultStringFormat(
+          NdbTimestampStringFormat::DefaultFormat);
+      break;
+    case log_timestamp_format::legacy_format:
+      NdbTimestamp_SetDefaultStringFormat(
+          NdbTimestampStringFormat::LegacyFormat);
+      break;
+    case log_timestamp_format::iso8601_utc:
+      NdbTimestamp_SetDefaultStringFormat(NdbTimestampStringFormat::Iso8601Utc);
+      break;
+    case log_timestamp_format::iso8601_system_time:
+      NdbTimestamp_SetDefaultStringFormat(
+          NdbTimestampStringFormat::Iso8601SystemTime);
+      break;
+    default:
+      return -1;
+  }
+  return 0;
+}
diff --git a/storage/ndb/src/ndbapi/ndb_internal.hpp b/storage/ndb/src/ndbapi/ndb_internal.hpp
index 3b5cdd73a75f..7bf504c6b3a5 100644
--- a/storage/ndb/src/ndbapi/ndb_internal.hpp
+++ b/storage/ndb/src/ndbapi/ndb_internal.hpp
@@ -43,5 +43,20 @@ class Ndb_internal {
   static void setForceShortRequests(Ndb *, bool val);
   static void set_TC_COMMIT_ACK_immediate(Ndb *, bool flag);
   static int send_dump_state_all(Ndb *, Uint32 *dumpStateCodeArray, Uint32 len);
+
+  enum class log_timestamp_format {
+    default_format,      // may change by NDBAPI version
+    legacy_format,       // 2025-05-15 11:49:29" in system time zone
+    iso8601_utc,         // 2025-05-15T09:49:29.236832Z
+    iso8601_system_time  // 2025-05-15T11:49:29.236832+02:00
+  };
+  /**
+   * set_log_timestamp_format
+   *
+   * Sets the timestamp format that NDBAPI uses for logs and printouts.
+   *
+   * @param format Set timestamp format to use.
+   */
+  static int set_log_timestamp_format(log_timestamp_format format);
 };
 #endif
diff --git a/storage/ndb/test/src/NDBT_Test.cpp b/storage/ndb/test/src/NDBT_Test.cpp
index f600250c71f0..654a2167f37b 100644
--- a/storage/ndb/test/src/NDBT_Test.cpp
+++ b/storage/ndb/test/src/NDBT_Test.cpp
@@ -26,11 +26,12 @@
 #include <ndb_global.h>
 #include "util/require.h"
 
-#include <time.h>
+#include <ctime>
 
 #include <ndb_opts.h>
 
 #include <portlib/NdbEnv.h>
+#include <portlib/NdbTimestamp.h>
 #include <NdbIndexStat.hpp>
 #include <logger/Logger.hpp>
 #include "NDBT.hpp"
@@ -1635,11 +1636,10 @@ void NDBT_TestSuite::printCases() {
 
 const char *NDBT_TestSuite::getDate(char *str, size_t len) {
   // Get current time
-  time_t now;
-  time(&now);
+  std::timespec now = NdbTimestamp_GetCurrentTime();
 
   // Print as timestamp to buf
-  Logger::format_timestamp(now, str, len);
+  Logger::format_timestamp(&now, str, len);
 
   return str;
 }
diff --git a/storage/ndb/tools/restore/Restore.cpp b/storage/ndb/tools/restore/Restore.cpp
index a5ae595e715e..913376b8bc70 100644
--- a/storage/ndb/tools/restore/Restore.cpp
+++ b/storage/ndb/tools/restore/Restore.cpp
@@ -24,6 +24,7 @@
 */
 
 #include <algorithm>
+#include <ctime>
 #include "util/require.h"
 
 #include <NdbTCP.h>
@@ -44,6 +45,7 @@
 #include "../src/kernel/vm/Emulator.hpp"
 #include "kernel/signaldata/FsOpenReq.hpp"
 #include "portlib/NdbMem.h"
+#include "portlib/NdbTimestamp.h"
 #include "portlib/ndb_file.h"
 #include "restore_tables.h"
 #include "util/ndb_opts.h"
@@ -2435,7 +2437,8 @@ void RestoreLogger::log_error(const char *fmt, ...) {
 
   NdbMutex_Lock(m_mutex);
   if (print_timestamp) {
-    Logger::format_timestamp(time(NULL), timestamp, sizeof(timestamp));
+    std::timespec now = NdbTimestamp_GetCurrentTime();
+    Logger::format_timestamp(&now, timestamp, sizeof(timestamp));
     err << timestamp << " ";
   }
 
@@ -2452,7 +2455,8 @@ void RestoreLogger::log_info(const char *fmt, ...) {
 
   NdbMutex_Lock(m_mutex);
   if (print_timestamp) {
-    Logger::format_timestamp(time(NULL), timestamp, sizeof(timestamp));
+    std::timespec now = NdbTimestamp_GetCurrentTime();
+    Logger::format_timestamp(&now, timestamp, sizeof(timestamp));
     info << timestamp << " ";
   }
 
@@ -2469,7 +2473,8 @@ void RestoreLogger::log_debug(const char *fmt, ...) {
 
   NdbMutex_Lock(m_mutex);
   if (print_timestamp) {
-    Logger::format_timestamp(time(NULL), timestamp, sizeof(timestamp));
+    std::timespec now = NdbTimestamp_GetCurrentTime();
+    Logger::format_timestamp(&now, timestamp, sizeof(timestamp));
     debug << timestamp << " ";
   }
 
diff --git a/storage/temptable/include/temptable/handler.h b/storage/temptable/include/temptable/handler.h
index 6fde818c4bee..570f9482895a 100644
--- a/storage/temptable/include/temptable/handler.h
+++ b/storage/temptable/include/temptable/handler.h
@@ -581,8 +581,7 @@ inline bool Handler::is_field_type_fixed_size(const Field &mysql_field) const {
   }
 }
 
-void kv_store_shards_debug_dump();
-void shared_block_pool_release(THD *thd);
+void close_connection(THD *thd);
 
 } /* namespace temptable */
 
diff --git a/storage/temptable/include/temptable/kv_store.h b/storage/temptable/include/temptable/kv_store.h
index cd67e93c7d7a..bf29b05ea11c 100644
--- a/storage/temptable/include/temptable/kv_store.h
+++ b/storage/temptable/include/temptable/kv_store.h
@@ -31,6 +31,7 @@ TempTable key-value store implementation. */
 #include <string>
 #include <unordered_map>
 
+#include "my_thread_local.h"  // my_thread_id
 #include "storage/temptable/include/temptable/constants.h"
 #include "storage/temptable/include/temptable/kv_store_logger.h"
 
@@ -120,6 +121,22 @@ class Key_value_store
     }
   }
 
+  /** Searches for all tables tagged with the given owner ID.
+   *
+   * [in] id  ID of the thread whose temptables we're looking for.
+   * @return Vector containing the names of all temptables owned by this thread.
+   * */
+  std::vector<std::string> find_all(my_thread_id id) {
+    Exclusive_or_shared_lock lock(m_lock);
+    std::vector<std::string> tables;
+    for (auto &[name, table] : m_kv_store) {
+      if (table.owner_id() == id) {
+        tables.push_back(name);
+      }
+    }
+    return tables;
+  }
+
   /** Removes the table (if one exists) with the given name (key).
    *
    * [in] key Name of a table to remove..
diff --git a/storage/temptable/include/temptable/table.h b/storage/temptable/include/temptable/table.h
index 2777afe2e2e0..9f407d77bcce 100644
--- a/storage/temptable/include/temptable/table.h
+++ b/storage/temptable/include/temptable/table.h
@@ -113,6 +113,10 @@ class Table {
 
   Result enable_indexes();
 
+  /** Returns the ID of the owner THD.
+   * @retval ID of the THD of the session using this temptable */
+  my_thread_id owner_id() const;
+
  private:
   /** Index entry for storing index pointer as well
    * as allocated memory size. */
@@ -168,6 +172,8 @@ class Table {
   Columns m_columns;
 
   TABLE_SHARE *m_mysql_table_share;
+
+  my_thread_id m_owner_id{};
 };
 
 /* Implementation of inlined methods. */
diff --git a/storage/temptable/src/handler.cc b/storage/temptable/src/handler.cc
index d9ea898dbe79..0646ee0e8c6e 100644
--- a/storage/temptable/src/handler.cc
+++ b/storage/temptable/src/handler.cc
@@ -30,8 +30,10 @@ TempTable public handler API implementation. */
 
 #include "my_base.h"
 #include "my_dbug.h"
+#include "mysql/components/services/log_builtins.h"
 #include "mysql/plugin.h"
 #include "sql/mysqld.h"
+#include "sql/sql_class.h"
 #include "sql/sql_thd_internal_api.h"
 #include "sql/system_variables.h"
 #include "storage/temptable/include/temptable/row.h"
@@ -41,27 +43,66 @@ TempTable public handler API implementation. */
 namespace temptable {
 
 /** Key-value store containing all tables for all existing connections.
- * See `Sharded_key_value_store` documentation for more details.
- * */
+ * See `Sharded_key_value_store` documentation for more details. */
 static Sharded_key_value_store<KV_STORE_SHARDS_COUNT> kv_store_shard;
 
 /* Pool of shared-blocks, an external state to the custom `TempTable` memory
  * allocator. See `Lock_free_shared_block_pool` documentation for more details.
- * */
+ */
 static Lock_free_shared_block_pool<SHARED_BLOCK_POOL_SIZE> shared_block_pool;
 
 /** Small helper function which debug-prints the miscellaneous statistics which
- * key-value store has collected.
- * */
-void kv_store_shards_debug_dump() { kv_store_shard.dbug_print(); }
+ * key-value store has collected. */
+static void kv_store_shards_debug_dump() { kv_store_shard.dbug_print(); }
+
+/** Helper function that calls the logging API to log errors. */
+static void log_event(const std::string &msg, loglevel lvl) {
+  LogEvent()
+      .type(LOG_TYPE_ERROR)
+      .prio(lvl)
+      .errcode(ER_TEMPTABLE_ENGINE_ERROR)
+      .subsys("Temptable")
+      .verbatim(msg.c_str());
+}
+
+/** Function that erases leaked temptables by accessing the appropriate shard
+ * for the THD involved and iterating through the shard to find all temptables
+ * owned by this THD. This should only be called during close_connection if
+ * there is a leaked temptable in the optimizer layer, so any time this is
+ * called it indicates a bug. It logs an error to the server logs. */
+static void erase_owned_temptables(THD *thd) {
+  const auto id = thd_thread_id(thd);
+  const std::vector<std::string> tables = kv_store_shard[id].find_all(id);
+
+  for (const auto &t : tables) {
+    kv_store_shard[id].erase(t);
+    thd->decrement_temptable_count();
+    std::stringstream msg;
+    msg << "Detected leaked temptable used internally for query execution: "
+        << t
+        << ". The leak has been cleaned up but we shouldn't end up here "
+           "during normal operation. Please report a bug.";
+    log_event(msg.str(), ERROR_LEVEL);
+  }
+}
 
 /** Small helper function which releases the slot (and memory occupied by the
- * Block) in shared-block pool.
- * */
-void shared_block_pool_release(THD *thd) {
+ * Block) in shared-block pool. */
+static void shared_block_pool_release(THD *thd) {
   shared_block_pool.try_release(thd_thread_id(thd));
 }
 
+/** Helper function that performs the temptable related cleanup. Called from
+ * plugin->close_connection. */
+void close_connection(THD *thd) {
+  if (thd->get_temptable_count() > 0) {
+    temptable::erase_owned_temptables(thd);
+  }
+  assert(thd->get_temptable_count() == 0);
+  temptable::kv_store_shards_debug_dump();
+  temptable::shared_block_pool_release(thd);
+}
+
 #if defined(HAVE_WINNUMA)
 /** Page size used in memory allocation. */
 DWORD win_page_size;
@@ -149,7 +190,9 @@ int Handler::create(const char *table_name, TABLE *mysql_table,
                               all_columns_are_fixed_size, per_table_limit));
 
     ret = insert_result.second ? Result::OK : Result::TABLE_EXIST;
-
+    if (insert_result.second) {
+      ha_thd()->increment_temptable_count();
+    }
   } catch (Result ex) {
     ret = ex;
   } catch (...) {
@@ -160,6 +203,7 @@ int Handler::create(const char *table_name, TABLE *mysql_table,
 }
 
 int Handler::delete_table(const char *table_name, const dd::Table *) {
+  DBUG_EXECUTE_IF("temptable_leak_all", { DBUG_RET(Result::OK); });
   DBUG_TRACE;
 
   assert(table_name != nullptr);
@@ -173,6 +217,7 @@ int Handler::delete_table(const char *table_name, const dd::Table *) {
       if (m_opened_table != table) {
         kv_store.erase(table_name);
         ret = Result::OK;
+        ha_thd()->decrement_temptable_count();
       } else {
         /* Attempt to delete the currently opened table. */
         ret = Result::UNSUPPORTED;
diff --git a/storage/temptable/src/plugin.cc b/storage/temptable/src/plugin.cc
index 13955063b0ff..4680f8885ea5 100644
--- a/storage/temptable/src/plugin.cc
+++ b/storage/temptable/src/plugin.cc
@@ -40,8 +40,7 @@ static handler *create_handler(handlerton *hton, TABLE_SHARE *table_share, bool,
 
 static int close_connection(handlerton *hton, THD *thd) {
   (void)hton;
-  temptable::kv_store_shards_debug_dump();
-  temptable::shared_block_pool_release(thd);
+  temptable::close_connection(thd);
   return 0;
 }
 
diff --git a/storage/temptable/src/table.cc b/storage/temptable/src/table.cc
index 95c22f32d2f0..16847e1773d2 100644
--- a/storage/temptable/src/table.cc
+++ b/storage/temptable/src/table.cc
@@ -39,6 +39,7 @@ TempTable Table implementation. */
 #include "mysqld_error.h"
 #include "sql/field.h"
 #include "sql/key.h"
+#include "sql/sql_class.h"
 #include "sql/table.h"
 #include "storage/temptable/include/temptable/allocator.h"
 #include "storage/temptable/include/temptable/block.h"
@@ -73,7 +74,8 @@ Table::Table(TABLE *mysql_table, Block *shared_block,
       m_index_entries(0, m_allocator),
       m_insert_undo(m_allocator),
       m_columns(m_allocator),
-      m_mysql_table_share(mysql_table->s) {
+      m_mysql_table_share(mysql_table->s),
+      m_owner_id(mysql_table->in_use->thread_id()) {
   const size_t number_of_indexes = mysql_table->s->keys;
   const size_t number_of_columns = mysql_table->s->fields;
   const unsigned char *mysql_row = nullptr;
@@ -262,6 +264,8 @@ Result Table::remove(const unsigned char *mysql_row_must_be,
   return Result::OK;
 }
 
+my_thread_id Table::owner_id() const { return m_owner_id; }
+
 void Table::indexes_create() {
   assert(m_index_entries.empty());
 
diff --git a/unittest/gunit/temptable/temptable-handler-t.cc b/unittest/gunit/temptable/temptable-handler-t.cc
index 8ab525f6439c..ec0454d67c66 100644
--- a/unittest/gunit/temptable/temptable-handler-t.cc
+++ b/unittest/gunit/temptable/temptable-handler-t.cc
@@ -578,4 +578,45 @@ TEST_F(Handler_test, IndexOnOff) {
   EXPECT_EQ(handler.delete_table(table_name, nullptr), 0);
 }
 
+TEST_F(Handler_test, LeakTable) {
+  const char *table_name = "t1";
+
+  Table_helper table_helper(table_name, thd());
+  table_helper.add_field_varstring("col0", 20, false);
+  table_helper.add_field_varstring("col1", 20, false);
+  table_helper.add_field_varstring("col2", 20, false);
+  table_helper.add_index(HA_KEY_ALG_HASH, true, {0});
+  table_helper.add_index(HA_KEY_ALG_BTREE, true, {1});
+  table_helper.add_index(HA_KEY_ALG_HASH, false, {0, 1});
+  table_helper.add_index(HA_KEY_ALG_BTREE, false, {0, 1});
+  table_helper.finalize();
+
+  temptable::Handler *handler =
+      new temptable::Handler(hton(), table_helper.table_share());
+  table_helper.set_handler(handler);
+
+  EXPECT_EQ(handler->create(table_name, table_helper.table(), nullptr, nullptr),
+            0);
+  EXPECT_EQ(handler->open(table_name, 0, 0, nullptr), 0);
+
+  /* Insert (success). */
+  table_helper.field<Field_varstring>(0)->store(1, false);
+  table_helper.field<Field_varstring>(1)->store(1, false);
+  table_helper.field<Field_varstring>(2)->store(1, false);
+  EXPECT_EQ(handler->write_row(table_helper.record_0()), 0);
+
+  table_helper.field<Field_varstring>(0)->store(2, false);
+  table_helper.field<Field_varstring>(1)->store(2, false);
+  table_helper.field<Field_varstring>(2)->store(2, false);
+  EXPECT_EQ(handler->write_row(table_helper.record_0()), 0);
+
+  table_helper.field<Field_varstring>(0)->store(3, false);
+  table_helper.field<Field_varstring>(1)->store(3, false);
+  table_helper.field<Field_varstring>(2)->store(3, false);
+  EXPECT_EQ(handler->write_row(table_helper.record_0()), 0);
+
+  temptable::close_connection(thd());
+  delete handler;
+}
+
 }  // namespace temptable_test
