Browse Source

curl 2025-11-05 (400fffa9)

Code extracted from:

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

at commit 400fffa90f30c7a2dc762fa33009d24851bd2016 (curl-8_17_0).
Curl Upstream 4 weeks ago
parent
commit
e28c33c159
100 changed files with 2545 additions and 2102 deletions
  1. 4 4
      CMake/FindBrotli.cmake
  2. 2 2
      CMake/FindCares.cmake
  3. 67 164
      CMake/FindGSS.cmake
  4. 83 0
      CMake/FindGnuTLS.cmake
  5. 4 4
      CMake/FindLDAP.cmake
  6. 2 2
      CMake/FindLibgsasl.cmake
  7. 2 2
      CMake/FindLibidn2.cmake
  8. 2 2
      CMake/FindLibpsl.cmake
  9. 2 2
      CMake/FindLibrtmp.cmake
  10. 2 2
      CMake/FindLibssh.cmake
  11. 2 2
      CMake/FindLibssh2.cmake
  12. 2 2
      CMake/FindLibuv.cmake
  13. 5 5
      CMake/FindMbedTLS.cmake
  14. 2 2
      CMake/FindNGHTTP2.cmake
  15. 2 2
      CMake/FindNGHTTP3.cmake
  16. 14 10
      CMake/FindNGTCP2.cmake
  17. 2 2
      CMake/FindNettle.cmake
  18. 2 2
      CMake/FindQuiche.cmake
  19. 2 2
      CMake/FindRustls.cmake
  20. 0 65
      CMake/FindWolfSSH.cmake
  21. 2 2
      CMake/FindWolfSSL.cmake
  22. 2 2
      CMake/FindZstd.cmake
  23. 100 30
      CMake/PickyWarnings.cmake
  24. 3 3
      CMake/unix-cache.cmake
  25. 3 2
      CMake/win32-cache.cmake
  26. 106 84
      CMakeLists.txt
  27. 7 7
      include/curl/curl.h
  28. 3 3
      include/curl/curlver.h
  29. 29 1
      include/curl/multi.h
  30. 228 217
      include/curl/typecheck-gcc.h
  31. 22 5
      lib/CMakeLists.txt
  32. 11 8
      lib/Makefile.inc
  33. 32 29
      lib/altsvc.c
  34. 6 5
      lib/amigaos.c
  35. 46 26
      lib/asyn-ares.c
  36. 27 60
      lib/asyn-thrdd.c
  37. 1 2
      lib/bufq.c
  38. 1 1
      lib/bufq.h
  39. 1 2
      lib/cf-h1-proxy.c
  40. 46 41
      lib/cf-h2-proxy.c
  41. 1 2
      lib/cf-haproxy.c
  42. 1 2
      lib/cf-https-connect.c
  43. 71 39
      lib/cf-ip-happy.c
  44. 79 63
      lib/cf-socket.c
  45. 7 6
      lib/cf-socket.h
  46. 26 35
      lib/cfilters.c
  47. 7 17
      lib/cfilters.h
  48. 19 13
      lib/conncache.c
  49. 21 14
      lib/connect.c
  50. 3 0
      lib/connect.h
  51. 1 3
      lib/content_encoding.c
  52. 40 29
      lib/cookie.c
  53. 1 2
      lib/cshutdn.c
  54. 6 2
      lib/curl_addrinfo.c
  55. 15 15
      lib/curl_config.h.cmake
  56. 0 68
      lib/curl_des.c
  57. 12 13
      lib/curl_fopen.c
  58. 2 0
      lib/curl_fopen.h
  59. 21 41
      lib/curl_get_line.c
  60. 1 1
      lib/curl_get_line.h
  61. 41 21
      lib/curl_gssapi.c
  62. 5 6
      lib/curl_gssapi.h
  63. 1 19
      lib/curl_mem_undef.h
  64. 3 3
      lib/curl_memory.h
  65. 76 39
      lib/curl_ntlm_core.c
  66. 0 27
      lib/curl_printf.h
  67. 4 5
      lib/curl_rtmp.c
  68. 5 4
      lib/curl_sasl.c
  69. 32 16
      lib/curl_setup.h
  70. 2 2
      lib/curl_sspi.c
  71. 10 61
      lib/curl_threads.c
  72. 3 25
      lib/curl_threads.h
  73. 47 27
      lib/curl_trc.c
  74. 22 13
      lib/curl_trc.h
  75. 5 7
      lib/curlx/base64.c
  76. 4 0
      lib/curlx/base64.h
  77. 6 0
      lib/curlx/curlx.h
  78. 324 0
      lib/curlx/fopen.c
  79. 60 0
      lib/curlx/fopen.h
  80. 0 3
      lib/curlx/inet_ntop.c
  81. 0 273
      lib/curlx/multibyte.c
  82. 361 0
      lib/curlx/strerr.c
  83. 5 15
      lib/curlx/strerr.h
  84. 8 1
      lib/curlx/version_win32.c
  85. 9 16
      lib/curlx/winapi.c
  86. 1 1
      lib/curlx/winapi.h
  87. 3 3
      lib/cw-out.c
  88. 1 2
      lib/cw-pause.c
  89. 3 3
      lib/dict.c
  90. 1 2
      lib/dllmain.c
  91. 28 25
      lib/doh.c
  92. 2 2
      lib/dynhds.c
  93. 28 25
      lib/easy.c
  94. 6 8
      lib/easy_lock.h
  95. 2 3
      lib/escape.c
  96. 2 3
      lib/fake_addrinfo.c
  97. 20 24
      lib/file.c
  98. 0 1
      lib/fileinfo.c
  99. 11 17
      lib/formdata.c
  100. 191 227
      lib/ftp.c

+ 4 - 4
CMake/FindBrotli.cmake

@@ -25,9 +25,9 @@
 #
 # Input variables:
 #
-# - `BROTLI_INCLUDE_DIR`:    The brotli include directory.
-# - `BROTLICOMMON_LIBRARY`:  Path to `brotlicommon` library.
-# - `BROTLIDEC_LIBRARY`:     Path to `brotlidec` library.
+# - `BROTLI_INCLUDE_DIR`:    Absolute path to brotli include directory.
+# - `BROTLICOMMON_LIBRARY`:  Absolute path to `brotlicommon` library.
+# - `BROTLIDEC_LIBRARY`:     Absolute path to `brotlidec` library.
 #
 # Result variables:
 #
@@ -51,7 +51,7 @@ endif()
 
 if(BROTLI_FOUND)
   set(Brotli_FOUND TRUE)
-  set(BROTLI_VERSION "${BROTLI_libbrotlicommon_VERSION}")
+  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()

+ 2 - 2
CMake/FindCares.cmake

@@ -25,8 +25,8 @@
 #
 # Input variables:
 #
-# - `CARES_INCLUDE_DIR`:   The c-ares include directory.
-# - `CARES_LIBRARY`:       Path to `cares` library.
+# - `CARES_INCLUDE_DIR`:   Absolute path to c-ares include directory.
+# - `CARES_LIBRARY`:       Absolute path to `cares` library.
 #
 # Result variables:
 #

+ 67 - 164
CMake/FindGSS.cmake

@@ -25,12 +25,12 @@
 #
 # Input variables:
 #
-# - `GSS_ROOT_DIR`:      Set this variable to the root installation of GSS. (also supported as environment)
+# - `GSS_ROOT_DIR`:      Absolute path to the root installation of GSS. (also supported as environment)
 #
 # Result variables:
 #
-# - `GSS_FOUND`:         System has the Heimdal library.
-# - `GSS_FLAVOUR`:       "GNU", "MIT" or "Heimdal" if anything found.
+# - `GSS_FOUND`:         System has a GSS library.
+# - `GSS_FLAVOUR`:       "GNU" or "MIT" if anything found.
 # - `GSS_INCLUDE_DIRS`:  The GSS include directories.
 # - `GSS_LIBRARIES`:     The GSS library names.
 # - `GSS_LIBRARY_DIRS`:  The GSS library directories.
@@ -41,16 +41,12 @@
 
 set(_gnu_modname "gss")
 set(_mit_modname "mit-krb5-gssapi")
-set(_heimdal_modname "heimdal-gssapi")
 
 include(CheckIncludeFile)
 include(CheckIncludeFiles)
 include(CheckTypeSize)
 
-set(_gss_root_hints
-  "${GSS_ROOT_DIR}"
-  "$ENV{GSS_ROOT_DIR}"
-)
+set(_gss_root_hints "${GSS_ROOT_DIR}" "$ENV{GSS_ROOT_DIR}")
 
 set(_gss_CFLAGS "")
 set(_gss_LIBRARY_DIRS "")
@@ -59,7 +55,7 @@ set(_gss_LIBRARY_DIRS "")
 if(NOT GSS_ROOT_DIR AND NOT "$ENV{GSS_ROOT_DIR}")
   if(CURL_USE_PKGCONFIG)
     find_package(PkgConfig QUIET)
-    pkg_search_module(_gss ${_gnu_modname} ${_mit_modname} ${_heimdal_modname})
+    pkg_search_module(_gss ${_gnu_modname} ${_mit_modname})
     list(APPEND _gss_root_hints "${_gss_PREFIX}")
     set(_gss_version "${_gss_VERSION}")
   endif()
@@ -69,37 +65,22 @@ if(NOT GSS_ROOT_DIR AND NOT "$ENV{GSS_ROOT_DIR}")
 endif()
 
 if(NOT _gss_FOUND)  # Not found by pkg-config. Let us take more traditional approach.
-  find_file(_gss_configure_script
-    NAMES
-      "krb5-config"
-    HINTS
-      ${_gss_root_hints}
-    PATH_SUFFIXES
-      "bin"
-    NO_CMAKE_PATH
-    NO_CMAKE_ENVIRONMENT_PATH
-  )
-
+  find_file(_gss_configure_script NAMES "krb5-config" PATH_SUFFIXES "bin" HINTS ${_gss_root_hints}
+    NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH)
   # If not found in user-supplied directories, maybe system knows better
-  find_file(_gss_configure_script
-    NAMES
-      "krb5-config"
-    PATH_SUFFIXES
-      "bin"
-  )
+  find_file(_gss_configure_script NAMES "krb5-config" PATH_SUFFIXES "bin")
 
   if(_gss_configure_script)
 
     set(_gss_INCLUDE_DIRS "")
     set(_gss_LIBRARIES "")
 
-    execute_process(
-      COMMAND ${_gss_configure_script} "--cflags" "gssapi"
+    execute_process(COMMAND ${_gss_configure_script} "--cflags" "gssapi"
       OUTPUT_VARIABLE _gss_cflags_raw
       RESULT_VARIABLE _gss_configure_failed
-      OUTPUT_STRIP_TRAILING_WHITESPACE
-    )
+      OUTPUT_STRIP_TRAILING_WHITESPACE)
     message(STATUS "FindGSS krb5-config --cflags: ${_gss_cflags_raw}")
+
     if(NOT _gss_configure_failed)  # 0 means success
       # Should also work in an odd case when multiple directories are given.
       string(STRIP "${_gss_cflags_raw}" _gss_cflags_raw)
@@ -116,12 +97,10 @@ if(NOT _gss_FOUND)  # Not found by pkg-config. Let us take more traditional appr
       endforeach()
     endif()
 
-    execute_process(
-      COMMAND ${_gss_configure_script} "--libs" "gssapi"
+    execute_process(COMMAND ${_gss_configure_script} "--libs" "gssapi"
       OUTPUT_VARIABLE _gss_lib_flags
       RESULT_VARIABLE _gss_configure_failed
-      OUTPUT_STRIP_TRAILING_WHITESPACE
-    )
+      OUTPUT_STRIP_TRAILING_WHITESPACE)
     message(STATUS "FindGSS krb5-config --libs: ${_gss_lib_flags}")
 
     if(NOT _gss_configure_failed)  # 0 means success
@@ -141,157 +120,93 @@ if(NOT _gss_FOUND)  # Not found by pkg-config. Let us take more traditional appr
       endforeach()
     endif()
 
-    execute_process(
-      COMMAND ${_gss_configure_script} "--version"
+    execute_process(COMMAND ${_gss_configure_script} "--version"
       OUTPUT_VARIABLE _gss_version
       RESULT_VARIABLE _gss_configure_failed
-      OUTPUT_STRIP_TRAILING_WHITESPACE
-    )
+      OUTPUT_STRIP_TRAILING_WHITESPACE)
 
     # Older versions may not have the "--version" parameter. In this case we just do not care.
     if(_gss_configure_failed)
       set(_gss_version 0)
+    else()
+      # Strip prefix string to leave the version number only
+      string(REPLACE "Kerberos 5 release " "" _gss_version "${_gss_version}")
     endif()
 
-    execute_process(
-      COMMAND ${_gss_configure_script} "--vendor"
+    execute_process(COMMAND ${_gss_configure_script} "--vendor"
       OUTPUT_VARIABLE _gss_vendor
       RESULT_VARIABLE _gss_configure_failed
-      OUTPUT_STRIP_TRAILING_WHITESPACE
-    )
+      OUTPUT_STRIP_TRAILING_WHITESPACE)
 
     # Older versions may not have the "--vendor" parameter. In this case we just do not care.
-    if(_gss_configure_failed)
-      set(GSS_FLAVOUR "Heimdal")  # most probably, should not really matter
-    else()
-      if(_gss_vendor MATCHES "H|heimdal")
-        set(GSS_FLAVOUR "Heimdal")
-      else()
-        set(GSS_FLAVOUR "MIT")
-      endif()
+    if(NOT _gss_configure_failed AND NOT _gss_vendor MATCHES "Heimdal|heimdal")
+      set(GSS_FLAVOUR "MIT")  # assume a default, should not really matter
     endif()
 
   else()  # Either there is no config script or we are on a platform that does not provide one (Windows?)
 
-    find_path(_gss_INCLUDE_DIRS NAMES "gssapi/gssapi.h"
-      HINTS
-        ${_gss_root_hints}
-      PATH_SUFFIXES
-        "include"
-        "inc"
-    )
+    find_path(_gss_INCLUDE_DIRS NAMES "gssapi/gssapi.h" HINTS ${_gss_root_hints} PATH_SUFFIXES "include" "inc")
+
+    if(_gss_INCLUDE_DIRS)  # We have found something
+      set(_gss_libdir_suffixes "")
 
-    if(_gss_INCLUDE_DIRS)  # jay, we have found something
       cmake_push_check_state()
       list(APPEND CMAKE_REQUIRED_INCLUDES "${_gss_INCLUDE_DIRS}")
       check_include_files("gssapi/gssapi_generic.h;gssapi/gssapi_krb5.h" _gss_have_mit_headers)
+      cmake_pop_check_state()
 
       if(_gss_have_mit_headers)
         set(GSS_FLAVOUR "MIT")
-      else()
-        # Prevent compiling the header - just check if we can include it
-        list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D__ROKEN_H__")
-        check_include_file("roken.h" _gss_have_roken_h)
-
-        check_include_file("heimdal/roken.h" _gss_have_heimdal_roken_h)
-        if(_gss_have_roken_h OR _gss_have_heimdal_roken_h)
-          set(GSS_FLAVOUR "Heimdal")
+        if(WIN32)
+          if(CMAKE_SIZEOF_VOID_P EQUAL 8)
+            list(APPEND _gss_libdir_suffixes "lib/AMD64")
+            set(_gss_libname "gssapi64")
+          else()
+            list(APPEND _gss_libdir_suffixes "lib/i386")
+            set(_gss_libname "gssapi32")
+          endif()
+        else()
+          list(APPEND _gss_libdir_suffixes "lib" "lib64")  # those suffixes are not checked for HINTS
+          set(_gss_libname "gssapi_krb5")
         endif()
       endif()
-      cmake_pop_check_state()
     else()
-      # I am not convinced if this is the right way but this is what autotools do at the moment
-      find_path(_gss_INCLUDE_DIRS NAMES "gssapi.h"
-        HINTS
-          ${_gss_root_hints}
-        PATH_SUFFIXES
-          "include"
-          "inc"
-      )
+      find_path(_gss_INCLUDE_DIRS NAMES "gss.h" HINTS ${_gss_root_hints} PATH_SUFFIXES "include")
 
       if(_gss_INCLUDE_DIRS)
-        set(GSS_FLAVOUR "Heimdal")
-      else()
-        find_path(_gss_INCLUDE_DIRS NAMES "gss.h"
-          HINTS
-            ${_gss_root_hints}
-          PATH_SUFFIXES
-            "include"
-        )
-
-        if(_gss_INCLUDE_DIRS)
-          set(GSS_FLAVOUR "GNU")
-          set(GSS_PC_REQUIRES "gss")
-        endif()
+        set(GSS_FLAVOUR "GNU")
+        set(GSS_PC_REQUIRES ${_gnu_modname})
+        set(_gss_libname "gss")
       endif()
     endif()
 
-    # If we have headers, check if we can link libraries
+    # If we have headers, look up libraries
     if(GSS_FLAVOUR)
-      set(_gss_libdir_suffixes "")
       set(_gss_libdir_hints ${_gss_root_hints})
-      get_filename_component(_gss_calculated_potential_root "${_gss_INCLUDE_DIRS}" DIRECTORY)
-      list(APPEND _gss_libdir_hints ${_gss_calculated_potential_root})
-
-      if(WIN32)
-        if(CMAKE_SIZEOF_VOID_P EQUAL 8)
-          list(APPEND _gss_libdir_suffixes "lib/AMD64")
-          if(GSS_FLAVOUR STREQUAL "GNU")
-            set(_gss_libname "gss")
-          elseif(GSS_FLAVOUR STREQUAL "MIT")
-            set(_gss_libname "gssapi64")
-          else()
-            set(_gss_libname "libgssapi")
-          endif()
-        else()
-          list(APPEND _gss_libdir_suffixes "lib/i386")
-          if(GSS_FLAVOUR STREQUAL "GNU")
-            set(_gss_libname "gss")
-          elseif(GSS_FLAVOUR STREQUAL "MIT")
-            set(_gss_libname "gssapi32")
-          else()
-            set(_gss_libname "libgssapi")
-          endif()
-        endif()
+      if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.20)
+        cmake_path(GET _gss_INCLUDE_DIRS PARENT_PATH _gss_calculated_potential_root)
       else()
-        list(APPEND _gss_libdir_suffixes "lib;lib64")  # those suffixes are not checked for HINTS
-        if(GSS_FLAVOUR STREQUAL "GNU")
-          set(_gss_libname "gss")
-        elseif(GSS_FLAVOUR STREQUAL "MIT")
-          set(_gss_libname "gssapi_krb5")
-        else()
-          set(_gss_libname "gssapi")
-        endif()
+        get_filename_component(_gss_calculated_potential_root "${_gss_INCLUDE_DIRS}" DIRECTORY)
       endif()
+      list(APPEND _gss_libdir_hints ${_gss_calculated_potential_root})
 
-      find_library(_gss_LIBRARIES NAMES ${_gss_libname}
-        HINTS
-          ${_gss_libdir_hints}
-        PATH_SUFFIXES
-          ${_gss_libdir_suffixes}
-      )
+      find_library(_gss_LIBRARIES NAMES ${_gss_libname} HINTS ${_gss_libdir_hints} PATH_SUFFIXES ${_gss_libdir_suffixes})
     endif()
   endif()
+  if(NOT GSS_FLAVOUR)
+    message(FATAL_ERROR "GNU or MIT GSS is required")
+  endif()
 else()
-  # _gss_MODULE_NAME set since CMake 3.16
-  if(_gss_MODULE_NAME STREQUAL _gnu_modname OR _gss_${_gnu_modname}_VERSION)
+  # _gss_MODULE_NAME set since CMake 3.16.
+  # _pkg_check_modules_pkg_name is undocumented and used as a fallback for CMake <3.16 versions.
+  if(_gss_MODULE_NAME STREQUAL _gnu_modname OR _pkg_check_modules_pkg_name STREQUAL _gnu_modname)
     set(GSS_FLAVOUR "GNU")
-    set(GSS_PC_REQUIRES "gss")
-    if(NOT _gss_version)  # for old CMake versions?
-      set(_gss_version ${_gss_${_gnu_modname}_VERSION})
-    endif()
-  elseif(_gss_MODULE_NAME STREQUAL _mit_modname OR _gss_${_mit_modname}_VERSION)
+    set(GSS_PC_REQUIRES ${_gnu_modname})
+  elseif(_gss_MODULE_NAME STREQUAL _mit_modname OR _pkg_check_modules_pkg_name STREQUAL _mit_modname)
     set(GSS_FLAVOUR "MIT")
-    set(GSS_PC_REQUIRES "mit-krb5-gssapi")
-    if(NOT _gss_version)  # for old CMake versions?
-      set(_gss_version ${_gss_${_mit_modname}_VERSION})
-    endif()
+    set(GSS_PC_REQUIRES ${_mit_modname})
   else()
-    set(GSS_FLAVOUR "Heimdal")
-    set(GSS_PC_REQUIRES "heimdal-gssapi")
-    if(NOT _gss_version)  # for old CMake versions?
-      set(_gss_version ${_gss_${_heimdal_modname}_VERSION})
-    endif()
+    message(FATAL_ERROR "GNU or MIT GSS is required")
   endif()
   message(STATUS "Found GSS/${GSS_FLAVOUR} (via pkg-config): ${_gss_INCLUDE_DIRS} (found version \"${_gss_version}\")")
 endif()
@@ -304,33 +219,21 @@ set(GSS_LIBRARY_DIRS ${_gss_LIBRARY_DIRS})
 set(GSS_CFLAGS ${_gss_CFLAGS})
 set(GSS_VERSION ${_gss_version})
 
-if(GSS_FLAVOUR)
-  if(NOT GSS_VERSION AND GSS_FLAVOUR STREQUAL "Heimdal")
-    if(CMAKE_SIZEOF_VOID_P EQUAL 8)
-      set(_heimdal_manifest_file "Heimdal.Application.amd64.manifest")
+if(NOT GSS_VERSION)
+  if(GSS_FLAVOUR STREQUAL "MIT")
+    if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.24)
+      cmake_host_system_information(RESULT _mit_version QUERY WINDOWS_REGISTRY
+        "HKLM/SOFTWARE/MIT/Kerberos/SDK/CurrentVersion" VALUE "VersionString")
     else()
-      set(_heimdal_manifest_file "Heimdal.Application.x86.manifest")
-    endif()
-
-    if(EXISTS "${GSS_INCLUDE_DIRS}/${_heimdal_manifest_file}")
-      file(STRINGS "${GSS_INCLUDE_DIRS}/${_heimdal_manifest_file}" _heimdal_version_str
-        REGEX "^.*version=\"[0-9]\\.[^\"]+\".*$")
-
-      string(REGEX MATCH "[0-9]\\.[^\"]+" GSS_VERSION "${_heimdal_version_str}")
-    endif()
-
-    if(NOT GSS_VERSION)
-      set(GSS_VERSION "Heimdal Unknown")
+      get_filename_component(_mit_version
+        "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MIT\\Kerberos\\SDK\\CurrentVersion;VersionString]" NAME CACHE)
     endif()
-  elseif(NOT GSS_VERSION AND GSS_FLAVOUR STREQUAL "MIT")
-    get_filename_component(_mit_version "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MIT\\Kerberos\\SDK\\CurrentVersion;VersionString]" NAME
-      CACHE)
     if(WIN32 AND _mit_version)
       set(GSS_VERSION "${_mit_version}")
     else()
       set(GSS_VERSION "MIT Unknown")
     endif()
-  elseif(NOT GSS_VERSION AND GSS_FLAVOUR STREQUAL "GNU")
+  else()  # GNU
     if(GSS_INCLUDE_DIRS AND EXISTS "${GSS_INCLUDE_DIRS}/gss.h")
       set(_version_regex "#[\t ]*define[\t ]+GSS_VERSION[\t ]+\"([^\"]*)\"")
       file(STRINGS "${GSS_INCLUDE_DIRS}/gss.h" _version_str REGEX "${_version_regex}")
@@ -350,7 +253,7 @@ find_package_handle_standard_args(GSS
   VERSION_VAR
     GSS_VERSION
   FAIL_MESSAGE
-    "Could NOT find GSS, try to set the path to GSS root folder in the system variable GSS_ROOT_DIR"
+    "Could NOT find GSS, try to set the absolute path to GSS installation root directory in the environment variable GSS_ROOT_DIR"
 )
 
 mark_as_advanced(

+ 83 - 0
CMake/FindGnuTLS.cmake

@@ -0,0 +1,83 @@
+#***************************************************************************
+#                                  _   _ ____  _
+#  Project                     ___| | | |  _ \| |
+#                             / __| | | | |_) | |
+#                            | (__| |_| |  _ <| |___
+#                             \___|\___/|_| \_\_____|
+#
+# Copyright (C) Daniel Stenberg, <[email protected]>, 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
+#
+###########################################################################
+# Find the GnuTLS library
+#
+# Input variables:
+#
+# - `GNUTLS_INCLUDE_DIR`:   Absolute path to GnuTLS include directory.
+# - `GNUTLS_LIBRARY`:       Absolute path to `gnutls` library.
+#
+# Result variables:
+#
+# - `GNUTLS_FOUND`:         System has GnuTLS.
+# - `GNUTLS_INCLUDE_DIRS`:  The GnuTLS include directories.
+# - `GNUTLS_LIBRARIES`:     The GnuTLS library names.
+# - `GNUTLS_LIBRARY_DIRS`:  The GnuTLS library directories.
+# - `GNUTLS_PC_REQUIRES`:   The GnuTLS pkg-config packages.
+# - `GNUTLS_CFLAGS`:        Required compiler flags.
+# - `GNUTLS_VERSION`:       Version of GnuTLS.
+
+set(GNUTLS_PC_REQUIRES "gnutls")
+
+if(CURL_USE_PKGCONFIG AND
+   NOT DEFINED GNUTLS_INCLUDE_DIR AND
+   NOT DEFINED GNUTLS_LIBRARY)
+  find_package(PkgConfig QUIET)
+  pkg_check_modules(GNUTLS ${GNUTLS_PC_REQUIRES})
+endif()
+
+if(GNUTLS_FOUND)
+  set(GnuTLS_FOUND TRUE)
+  string(REPLACE ";" " " GNUTLS_CFLAGS "${GNUTLS_CFLAGS}")
+  message(STATUS "Found GnuTLS (via pkg-config): ${GNUTLS_INCLUDE_DIRS} (found version \"${GNUTLS_VERSION}\")")
+else()
+  find_path(GNUTLS_INCLUDE_DIR NAMES "gnutls/gnutls.h")
+  find_library(GNUTLS_LIBRARY NAMES "gnutls" "libgnutls")
+
+  unset(GNUTLS_VERSION CACHE)
+  if(GNUTLS_INCLUDE_DIR AND EXISTS "${GNUTLS_INCLUDE_DIR}/gnutls/gnutls.h")
+    set(_version_regex "#[\t ]*define[\t ]+GNUTLS_VERSION[\t ]+\"([^\"]*)\"")
+    file(STRINGS "${GNUTLS_INCLUDE_DIR}/gnutls/gnutls.h" _version_str REGEX "${_version_regex}")
+    string(REGEX REPLACE "${_version_regex}" "\\1" _version_str "${_version_str}")
+    set(GNUTLS_VERSION "${_version_str}")
+    unset(_version_regex)
+    unset(_version_str)
+  endif()
+
+  include(FindPackageHandleStandardArgs)
+  find_package_handle_standard_args(GnuTLS
+    REQUIRED_VARS
+      GNUTLS_INCLUDE_DIR
+      GNUTLS_LIBRARY
+    VERSION_VAR
+      GNUTLS_VERSION
+  )
+
+  if(GNUTLS_FOUND)
+    set(GNUTLS_INCLUDE_DIRS ${GNUTLS_INCLUDE_DIR})
+    set(GNUTLS_LIBRARIES    ${GNUTLS_LIBRARY})
+  endif()
+
+  mark_as_advanced(GNUTLS_INCLUDE_DIR GNUTLS_LIBRARY)
+endif()

+ 4 - 4
CMake/FindLDAP.cmake

@@ -25,9 +25,9 @@
 #
 # Input variables:
 #
-# - `LDAP_INCLUDE_DIR`:   The ldap include directory.
-# - `LDAP_LIBRARY`:       Path to `ldap` library.
-# - `LDAP_LBER_LIBRARY`:  Path to `lber` library.
+# - `LDAP_INCLUDE_DIR`:   Absolute path to ldap include directory.
+# - `LDAP_LIBRARY`:       Absolute path to `ldap` library.
+# - `LDAP_LBER_LIBRARY`:  Absolute path to `lber` library.
 #
 # Result variables:
 #
@@ -50,7 +50,7 @@ if(CURL_USE_PKGCONFIG AND
 endif()
 
 if(LDAP_FOUND)
-  set(LDAP_VERSION "${LDAP_ldap_VERSION}")
+  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()

+ 2 - 2
CMake/FindLibgsasl.cmake

@@ -25,8 +25,8 @@
 #
 # Input variables:
 #
-# - `LIBGSASL_INCLUDE_DIR`:   The libgsasl include directory.
-# - `LIBGSASL_LIBRARY`:       Path to `libgsasl` library.
+# - `LIBGSASL_INCLUDE_DIR`:   Absolute path to libgsasl include directory.
+# - `LIBGSASL_LIBRARY`:       Absolute path to `libgsasl` library.
 #
 # Result variables:
 #

+ 2 - 2
CMake/FindLibidn2.cmake

@@ -25,8 +25,8 @@
 #
 # Input variables:
 #
-# - `LIBIDN2_INCLUDE_DIR`:   The libidn2 include directory.
-# - `LIBIDN2_LIBRARY`:       Path to `libidn2` library.
+# - `LIBIDN2_INCLUDE_DIR`:   Absolute path to libidn2 include directory.
+# - `LIBIDN2_LIBRARY`:       Absolute path to `libidn2` library.
 #
 # Result variables:
 #

+ 2 - 2
CMake/FindLibpsl.cmake

@@ -25,8 +25,8 @@
 #
 # Input variables:
 #
-# - `LIBPSL_INCLUDE_DIR`:   The libpsl include directory.
-# - `LIBPSL_LIBRARY`:       Path to `libpsl` library.
+# - `LIBPSL_INCLUDE_DIR`:   Absolute path to libpsl include directory.
+# - `LIBPSL_LIBRARY`:       Absolute path to `libpsl` library.
 #
 # Result variables:
 #

+ 2 - 2
CMake/FindLibrtmp.cmake

@@ -25,8 +25,8 @@
 #
 # Input variables:
 #
-# - `LIBRTMP_INCLUDE_DIR`:   The librtmp include directory.
-# - `LIBRTMP_LIBRARY`:       Path to `librtmp` library.
+# - `LIBRTMP_INCLUDE_DIR`:   Absolute path to librtmp include directory.
+# - `LIBRTMP_LIBRARY`:       Absolute path to `librtmp` library.
 #
 # Result variables:
 #

+ 2 - 2
CMake/FindLibssh.cmake

@@ -25,8 +25,8 @@
 #
 # Input variables:
 #
-# - `LIBSSH_INCLUDE_DIR`:   The libssh include directory.
-# - `LIBSSH_LIBRARY`:       Path to libssh library.
+# - `LIBSSH_INCLUDE_DIR`:   Absolute path to libssh include directory.
+# - `LIBSSH_LIBRARY`:       Absolute path to `libssh` library.
 #
 # Result variables:
 #

+ 2 - 2
CMake/FindLibssh2.cmake

@@ -25,8 +25,8 @@
 #
 # Input variables:
 #
-# - `LIBSSH2_INCLUDE_DIR`:   The libssh2 include directory.
-# - `LIBSSH2_LIBRARY`:       Path to `libssh2` library.
+# - `LIBSSH2_INCLUDE_DIR`:   Absolute path to libssh2 include directory.
+# - `LIBSSH2_LIBRARY`:       Absolute path to `libssh2` library.
 #
 # Result variables:
 #

+ 2 - 2
CMake/FindLibuv.cmake

@@ -25,8 +25,8 @@
 #
 # Input variables:
 #
-# - `LIBUV_INCLUDE_DIR`:   The libuv include directory.
-# - `LIBUV_LIBRARY`:       Path to `libuv` library.
+# - `LIBUV_INCLUDE_DIR`:   Absolute path to libuv include directory.
+# - `LIBUV_LIBRARY`:       Absolute path to `libuv` library.
 #
 # Result variables:
 #

+ 5 - 5
CMake/FindMbedTLS.cmake

@@ -25,10 +25,10 @@
 #
 # Input variables:
 #
-# - `MBEDTLS_INCLUDE_DIR`:   The mbedTLS include directory.
-# - `MBEDTLS_LIBRARY`:       Path to `mbedtls` library.
-# - `MBEDX509_LIBRARY`:      Path to `mbedx509` library.
-# - `MBEDCRYPTO_LIBRARY`:    Path to `mbedcrypto` library.
+# - `MBEDTLS_INCLUDE_DIR`:   Absolute path to mbedTLS include directory.
+# - `MBEDTLS_LIBRARY`:       Absolute path to `mbedtls` library.
+# - `MBEDX509_LIBRARY`:      Absolute path to `mbedx509` library.
+# - `MBEDCRYPTO_LIBRARY`:    Absolute path to `mbedcrypto` library.
 #
 # Result variables:
 #
@@ -59,7 +59,7 @@ endif()
 
 if(MBEDTLS_FOUND)
   set(MbedTLS_FOUND TRUE)
-  set(MBEDTLS_VERSION "${MBEDTLS_mbedtls_VERSION}")
+  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()

+ 2 - 2
CMake/FindNGHTTP2.cmake

@@ -25,8 +25,8 @@
 #
 # Input variables:
 #
-# - `NGHTTP2_INCLUDE_DIR`:   The nghttp2 include directory.
-# - `NGHTTP2_LIBRARY`:       Path to `nghttp2` library.
+# - `NGHTTP2_INCLUDE_DIR`:   Absolute path to nghttp2 include directory.
+# - `NGHTTP2_LIBRARY`:       Absolute path to `nghttp2` library.
 #
 # Result variables:
 #

+ 2 - 2
CMake/FindNGHTTP3.cmake

@@ -25,8 +25,8 @@
 #
 # Input variables:
 #
-# - `NGHTTP3_INCLUDE_DIR`:   The nghttp3 include directory.
-# - `NGHTTP3_LIBRARY`:       Path to `nghttp3` library.
+# - `NGHTTP3_INCLUDE_DIR`:   Absolute path to nghttp3 include directory.
+# - `NGHTTP3_LIBRARY`:       Absolute path to `nghttp3` library.
 #
 # Result variables:
 #

+ 14 - 10
CMake/FindNGTCP2.cmake

@@ -35,14 +35,14 @@
 #
 # Input variables:
 #
-# - `NGTCP2_INCLUDE_DIR`:               The ngtcp2 include directory.
-# - `NGTCP2_LIBRARY`:                   Path to `ngtcp2` library.
-# - `NGTCP2_CRYPTO_BORINGSSL_LIBRARY`:  Path to `ngtcp2_crypto_boringssl` library.
-# - `NGTCP2_CRYPTO_GNUTLS_LIBRARY`:     Path to `ngtcp2_crypto_gnutls` library.
-# - `NGTCP2_CRYPTO_LIBRESSL_LIBRARY`:   Path to `ngtcp2_crypto_libressl` library.
-# - `NGTCP2_CRYPTO_OSSL_LIBRARY`:       Path to `ngtcp2_crypto_ossl` library.
-# - `NGTCP2_CRYPTO_QUICTLS_LIBRARY`:    Path to `ngtcp2_crypto_quictls` library.
-# - `NGTCP2_CRYPTO_WOLFSSL_LIBRARY`:    Path to `ngtcp2_crypto_wolfssl` library.
+# - `NGTCP2_INCLUDE_DIR`:               Absolute path to ngtcp2 include directory.
+# - `NGTCP2_LIBRARY`:                   Absolute path to `ngtcp2` library.
+# - `NGTCP2_CRYPTO_BORINGSSL_LIBRARY`:  Absolute path to `ngtcp2_crypto_boringssl` library.
+# - `NGTCP2_CRYPTO_GNUTLS_LIBRARY`:     Absolute path to `ngtcp2_crypto_gnutls` library.
+# - `NGTCP2_CRYPTO_LIBRESSL_LIBRARY`:   Absolute path to `ngtcp2_crypto_libressl` library.
+# - `NGTCP2_CRYPTO_OSSL_LIBRARY`:       Absolute path to `ngtcp2_crypto_ossl` library.
+# - `NGTCP2_CRYPTO_QUICTLS_LIBRARY`:    Absolute path to `ngtcp2_crypto_quictls` library.
+# - `NGTCP2_CRYPTO_WOLFSSL_LIBRARY`:    Absolute path to `ngtcp2_crypto_wolfssl` library.
 #
 # Result variables:
 #
@@ -86,7 +86,7 @@ if(CURL_USE_PKGCONFIG AND
 endif()
 
 if(NGTCP2_FOUND)
-  set(NGTCP2_VERSION "${NGTCP2_libngtcp2_VERSION}")
+  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()
@@ -104,7 +104,11 @@ else()
   endif()
 
   if(_ngtcp2_crypto_backend)
-    get_filename_component(_ngtcp2_library_dir "${NGTCP2_LIBRARY}" DIRECTORY)
+    if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.20)
+      cmake_path(GET NGTCP2_LIBRARY PARENT_PATH _ngtcp2_library_dir)
+    else()
+      get_filename_component(_ngtcp2_library_dir "${NGTCP2_LIBRARY}" DIRECTORY)
+    endif()
     find_library(${_crypto_library_upper}_LIBRARY NAMES ${_crypto_library_lower} HINTS ${_ngtcp2_library_dir})
 
     if(${_crypto_library_upper}_LIBRARY)

+ 2 - 2
CMake/FindNettle.cmake

@@ -25,8 +25,8 @@
 #
 # Input variables:
 #
-# - `NETTLE_INCLUDE_DIR`:   The nettle include directory.
-# - `NETTLE_LIBRARY`:       Path to `nettle` library.
+# - `NETTLE_INCLUDE_DIR`:   Absolute path to nettle include directory.
+# - `NETTLE_LIBRARY`:       Absolute path to `nettle` library.
 #
 # Result variables:
 #

+ 2 - 2
CMake/FindQuiche.cmake

@@ -25,8 +25,8 @@
 #
 # Input variables:
 #
-# - `QUICHE_INCLUDE_DIR`:   The quiche include directory.
-# - `QUICHE_LIBRARY`:       Path to `quiche` library.
+# - `QUICHE_INCLUDE_DIR`:   Absolute path to quiche include directory.
+# - `QUICHE_LIBRARY`:       Absolute path to `quiche` library.
 #
 # Result variables:
 #

+ 2 - 2
CMake/FindRustls.cmake

@@ -25,8 +25,8 @@
 #
 # Input variables:
 #
-# - `RUSTLS_INCLUDE_DIR`:   The Rustls include directory.
-# - `RUSTLS_LIBRARY`:       Path to `rustls` library.
+# - `RUSTLS_INCLUDE_DIR`:   Absolute path to Rustls include directory.
+# - `RUSTLS_LIBRARY`:       Absolute path to `rustls` library.
 #
 # Result variables:
 #

+ 0 - 65
CMake/FindWolfSSH.cmake

@@ -1,65 +0,0 @@
-#***************************************************************************
-#                                  _   _ ____  _
-#  Project                     ___| | | |  _ \| |
-#                             / __| | | | |_) | |
-#                            | (__| |_| |  _ <| |___
-#                             \___|\___/|_| \_\_____|
-#
-# Copyright (C) Daniel Stenberg, <[email protected]>, 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
-#
-###########################################################################
-# Find the wolfSSH library
-#
-# Input variables:
-#
-# - `WOLFSSH_INCLUDE_DIR`:   The wolfSSH include directory.
-# - `WOLFSSH_LIBRARY`:       Path to `wolfssh` library.
-#
-# Result variables:
-#
-# - `WOLFSSH_FOUND`:         System has wolfSSH.
-# - `WOLFSSH_INCLUDE_DIRS`:  The wolfSSH include directories.
-# - `WOLFSSH_LIBRARIES`:     The wolfSSH library names.
-# - `WOLFSSH_VERSION`:       Version of wolfSSH.
-
-find_path(WOLFSSH_INCLUDE_DIR NAMES "wolfssh/ssh.h")
-find_library(WOLFSSH_LIBRARY NAMES "wolfssh" "libwolfssh")
-
-unset(WOLFSSH_VERSION CACHE)
-if(WOLFSSH_INCLUDE_DIR AND EXISTS "${WOLFSSH_INCLUDE_DIR}/wolfssh/version.h")
-  set(_version_regex "#[\t ]*define[\t ]+LIBWOLFSSH_VERSION_STRING[\t ]+\"([^\"]*)\"")
-  file(STRINGS "${WOLFSSH_INCLUDE_DIR}/wolfssh/version.h" _version_str REGEX "${_version_regex}")
-  string(REGEX REPLACE "${_version_regex}" "\\1" _version_str "${_version_str}")
-  set(WOLFSSH_VERSION "${_version_str}")
-  unset(_version_regex)
-  unset(_version_str)
-endif()
-
-include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(WolfSSH
-  REQUIRED_VARS
-    WOLFSSH_INCLUDE_DIR
-    WOLFSSH_LIBRARY
-  VERSION_VAR
-    WOLFSSH_VERSION
-)
-
-if(WOLFSSH_FOUND)
-  set(WOLFSSH_INCLUDE_DIRS ${WOLFSSH_INCLUDE_DIR})
-  set(WOLFSSH_LIBRARIES    ${WOLFSSH_LIBRARY})
-endif()
-
-mark_as_advanced(WOLFSSH_INCLUDE_DIR WOLFSSH_LIBRARY)

+ 2 - 2
CMake/FindWolfSSL.cmake

@@ -25,8 +25,8 @@
 #
 # Input variables:
 #
-# - `WOLFSSL_INCLUDE_DIR`:   The wolfSSL include directory.
-# - `WOLFSSL_LIBRARY`:       Path to `wolfssl` library.
+# - `WOLFSSL_INCLUDE_DIR`:   Absolute path to wolfSSL include directory.
+# - `WOLFSSL_LIBRARY`:       Absolute path to `wolfssl` library.
 #
 # Result variables:
 #

+ 2 - 2
CMake/FindZstd.cmake

@@ -25,8 +25,8 @@
 #
 # Input variables:
 #
-# - `ZSTD_INCLUDE_DIR`:   The zstd include directory.
-# - `ZSTD_LIBRARY`:       Path to `zstd` library.
+# - `ZSTD_INCLUDE_DIR`:   Absolute path to zstd include directory.
+# - `ZSTD_LIBRARY`:       Absolute path to `zstd` library.
 #
 # Result variables:
 #

+ 100 - 30
CMake/PickyWarnings.cmake

@@ -29,12 +29,10 @@ set(_picky_nocheck "")  # not to pass to feature checks
 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()
+  elseif(MSVC)
+    list(APPEND _picky_nocheck "-WX")
+  else()  # llvm/clang and gcc-style options
+    list(APPEND _picky_nocheck "-Werror")
   endif()
 
   if((CMAKE_C_COMPILER_ID STREQUAL "GNU" AND
@@ -47,8 +45,8 @@ endif()
 
 if(APPLE AND
    (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
+   (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 6.1))
+  list(APPEND _picky "-Werror=partial-availability")  # clang 3.6  appleclang 6.1
 endif()
 
 if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang")
@@ -87,6 +85,9 @@ if(PICKY_COMPILER)
     set(_picky_detect
     )
 
+    # Notes: -Wno-* options should ideally be disabled at their precise cutoff versions,
+    #        to suppress undesired warnings in case -Weverything is passed as a custom option.
+
     # Assume these options always exist with both clang and gcc.
     # Require clang 3.0 / gcc 2.95 or later.
     list(APPEND _picky_enable
@@ -121,13 +122,14 @@ if(PICKY_COMPILER)
       -Wmissing-field-initializers         # clang  2.7  gcc  4.1
       -Wmissing-noreturn                   # clang  2.7  gcc  4.1
       -Wno-format-nonliteral               # clang  1.0  gcc  2.96 (3.0)
+      -Wno-padded                          # clang  2.9  gcc  4.1               # Not used: We cannot change public structs
       -Wno-sign-conversion                 # clang  2.9  gcc  4.3
+      -Wno-switch-default                  # clang  2.7  gcc  4.1               # Not used: Annoying to fix or silence
+      -Wno-switch-enum                     # clang  2.7  gcc  4.1               # Not used: It basically disallows default case
       -Wno-system-headers                  # clang  1.0  gcc  3.0
-    # -Wpadded                             # clang  2.9  gcc  4.1               # Not used: We cannot change public structs
       -Wold-style-definition               # clang  2.7  gcc  3.4
       -Wredundant-decls                    # clang  2.7  gcc  4.1
       -Wstrict-prototypes                  # clang  1.0  gcc  3.3
-    # -Wswitch-enum                        # clang  2.7  gcc  4.1               # Not used: It basically disallows default case
       -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
@@ -139,6 +141,8 @@ if(PICKY_COMPILER)
     if(CMAKE_C_COMPILER_ID MATCHES "Clang")
       list(APPEND _picky_enable
         ${_picky_common_old}
+        -Wconditional-uninitialized        # clang  3.0
+        -Wno-used-but-marked-unused        # clang  3.0                         # Triggered by typecheck-gcc.h (with clang 14+)
         -Wshift-sign-overflow              # clang  2.9
         -Wshorten-64-to-32                 # clang  1.0
         -Wformat=2                         # clang  3.0  gcc  4.8
@@ -149,39 +153,102 @@ if(PICKY_COMPILER)
         )
       endif()
       # Enable based on compiler version
+      if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 3.1)
+        list(APPEND _picky_enable
+          -Wno-covered-switch-default      # clang  3.1            appleclang  3.1  # Annoying to fix or silence
+          -Wno-disabled-macro-expansion    # clang  3.1            appleclang  3.1  # Triggered by typecheck-gcc.h (with clang 14+)
+        )
+        if(MSVC)
+          list(APPEND _picky_enable
+            -Wno-format-non-iso            # clang  3.1            appleclang  3.1  # 'q' length modifier is not supported by ISO C
+          )
+        else()
+          list(APPEND _picky_enable
+            -Wformat-non-iso               # clang  3.1            appleclang  3.1
+          )
+        endif()
+      endif()
+      if((CMAKE_C_COMPILER_ID STREQUAL "Clang"      AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 3.3) OR
+         (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 5.0))
+        list(APPEND _picky_enable
+          -Wenum-conversion                # clang  3.2  gcc 10.0  appleclang  4.2  g++ 11.0
+          -Wmissing-variable-declarations  # clang  3.2            appleclang  4.2
+          -Wno-documentation-unknown-command # clang  3.3            appleclang  5.0
+          -Wsometimes-uninitialized        # clang  3.2            appleclang  4.2
+        )
+      endif()
       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))
+         (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 6.1))
         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
+          -Wdouble-promotion               # clang  3.6  gcc  4.6  appleclang  6.1
           -Wheader-guard                   # clang  3.4            appleclang  5.1
           -Wpragmas                        # clang  3.5  gcc  4.1  appleclang  6.0
-          -Wsometimes-uninitialized        # clang  3.2            appleclang  4.6
         # -Wunreachable-code-break         # clang  3.5            appleclang  6.0  # Not used: Silent in "unity" builds
           -Wunused-const-variable          # clang  3.4  gcc  6.0  appleclang  5.1
         )
       endif()
       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))
+         (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 8.1))
         list(APPEND _picky_enable
-          -Wcomma                          # clang  3.9            appleclang  8.3
-          -Wmissing-variable-declarations  # clang  3.2            appleclang  4.6
+          -Wcomma                          # clang  3.9            appleclang  8.1
         )
+        if(MSVC)
+          list(APPEND _picky_enable
+            -Wno-nonportable-system-include-path  # clang 3.9            appleclang  8.1  # No truly portable solution to this
+          )
+        endif()
       endif()
       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))
+         (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 11))
         list(APPEND _picky_enable
-          -Wassign-enum                    # clang  7.0            appleclang 10.3
-          -Wextra-semi-stmt                # clang  7.0            appleclang 10.3
+          -Wassign-enum                    # clang  7.0            appleclang 11.0
+          -Wextra-semi-stmt                # clang  7.0            appleclang 11.0
         )
       endif()
       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))
+         (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 12))
         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
+          -Wimplicit-fallthrough           # clang  4.0  gcc  7.0  appleclang  9.0  # We do silencing for clang 10.0 and above only
+          -Wxor-used-as-pow                # clang 10.0  gcc 13.0  appleclang 12.0
         )
       endif()
+      if((CMAKE_C_COMPILER_ID STREQUAL "Clang"      AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 13.0) OR
+         (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 13.1))
+        list(APPEND _picky_enable
+          -Wcast-function-type             # clang 13.0            appleclang 13.1
+          -Wreserved-identifier            # clang 13.0            appleclang 13.1  # Keep it before -Wno-reserved-macro-identifier
+            -Wno-reserved-macro-identifier # clang 13.0            appleclang 13.1  # External macros have to be set sometimes
+        )
+      endif()
+      if((CMAKE_C_COMPILER_ID STREQUAL "Clang"      AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 16.0) OR
+         (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 15.0))
+        list(APPEND _picky_enable
+          -Wno-unsafe-buffer-usage         # clang 16.0            appleclang 15.0
+        )
+      endif()
+      if((CMAKE_C_COMPILER_ID STREQUAL "Clang"      AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 16.0) OR
+         (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 16.0))
+        list(APPEND _picky_enable
+          -Wcast-function-type-strict      # clang 16.0            appleclang 16.0
+        )
+      endif()
+      if(CMAKE_C_COMPILER_ID STREQUAL "Clang"      AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 21.0)
+        list(APPEND _picky_enable
+          -Warray-compare                  # clang 20.0  gcc 12.0  appleclang ?
+          -Wc++-hidden-decl                # clang 21.0            appleclang ?
+          -Wno-implicit-void-ptr-cast      # clang 21.0            appleclang ?
+          -Wtentative-definition-compat    # clang 21.0            appleclang ?
+        )
+        if(WIN32)
+          list(APPEND _picky_enable
+            -Wno-c++-keyword               # clang 21.0            appleclang ?  # `wchar_t` triggers it on Windows
+          )
+        else()
+          list(APPEND _picky_enable
+            -Wc++-keyword                  # clang 21.0            appleclang ?
+          )
+        endif()
+      endif()
     else()  # gcc
       # Enable based on compiler version
       if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 4.3)
@@ -207,7 +274,7 @@ if(PICKY_COMPILER)
       endif()
       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
+          -Wdouble-promotion               # clang  3.6  gcc  4.6  appleclang  6.1
           -Wformat=2                       # clang  3.0  gcc  4.8
           -Wtrampolines                    #             gcc  4.6
         )
@@ -232,21 +299,21 @@ if(PICKY_COMPILER)
           -Walloc-zero                     #             gcc  7.0
           -Wduplicated-branches            #             gcc  7.0
           -Wformat-truncation=2            #             gcc  7.0
-          -Wimplicit-fallthrough           # clang  4.0  gcc  7.0
+          -Wimplicit-fallthrough           # clang  4.0  gcc  7.0  appleclang  9.0
           -Wrestrict                       #             gcc  7.0
         )
       endif()
       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
+          -Wenum-conversion                # clang  3.2  gcc 10.0  appleclang  4.2  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
+          -Warray-compare                  # clang 20.0  gcc 12.0  appleclang ?
           -Wenum-int-mismatch              #             gcc 13.0
-          -Wxor-used-as-pow                # clang 10.0  gcc 13.0
+          -Wxor-used-as-pow                # clang 10.0  gcc 13.0  appleclang 12.0
         )
       endif()
       if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 15.0)
@@ -262,8 +329,11 @@ if(PICKY_COMPILER)
 
     set(_picky_skipped "")
     foreach(_ccopt IN LISTS _picky_enable)
-      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(REGEX MATCH "-W([a-z0-9+-]+)" _ccmatch "${_ccopt}")
+      string(REPLACE "+" "\\+" _cmake_match_1 "${CMAKE_MATCH_1}")  # escape '+' to make it a valid regex
+      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}")

+ 3 - 3
CMake/unix-cache.cmake

@@ -142,7 +142,7 @@ set(HAVE_GETRLIMIT 1)
 set(HAVE_GETSOCKNAME 1)
 set(HAVE_GETTIMEOFDAY 1)
 if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
-  set(HAVE_GLIBC_STRERROR_R 1)
+  # Depends on C library.
 else()
   set(HAVE_GLIBC_STRERROR_R 0)
 endif()
@@ -164,7 +164,7 @@ else()
 endif()
 set(HAVE_LIBGEN_H 1)
 if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
-  set(HAVE_LINUX_TCP_H 1)
+  # Requires Linux kernel userspace headers. Expected with glibc. May be missing by default with MUSL.
 else()
   set(HAVE_LINUX_TCP_H 0)
 endif()
@@ -204,7 +204,7 @@ endif()
 set(HAVE_POLL 1)
 set(HAVE_POLL_H 1)
 if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
-  set(HAVE_POSIX_STRERROR_R 0)
+  # Depends on C library.
 else()
   set(HAVE_POSIX_STRERROR_R 1)
 endif()

+ 3 - 2
CMake/win32-cache.cmake

@@ -191,7 +191,8 @@ if(MINGW OR MSVC)
   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
+  curl_prefill_type_size("CURL_SOCKET_T" ${CMAKE_SIZEOF_VOID_P})
+  curl_prefill_type_size("SIZE_T" ${CMAKE_SIZEOF_VOID_P})
   # 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.
@@ -200,7 +201,7 @@ if(MINGW OR MSVC)
     set(HAVE_FILE_OFFSET_BITS 0)
     curl_prefill_type_size("OFF_T" 4)
   else()
-    # SSIZE_T: 8 for _WIN64, 4 otherwise
+    curl_prefill_type_size("SSIZE_T" ${CMAKE_SIZEOF_VOID_P})
     set(HAVE_FILE_OFFSET_BITS 1)  # mingw-w64 v3+
     curl_prefill_type_size("OFF_T" 8)  # mingw-w64 v3+
   endif()

+ 106 - 84
CMakeLists.txt

@@ -148,6 +148,13 @@ endif()
 if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
   string(APPEND _target_flags " GCC")
 endif()
+if(CMAKE_C_COMPILER_ID STREQUAL "AppleClang")
+  string(APPEND _target_flags " APPLE-CLANG")
+elseif(CMAKE_C_COMPILER_ID STREQUAL "Clang" AND MSVC)
+  string(APPEND _target_flags " CLANG-CL")
+elseif(CMAKE_C_COMPILER_ID MATCHES "Clang")
+  string(APPEND _target_flags " LLVM-CLANG")
+endif()
 if(MINGW)
   string(APPEND _target_flags " MINGW")
 endif()
@@ -311,6 +318,26 @@ if(CURL_CLANG_TIDY)
   endif()
 endif()
 
+option(CURL_CODE_COVERAGE "Enable code coverage build options" OFF)
+if(CURL_CODE_COVERAGE)
+  if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
+    set(CURL_COVERAGE_MACROS "NDEBUG")
+    set(CURL_COVERAGE_CFLAGS "-O0" "-g" "-fprofile-arcs")
+    if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 4.1)
+      list(APPEND CURL_COVERAGE_CFLAGS "--coverage")
+    else()
+      list(APPEND CURL_COVERAGE_CFLAGS "-ftest-coverage")
+    endif()
+    set(CURL_COVERAGE_LIBS "gcov")
+  elseif(CMAKE_C_COMPILER_ID MATCHES "Clang")
+    set(CURL_COVERAGE_MACROS "NDEBUG")
+    set(CURL_COVERAGE_CFLAGS "-O0" "-g" "-fprofile-instr-generate" "-fcoverage-mapping")
+    set(CURL_COVERAGE_LDFLAGS "-fprofile-instr-generate" "-fcoverage-mapping")
+  else()
+    set(CURL_CODE_COVERAGE OFF)
+  endif()
+endif()
+
 # For debug libs and exes, add "-d" postfix
 if(NOT DEFINED CMAKE_DEBUG_POSTFIX)
   set(CMAKE_DEBUG_POSTFIX "-d")
@@ -618,7 +645,7 @@ elseif(DOS)
     include_directories(SYSTEM "${WATT_ROOT}/inc")
     list(APPEND CMAKE_REQUIRED_INCLUDES "${WATT_ROOT}/inc")
   else()
-    message(FATAL_ERROR "Set WATT_ROOT variable to the root installation of Watt-32.")
+    message(FATAL_ERROR "Set WATT_ROOT variable to the absolute path to the root installation of Watt-32.")
   endif()
 elseif(AMIGA)
   if(AMISSL_INCLUDE_DIR AND AMISSL_STUBS_LIBRARY AND AMISSL_AUTO_LIBRARY)
@@ -737,6 +764,26 @@ if(CURL_WINDOWS_SSPI AND NOT WINDOWS_STORE)
   set(USE_WINDOWS_SSPI ON)
 endif()
 
+if(APPLE)
+  option(USE_APPLE_SECTRUST "Use Apple OS-native certificate verification" OFF)
+  if(USE_APPLE_SECTRUST)
+    if(NOT CURL_USE_OPENSSL AND NOT CURL_USE_GNUTLS)
+      message(FATAL_ERROR "Apple SecTrust is only supported with Openssl/GnuTLS")
+    endif()
+    find_library(COREFOUNDATION_FRAMEWORK NAMES "Security")
+    mark_as_advanced(COREFOUNDATION_FRAMEWORK)
+    if(NOT COREFOUNDATION_FRAMEWORK)
+      message(FATAL_ERROR "Security framework not found")
+    endif()
+    list(APPEND CURL_LIBS "-framework Security")
+
+    set(_use_core_foundation_and_core_services ON)
+    message(STATUS "Apple OS-native certificate verification enabled")
+  endif()
+else()
+  set(USE_APPLE_SECTRUST OFF)
+endif()
+
 if(_use_core_foundation_and_core_services)
   find_library(COREFOUNDATION_FRAMEWORK NAMES "CoreFoundation")
   mark_as_advanced(COREFOUNDATION_FRAMEWORK)
@@ -821,6 +868,18 @@ if(CURL_USE_MBEDTLS)
     set(_valid_default_ssl_backend TRUE)
   endif()
   set(_curl_ca_bundle_supported TRUE)
+
+  if(MBEDTLS_VERSION VERSION_GREATER_EQUAL 4.0.0)
+    set(HAVE_MBEDTLS_DES_CRYPT_ECB 0)  # pre-fill detection result
+  endif()
+  if(NOT DEFINED HAVE_MBEDTLS_DES_CRYPT_ECB)
+    cmake_push_check_state()
+    list(APPEND CMAKE_REQUIRED_INCLUDES "${MBEDTLS_INCLUDE_DIRS}")
+    list(APPEND CMAKE_REQUIRED_LIBRARIES "${MBEDTLS_LIBRARIES}")
+    curl_required_libpaths("${MBEDTLS_LIBRARY_DIRS}")
+    check_function_exists("mbedtls_des_crypt_ecb" HAVE_MBEDTLS_DES_CRYPT_ECB)  # in mbedTLS <4
+    cmake_pop_check_state()
+  endif()
 endif()
 
 if(CURL_USE_WOLFSSL)
@@ -843,27 +902,23 @@ if(CURL_USE_WOLFSSL)
 endif()
 
 if(CURL_USE_GNUTLS)
-  if(CURL_USE_PKGCONFIG)
-    find_package(PkgConfig QUIET)
-    pkg_check_modules(GNUTLS "gnutls")
-    if(GNUTLS_FOUND)
-      set(GNUTLS_LIBRARIES ${GNUTLS_LINK_LIBRARIES})
-      string(REPLACE ";" " " GNUTLS_CFLAGS "${GNUTLS_CFLAGS}")
-      if(GNUTLS_CFLAGS)
-        string(APPEND CMAKE_C_FLAGS " ${GNUTLS_CFLAGS}")
-      endif()
-    endif()
-  endif()
-  if(NOT GNUTLS_FOUND)
-    find_package(GnuTLS REQUIRED)
+  find_package(GnuTLS REQUIRED)
+  list(APPEND CURL_LIBS ${GNUTLS_LIBRARIES})
+  list(APPEND CURL_LIBDIRS ${GNUTLS_LIBRARY_DIRS})
+  list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${GNUTLS_PC_REQUIRES})
+  include_directories(SYSTEM ${GNUTLS_INCLUDE_DIRS})
+  link_directories(${GNUTLS_LIBRARY_DIRS})
+  if(GNUTLS_CFLAGS)
+    string(APPEND CMAKE_C_FLAGS " ${GNUTLS_CFLAGS}")
   endif()
+
   find_package(Nettle REQUIRED)
   set(_ssl_enabled ON)
   set(USE_GNUTLS ON)
-  list(APPEND CURL_LIBS ${GNUTLS_LIBRARIES} ${NETTLE_LIBRARIES})
-  list(APPEND CURL_LIBDIRS ${GNUTLS_LIBRARY_DIRS} ${NETTLE_LIBRARY_DIRS})
-  list(APPEND LIBCURL_PC_REQUIRES_PRIVATE "gnutls" ${NETTLE_PC_REQUIRES})
-  include_directories(SYSTEM ${GNUTLS_INCLUDE_DIRS} ${NETTLE_INCLUDE_DIRS})
+  list(APPEND CURL_LIBS ${NETTLE_LIBRARIES})
+  list(APPEND CURL_LIBDIRS ${NETTLE_LIBRARY_DIRS})
+  list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${NETTLE_PC_REQUIRES})
+  include_directories(SYSTEM ${NETTLE_INCLUDE_DIRS})
   link_directories(${NETTLE_LIBRARY_DIRS})
   if(NETTLE_CFLAGS)
     string(APPEND CMAKE_C_FLAGS " ${NETTLE_CFLAGS}")
@@ -878,6 +933,7 @@ if(CURL_USE_GNUTLS)
     cmake_push_check_state()
     list(APPEND CMAKE_REQUIRED_INCLUDES "${GNUTLS_INCLUDE_DIRS}")
     list(APPEND CMAKE_REQUIRED_LIBRARIES "${GNUTLS_LIBRARIES}")
+    curl_required_libpaths("${GNUTLS_LIBRARY_DIRS}")
     check_symbol_exists("gnutls_srp_verifier" "gnutls/gnutls.h" HAVE_GNUTLS_SRP)
     cmake_pop_check_state()
   endif()
@@ -1032,6 +1088,9 @@ if(USE_WOLFSSL)
 endif()
 
 if(USE_OPENSSL)
+  if(NOT DEFINED HAVE_DES_ECB_ENCRYPT)
+    curl_openssl_check_exists("DES_ecb_encrypt" "openssl/des.h" HAVE_DES_ECB_ENCRYPT)
+  endif()
   if(NOT DEFINED HAVE_SSL_SET0_WBIO)
     curl_openssl_check_exists("SSL_set0_wbio" HAVE_SSL_SET0_WBIO)
   endif()
@@ -1364,23 +1423,6 @@ if(NOT USE_LIBSSH2 AND CURL_USE_LIBSSH)
   set(USE_LIBSSH ON)
 endif()
 
-# wolfSSH
-option(CURL_USE_WOLFSSH "Use wolfSSH" OFF)
-mark_as_advanced(CURL_USE_WOLFSSH)
-set(USE_WOLFSSH OFF)
-if(NOT USE_LIBSSH2 AND NOT USE_LIBSSH AND CURL_USE_WOLFSSH)
-  if(USE_WOLFSSL)
-    find_package(WolfSSH)
-    if(WOLFSSH_FOUND)
-      set(CURL_LIBS ${WOLFSSH_LIBRARIES} ${CURL_LIBS})  # keep it before TLS-crypto, compression
-      include_directories(SYSTEM ${WOLFSSH_INCLUDE_DIRS})
-      set(USE_WOLFSSH ON)
-    endif()
-  else()
-    message(WARNING "wolfSSH requires wolfSSL. Skipping.")
-  endif()
-endif()
-
 option(CURL_USE_GSASL "Use libgsasl" OFF)
 mark_as_advanced(CURL_USE_GSASL)
 if(CURL_USE_GSASL)
@@ -1415,38 +1457,8 @@ if(CURL_USE_GSSAPI)
 
     if(GSS_FLAVOUR STREQUAL "GNU")
       set(HAVE_GSSGNU 1)
-    else()
-      cmake_push_check_state()
-      list(APPEND CMAKE_REQUIRED_INCLUDES "${GSS_INCLUDE_DIRS}")
-
-      set(_include_list "")
-      check_include_file("gssapi/gssapi.h" HAVE_GSSAPI_GSSAPI_H)
-      if(HAVE_GSSAPI_GSSAPI_H)
-        list(APPEND _include_list "gssapi/gssapi.h")
-      endif()
-      check_include_files("${_include_list};gssapi/gssapi_generic.h" HAVE_GSSAPI_GSSAPI_GENERIC_H)
-
-      if(GSS_FLAVOUR STREQUAL "MIT")
-        check_include_files("${_include_list};gssapi/gssapi_krb5.h" _have_gssapi_gssapi_krb5_h)
-        if(HAVE_GSSAPI_GSSAPI_GENERIC_H)
-          list(APPEND _include_list "gssapi/gssapi_generic.h")
-        endif()
-        if(_have_gssapi_gssapi_krb5_h)
-          list(APPEND _include_list "gssapi/gssapi_krb5.h")
-        endif()
-
-        if(NOT DEFINED HAVE_GSS_C_NT_HOSTBASED_SERVICE)
-          string(APPEND CMAKE_REQUIRED_FLAGS " ${GSS_CFLAGS}")
-          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()
-        if(NOT HAVE_GSS_C_NT_HOSTBASED_SERVICE)
-          set(HAVE_OLD_GSSMIT ON)
-        endif()
-      endif()
-      unset(_include_list)
-      cmake_pop_check_state()
+    elseif(GSS_VERSION)  # MIT
+      set(CURL_KRB5_VERSION "\"${GSS_VERSION}\"")
     endif()
   else()
     message(WARNING "GSSAPI has been requested, but no supporting libraries found. Skipping.")
@@ -1502,14 +1514,16 @@ endif()
 # CA handling
 #
 if(_curl_ca_bundle_supported)
+  set(_ca_opt_desc "Set 'none' to disable or 'auto' for auto-detection. Defaults to 'auto'.")
+
   set(CURL_CA_BUNDLE "auto" CACHE
-    STRING "Path to the CA bundle. Set 'none' to disable or 'auto' for auto-detection. Defaults to 'auto'.")
+    STRING "Absolute path to the CA bundle. ${_ca_opt_desc}")
   set(CURL_CA_FALLBACK OFF CACHE
     BOOL "Use built-in CA store of OpenSSL. Defaults to OFF")
   set(CURL_CA_PATH "auto" CACHE
-    STRING "Location of default CA path. Set 'none' to disable or 'auto' for auto-detection. Defaults to 'auto'.")
+    STRING "Absolute path to a directory containing CA certificates stored individually. ${_ca_opt_desc}")
   set(CURL_CA_EMBED "" CACHE
-    STRING "Path to the CA bundle to embed in the curl tool.")
+    STRING "Absolute path to the CA bundle to embed in the curl tool.")
 
   if(CURL_CA_FALLBACK AND NOT CURL_USE_OPENSSL)
     message(FATAL_ERROR "CURL_CA_FALLBACK only works with OpenSSL.")
@@ -1521,7 +1535,7 @@ if(_curl_ca_bundle_supported)
     unset(CURL_CA_BUNDLE CACHE)
   elseif(CURL_CA_BUNDLE STREQUAL "auto")
     unset(CURL_CA_BUNDLE CACHE)
-    if(NOT CMAKE_CROSSCOMPILING AND NOT WIN32)
+    if(NOT CMAKE_CROSSCOMPILING AND NOT WIN32 AND NOT USE_APPLE_SECTRUST)
       set(_curl_ca_bundle_autodetect TRUE)
     endif()
   else()
@@ -1560,8 +1574,8 @@ if(_curl_ca_bundle_supported)
         if(EXISTS "${_search_ca_bundle_path}")
           message(STATUS "Found CA bundle: ${_search_ca_bundle_path}")
           set(CURL_CA_BUNDLE "${_search_ca_bundle_path}" CACHE
-            STRING "Path to the CA bundle. Set 'none' to disable or 'auto' for auto-detection. Defaults to 'auto'.")
-          set(CURL_CA_BUNDLE_SET TRUE CACHE BOOL "Path to the CA bundle has been set")
+            STRING "Absolute path to the CA bundle. ${_ca_opt_desc}")
+          set(CURL_CA_BUNDLE_SET TRUE CACHE BOOL "Absolute path to the CA bundle has been set")
           break()
         endif()
       endforeach()
@@ -1574,8 +1588,8 @@ if(_curl_ca_bundle_supported)
         unset(_curl_ca_files_found)
         message(STATUS "Found CA path: ${_search_ca_path}")
         set(CURL_CA_PATH "${_search_ca_path}" CACHE
-          STRING "Location of default CA path. Set 'none' to disable or 'auto' for auto-detection. Defaults to 'auto'.")
-        set(CURL_CA_PATH_SET TRUE CACHE BOOL "Path to the CA bundle has been set")
+          STRING "Absolute path to a directory containing CA certificates stored individually. ${_ca_opt_desc}")
+        set(CURL_CA_PATH_SET TRUE CACHE BOOL "Absolute path to the CA bundle has been set")
       endif()
     endif()
   endif()
@@ -1984,6 +1998,9 @@ if(WIN32)
 endif()
 
 list(APPEND CURL_LIBS ${CURL_NETWORK_AND_TIME_LIBS})
+if(CURL_CODE_COVERAGE)
+  list(APPEND CURL_LIBS ${CURL_COVERAGE_LIBS})
+endif()
 
 if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")  # MSVC but exclude clang-cl
   set_property(DIRECTORY APPEND PROPERTY COMPILE_OPTIONS "-MP")  # Parallel compilation
@@ -2010,9 +2027,8 @@ endif()
 # (= regenerate it).
 function(curl_transform_makefile_inc _input_file _output_file)
   file(READ ${_input_file} _makefile_inc_text)
-  # cmake-lint: disable=W0106
-  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(REPLACE "$(top_srcdir)"   "\${PROJECT_SOURCE_DIR}" _makefile_inc_text ${_makefile_inc_text})  # cmake-lint: disable=W0106
+  string(REPLACE "$(top_builddir)" "\${PROJECT_BINARY_DIR}" _makefile_inc_text ${_makefile_inc_text})  # cmake-lint: disable=W0106
 
   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})
@@ -2079,8 +2095,8 @@ endmacro()
 # NTLM support requires crypto functions from various SSL libs.
 # These conditions must match those in lib/curl_setup.h.
 if(NOT CURL_DISABLE_NTLM AND
-   (USE_OPENSSL OR
-    USE_MBEDTLS OR
+   ((USE_OPENSSL AND HAVE_DES_ECB_ENCRYPT) OR
+    (USE_MBEDTLS AND HAVE_MBEDTLS_DES_CRYPT_ECB) OR
     USE_GNUTLS OR
     USE_WIN32_CRYPTO OR
     (USE_WOLFSSL AND HAVE_WOLFSSL_DES_ECB_ENCRYPT)))
@@ -2114,8 +2130,8 @@ curl_add_if("SMBS"          NOT CURL_DISABLE_SMB AND _ssl_enabled AND
                             _use_curl_ntlm_core AND (SIZEOF_CURL_OFF_T GREATER 4))
 curl_add_if("SMTP"          NOT CURL_DISABLE_SMTP)
 curl_add_if("SMTPS"         NOT CURL_DISABLE_SMTP AND _ssl_enabled)
-curl_add_if("SCP"           USE_LIBSSH2 OR USE_LIBSSH OR USE_WOLFSSH)
-curl_add_if("SFTP"          USE_LIBSSH2 OR USE_LIBSSH OR USE_WOLFSSH)
+curl_add_if("SCP"           USE_LIBSSH2 OR USE_LIBSSH)
+curl_add_if("SFTP"          USE_LIBSSH2 OR USE_LIBSSH)
 curl_add_if("IPFS"          NOT CURL_DISABLE_IPFS)
 curl_add_if("IPNS"          NOT CURL_DISABLE_IPFS)
 curl_add_if("RTSP"          NOT CURL_DISABLE_RTSP)
@@ -2175,6 +2191,7 @@ curl_add_if("HTTPSRR"       _ssl_enabled AND USE_HTTPSRR)
 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)
+curl_add_if("AppleSecTrust" USE_APPLE_SECTRUST AND _ssl_enabled AND (USE_OPENSSL OR USE_GNUTLS))
 if(_items)
   if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13)
     list(SORT _items CASE INSENSITIVE)
@@ -2309,8 +2326,13 @@ if(NOT CURL_DISABLE_INSTALL)
     elseif(_lib MATCHES "/")
       # This gets a bit more complex, because we want to specify the
       # directory separately, and only once per directory
-      get_filename_component(_libdir ${_lib} DIRECTORY)
-      get_filename_component(_libname ${_lib} NAME_WE)
+      if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.20)
+        cmake_path(GET _lib PARENT_PATH _libdir)
+        cmake_path(GET _lib STEM _libname)
+      else()
+        get_filename_component(_libdir "${_lib}" DIRECTORY)
+        get_filename_component(_libname "${_lib}" NAME_WE)
+      endif()
       if(_libname MATCHES "^lib")
         if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.20)
           cmake_path(SET _libdir NORMALIZE "${_libdir}")

+ 7 - 7
include/curl/curl.h

@@ -401,12 +401,12 @@ typedef int (*curl_seek_callback)(void *instream,
 #define CURL_TRAILERFUNC_ABORT 1
 
 typedef size_t (*curl_read_callback)(char *buffer,
-                                      size_t size,
-                                      size_t nitems,
-                                      void *instream);
+                                     size_t size,
+                                     size_t nitems,
+                                     void *instream);
 
 typedef int (*curl_trailer_callback)(struct curl_slist **list,
-                                      void *userdata);
+                                     void *userdata);
 
 typedef enum {
   CURLSOCKTYPE_IPCXN,  /* socket created for a specific IP connection */
@@ -1357,7 +1357,8 @@ typedef enum {
   /* Set the krb4/5 security level, this also enables krb4/5 awareness. This
    * is a string, 'clear', 'safe', 'confidential' or 'private'. If the string
    * is set but does not match one of these, 'private' will be used.  */
-  CURLOPT(CURLOPT_KRBLEVEL, CURLOPTTYPE_STRINGPOINT, 63),
+  CURLOPTDEPRECATED(CURLOPT_KRBLEVEL, CURLOPTTYPE_STRINGPOINT, 63,
+                    8.17.0, "removed"),
 
   /* Set if we should verify the peer in ssl handshake, set 1 to verify. */
   CURLOPT(CURLOPT_SSL_VERIFYPEER, CURLOPTTYPE_LONG, 64),
@@ -1952,8 +1953,7 @@ typedef enum {
   /* Pass in a bitmask of "header options" */
   CURLOPT(CURLOPT_HEADEROPT, CURLOPTTYPE_VALUES, 229),
 
-  /* The public key in DER form used to validate the peer public key
-     this option is used only if SSL_VERIFYPEER is true */
+  /* The public key used to validate the peer public key */
   CURLOPT(CURLOPT_PINNEDPUBLICKEY, CURLOPTTYPE_STRINGPOINT, 230),
 
   /* Path to Unix domain socket */

+ 3 - 3
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.16.0-DEV"
+#define LIBCURL_VERSION "8.17.0-DEV"
 
 /* The numeric version number is also available "in parts" by using these
    defines: */
 #define LIBCURL_VERSION_MAJOR 8
-#define LIBCURL_VERSION_MINOR 16
+#define LIBCURL_VERSION_MINOR 17
 #define LIBCURL_VERSION_PATCH 0
 /* This is the numeric version of the libcurl version number, meant for easier
    parsing and comparisons by programs. The LIBCURL_VERSION_NUM define will
@@ -58,7 +58,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 0x081000
+#define LIBCURL_VERSION_NUM 0x081100
 
 /*
  * This is the date and time when the full source package was created. The

+ 29 - 1
include/curl/multi.h

@@ -398,6 +398,12 @@ typedef enum {
   /* network has changed, adjust caches/connection reuse */
   CURLOPT(CURLMOPT_NETWORK_CHANGED, CURLOPTTYPE_LONG, 17),
 
+  /* This is the notify callback function pointer */
+  CURLOPT(CURLMOPT_NOTIFYFUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 18),
+
+  /* This is the argument passed to the notify callback */
+  CURLOPT(CURLMOPT_NOTIFYDATA, CURLOPTTYPE_OBJECTPOINT, 19),
+
   CURLMOPT_LASTENTRY /* the last unused */
 } CURLMoption;
 
@@ -464,7 +470,9 @@ typedef enum {
    * be read via `curl_multi_info_read()`. */
   CURLMINFO_XFERS_DONE = 4,
   /* The total number of easy handles added to the multi handle, ever. */
-  CURLMINFO_XFERS_ADDED = 5
+  CURLMINFO_XFERS_ADDED = 5,
+
+  CURLMINFO_LASTENTRY /* the last unused */
 } CURLMinfo_offt;
 
 /*
@@ -518,6 +526,26 @@ CURL_EXTERN CURLMcode curl_multi_waitfds(CURLM *multi,
                                          unsigned int size,
                                          unsigned int *fd_count);
 
+/*
+ * Notifications dispatched by a multi handle, when enabled.
+ */
+#define CURLMNOTIFY_INFO_READ    0
+#define CURLMNOTIFY_EASY_DONE    1
+
+/*
+ * Callback to install via CURLMOPT_NOTIFYFUNCTION.
+ */
+typedef void (*curl_notify_callback)(CURLM *multi,
+                                     unsigned int notification,
+                                     CURL *easy,
+                                     void *user_data);
+
+CURL_EXTERN CURLMcode curl_multi_notify_disable(CURLM *multi,
+                                                unsigned int notification);
+
+CURL_EXTERN CURLMcode curl_multi_notify_enable(CURLM *multi,
+                                               unsigned int notification);
+
 #ifdef __cplusplus
 } /* end of extern "C" */
 #endif

+ 228 - 217
include/curl/typecheck-gcc.h

@@ -29,9 +29,9 @@
 /* To add a new kind of warning, add an
  *   if(curlcheck_sometype_option(_curl_opt))
  *     if(!curlcheck_sometype(value))
- *       _curl_easy_setopt_err_sometype();
+ *       Wcurl_easy_setopt_err_sometype();
  * block and define curlcheck_sometype_option, curlcheck_sometype and
- * _curl_easy_setopt_err_sometype below
+ * Wcurl_easy_setopt_err_sometype below
  *
  * NOTE: We use two nested 'if' statements here instead of the && operator, in
  *       order to work around gcc bug #32061. It affects only gcc 4.3.x/4.4.x
@@ -47,113 +47,113 @@
         CURL_IGNORE_DEPRECATION(                                        \
           if(curlcheck_long_option(option))                             \
             if(!curlcheck_long(value))                                  \
-              _curl_easy_setopt_err_long();                             \
+              Wcurl_easy_setopt_err_long();                             \
           if(curlcheck_off_t_option(option))                            \
             if(!curlcheck_off_t(value))                                 \
-              _curl_easy_setopt_err_curl_off_t();                       \
+              Wcurl_easy_setopt_err_curl_off_t();                       \
           if(curlcheck_string_option(option))                           \
             if(!curlcheck_string(value))                                \
-              _curl_easy_setopt_err_string();                           \
+              Wcurl_easy_setopt_err_string();                           \
           if((option) == CURLOPT_PRIVATE) { }                           \
           if(curlcheck_write_cb_option(option))                         \
             if(!curlcheck_write_cb(value))                              \
-              _curl_easy_setopt_err_write_callback();                   \
+              Wcurl_easy_setopt_err_write_callback();                   \
           if(curlcheck_curl_option(option))                             \
             if(!curlcheck_curl(value))                                  \
-              _curl_easy_setopt_err_curl();                             \
+              Wcurl_easy_setopt_err_curl();                             \
           if((option) == CURLOPT_RESOLVER_START_FUNCTION)               \
             if(!curlcheck_resolver_start_callback(value))               \
-              _curl_easy_setopt_err_resolver_start_callback();          \
+              Wcurl_easy_setopt_err_resolver_start_callback();          \
           if((option) == CURLOPT_READFUNCTION)                          \
             if(!curlcheck_read_cb(value))                               \
-              _curl_easy_setopt_err_read_cb();                          \
+              Wcurl_easy_setopt_err_read_cb();                          \
           if((option) == CURLOPT_IOCTLFUNCTION)                         \
             if(!curlcheck_ioctl_cb(value))                              \
-              _curl_easy_setopt_err_ioctl_cb();                         \
+              Wcurl_easy_setopt_err_ioctl_cb();                         \
           if((option) == CURLOPT_SOCKOPTFUNCTION)                       \
             if(!curlcheck_sockopt_cb(value))                            \
-              _curl_easy_setopt_err_sockopt_cb();                       \
+              Wcurl_easy_setopt_err_sockopt_cb();                       \
           if((option) == CURLOPT_OPENSOCKETFUNCTION)                    \
             if(!curlcheck_opensocket_cb(value))                         \
-              _curl_easy_setopt_err_opensocket_cb();                    \
+              Wcurl_easy_setopt_err_opensocket_cb();                    \
           if((option) == CURLOPT_PROGRESSFUNCTION)                      \
             if(!curlcheck_progress_cb(value))                           \
-              _curl_easy_setopt_err_progress_cb();                      \
+              Wcurl_easy_setopt_err_progress_cb();                      \
           if((option) == CURLOPT_XFERINFOFUNCTION)                      \
             if(!curlcheck_xferinfo_cb(value))                           \
-              _curl_easy_setopt_err_xferinfo_cb();                      \
+              Wcurl_easy_setopt_err_xferinfo_cb();                      \
           if((option) == CURLOPT_DEBUGFUNCTION)                         \
             if(!curlcheck_debug_cb(value))                              \
-              _curl_easy_setopt_err_debug_cb();                         \
+              Wcurl_easy_setopt_err_debug_cb();                         \
           if((option) == CURLOPT_SSL_CTX_FUNCTION)                      \
             if(!curlcheck_ssl_ctx_cb(value))                            \
-              _curl_easy_setopt_err_ssl_ctx_cb();                       \
+              Wcurl_easy_setopt_err_ssl_ctx_cb();                       \
           if(curlcheck_conv_cb_option(option))                          \
             if(!curlcheck_conv_cb(value))                               \
-              _curl_easy_setopt_err_conv_cb();                          \
+              Wcurl_easy_setopt_err_conv_cb();                          \
           if((option) == CURLOPT_SEEKFUNCTION)                          \
             if(!curlcheck_seek_cb(value))                               \
-              _curl_easy_setopt_err_seek_cb();                          \
+              Wcurl_easy_setopt_err_seek_cb();                          \
           if((option) == CURLOPT_CHUNK_BGN_FUNCTION)                    \
             if(!curlcheck_chunk_bgn_cb(value))                          \
-              _curl_easy_setopt_err_chunk_bgn_cb();                     \
+              Wcurl_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();                     \
+              Wcurl_easy_setopt_err_chunk_end_cb();                     \
           if((option) == CURLOPT_CLOSESOCKETFUNCTION)                   \
             if(!curlcheck_close_socket_cb(value))                       \
-              _curl_easy_setopt_err_close_socket_cb();                  \
+              Wcurl_easy_setopt_err_close_socket_cb();                  \
           if((option) == CURLOPT_FNMATCH_FUNCTION)                      \
             if(!curlcheck_fnmatch_cb(value))                            \
-              _curl_easy_setopt_err_fnmatch_cb();                       \
+              Wcurl_easy_setopt_err_fnmatch_cb();                       \
           if((option) == CURLOPT_HSTSREADFUNCTION)                      \
             if(!curlcheck_hstsread_cb(value))                           \
-              _curl_easy_setopt_err_hstsread_cb();                      \
+              Wcurl_easy_setopt_err_hstsread_cb();                      \
           if((option) == CURLOPT_HSTSWRITEFUNCTION)                     \
             if(!curlcheck_hstswrite_cb(value))                          \
-              _curl_easy_setopt_err_hstswrite_cb();                     \
+              Wcurl_easy_setopt_err_hstswrite_cb();                     \
           if((option) == CURLOPT_SSH_HOSTKEYFUNCTION)                   \
             if(!curlcheck_ssh_hostkey_cb(value))                        \
-              _curl_easy_setopt_err_ssh_hostkey_cb();                   \
+              Wcurl_easy_setopt_err_ssh_hostkey_cb();                   \
           if((option) == CURLOPT_SSH_KEYFUNCTION)                       \
             if(!curlcheck_ssh_key_cb(value))                            \
-              _curl_easy_setopt_err_ssh_key_cb();                       \
+              Wcurl_easy_setopt_err_ssh_key_cb();                       \
           if((option) == CURLOPT_INTERLEAVEFUNCTION)                    \
             if(!curlcheck_interleave_cb(value))                         \
-              _curl_easy_setopt_err_interleave_cb();                    \
+              Wcurl_easy_setopt_err_interleave_cb();                    \
           if((option) == CURLOPT_PREREQFUNCTION)                        \
             if(!curlcheck_prereq_cb(value))                             \
-              _curl_easy_setopt_err_prereq_cb();                        \
+              Wcurl_easy_setopt_err_prereq_cb();                        \
           if((option) == CURLOPT_TRAILERFUNCTION)                       \
             if(!curlcheck_trailer_cb(value))                            \
-              _curl_easy_setopt_err_trailer_cb();                       \
+              Wcurl_easy_setopt_err_trailer_cb();                       \
           if(curlcheck_cb_data_option(option))                          \
             if(!curlcheck_cb_data(value))                               \
-              _curl_easy_setopt_err_cb_data();                          \
+              Wcurl_easy_setopt_err_cb_data();                          \
           if((option) == CURLOPT_ERRORBUFFER)                           \
             if(!curlcheck_error_buffer(value))                          \
-              _curl_easy_setopt_err_error_buffer();                     \
+              Wcurl_easy_setopt_err_error_buffer();                     \
           if((option) == CURLOPT_CURLU)                                 \
             if(!curlcheck_ptr((value), CURLU))                          \
-              _curl_easy_setopt_err_curlu();                            \
+              Wcurl_easy_setopt_err_curlu();                            \
           if((option) == CURLOPT_STDERR)                                \
             if(!curlcheck_FILE(value))                                  \
-              _curl_easy_setopt_err_FILE();                             \
+              Wcurl_easy_setopt_err_FILE();                             \
           if(curlcheck_postfields_option(option))                       \
             if(!curlcheck_postfields(value))                            \
-              _curl_easy_setopt_err_postfields();                       \
+              Wcurl_easy_setopt_err_postfields();                       \
           if((option) == CURLOPT_HTTPPOST)                              \
             if(!curlcheck_arr((value), struct curl_httppost))           \
-              _curl_easy_setopt_err_curl_httpost();                     \
+              Wcurl_easy_setopt_err_curl_httpost();                     \
           if((option) == CURLOPT_MIMEPOST)                              \
             if(!curlcheck_ptr((value), curl_mime))                      \
-              _curl_easy_setopt_err_curl_mimepost();                    \
+              Wcurl_easy_setopt_err_curl_mimepost();                    \
           if(curlcheck_slist_option(option))                            \
             if(!curlcheck_arr((value), struct curl_slist))              \
-              _curl_easy_setopt_err_curl_slist();                       \
+              Wcurl_easy_setopt_err_curl_slist();                       \
           if((option) == CURLOPT_SHARE)                                 \
             if(!curlcheck_ptr((value), CURLSH))                         \
-              _curl_easy_setopt_err_CURLSH();                           \
+              Wcurl_easy_setopt_err_CURLSH();                           \
           )                                                             \
           }                                                             \
       curl_easy_setopt(handle, option, value);                          \
@@ -166,28 +166,28 @@
         CURL_IGNORE_DEPRECATION(                                        \
           if(curlcheck_string_info(info))                               \
             if(!curlcheck_arr((arg), char *))                           \
-              _curl_easy_getinfo_err_string();                          \
+              Wcurl_easy_getinfo_err_string();                          \
           if(curlcheck_long_info(info))                                 \
             if(!curlcheck_arr((arg), long))                             \
-              _curl_easy_getinfo_err_long();                            \
+              Wcurl_easy_getinfo_err_long();                            \
           if(curlcheck_double_info(info))                               \
             if(!curlcheck_arr((arg), double))                           \
-              _curl_easy_getinfo_err_double();                          \
+              Wcurl_easy_getinfo_err_double();                          \
           if(curlcheck_slist_info(info))                                \
             if(!curlcheck_arr((arg), struct curl_slist *))              \
-              _curl_easy_getinfo_err_curl_slist();                      \
+              Wcurl_easy_getinfo_err_curl_slist();                      \
           if(curlcheck_tlssessioninfo_info(info))                       \
             if(!curlcheck_arr((arg), struct curl_tlssessioninfo *))     \
-              _curl_easy_getinfo_err_curl_tlssessioninfo();             \
+              Wcurl_easy_getinfo_err_curl_tlssessioninfo();             \
           if(curlcheck_certinfo_info(info))                             \
             if(!curlcheck_arr((arg), struct curl_certinfo *))           \
-              _curl_easy_getinfo_err_curl_certinfo();                   \
+              Wcurl_easy_getinfo_err_curl_certinfo();                   \
           if(curlcheck_socket_info(info))                               \
             if(!curlcheck_arr((arg), curl_socket_t))                    \
-              _curl_easy_getinfo_err_curl_socket();                     \
+              Wcurl_easy_getinfo_err_curl_socket();                     \
           if(curlcheck_off_t_info(info))                                \
             if(!curlcheck_arr((arg), curl_off_t))                       \
-              _curl_easy_getinfo_err_curl_off_t();                      \
+              Wcurl_easy_getinfo_err_curl_off_t();                      \
           )                                                             \
           }                                                             \
       curl_easy_getinfo(handle, info, arg);                             \
@@ -198,25 +198,28 @@
       if(__builtin_constant_p(option)) {                                \
         if(curlcheck_long_option(option))                               \
           if(!curlcheck_long(value))                                    \
-            _curl_multi_setopt_err_long();                              \
+            Wcurl_multi_setopt_err_long();                              \
         if(curlcheck_off_t_option(option))                              \
           if(!curlcheck_off_t(value))                                   \
-            _curl_multi_setopt_err_curl_off_t();                        \
+            Wcurl_multi_setopt_err_curl_off_t();                        \
         if(curlcheck_multicb_data_option(option))                       \
           if(!curlcheck_cb_data(value))                                 \
-            _curl_multi_setopt_err_cb_data();                           \
+            Wcurl_multi_setopt_err_cb_data();                           \
         if(curlcheck_charpp_option(option))                             \
           if(!curlcheck_ptrptr(value, char))                            \
-            _curl_multi_setopt_err_charpp();                            \
+            Wcurl_multi_setopt_err_charpp();                            \
+        if((option) == CURLMOPT_NOTIFYFUNCTION)                         \
+          if(!curlcheck_multinotify_cb(value))                          \
+            Wcurl_multi_setopt_err_notifycb();                          \
         if((option) == CURLMOPT_PUSHFUNCTION)                           \
           if(!curlcheck_multipush_cb(value))                            \
-            _curl_multi_setopt_err_pushcb();                            \
+            Wcurl_multi_setopt_err_pushcb();                            \
         if((option) == CURLMOPT_SOCKETFUNCTION)                         \
           if(!curlcheck_multisocket_cb(value))                          \
-            _curl_multi_setopt_err_socketcb();                          \
+            Wcurl_multi_setopt_err_socketcb();                          \
         if((option) == CURLMOPT_TIMERFUNCTION)                          \
           if(!curlcheck_multitimer_cb(value))                           \
-            _curl_multi_setopt_err_timercb();                           \
+            Wcurl_multi_setopt_err_timercb();                           \
       }                                                                 \
       curl_multi_setopt(handle, option, value);                         \
     })
@@ -224,7 +227,8 @@
 /* evaluates to true if the option takes a data argument to pass to a
    callback */
 #define curlcheck_multicb_data_option(option)                           \
-  ((option) == CURLMOPT_PUSHDATA ||                                     \
+  ((option) == CURLMOPT_NOTIFYDATA ||                                   \
+   (option) == CURLMOPT_PUSHDATA ||                                     \
    (option) == CURLMOPT_SOCKETDATA ||                                   \
    (option) == CURLMOPT_TIMERDATA ||                                    \
    0)
@@ -246,17 +250,22 @@
    curlcheck_cb_compatible((expr), curl_socket_callback))
 
 /* evaluates to true if expr is of type curl_push_callback */
-#define curlcheck_multipush_cb(expr)                                  \
-  (curlcheck_NULL(expr) ||                                            \
+#define curlcheck_multipush_cb(expr)                                    \
+  (curlcheck_NULL(expr) ||                                              \
    curlcheck_cb_compatible((expr), curl_push_callback))
 
+/* evaluates to true if expr is of type curl_push_callback */
+#define curlcheck_multinotify_cb(expr)                                  \
+  (curlcheck_NULL(expr) ||                                              \
+   curlcheck_cb_compatible((expr), curl_notify_callback))
+
 /*
  * For now, just make sure that the functions are called with three arguments
  */
 #define curl_share_setopt(share,opt,param) curl_share_setopt(share,opt,param)
 
 
-/* the actual warnings, triggered by calling the _curl_easy_setopt_err*
+/* the actual warnings, triggered by calling the Wcurl_easy_setopt_err*
  * functions */
 
 /* To define a new warning, use _CURL_WARNING(identifier, "message") */
@@ -265,132 +274,134 @@
   __attribute__((__unused__)) __attribute__((__noinline__))             \
   id(void) { __asm__(""); }
 
-CURLWARNING(_curl_multi_setopt_err_long,
+CURLWARNING(Wcurl_multi_setopt_err_long,
             "curl_multi_setopt expects a long argument")
-CURLWARNING(_curl_multi_setopt_err_curl_off_t,
+CURLWARNING(Wcurl_multi_setopt_err_curl_off_t,
             "curl_multi_setopt expects a curl_off_t argument")
-CURLWARNING(_curl_multi_setopt_err_cb_data,
+CURLWARNING(Wcurl_multi_setopt_err_cb_data,
             "curl_multi_setopt expects a 'void *' argument")
-CURLWARNING(_curl_multi_setopt_err_charpp,
+CURLWARNING(Wcurl_multi_setopt_err_charpp,
             "curl_multi_setopt expects a 'char **' argument")
-CURLWARNING(_curl_multi_setopt_err_pushcb,
+CURLWARNING(Wcurl_multi_setopt_err_pushcb,
             "curl_multi_setopt expects a curl_push_callback argument")
-CURLWARNING(_curl_multi_setopt_err_socketcb,
+CURLWARNING(Wcurl_multi_setopt_err_notifycb,
+            "curl_multi_setopt expects a curl_notify_callback argument")
+CURLWARNING(Wcurl_multi_setopt_err_socketcb,
             "curl_multi_setopt expects a curl_socket_callback argument")
-CURLWARNING(_curl_multi_setopt_err_timercb,
+CURLWARNING(Wcurl_multi_setopt_err_timercb,
             "curl_multi_setopt expects a curl_multi_timer_callback argument")
 
-CURLWARNING(_curl_easy_setopt_err_long,
+CURLWARNING(Wcurl_easy_setopt_err_long,
             "curl_easy_setopt expects a long argument")
-CURLWARNING(_curl_easy_setopt_err_curl_off_t,
+CURLWARNING(Wcurl_easy_setopt_err_curl_off_t,
             "curl_easy_setopt expects a curl_off_t argument")
-CURLWARNING(_curl_easy_setopt_err_string,
+CURLWARNING(Wcurl_easy_setopt_err_string,
             "curl_easy_setopt expects a "
             "string ('char *' or char[]) argument")
-CURLWARNING(_curl_easy_setopt_err_write_callback,
+CURLWARNING(Wcurl_easy_setopt_err_write_callback,
             "curl_easy_setopt expects a curl_write_callback argument")
-CURLWARNING(_curl_easy_setopt_err_resolver_start_callback,
+CURLWARNING(Wcurl_easy_setopt_err_resolver_start_callback,
             "curl_easy_setopt expects a "
             "curl_resolver_start_callback argument")
-CURLWARNING(_curl_easy_setopt_err_read_cb,
+CURLWARNING(Wcurl_easy_setopt_err_read_cb,
             "curl_easy_setopt expects a curl_read_callback argument")
-CURLWARNING(_curl_easy_setopt_err_ioctl_cb,
+CURLWARNING(Wcurl_easy_setopt_err_ioctl_cb,
             "curl_easy_setopt expects a curl_ioctl_callback argument")
-CURLWARNING(_curl_easy_setopt_err_sockopt_cb,
+CURLWARNING(Wcurl_easy_setopt_err_sockopt_cb,
             "curl_easy_setopt expects a curl_sockopt_callback argument")
-CURLWARNING(_curl_easy_setopt_err_opensocket_cb,
+CURLWARNING(Wcurl_easy_setopt_err_opensocket_cb,
             "curl_easy_setopt expects a "
             "curl_opensocket_callback argument")
-CURLWARNING(_curl_easy_setopt_err_progress_cb,
+CURLWARNING(Wcurl_easy_setopt_err_progress_cb,
             "curl_easy_setopt expects a curl_progress_callback argument")
-CURLWARNING(_curl_easy_setopt_err_xferinfo_cb,
+CURLWARNING(Wcurl_easy_setopt_err_xferinfo_cb,
             "curl_easy_setopt expects a curl_xferinfo_callback argument")
-CURLWARNING(_curl_easy_setopt_err_debug_cb,
+CURLWARNING(Wcurl_easy_setopt_err_debug_cb,
             "curl_easy_setopt expects a curl_debug_callback argument")
-CURLWARNING(_curl_easy_setopt_err_ssl_ctx_cb,
+CURLWARNING(Wcurl_easy_setopt_err_ssl_ctx_cb,
             "curl_easy_setopt expects a curl_ssl_ctx_callback argument")
-CURLWARNING(_curl_easy_setopt_err_conv_cb,
+CURLWARNING(Wcurl_easy_setopt_err_conv_cb,
             "curl_easy_setopt expects a curl_conv_callback argument")
-CURLWARNING(_curl_easy_setopt_err_seek_cb,
+CURLWARNING(Wcurl_easy_setopt_err_seek_cb,
             "curl_easy_setopt expects a curl_seek_callback argument")
-CURLWARNING(_curl_easy_setopt_err_cb_data,
+CURLWARNING(Wcurl_easy_setopt_err_cb_data,
             "curl_easy_setopt expects a "
             "private data pointer as argument")
-CURLWARNING(_curl_easy_setopt_err_chunk_bgn_cb,
+CURLWARNING(Wcurl_easy_setopt_err_chunk_bgn_cb,
             "curl_easy_setopt expects a curl_chunk_bgn_callback argument")
-CURLWARNING(_curl_easy_setopt_err_chunk_end_cb,
+CURLWARNING(Wcurl_easy_setopt_err_chunk_end_cb,
             "curl_easy_setopt expects a curl_chunk_end_callback argument")
-CURLWARNING(_curl_easy_setopt_err_close_socket_cb,
+CURLWARNING(Wcurl_easy_setopt_err_close_socket_cb,
             "curl_easy_setopt expects a curl_closesocket_callback argument")
-CURLWARNING(_curl_easy_setopt_err_fnmatch_cb,
+CURLWARNING(Wcurl_easy_setopt_err_fnmatch_cb,
             "curl_easy_setopt expects a curl_fnmatch_callback argument")
-CURLWARNING(_curl_easy_setopt_err_hstsread_cb,
+CURLWARNING(Wcurl_easy_setopt_err_hstsread_cb,
             "curl_easy_setopt expects a curl_hstsread_callback argument")
-CURLWARNING(_curl_easy_setopt_err_hstswrite_cb,
+CURLWARNING(Wcurl_easy_setopt_err_hstswrite_cb,
             "curl_easy_setopt expects a curl_hstswrite_callback argument")
-CURLWARNING(_curl_easy_setopt_err_ssh_key_cb,
+CURLWARNING(Wcurl_easy_setopt_err_ssh_key_cb,
             "curl_easy_setopt expects a curl_sshkeycallback argument")
-CURLWARNING(_curl_easy_setopt_err_ssh_hostkey_cb,
+CURLWARNING(Wcurl_easy_setopt_err_ssh_hostkey_cb,
             "curl_easy_setopt expects a curl_sshhostkeycallback argument")
-CURLWARNING(_curl_easy_setopt_err_interleave_cb,
+CURLWARNING(Wcurl_easy_setopt_err_interleave_cb,
             "curl_easy_setopt expects a curl_interleave_callback argument")
-CURLWARNING(_curl_easy_setopt_err_prereq_cb,
+CURLWARNING(Wcurl_easy_setopt_err_prereq_cb,
             "curl_easy_setopt expects a curl_prereq_callback argument")
-CURLWARNING(_curl_easy_setopt_err_trailer_cb,
+CURLWARNING(Wcurl_easy_setopt_err_trailer_cb,
             "curl_easy_setopt expects a curl_trailerfunc_ok argument")
-CURLWARNING(_curl_easy_setopt_err_error_buffer,
+CURLWARNING(Wcurl_easy_setopt_err_error_buffer,
             "curl_easy_setopt expects a "
             "char buffer of CURL_ERROR_SIZE as argument")
-CURLWARNING(_curl_easy_setopt_err_curlu,
+CURLWARNING(Wcurl_easy_setopt_err_curlu,
             "curl_easy_setopt expects a 'CURLU *' argument")
-CURLWARNING(_curl_easy_setopt_err_curl,
+CURLWARNING(Wcurl_easy_setopt_err_curl,
             "curl_easy_setopt expects a 'CURL *' argument")
-CURLWARNING(_curl_easy_setopt_err_FILE,
+CURLWARNING(Wcurl_easy_setopt_err_FILE,
             "curl_easy_setopt expects a 'FILE *' argument")
-CURLWARNING(_curl_easy_setopt_err_postfields,
+CURLWARNING(Wcurl_easy_setopt_err_postfields,
             "curl_easy_setopt expects a 'void *' or 'char *' argument")
-CURLWARNING(_curl_easy_setopt_err_curl_httpost,
+CURLWARNING(Wcurl_easy_setopt_err_curl_httpost,
             "curl_easy_setopt expects a 'struct curl_httppost *' "
             "argument")
-CURLWARNING(_curl_easy_setopt_err_curl_mimepost,
+CURLWARNING(Wcurl_easy_setopt_err_curl_mimepost,
             "curl_easy_setopt expects a 'curl_mime *' "
             "argument")
-CURLWARNING(_curl_easy_setopt_err_curl_slist,
+CURLWARNING(Wcurl_easy_setopt_err_curl_slist,
             "curl_easy_setopt expects a 'struct curl_slist *' argument")
-CURLWARNING(_curl_easy_setopt_err_CURLSH,
+CURLWARNING(Wcurl_easy_setopt_err_CURLSH,
             "curl_easy_setopt expects a CURLSH* argument")
-CURLWARNING(_curl_easy_getinfo_err_string,
+CURLWARNING(Wcurl_easy_getinfo_err_string,
             "curl_easy_getinfo expects a pointer to 'char *'")
-CURLWARNING(_curl_easy_getinfo_err_long,
+CURLWARNING(Wcurl_easy_getinfo_err_long,
             "curl_easy_getinfo expects a pointer to long")
-CURLWARNING(_curl_easy_getinfo_err_double,
+CURLWARNING(Wcurl_easy_getinfo_err_double,
             "curl_easy_getinfo expects a pointer to double")
-CURLWARNING(_curl_easy_getinfo_err_curl_slist,
+CURLWARNING(Wcurl_easy_getinfo_err_curl_slist,
             "curl_easy_getinfo expects a pointer to 'struct curl_slist *'")
-CURLWARNING(_curl_easy_getinfo_err_curl_tlssessioninfo,
+CURLWARNING(Wcurl_easy_getinfo_err_curl_tlssessioninfo,
             "curl_easy_getinfo expects a pointer to "
             "'struct curl_tlssessioninfo *'")
-CURLWARNING(_curl_easy_getinfo_err_curl_certinfo,
+CURLWARNING(Wcurl_easy_getinfo_err_curl_certinfo,
             "curl_easy_getinfo expects a pointer to "
             "'struct curl_certinfo *'")
-CURLWARNING(_curl_easy_getinfo_err_curl_socket,
+CURLWARNING(Wcurl_easy_getinfo_err_curl_socket,
             "curl_easy_getinfo expects a pointer to curl_socket_t")
-CURLWARNING(_curl_easy_getinfo_err_curl_off_t,
+CURLWARNING(Wcurl_easy_getinfo_err_curl_off_t,
             "curl_easy_getinfo expects a pointer to curl_off_t")
 
 /* groups of curl_easy_setops options that take the same type of argument */
 
 /* evaluates to true if option takes a long argument */
-#define curlcheck_long_option(option)                   \
+#define curlcheck_long_option(option)                                   \
   (0 < (option) && (option) < CURLOPTTYPE_OBJECTPOINT)
 
 #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 ||                            \
+#define curlcheck_curl_option(option)                                   \
+  ((option) == CURLOPT_STREAM_DEPENDS ||                                \
+   (option) == CURLOPT_STREAM_DEPENDS_E ||                              \
    0)
 
 /* evaluates to true if option takes a char* argument */
@@ -673,7 +684,7 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t,
   (curlcheck_ptr((expr), void) ||                                       \
    curlcheck_ptr((expr), FILE))
 #else /* be less strict */
-#define curlcheck_cb_data(expr)                 \
+#define curlcheck_cb_data(expr)                                         \
   curlcheck_any_ptr(expr)
 #endif
 
@@ -704,60 +715,60 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t,
   (curlcheck_NULL(expr) ||                                              \
    curlcheck_cb_compatible((expr), __typeof__(fread) *) ||              \
    curlcheck_cb_compatible((expr), curl_read_callback) ||               \
-   curlcheck_cb_compatible((expr), _curl_read_callback1) ||             \
-   curlcheck_cb_compatible((expr), _curl_read_callback2) ||             \
-   curlcheck_cb_compatible((expr), _curl_read_callback3) ||             \
-   curlcheck_cb_compatible((expr), _curl_read_callback4) ||             \
-   curlcheck_cb_compatible((expr), _curl_read_callback5) ||             \
-   curlcheck_cb_compatible((expr), _curl_read_callback6))
-typedef size_t (*_curl_read_callback1)(char *, size_t, size_t, void *);
-typedef size_t (*_curl_read_callback2)(char *, size_t, size_t, const void *);
-typedef size_t (*_curl_read_callback3)(char *, size_t, size_t, FILE *);
-typedef size_t (*_curl_read_callback4)(void *, size_t, size_t, void *);
-typedef size_t (*_curl_read_callback5)(void *, size_t, size_t, const void *);
-typedef size_t (*_curl_read_callback6)(void *, size_t, size_t, FILE *);
+   curlcheck_cb_compatible((expr), Wcurl_read_callback1) ||             \
+   curlcheck_cb_compatible((expr), Wcurl_read_callback2) ||             \
+   curlcheck_cb_compatible((expr), Wcurl_read_callback3) ||             \
+   curlcheck_cb_compatible((expr), Wcurl_read_callback4) ||             \
+   curlcheck_cb_compatible((expr), Wcurl_read_callback5) ||             \
+   curlcheck_cb_compatible((expr), Wcurl_read_callback6))
+typedef size_t (*Wcurl_read_callback1)(char *, size_t, size_t, void *);
+typedef size_t (*Wcurl_read_callback2)(char *, size_t, size_t, const void *);
+typedef size_t (*Wcurl_read_callback3)(char *, size_t, size_t, FILE *);
+typedef size_t (*Wcurl_read_callback4)(void *, size_t, size_t, void *);
+typedef size_t (*Wcurl_read_callback5)(void *, size_t, size_t, const void *);
+typedef size_t (*Wcurl_read_callback6)(void *, size_t, size_t, FILE *);
 
 /* evaluates to true if expr is of type curl_write_callback or "similar" */
 #define curlcheck_write_cb(expr)                                        \
   (curlcheck_read_cb(expr) ||                                           \
    curlcheck_cb_compatible((expr), __typeof__(fwrite) *) ||             \
    curlcheck_cb_compatible((expr), curl_write_callback) ||              \
-   curlcheck_cb_compatible((expr), _curl_write_callback1) ||            \
-   curlcheck_cb_compatible((expr), _curl_write_callback2) ||            \
-   curlcheck_cb_compatible((expr), _curl_write_callback3) ||            \
-   curlcheck_cb_compatible((expr), _curl_write_callback4) ||            \
-   curlcheck_cb_compatible((expr), _curl_write_callback5) ||            \
-   curlcheck_cb_compatible((expr), _curl_write_callback6))
-typedef size_t (*_curl_write_callback1)(const char *, size_t, size_t, void *);
-typedef size_t (*_curl_write_callback2)(const char *, size_t, size_t,
+   curlcheck_cb_compatible((expr), Wcurl_write_callback1) ||            \
+   curlcheck_cb_compatible((expr), Wcurl_write_callback2) ||            \
+   curlcheck_cb_compatible((expr), Wcurl_write_callback3) ||            \
+   curlcheck_cb_compatible((expr), Wcurl_write_callback4) ||            \
+   curlcheck_cb_compatible((expr), Wcurl_write_callback5) ||            \
+   curlcheck_cb_compatible((expr), Wcurl_write_callback6))
+typedef size_t (*Wcurl_write_callback1)(const char *, size_t, size_t, void *);
+typedef size_t (*Wcurl_write_callback2)(const char *, size_t, size_t,
                                        const void *);
-typedef size_t (*_curl_write_callback3)(const char *, size_t, size_t, FILE *);
-typedef size_t (*_curl_write_callback4)(const void *, size_t, size_t, void *);
-typedef size_t (*_curl_write_callback5)(const void *, size_t, size_t,
+typedef size_t (*Wcurl_write_callback3)(const char *, size_t, size_t, FILE *);
+typedef size_t (*Wcurl_write_callback4)(const void *, size_t, size_t, void *);
+typedef size_t (*Wcurl_write_callback5)(const void *, size_t, size_t,
                                        const void *);
-typedef size_t (*_curl_write_callback6)(const void *, size_t, size_t, FILE *);
+typedef size_t (*Wcurl_write_callback6)(const void *, size_t, size_t, FILE *);
 
 /* evaluates to true if expr is of type curl_ioctl_callback or "similar" */
 #define curlcheck_ioctl_cb(expr)                                        \
   (curlcheck_NULL(expr) ||                                              \
    curlcheck_cb_compatible((expr), curl_ioctl_callback) ||              \
-   curlcheck_cb_compatible((expr), _curl_ioctl_callback1) ||            \
-   curlcheck_cb_compatible((expr), _curl_ioctl_callback2) ||            \
-   curlcheck_cb_compatible((expr), _curl_ioctl_callback3) ||            \
-   curlcheck_cb_compatible((expr), _curl_ioctl_callback4))
-typedef curlioerr (*_curl_ioctl_callback1)(CURL *, int, void *);
-typedef curlioerr (*_curl_ioctl_callback2)(CURL *, int, const void *);
-typedef curlioerr (*_curl_ioctl_callback3)(CURL *, curliocmd, void *);
-typedef curlioerr (*_curl_ioctl_callback4)(CURL *, curliocmd, const void *);
+   curlcheck_cb_compatible((expr), Wcurl_ioctl_callback1) ||            \
+   curlcheck_cb_compatible((expr), Wcurl_ioctl_callback2) ||            \
+   curlcheck_cb_compatible((expr), Wcurl_ioctl_callback3) ||            \
+   curlcheck_cb_compatible((expr), Wcurl_ioctl_callback4))
+typedef curlioerr (*Wcurl_ioctl_callback1)(CURL *, int, void *);
+typedef curlioerr (*Wcurl_ioctl_callback2)(CURL *, int, const void *);
+typedef curlioerr (*Wcurl_ioctl_callback3)(CURL *, curliocmd, void *);
+typedef curlioerr (*Wcurl_ioctl_callback4)(CURL *, curliocmd, const void *);
 
 /* evaluates to true if expr is of type curl_sockopt_callback or "similar" */
 #define curlcheck_sockopt_cb(expr)                                      \
   (curlcheck_NULL(expr) ||                                              \
    curlcheck_cb_compatible((expr), curl_sockopt_callback) ||            \
-   curlcheck_cb_compatible((expr), _curl_sockopt_callback1) ||          \
-   curlcheck_cb_compatible((expr), _curl_sockopt_callback2))
-typedef int (*_curl_sockopt_callback1)(void *, curl_socket_t, curlsocktype);
-typedef int (*_curl_sockopt_callback2)(const void *, curl_socket_t,
+   curlcheck_cb_compatible((expr), Wcurl_sockopt_callback1) ||          \
+   curlcheck_cb_compatible((expr), Wcurl_sockopt_callback2))
+typedef int (*Wcurl_sockopt_callback1)(void *, curl_socket_t, curlsocktype);
+typedef int (*Wcurl_sockopt_callback2)(const void *, curl_socket_t,
                                       curlsocktype);
 
 /* evaluates to true if expr is of type curl_opensocket_callback or
@@ -765,28 +776,28 @@ typedef int (*_curl_sockopt_callback2)(const void *, curl_socket_t,
 #define curlcheck_opensocket_cb(expr)                                   \
   (curlcheck_NULL(expr) ||                                              \
    curlcheck_cb_compatible((expr), curl_opensocket_callback) ||         \
-   curlcheck_cb_compatible((expr), _curl_opensocket_callback1) ||       \
-   curlcheck_cb_compatible((expr), _curl_opensocket_callback2) ||       \
-   curlcheck_cb_compatible((expr), _curl_opensocket_callback3) ||       \
-   curlcheck_cb_compatible((expr), _curl_opensocket_callback4))
-typedef curl_socket_t (*_curl_opensocket_callback1)
+   curlcheck_cb_compatible((expr), Wcurl_opensocket_callback1) ||       \
+   curlcheck_cb_compatible((expr), Wcurl_opensocket_callback2) ||       \
+   curlcheck_cb_compatible((expr), Wcurl_opensocket_callback3) ||       \
+   curlcheck_cb_compatible((expr), Wcurl_opensocket_callback4))
+typedef curl_socket_t (*Wcurl_opensocket_callback1)
   (void *, curlsocktype, struct curl_sockaddr *);
-typedef curl_socket_t (*_curl_opensocket_callback2)
+typedef curl_socket_t (*Wcurl_opensocket_callback2)
   (void *, curlsocktype, const struct curl_sockaddr *);
-typedef curl_socket_t (*_curl_opensocket_callback3)
+typedef curl_socket_t (*Wcurl_opensocket_callback3)
   (const void *, curlsocktype, struct curl_sockaddr *);
-typedef curl_socket_t (*_curl_opensocket_callback4)
+typedef curl_socket_t (*Wcurl_opensocket_callback4)
   (const void *, curlsocktype, const struct curl_sockaddr *);
 
 /* evaluates to true if expr is of type curl_progress_callback or "similar" */
 #define curlcheck_progress_cb(expr)                                     \
   (curlcheck_NULL(expr) ||                                              \
    curlcheck_cb_compatible((expr), curl_progress_callback) ||           \
-   curlcheck_cb_compatible((expr), _curl_progress_callback1) ||         \
-   curlcheck_cb_compatible((expr), _curl_progress_callback2))
-typedef int (*_curl_progress_callback1)(void *,
+   curlcheck_cb_compatible((expr), Wcurl_progress_callback1) ||         \
+   curlcheck_cb_compatible((expr), Wcurl_progress_callback2))
+typedef int (*Wcurl_progress_callback1)(void *,
     double, double, double, double);
-typedef int (*_curl_progress_callback2)(const void *,
+typedef int (*Wcurl_progress_callback2)(const void *,
     double, double, double, double);
 
 /* evaluates to true if expr is of type curl_xferinfo_callback */
@@ -798,29 +809,29 @@ typedef int (*_curl_progress_callback2)(const void *,
 #define curlcheck_debug_cb(expr)                                        \
   (curlcheck_NULL(expr) ||                                              \
    curlcheck_cb_compatible((expr), curl_debug_callback) ||              \
-   curlcheck_cb_compatible((expr), _curl_debug_callback1) ||            \
-   curlcheck_cb_compatible((expr), _curl_debug_callback2) ||            \
-   curlcheck_cb_compatible((expr), _curl_debug_callback3) ||            \
-   curlcheck_cb_compatible((expr), _curl_debug_callback4) ||            \
-   curlcheck_cb_compatible((expr), _curl_debug_callback5) ||            \
-   curlcheck_cb_compatible((expr), _curl_debug_callback6) ||            \
-   curlcheck_cb_compatible((expr), _curl_debug_callback7) ||            \
-   curlcheck_cb_compatible((expr), _curl_debug_callback8))
-typedef int (*_curl_debug_callback1) (CURL *,
+   curlcheck_cb_compatible((expr), Wcurl_debug_callback1) ||            \
+   curlcheck_cb_compatible((expr), Wcurl_debug_callback2) ||            \
+   curlcheck_cb_compatible((expr), Wcurl_debug_callback3) ||            \
+   curlcheck_cb_compatible((expr), Wcurl_debug_callback4) ||            \
+   curlcheck_cb_compatible((expr), Wcurl_debug_callback5) ||            \
+   curlcheck_cb_compatible((expr), Wcurl_debug_callback6) ||            \
+   curlcheck_cb_compatible((expr), Wcurl_debug_callback7) ||            \
+   curlcheck_cb_compatible((expr), Wcurl_debug_callback8))
+typedef int (*Wcurl_debug_callback1) (CURL *,
     curl_infotype, char *, size_t, void *);
-typedef int (*_curl_debug_callback2) (CURL *,
+typedef int (*Wcurl_debug_callback2) (CURL *,
     curl_infotype, char *, size_t, const void *);
-typedef int (*_curl_debug_callback3) (CURL *,
+typedef int (*Wcurl_debug_callback3) (CURL *,
     curl_infotype, const char *, size_t, void *);
-typedef int (*_curl_debug_callback4) (CURL *,
+typedef int (*Wcurl_debug_callback4) (CURL *,
     curl_infotype, const char *, size_t, const void *);
-typedef int (*_curl_debug_callback5) (CURL *,
+typedef int (*Wcurl_debug_callback5) (CURL *,
     curl_infotype, unsigned char *, size_t, void *);
-typedef int (*_curl_debug_callback6) (CURL *,
+typedef int (*Wcurl_debug_callback6) (CURL *,
     curl_infotype, unsigned char *, size_t, const void *);
-typedef int (*_curl_debug_callback7) (CURL *,
+typedef int (*Wcurl_debug_callback7) (CURL *,
     curl_infotype, const unsigned char *, size_t, void *);
-typedef int (*_curl_debug_callback8) (CURL *,
+typedef int (*Wcurl_debug_callback8) (CURL *,
     curl_infotype, const unsigned char *, size_t, const void *);
 
 /* evaluates to true if expr is of type curl_ssl_ctx_callback or "similar" */
@@ -828,66 +839,66 @@ typedef int (*_curl_debug_callback8) (CURL *,
 #define curlcheck_ssl_ctx_cb(expr)                                      \
   (curlcheck_NULL(expr) ||                                              \
    curlcheck_cb_compatible((expr), curl_ssl_ctx_callback) ||            \
-   curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback1) ||          \
-   curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback2) ||          \
-   curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback3) ||          \
-   curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback4) ||          \
-   curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback5) ||          \
-   curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback6) ||          \
-   curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback7) ||          \
-   curlcheck_cb_compatible((expr), _curl_ssl_ctx_callback8))
-typedef CURLcode (*_curl_ssl_ctx_callback1)(CURL *, void *, void *);
-typedef CURLcode (*_curl_ssl_ctx_callback2)(CURL *, void *, const void *);
-typedef CURLcode (*_curl_ssl_ctx_callback3)(CURL *, const void *, void *);
-typedef CURLcode (*_curl_ssl_ctx_callback4)(CURL *, const void *,
+   curlcheck_cb_compatible((expr), Wcurl_ssl_ctx_callback1) ||          \
+   curlcheck_cb_compatible((expr), Wcurl_ssl_ctx_callback2) ||          \
+   curlcheck_cb_compatible((expr), Wcurl_ssl_ctx_callback3) ||          \
+   curlcheck_cb_compatible((expr), Wcurl_ssl_ctx_callback4) ||          \
+   curlcheck_cb_compatible((expr), Wcurl_ssl_ctx_callback5) ||          \
+   curlcheck_cb_compatible((expr), Wcurl_ssl_ctx_callback6) ||          \
+   curlcheck_cb_compatible((expr), Wcurl_ssl_ctx_callback7) ||          \
+   curlcheck_cb_compatible((expr), Wcurl_ssl_ctx_callback8))
+typedef CURLcode (*Wcurl_ssl_ctx_callback1)(CURL *, void *, void *);
+typedef CURLcode (*Wcurl_ssl_ctx_callback2)(CURL *, void *, const void *);
+typedef CURLcode (*Wcurl_ssl_ctx_callback3)(CURL *, const void *, void *);
+typedef CURLcode (*Wcurl_ssl_ctx_callback4)(CURL *, const void *,
                                             const void *);
 #ifdef HEADER_SSL_H
 /* hack: if we included OpenSSL's ssl.h, we know about SSL_CTX
  * this will of course break if we are included before OpenSSL headers...
  */
-typedef CURLcode (*_curl_ssl_ctx_callback5)(CURL *, SSL_CTX *, void *);
-typedef CURLcode (*_curl_ssl_ctx_callback6)(CURL *, SSL_CTX *, const void *);
-typedef CURLcode (*_curl_ssl_ctx_callback7)(CURL *, const SSL_CTX *, void *);
-typedef CURLcode (*_curl_ssl_ctx_callback8)(CURL *, const SSL_CTX *,
+typedef CURLcode (*Wcurl_ssl_ctx_callback5)(CURL *, SSL_CTX *, void *);
+typedef CURLcode (*Wcurl_ssl_ctx_callback6)(CURL *, SSL_CTX *, const void *);
+typedef CURLcode (*Wcurl_ssl_ctx_callback7)(CURL *, const SSL_CTX *, void *);
+typedef CURLcode (*Wcurl_ssl_ctx_callback8)(CURL *, const SSL_CTX *,
                                             const void *);
 #else
-typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback5;
-typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback6;
-typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback7;
-typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback8;
+typedef Wcurl_ssl_ctx_callback1 Wcurl_ssl_ctx_callback5;
+typedef Wcurl_ssl_ctx_callback1 Wcurl_ssl_ctx_callback6;
+typedef Wcurl_ssl_ctx_callback1 Wcurl_ssl_ctx_callback7;
+typedef Wcurl_ssl_ctx_callback1 Wcurl_ssl_ctx_callback8;
 #endif
 
 /* evaluates to true if expr is of type curl_conv_callback or "similar" */
 #define curlcheck_conv_cb(expr)                                         \
   (curlcheck_NULL(expr) ||                                              \
    curlcheck_cb_compatible((expr), curl_conv_callback) ||               \
-   curlcheck_cb_compatible((expr), _curl_conv_callback1) ||             \
-   curlcheck_cb_compatible((expr), _curl_conv_callback2) ||             \
-   curlcheck_cb_compatible((expr), _curl_conv_callback3) ||             \
-   curlcheck_cb_compatible((expr), _curl_conv_callback4))
-typedef CURLcode (*_curl_conv_callback1)(char *, size_t length);
-typedef CURLcode (*_curl_conv_callback2)(const char *, size_t length);
-typedef CURLcode (*_curl_conv_callback3)(void *, size_t length);
-typedef CURLcode (*_curl_conv_callback4)(const void *, size_t length);
+   curlcheck_cb_compatible((expr), Wcurl_conv_callback1) ||             \
+   curlcheck_cb_compatible((expr), Wcurl_conv_callback2) ||             \
+   curlcheck_cb_compatible((expr), Wcurl_conv_callback3) ||             \
+   curlcheck_cb_compatible((expr), Wcurl_conv_callback4))
+typedef CURLcode (*Wcurl_conv_callback1)(char *, size_t length);
+typedef CURLcode (*Wcurl_conv_callback2)(const char *, size_t length);
+typedef CURLcode (*Wcurl_conv_callback3)(void *, size_t length);
+typedef CURLcode (*Wcurl_conv_callback4)(const void *, size_t length);
 
 /* evaluates to true if expr is of type curl_seek_callback or "similar" */
 #define curlcheck_seek_cb(expr)                                         \
   (curlcheck_NULL(expr) ||                                              \
    curlcheck_cb_compatible((expr), curl_seek_callback) ||               \
-   curlcheck_cb_compatible((expr), _curl_seek_callback1) ||             \
-   curlcheck_cb_compatible((expr), _curl_seek_callback2))
-typedef CURLcode (*_curl_seek_callback1)(void *, curl_off_t, int);
-typedef CURLcode (*_curl_seek_callback2)(const void *, curl_off_t, int);
+   curlcheck_cb_compatible((expr), Wcurl_seek_callback1) ||             \
+   curlcheck_cb_compatible((expr), Wcurl_seek_callback2))
+typedef CURLcode (*Wcurl_seek_callback1)(void *, curl_off_t, int);
+typedef CURLcode (*Wcurl_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 *,
+   curlcheck_cb_compatible((expr), Wcurl_chunk_bgn_callback1) ||        \
+   curlcheck_cb_compatible((expr), Wcurl_chunk_bgn_callback2))
+typedef long (*Wcurl_chunk_bgn_callback1)(struct curl_fileinfo *,
                                           void *, int);
-typedef long (*_curl_chunk_bgn_callback2)(void *, void *, int);
+typedef long (*Wcurl_chunk_bgn_callback2)(void *, void *, int);
 
 /* evaluates to true if expr is of type curl_chunk_end_callback */
 #define curlcheck_chunk_end_cb(expr)                                    \
@@ -927,11 +938,11 @@ typedef long (*_curl_chunk_bgn_callback2)(void *, void *, int);
 /* 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,
+   curlcheck_cb_compatible((expr), Wcurl_interleave_callback1) ||       \
+   curlcheck_cb_compatible((expr), Wcurl_interleave_callback2))
+typedef size_t (*Wcurl_interleave_callback1)(void *p, size_t s,
                                              size_t n, void *u);
-typedef size_t (*_curl_interleave_callback2)(char *p, size_t s,
+typedef size_t (*Wcurl_interleave_callback2)(char *p, size_t s,
                                              size_t n, void *u);
 
 /* evaluates to true if expr is of type curl_prereq_callback */

+ 22 - 5
lib/CMakeLists.txt

@@ -112,7 +112,7 @@ if(SHARE_LIB_OBJECT AND CMAKE_VERSION VERSION_GREATER_EQUAL 3.12)
   set_target_properties(${LIB_OBJECT} PROPERTIES
     POSITION_INDEPENDENT_CODE ON)
   if(CURL_HIDES_PRIVATE_SYMBOLS)
-    set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_FLAGS "${CURL_CFLAG_SYMBOLS_HIDE}")
+    set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_OPTIONS "${CURL_CFLAG_SYMBOLS_HIDE}")
     set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS")
   endif()
   if(CURL_HAS_LTO)
@@ -124,6 +124,10 @@ if(SHARE_LIB_OBJECT AND CMAKE_VERSION VERSION_GREATER_EQUAL 3.12)
       set_target_properties(${LIB_OBJECT} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
     endif()
   endif()
+  if(CURL_CODE_COVERAGE)
+    set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_DEFINITIONS ${CURL_COVERAGE_MACROS})
+    set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_OPTIONS ${CURL_COVERAGE_CFLAGS})
+  endif()
 
   target_include_directories(${LIB_OBJECT} INTERFACE
     "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
@@ -150,7 +154,7 @@ if(BUILD_STATIC_LIBS)
     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_OPTIONS "${CURL_CFLAG_SYMBOLS_HIDE}")
     set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS")
   endif()
   if(CURL_HAS_LTO)
@@ -162,6 +166,10 @@ if(BUILD_STATIC_LIBS)
       set_target_properties(${LIB_STATIC} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
     endif()
   endif()
+  if(CURL_CODE_COVERAGE)
+    set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_DEFINITIONS ${CURL_COVERAGE_MACROS})
+    set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_OPTIONS ${CURL_COVERAGE_CFLAGS})
+  endif()
 
   target_include_directories(${LIB_STATIC} INTERFACE
     "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
@@ -186,7 +194,7 @@ if(BUILD_SHARED_LIBS)
     IMPORT_PREFIX "" IMPORT_SUFFIX "${IMPORT_LIB_SUFFIX}${CMAKE_IMPORT_LIBRARY_SUFFIX}"
     POSITION_INDEPENDENT_CODE ON)
   if(CURL_HIDES_PRIVATE_SYMBOLS)
-    set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_FLAGS "${CURL_CFLAG_SYMBOLS_HIDE}")
+    set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_OPTIONS "${CURL_CFLAG_SYMBOLS_HIDE}")
     set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS")
   endif()
   if(CURL_HAS_LTO)
@@ -198,6 +206,15 @@ if(BUILD_SHARED_LIBS)
       set_target_properties(${LIB_SHARED} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
     endif()
   endif()
+  if(CURL_CODE_COVERAGE)
+    set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_DEFINITIONS ${CURL_COVERAGE_MACROS})
+    set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_OPTIONS ${CURL_COVERAGE_CFLAGS})
+    if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13)
+      set_property(TARGET ${LIB_SHARED} APPEND PROPERTY LINK_OPTIONS ${CURL_COVERAGE_LDFLAGS})
+    else()
+      set_property(TARGET ${LIB_SHARED} APPEND PROPERTY LINK_FLAGS ${CURL_COVERAGE_LDFLAGS})
+    endif()
+  endif()
 
   target_include_directories(${LIB_SHARED} INTERFACE
     "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
@@ -272,9 +289,9 @@ if(BUILD_SHARED_LIBS)
     check_c_source_compiles("int main(void) { return 0; }" HAVE_VERSIONED_SYMBOLS)
     if(HAVE_VERSIONED_SYMBOLS)
       if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13)
-        set_target_properties(${LIB_SHARED} PROPERTIES LINK_OPTIONS "${CMAKE_REQUIRED_LINK_OPTIONS}")
+        set_property(TARGET ${LIB_SHARED} APPEND PROPERTY LINK_OPTIONS "${CMAKE_REQUIRED_LINK_OPTIONS}")
       else()
-        set_target_properties(${LIB_SHARED} PROPERTIES LINK_FLAGS "${CMAKE_REQUIRED_LINK_OPTIONS}")
+        set_property(TARGET ${LIB_SHARED} APPEND PROPERTY LINK_FLAGS "${CMAKE_REQUIRED_LINK_OPTIONS}")
       endif()
     else()
       message(WARNING "Versioned symbols requested, but not supported by the toolchain.")

+ 11 - 8
lib/Makefile.inc

@@ -26,10 +26,12 @@
 LIB_CURLX_CFILES = \
   curlx/base64.c   \
   curlx/dynbuf.c   \
+  curlx/fopen.c    \
   curlx/inet_ntop.c \
   curlx/inet_pton.c \
   curlx/multibyte.c \
   curlx/nonblock.c \
+  curlx/strerr.c   \
   curlx/strparse.c \
   curlx/timediff.c \
   curlx/timeval.c  \
@@ -43,10 +45,12 @@ LIB_CURLX_HFILES = \
   curlx/base64.h   \
   curlx/curlx.h    \
   curlx/dynbuf.h   \
+  curlx/fopen.h    \
   curlx/inet_ntop.h \
   curlx/inet_pton.h \
   curlx/multibyte.h \
   curlx/nonblock.h \
+  curlx/strerr.h   \
   curlx/strparse.h \
   curlx/timediff.h \
   curlx/timeval.h  \
@@ -75,6 +79,7 @@ LIB_VAUTH_HFILES =      \
   vauth/vauth.h
 
 LIB_VTLS_CFILES =           \
+  vtls/apple.c              \
   vtls/cipher_suite.c       \
   vtls/gtls.c               \
   vtls/hostcheck.c          \
@@ -92,6 +97,7 @@ LIB_VTLS_CFILES =           \
   vtls/x509asn1.c
 
 LIB_VTLS_HFILES =           \
+  vtls/apple.h              \
   vtls/cipher_suite.h       \
   vtls/gtls.h               \
   vtls/hostcheck.h          \
@@ -127,8 +133,7 @@ LIB_VQUIC_HFILES = \
 LIB_VSSH_CFILES =  \
   vssh/libssh.c    \
   vssh/libssh2.c   \
-  vssh/curl_path.c \
-  vssh/wolfssh.c
+  vssh/curl_path.c
 
 LIB_VSSH_HFILES =    \
   vssh/curl_path.h   \
@@ -155,9 +160,9 @@ LIB_CFILES =         \
   cookie.c           \
   cshutdn.c          \
   curl_addrinfo.c    \
-  curl_des.c         \
   curl_endian.c      \
   curl_fnmatch.c     \
+  curl_fopen.c       \
   curl_get_line.c    \
   curl_gethostname.c \
   curl_gssapi.c      \
@@ -182,7 +187,6 @@ LIB_CFILES =         \
   fake_addrinfo.c    \
   file.c             \
   fileinfo.c         \
-  fopen.c            \
   formdata.c         \
   ftp.c              \
   ftplistparser.c    \
@@ -209,7 +213,6 @@ LIB_CFILES =         \
   idn.c              \
   if2ip.c            \
   imap.c             \
-  krb5.c             \
   ldap.c             \
   llist.c            \
   macos.c            \
@@ -221,6 +224,7 @@ LIB_CFILES =         \
   mqtt.c             \
   multi.c            \
   multi_ev.c         \
+  multi_ntfy.c       \
   netrc.c            \
   noproxy.c          \
   openldap.c         \
@@ -285,14 +289,13 @@ LIB_HFILES =         \
   cookie.h           \
   curl_addrinfo.h    \
   curl_ctype.h       \
-  curl_des.h         \
   curl_endian.h      \
   curl_fnmatch.h     \
+  curl_fopen.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         \
@@ -323,7 +326,6 @@ LIB_HFILES =         \
   fake_addrinfo.h    \
   file.h             \
   fileinfo.h         \
-  fopen.h            \
   formdata.h         \
   ftp.h              \
   ftplistparser.h    \
@@ -354,6 +356,7 @@ LIB_HFILES =         \
   mqtt.h             \
   multihandle.h      \
   multi_ev.h         \
+  multi_ntfy.h       \
   multiif.h          \
   netrc.h            \
   noproxy.h          \

+ 32 - 29
lib/altsvc.c

@@ -31,19 +31,18 @@
 #include <curl/curl.h>
 #include "urldata.h"
 #include "altsvc.h"
+#include "curl_fopen.h"
 #include "curl_get_line.h"
 #include "parsedate.h"
 #include "sendf.h"
 #include "curlx/warnless.h"
-#include "fopen.h"
 #include "rename.h"
 #include "strdup.h"
 #include "curlx/inet_pton.h"
 #include "curlx/strparse.h"
 #include "connect.h"
 
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
+/* The last 2 #include files should be in this order */
 #include "curl_memory.h"
 #include "memdebug.h"
 
@@ -227,18 +226,22 @@ static CURLcode altsvc_load(struct altsvcinfo *asi, const char *file)
   if(!asi->filename)
     return CURLE_OUT_OF_MEMORY;
 
-  fp = fopen(file, FOPEN_READTEXT);
+  fp = curlx_fopen(file, FOPEN_READTEXT);
   if(fp) {
+    bool eof = FALSE;
     struct dynbuf buf;
     curlx_dyn_init(&buf, MAX_ALTSVC_LINE);
-    while(Curl_get_line(&buf, fp)) {
-      const char *lineptr = curlx_dyn_ptr(&buf);
-      curlx_str_passblanks(&lineptr);
-      if(curlx_str_single(&lineptr, '#'))
-        altsvc_add(asi, lineptr);
-    }
+    do {
+      result = Curl_get_line(&buf, fp, &eof);
+      if(!result) {
+        const char *lineptr = curlx_dyn_ptr(&buf);
+        curlx_str_passblanks(&lineptr);
+        if(curlx_str_single(&lineptr, '#'))
+          altsvc_add(asi, lineptr);
+      }
+    } while(!result && !eof);
     curlx_dyn_free(&buf); /* free the line buffer */
-    fclose(fp);
+    curlx_fclose(fp);
   }
   return result;
 }
@@ -270,23 +273,23 @@ static CURLcode altsvc_out(struct altsvc *as, FILE *fp)
     }
   }
 #endif
-  fprintf(fp,
-          "%s %s%s%s %u "
-          "%s %s%s%s %u "
-          "\"%d%02d%02d "
-          "%02d:%02d:%02d\" "
-          "%u %u\n",
-          Curl_alpnid2str(as->src.alpnid),
-          src6_pre, as->src.host, src6_post,
-          as->src.port,
-
-          Curl_alpnid2str(as->dst.alpnid),
-          dst6_pre, as->dst.host, dst6_post,
-          as->dst.port,
-
-          stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday,
-          stamp.tm_hour, stamp.tm_min, stamp.tm_sec,
-          as->persist, as->prio);
+  curl_mfprintf(fp,
+                "%s %s%s%s %u "
+                "%s %s%s%s %u "
+                "\"%d%02d%02d "
+                "%02d:%02d:%02d\" "
+                "%u %u\n",
+                Curl_alpnid2str(as->src.alpnid),
+                src6_pre, as->src.host, src6_post,
+                as->src.port,
+
+                Curl_alpnid2str(as->dst.alpnid),
+                dst6_pre, as->dst.host, dst6_post,
+                as->dst.port,
+
+                stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday,
+                stamp.tm_hour, stamp.tm_min, stamp.tm_sec,
+                as->persist, as->prio);
   return CURLE_OK;
 }
 
@@ -391,7 +394,7 @@ CURLcode Curl_altsvc_save(struct Curl_easy *data,
       if(result)
         break;
     }
-    fclose(out);
+    curlx_fclose(out);
     if(!result && tempstore && Curl_rename(tempstore, file))
       result = CURLE_WRITE_ERROR;
 

+ 6 - 5
lib/amigaos.c

@@ -199,8 +199,9 @@ struct Library *SocketBase = NULL;
 
 #ifdef __libnix__
 void __request(const char *msg);
+#define CURL_AMIGA_REQUEST(msg)  __request(msg)
 #else
-# define __request(msg)       Printf((const unsigned char *)(msg "\n\a"), 0)
+#define CURL_AMIGA_REQUEST(msg)  Printf((const unsigned char *)(msg "\n\a"), 0)
 #endif
 
 void Curl_amiga_cleanup(void)
@@ -217,14 +218,14 @@ CURLcode Curl_amiga_init(void)
     SocketBase = OpenLibrary((const unsigned char *)"bsdsocket.library", 4);
 
   if(!SocketBase) {
-    __request("No TCP/IP Stack running!");
+    CURL_AMIGA_REQUEST("No TCP/IP Stack running!");
     return CURLE_FAILED_INIT;
   }
 
-  if(SocketBaseTags(SBTM_SETVAL(SBTC_ERRNOPTR(sizeof(errno))), (ULONG) &errno,
-                    SBTM_SETVAL(SBTC_LOGTAGPTR), (ULONG) "curl",
+  if(SocketBaseTags(SBTM_SETVAL(SBTC_ERRNOPTR(sizeof(errno))), (ULONG)&errno,
+                    SBTM_SETVAL(SBTC_LOGTAGPTR), (ULONG)"curl",
                     TAG_DONE)) {
-    __request("SocketBaseTags ERROR");
+    CURL_AMIGA_REQUEST("SocketBaseTags ERROR");
     return CURLE_FAILED_INIT;
   }
 

+ 46 - 26
lib/asyn-ares.c

@@ -61,7 +61,6 @@
 #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
@@ -85,6 +84,17 @@
 #if ARES_VERSION >= 0x011000
 /* 1.16.0 or later has ares_getaddrinfo */
 #define HAVE_CARES_GETADDRINFO 1
+#else
+/* 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
 #endif
 
 #ifdef USE_HTTPSRR
@@ -94,22 +104,10 @@
 #define HTTPSRR_WORKS
 #endif
 
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
+/* The last 2 #include files should be in this order */
 #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;
@@ -303,11 +301,13 @@ CURLcode Curl_async_is_resolved(struct Curl_easy *data,
 
   if(data->state.async.done) {
     *dns = data->state.async.dns;
-    return CURLE_OK;
+    return ares->result;
   }
 
-  if(Curl_ares_perform(ares->channel, 0) < 0)
-    return CURLE_UNRECOVERABLE_POLL;
+  if(Curl_ares_perform(ares->channel, 0) < 0) {
+    result = CURLE_UNRECOVERABLE_POLL;
+    goto out;
+  }
 
 #ifndef HAVE_CARES_GETADDRINFO
   /* Now that we have checked for any last minute results above, see if there
@@ -372,6 +372,9 @@ CURLcode Curl_async_is_resolved(struct Curl_easy *data,
                  result, *dns ? "" : "not ");
     async_ares_cleanup(data);
   }
+
+out:
+  ares->result = result;
   return result;
 }
 
@@ -513,8 +516,6 @@ static void async_ares_hostbyname_cb(void *user_data,
   (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(ARES_SUCCESS == status) {
@@ -729,6 +730,9 @@ struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data,
                                              int *waitp)
 {
   struct async_ares_ctx *ares = &data->state.async.ares;
+#ifdef USE_HTTPSRR
+  char *rrname = NULL;
+#endif
   *waitp = 0; /* default to synchronous response */
 
   if(async_ares_init_lazy(data))
@@ -741,14 +745,27 @@ struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data,
   data->state.async.hostname = strdup(hostname);
   if(!data->state.async.hostname)
     return NULL;
+#ifdef USE_HTTPSRR
+  if(port != 443) {
+    rrname = curl_maprintf("_%d_.https.%s", port, hostname);
+    if(!rrname) {
+      free(data->state.async.hostname);
+      return NULL;
+    }
+  }
+#endif
 
   /* initial status - failed */
   ares->ares_status = ARES_ENOTFOUND;
   ares->result = CURLE_OK;
 
-#if ARES_VERSION >= 0x011800  /* >= v1.24.0 */
-  CURL_TRC_DNS(data, "asyn-ares: servers=%s",
-               ares_get_servers_csv(ares->channel));
+#if !defined(CURL_DISABLE_VERBOSE_STRINGS) && \
+  ARES_VERSION >= 0x011800  /* >= v1.24.0 */
+  if(CURL_TRC_DNS_is_verbose(data)) {
+    char *csv = ares_get_servers_csv(ares->channel);
+    CURL_TRC_DNS(data, "asyn-ares: servers=%s", csv);
+    ares_free_string(csv);
+  }
 #endif
 
 #ifdef HAVE_CARES_GETADDRINFO
@@ -778,7 +795,7 @@ struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data,
      * accordingly to save a call to getservbyname in inside C-Ares
      */
     hints.ai_flags = ARES_AI_NUMERICSERV;
-    msnprintf(service, sizeof(service), "%d", port);
+    curl_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);
@@ -790,7 +807,7 @@ struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *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,
+    ares_gethostbyname(ares->channel, data->state.async.hostname, PF_INET,
                        async_ares_hostbyname_cb, data);
     CURL_TRC_DNS(data, "asyn-ares: fire off query for AAAA");
     ares->num_pending = 2;
@@ -809,11 +826,14 @@ struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data,
 #endif
 #ifdef USE_HTTPSRR
   {
-    CURL_TRC_DNS(data, "asyn-ares: fire off query for HTTPSRR");
+    CURL_TRC_DNS(data, "asyn-ares: fire off query for HTTPSRR: %s",
+                 rrname ? rrname : data->state.async.hostname);
     memset(&ares->hinfo, 0, sizeof(ares->hinfo));
     ares->hinfo.port = -1;
+    ares->hinfo.rrname = rrname;
     ares->num_pending++; /* one more */
-    ares_query_dnsrec(ares->channel, data->state.async.hostname,
+    ares_query_dnsrec(ares->channel,
+                      rrname ? rrname : data->state.async.hostname,
                       ARES_CLASS_IN, ARES_REC_TYPE_HTTPS,
                       async_ares_rr_done, data, NULL);
   }

+ 27 - 60
lib/asyn-thrdd.c

@@ -64,7 +64,6 @@
 #include "multiif.h"
 #include "curl_threads.h"
 #include "select.h"
-#include "strdup.h"
 
 #ifdef USE_ARES
 #include <ares.h>
@@ -73,8 +72,7 @@
 #endif
 #endif
 
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
+/* The last 2 #include files should be in this order */
 #include "curl_memory.h"
 #include "memdebug.h"
 
@@ -199,14 +197,6 @@ err_exit:
   return NULL;
 }
 
-static void async_thrd_cleanup(void *arg)
-{
-  struct async_thrdd_addr_ctx *addr_ctx = arg;
-
-  Curl_thread_disable_cancel();
-  addr_ctx_unlink(&addr_ctx, NULL);
-}
-
 #ifdef HAVE_GETADDRINFO
 
 /*
@@ -220,15 +210,6 @@ static CURL_THREAD_RETURN_T CURL_STDCALL getaddrinfo_thread(void *arg)
   struct async_thrdd_addr_ctx *addr_ctx = arg;
   bool do_abort;
 
-/* clang complains about empty statements and the pthread_cleanup* macros
- * are pretty ill defined. */
-#if defined(__clang__)
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wextra-semi-stmt"
-#endif
-
-  Curl_thread_push_cleanup(async_thrd_cleanup, addr_ctx);
-
   Curl_mutex_acquire(&addr_ctx->mutx);
   do_abort = addr_ctx->do_abort;
   Curl_mutex_release(&addr_ctx->mutx);
@@ -237,10 +218,7 @@ static CURL_THREAD_RETURN_T CURL_STDCALL getaddrinfo_thread(void *arg)
     char service[12];
     int rc;
 
-#ifdef DEBUGBUILD
-    Curl_resolve_test_delay();
-#endif
-    msnprintf(service, sizeof(service), "%d", addr_ctx->port);
+    curl_msnprintf(service, sizeof(service), "%d", addr_ctx->port);
 
     rc = Curl_getaddrinfo_ex(addr_ctx->hostname, service,
                              &addr_ctx->hints, &addr_ctx->res);
@@ -274,11 +252,6 @@ static CURL_THREAD_RETURN_T CURL_STDCALL getaddrinfo_thread(void *arg)
 
   }
 
-  Curl_thread_pop_cleanup();
-#if defined(__clang__)
-#pragma clang diagnostic pop
-#endif
-
   addr_ctx_unlink(&addr_ctx, NULL);
   return 0;
 }
@@ -293,24 +266,11 @@ static CURL_THREAD_RETURN_T CURL_STDCALL gethostbyname_thread(void *arg)
   struct async_thrdd_addr_ctx *addr_ctx = arg;
   bool do_abort;
 
-/* clang complains about empty statements and the pthread_cleanup* macros
- * are pretty ill defined. */
-#if defined(__clang__)
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wextra-semi-stmt"
-#endif
-
-  Curl_thread_push_cleanup(async_thrd_cleanup, addr_ctx);
-
   Curl_mutex_acquire(&addr_ctx->mutx);
   do_abort = addr_ctx->do_abort;
   Curl_mutex_release(&addr_ctx->mutx);
 
   if(!do_abort) {
-#ifdef DEBUGBUILD
-    Curl_resolve_test_delay();
-#endif
-
     addr_ctx->res = Curl_ipv4_resolve_r(addr_ctx->hostname, addr_ctx->port);
     if(!addr_ctx->res) {
       addr_ctx->sock_error = SOCKERRNO;
@@ -337,12 +297,7 @@ static CURL_THREAD_RETURN_T CURL_STDCALL gethostbyname_thread(void *arg)
 #endif
   }
 
-  Curl_thread_pop_cleanup();
-#if defined(__clang__)
-#pragma clang diagnostic pop
-#endif
-
-  async_thrd_cleanup(addr_ctx);
+  addr_ctx_unlink(&addr_ctx, NULL);
   return 0;
 }
 
@@ -381,12 +336,12 @@ static void async_thrdd_destroy(struct Curl_easy *data)
       CURL_TRC_DNS(data, "async_thrdd_destroy, thread joined");
     }
     else {
-      /* thread is still running. Detach the thread while mutexed, it will
-       * trigger the cleanup when it releases its reference. */
+      /* thread is still running. Detach it. */
       Curl_thread_destroy(&addr->thread_hnd);
       CURL_TRC_DNS(data, "async_thrdd_destroy, thread detached");
     }
   }
+  /* release our reference to the shared context */
   addr_ctx_unlink(&thrdd->addr, data);
 }
 
@@ -406,12 +361,18 @@ static void async_thrdd_rr_done(void *user_data, ares_status_t status,
   thrdd->rr.result = Curl_httpsrr_from_ares(data, dnsrec, &thrdd->rr.hinfo);
 }
 
-static CURLcode async_rr_start(struct Curl_easy *data)
+static CURLcode async_rr_start(struct Curl_easy *data, int port)
 {
   struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
   int status;
+  char *rrname = NULL;
 
   DEBUGASSERT(!thrdd->rr.channel);
+  if(port != 443) {
+    rrname = curl_maprintf("_%d_.https.%s", port, data->conn->host.name);
+    if(!rrname)
+      return CURLE_OUT_OF_MEMORY;
+  }
   status = ares_init_options(&thrdd->rr.channel, NULL, 0);
   if(status != ARES_SUCCESS) {
     thrdd->rr.channel = NULL;
@@ -428,8 +389,9 @@ static CURLcode async_rr_start(struct Curl_easy *data)
 
   memset(&thrdd->rr.hinfo, 0, sizeof(thrdd->rr.hinfo));
   thrdd->rr.hinfo.port = -1;
+  thrdd->rr.hinfo.rrname = rrname;
   ares_query_dnsrec(thrdd->rr.channel,
-                    data->conn->host.name, ARES_CLASS_IN,
+                    rrname ? rrname : data->conn->host.name, ARES_CLASS_IN,
                     ARES_REC_TYPE_HTTPS,
                     async_thrdd_rr_done, data, NULL);
   CURL_TRC_DNS(data, "Issued HTTPS-RR request for %s", data->conn->host.name);
@@ -499,7 +461,7 @@ static bool async_thrdd_init(struct Curl_easy *data,
   }
 
 #ifdef USE_HTTPSRR_ARES
-  if(async_rr_start(data))
+  if(async_rr_start(data, port))
     infof(data, "Failed HTTPS RR operation");
 #endif
   CURL_TRC_DNS(data, "resolve thread started for of %s:%d", hostname, port);
@@ -532,10 +494,12 @@ static void async_thrdd_shutdown(struct Curl_easy *data)
   done = addr_ctx->thrd_done;
   Curl_mutex_release(&addr_ctx->mutx);
 
-  DEBUGASSERT(addr_ctx->thread_hnd != curl_thread_t_null);
-  if(!done && (addr_ctx->thread_hnd != curl_thread_t_null)) {
-    CURL_TRC_DNS(data, "cancelling resolve thread");
-    (void)Curl_thread_cancel(&addr_ctx->thread_hnd);
+  /* Wait for the thread to terminate if it is already marked done. If it is
+     not done yet we cannot do anything here. We had tried pthread_cancel but
+     it caused hanging and resource leaks (#18532). */
+  if(done && (addr_ctx->thread_hnd != curl_thread_t_null)) {
+    Curl_thread_join(&addr_ctx->thread_hnd);
+    CURL_TRC_DNS(data, "async_thrdd_shutdown, thread joined");
   }
 }
 
@@ -553,9 +517,11 @@ static CURLcode asyn_thrdd_await(struct Curl_easy *data,
     if(!entry)
       async_thrdd_shutdown(data);
 
-    CURL_TRC_DNS(data, "resolve, wait for thread to finish");
-    if(!Curl_thread_join(&addr_ctx->thread_hnd)) {
-      DEBUGASSERT(0);
+    if(addr_ctx->thread_hnd != curl_thread_t_null) {
+      CURL_TRC_DNS(data, "resolve, wait for thread to finish");
+      if(!Curl_thread_join(&addr_ctx->thread_hnd)) {
+        DEBUGASSERT(0);
+      }
     }
 
     if(entry)
@@ -650,6 +616,7 @@ CURLcode Curl_async_is_resolved(struct Curl_easy *data,
 
     data->state.async.done = TRUE;
     Curl_resolv_unlink(data, &data->state.async.dns);
+    Curl_expire_done(data, EXPIRE_ASYNC_NAME);
 
     if(thrdd->addr->res) {
       data->state.async.dns =

+ 1 - 2
lib/bufq.c

@@ -25,8 +25,7 @@
 #include "curl_setup.h"
 #include "bufq.h"
 
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
+/* The last 2 #include files should be in this order */
 #include "curl_memory.h"
 #include "memdebug.h"
 

+ 1 - 1
lib/bufq.h

@@ -225,7 +225,7 @@ typedef CURLcode Curl_bufq_reader(void *reader_ctx,
                                   size_t *pnread);
 
 /**
- * Read date and append it to the end of the buffer queue until the
+ * Read bytes and append them to the end of the buffer queue until the
  * reader returns blocking or the queue is full. A reader returns
  * CURLE_AGAIN to indicate blocking.
  * Returns the total amount of buf read (may be 0) in `pnread` on success.

+ 1 - 2
lib/cf-h1-proxy.c

@@ -46,8 +46,7 @@
 #include "multiif.h"
 #include "curlx/strparse.h"
 
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
+/* The last 2 #include files should be in this order */
 #include "curl_memory.h"
 #include "memdebug.h"
 

+ 46 - 41
lib/cf-h2-proxy.c

@@ -44,8 +44,7 @@
 #include "select.h"
 #include "cf-h2-proxy.h"
 
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
+/* The last 2 #include files should be in this order */
 #include "curl_memory.h"
 #include "memdebug.h"
 
@@ -101,8 +100,8 @@ static CURLcode tunnel_stream_init(struct Curl_cfilter *cf,
     return result;
 
   ts->authority = /* host:port with IPv6 support */
-    aprintf("%s%s%s:%d", ipv6_ip ? "[":"", hostname,
-            ipv6_ip ? "]" : "", port);
+    curl_maprintf("%s%s%s:%d", ipv6_ip ? "[":"", hostname,
+                  ipv6_ip ? "]" : "", port);
   if(!ts->authority)
     return CURLE_OUT_OF_MEMORY;
 
@@ -433,6 +432,11 @@ static int proxy_h2_process_pending_input(struct Curl_cfilter *cf,
       *err = CURLE_RECV_ERROR;
       return -1;
     }
+    else if(!rv) {
+      /* nghttp2 does not want to process more, but has no error. This
+       * probably cannot happen, but be safe. */
+      break;
+    }
     Curl_bufq_skip(&ctx->inbufq, (size_t)rv);
     if(Curl_bufq_is_empty(&ctx->inbufq)) {
       CURL_TRC_CF(data, cf, "[0] all data in connection buffer processed");
@@ -551,47 +555,47 @@ static int proxy_h2_fr_print(const nghttp2_frame *frame,
 {
   switch(frame->hd.type) {
     case NGHTTP2_DATA: {
-      return msnprintf(buffer, blen,
-                       "FRAME[DATA, len=%d, eos=%d, padlen=%d]",
-                       (int)frame->hd.length,
-                       !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM),
-                       (int)frame->data.padlen);
+      return curl_msnprintf(buffer, blen,
+                            "FRAME[DATA, len=%d, eos=%d, padlen=%d]",
+                            (int)frame->hd.length,
+                            !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM),
+                            (int)frame->data.padlen);
     }
     case NGHTTP2_HEADERS: {
-      return msnprintf(buffer, blen,
-                       "FRAME[HEADERS, len=%d, hend=%d, eos=%d]",
-                       (int)frame->hd.length,
-                       !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS),
-                       !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM));
+      return curl_msnprintf(buffer, blen,
+                            "FRAME[HEADERS, len=%d, hend=%d, eos=%d]",
+                            (int)frame->hd.length,
+                            !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS),
+                            !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM));
     }
     case NGHTTP2_PRIORITY: {
-      return msnprintf(buffer, blen,
-                       "FRAME[PRIORITY, len=%d, flags=%d]",
-                       (int)frame->hd.length, frame->hd.flags);
+      return curl_msnprintf(buffer, blen,
+                            "FRAME[PRIORITY, len=%d, flags=%d]",
+                            (int)frame->hd.length, frame->hd.flags);
     }
     case NGHTTP2_RST_STREAM: {
-      return msnprintf(buffer, blen,
-                       "FRAME[RST_STREAM, len=%d, flags=%d, error=%u]",
-                       (int)frame->hd.length, frame->hd.flags,
-                       frame->rst_stream.error_code);
+      return curl_msnprintf(buffer, blen,
+                            "FRAME[RST_STREAM, len=%d, flags=%d, error=%u]",
+                            (int)frame->hd.length, frame->hd.flags,
+                            frame->rst_stream.error_code);
     }
     case NGHTTP2_SETTINGS: {
       if(frame->hd.flags & NGHTTP2_FLAG_ACK) {
-        return msnprintf(buffer, blen, "FRAME[SETTINGS, ack=1]");
+        return curl_msnprintf(buffer, blen, "FRAME[SETTINGS, ack=1]");
       }
-      return msnprintf(buffer, blen,
-                       "FRAME[SETTINGS, len=%d]", (int)frame->hd.length);
+      return curl_msnprintf(buffer, blen,
+                            "FRAME[SETTINGS, len=%d]", (int)frame->hd.length);
     }
     case NGHTTP2_PUSH_PROMISE:
-      return msnprintf(buffer, blen,
-                       "FRAME[PUSH_PROMISE, len=%d, hend=%d]",
-                       (int)frame->hd.length,
-                       !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS));
+      return curl_msnprintf(buffer, blen,
+                            "FRAME[PUSH_PROMISE, len=%d, hend=%d]",
+                            (int)frame->hd.length,
+                            !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS));
     case NGHTTP2_PING:
-      return msnprintf(buffer, blen,
-                       "FRAME[PING, len=%d, ack=%d]",
-                       (int)frame->hd.length,
-                       frame->hd.flags & NGHTTP2_FLAG_ACK);
+      return curl_msnprintf(buffer, blen,
+                            "FRAME[PING, len=%d, ack=%d]",
+                            (int)frame->hd.length,
+                            frame->hd.flags & NGHTTP2_FLAG_ACK);
     case NGHTTP2_GOAWAY: {
       char scratch[128];
       size_t s_len = CURL_ARRAYSIZE(scratch);
@@ -600,19 +604,20 @@ static int proxy_h2_fr_print(const nghttp2_frame *frame,
       if(len)
         memcpy(scratch, frame->goaway.opaque_data, len);
       scratch[len] = '\0';
-      return msnprintf(buffer, blen, "FRAME[GOAWAY, error=%d, reason='%s', "
-                       "last_stream=%d]", frame->goaway.error_code,
-                       scratch, frame->goaway.last_stream_id);
+      return curl_msnprintf(buffer, blen,
+                            "FRAME[GOAWAY, error=%d, reason='%s', "
+                            "last_stream=%d]", frame->goaway.error_code,
+                            scratch, frame->goaway.last_stream_id);
     }
     case NGHTTP2_WINDOW_UPDATE: {
-      return msnprintf(buffer, blen,
-                       "FRAME[WINDOW_UPDATE, incr=%d]",
-                       frame->window_update.window_size_increment);
+      return curl_msnprintf(buffer, blen,
+                            "FRAME[WINDOW_UPDATE, incr=%d]",
+                            frame->window_update.window_size_increment);
     }
     default:
-      return msnprintf(buffer, blen, "FRAME[%d, len=%d, flags=%d]",
-                       frame->hd.type, (int)frame->hd.length,
-                       frame->hd.flags);
+      return curl_msnprintf(buffer, blen, "FRAME[%d, len=%d, flags=%d]",
+                            frame->hd.type, (int)frame->hd.length,
+                            frame->hd.flags);
   }
 }
 

+ 1 - 2
lib/cf-haproxy.c

@@ -34,8 +34,7 @@
 #include "multiif.h"
 #include "select.h"
 
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
+/* The last 2 #include files should be in this order */
 #include "curl_memory.h"
 #include "memdebug.h"
 

+ 1 - 2
lib/cf-https-connect.c

@@ -38,8 +38,7 @@
 #include "select.h"
 #include "vquic/vquic.h"
 
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
+/* The last 2 #include files should be in this order */
 #include "curl_memory.h"
 #include "memdebug.h"
 

+ 71 - 39
lib/cf-ip-happy.c

@@ -41,9 +41,6 @@
 #ifdef HAVE_NETDB_H
 #include <netdb.h>
 #endif
-#ifdef HAVE_FCNTL_H
-#include <fcntl.h>
-#endif
 #ifdef HAVE_ARPA_INET_H
 #include <arpa/inet.h>
 #endif
@@ -63,8 +60,7 @@
 #include "select.h"
 #include "vquic/vquic.h" /* for quic cfilters */
 
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
+/* The last 2 #include files should be in this order */
 #include "curl_memory.h"
 #include "memdebug.h"
 
@@ -156,12 +152,17 @@ static const struct Curl_addrinfo *cf_ai_iter_next(struct cf_ai_iter *iter)
   return iter->last;
 }
 
-#ifdef USE_IPV6
-static bool cf_ai_iter_done(struct cf_ai_iter *iter)
+static bool cf_ai_iter_has_more(struct cf_ai_iter *iter)
 {
-  return (iter->n >= 0) && !iter->last;
+  const struct Curl_addrinfo *addr = iter->last ? iter->last->ai_next :
+    ((iter->n < 0) ? iter->head : NULL);
+  while(addr) {
+    if(addr->ai_family == iter->ai_family)
+      return TRUE;
+    addr = addr->ai_next;
+  }
+  return FALSE;
 }
-#endif
 
 struct cf_ip_attempt {
   struct cf_ip_attempt *next;
@@ -356,7 +357,7 @@ static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs,
 {
   CURLcode result = CURLE_OK;
   struct cf_ip_attempt *a = NULL, **panchor;
-  bool do_more, more_possible;
+  bool do_more;
   struct curltime now;
   timediff_t next_expire_ms;
   int i, inconclusive, ongoing;
@@ -367,7 +368,6 @@ static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs,
 evaluate:
   now = curlx_now();
   ongoing = inconclusive = 0;
-  more_possible = TRUE;
 
   /* check if a running baller connects now */
   i = -1;
@@ -407,8 +407,14 @@ evaluate:
     do_more = TRUE;
   }
   else {
-    do_more = (curlx_timediff(now, bs->last_attempt_started) >=
-               bs->attempt_delay_ms);
+    bool more_possible = cf_ai_iter_has_more(&bs->addr_iter);
+#ifdef USE_IPV6
+    if(!more_possible)
+      more_possible = cf_ai_iter_has_more(&bs->ipv6_iter);
+#endif
+    do_more = more_possible &&
+              (curlx_timediff(now, bs->last_attempt_started) >=
+              bs->attempt_delay_ms);
     if(do_more)
       CURL_TRC_CF(data, cf, "happy eyeballs timeout expired, "
                   "start next attempt");
@@ -421,7 +427,7 @@ evaluate:
     int ai_family = 0;
 #ifdef USE_IPV6
     if((bs->last_attempt_ai_family == AF_INET) ||
-        cf_ai_iter_done(&bs->addr_iter)) {
+        !cf_ai_iter_has_more(&bs->addr_iter)) {
        addr = cf_ai_iter_next(&bs->ipv6_iter);
        ai_family = bs->ipv6_iter.ai_family;
     }
@@ -454,10 +460,10 @@ evaluate:
     else if(inconclusive) {
       /* tried all addresses, no success but some where inconclusive.
        * Let's restart the inconclusive ones. */
-      if(curlx_timediff(now, bs->last_attempt_started) >=
-         bs->attempt_delay_ms) {
-        CURL_TRC_CF(data, cf, "tried all addresses with inconclusive results"
-                    ", restarting one");
+      timediff_t since_ms = curlx_timediff(now, bs->last_attempt_started);
+      timediff_t delay_ms = bs->attempt_delay_ms - since_ms;
+      if(delay_ms <= 0) {
+        CURL_TRC_CF(data, cf, "all attempts inconclusive, restarting one");
         i = -1;
         for(a = bs->running; a; a = a->next) {
           ++i;
@@ -472,14 +478,17 @@ evaluate:
         }
         DEBUGASSERT(0); /* should not come here */
       }
+      else {
+        /* let's wait some more before restarting */
+        infof(data, "connect attempts inconclusive, retrying "
+                    "in %" FMT_TIMEDIFF_T "ms", delay_ms);
+        Curl_expire(data, delay_ms, EXPIRE_HAPPY_EYEBALLS);
+      }
       /* attempt timeout for restart has not expired yet */
       goto out;
     }
-    else if(ongoing) {
+    else if(!ongoing) {
       /* no more addresses, no inconclusive attempts */
-      more_possible = FALSE;
-    }
-    else {
       CURL_TRC_CF(data, cf, "no more attempts to try");
       result = CURLE_COULDNT_CONNECT;
       i = 0;
@@ -493,21 +502,34 @@ evaluate:
 
 out:
   if(!result) {
+    bool more_possible;
+
     /* when do we need to be called again? */
     next_expire_ms = Curl_timeleft(data, &now, TRUE);
+    if(next_expire_ms <= 0) {
+      failf(data, "Connection timeout after %" FMT_OFF_T " ms",
+            curlx_timediff(now, data->progress.t_startsingle));
+      return CURLE_OPERATION_TIMEDOUT;
+    }
+
+    more_possible = cf_ai_iter_has_more(&bs->addr_iter);
+#ifdef USE_IPV6
+    if(!more_possible)
+      more_possible = cf_ai_iter_has_more(&bs->ipv6_iter);
+#endif
     if(more_possible) {
       timediff_t expire_ms, elapsed_ms;
       elapsed_ms = curlx_timediff(now, bs->last_attempt_started);
       expire_ms = CURLMAX(bs->attempt_delay_ms - elapsed_ms, 0);
       next_expire_ms = CURLMIN(next_expire_ms, expire_ms);
+      if(next_expire_ms <= 0) {
+        CURL_TRC_CF(data, cf, "HAPPY_EYBALLS timeout due, re-evaluate");
+        goto evaluate;
+      }
+      CURL_TRC_CF(data, cf, "next HAPPY_EYBALLS timeout in %" FMT_TIMEDIFF_T
+                  "ms", next_expire_ms);
+      Curl_expire(data, next_expire_ms, EXPIRE_HAPPY_EYEBALLS);
     }
-
-    if(next_expire_ms <= 0) {
-      failf(data, "Connection timeout after %" FMT_OFF_T " ms",
-            curlx_timediff(now, data->progress.t_startsingle));
-      return CURLE_OPERATION_TIMEDOUT;
-    }
-    Curl_expire(data, next_expire_ms, EXPIRE_HAPPY_EYEBALLS);
   }
   return result;
 }
@@ -627,26 +649,36 @@ static CURLcode is_connected(struct Curl_cfilter *cf,
 
   {
     const char *hostname, *proxy_name = NULL;
-    int port;
+    char viamsg[160];
 #ifndef CURL_DISABLE_PROXY
     if(conn->bits.socksproxy)
       proxy_name = conn->socks_proxy.host.name;
     else if(conn->bits.httpproxy)
       proxy_name = conn->http_proxy.host.name;
 #endif
-    hostname = conn->bits.conn_to_host ?
-               conn->conn_to_host.name : conn->host.name;
+    hostname = conn->bits.conn_to_host ? conn->conn_to_host.name :
+      conn->host.name;
 
-    if(cf->sockindex == SECONDARYSOCKET)
-      port = conn->secondary_port;
-    else if(cf->conn->bits.conn_to_port)
-      port = conn->conn_to_port;
+#ifdef USE_UNIX_SOCKETS
+    if(conn->unix_domain_socket)
+      curl_msnprintf(viamsg, sizeof(viamsg), "over %s",
+                     conn->unix_domain_socket);
     else
-      port = conn->remote_port;
+#endif
+    {
+      int port;
+      if(cf->sockindex == SECONDARYSOCKET)
+        port = conn->secondary_port;
+      else if(cf->conn->bits.conn_to_port)
+        port = conn->conn_to_port;
+      else
+        port = conn->remote_port;
+      curl_msnprintf(viamsg, sizeof(viamsg), "port %u", port);
+    }
 
-    failf(data, "Failed to connect to %s port %u %s%s%safter "
+    failf(data, "Failed to connect to %s %s %s%s%safter "
           "%" FMT_TIMEDIFF_T " ms: %s",
-          hostname, port,
+          hostname, viamsg,
           proxy_name ? "via " : "",
           proxy_name ? proxy_name : "",
           proxy_name ? " " : "",

+ 79 - 63
lib/cf-socket.c

@@ -44,9 +44,6 @@
 #ifdef HAVE_NETDB_H
 #include <netdb.h>
 #endif
-#ifdef HAVE_FCNTL_H
-#include <fcntl.h>
-#endif
 #ifdef HAVE_ARPA_INET_H
 #include <arpa/inet.h>
 #endif
@@ -65,7 +62,6 @@
 #include "bufq.h"
 #include "sendf.h"
 #include "if2ip.h"
-#include "strerror.h"
 #include "cfilters.h"
 #include "cf-socket.h"
 #include "connect.h"
@@ -83,10 +79,10 @@
 #include "strdup.h"
 #include "system_win32.h"
 #include "curlx/version_win32.h"
+#include "curlx/strerr.h"
 #include "curlx/strparse.h"
 
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
+/* The last 2 #include files should be in this order */
 #include "curl_memory.h"
 #include "memdebug.h"
 
@@ -117,7 +113,7 @@ static void tcpnodelay(struct Curl_easy *data, curl_socket_t sockfd)
   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)));
+          curlx_strerror(SOCKERRNO, buffer, sizeof(buffer)));
 #else
   (void)data;
   (void)sockfd;
@@ -139,7 +135,7 @@ static void nosigpipe(struct Curl_easy *data,
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
     char buffer[STRERROR_LEN];
     infof(data, "Could not set SO_NOSIGPIPE: %s",
-          Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
+          curlx_strerror(SOCKERRNO, buffer, sizeof(buffer)));
 #endif
   }
 }
@@ -336,12 +332,11 @@ static CURLcode sock_assign_addr(struct Curl_sockaddr_ex *dest,
   }
   dest->addrlen = (unsigned int)ai->ai_addrlen;
 
-  if(dest->addrlen > sizeof(struct Curl_sockaddr_storage)) {
-    DEBUGASSERT(0);
+  DEBUGASSERT(dest->addrlen <= sizeof(dest->curl_sa_addrbuf));
+  if(dest->addrlen > sizeof(dest->curl_sa_addrbuf))
     return CURLE_TOO_LARGE;
-  }
 
-  memcpy(&dest->curl_sa_addr, ai->ai_addr, dest->addrlen);
+  memcpy(&dest->curl_sa_addrbuf, ai->ai_addr, dest->addrlen);
   return CURLE_OK;
 }
 
@@ -349,6 +344,8 @@ static CURLcode socket_open(struct Curl_easy *data,
                             struct Curl_sockaddr_ex *addr,
                             curl_socket_t *sockfd)
 {
+  char errbuf[STRERROR_LEN];
+
   DEBUGASSERT(data);
   DEBUGASSERT(data->conn);
   if(data->set.fopensocket) {
@@ -369,12 +366,25 @@ static CURLcode socket_open(struct Curl_easy *data,
   }
   else {
     /* opensocket callback not set, so simply create the socket now */
-    *sockfd = socket(addr->family, addr->socktype, addr->protocol);
+    *sockfd = CURL_SOCKET(addr->family, addr->socktype, addr->protocol);
   }
 
-  if(*sockfd == CURL_SOCKET_BAD)
+  if(*sockfd == CURL_SOCKET_BAD) {
     /* no socket, no connection */
+    failf(data, "failed to open socket: %s",
+          curlx_strerror(SOCKERRNO, errbuf, sizeof(errbuf)));
+    return CURLE_COULDNT_CONNECT;
+  }
+
+#ifdef HAVE_FCNTL
+  if(fcntl(*sockfd, F_SETFD, FD_CLOEXEC) < 0) {
+    failf(data, "fcntl set CLOEXEC: %s",
+          curlx_strerror(SOCKERRNO, errbuf, sizeof(errbuf)));
+    close(*sockfd);
+    *sockfd = CURL_SOCKET_BAD;
     return CURLE_COULDNT_CONNECT;
+  }
+#endif
 
 #if defined(USE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
   if(data->conn->scope_id && (addr->family == AF_INET6)) {
@@ -453,7 +463,7 @@ int Curl_socket_close(struct Curl_easy *data, struct connectdata *conn,
 /* When you run a program that uses the Windows Sockets API, you may
    experience slow performance when you copy data to a TCP server.
 
-   https://support.microsoft.com/kb/823764
+   https://learn.microsoft.com/troubleshoot/windows-server/networking/slow-performance-copy-data-tcp-server-sockets-api
 
    Work-around: Make the Socket Send Buffer Size Larger Than the Program Send
    Buffer Size
@@ -648,7 +658,7 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
           char buffer[STRERROR_LEN];
           data->state.os_errno = error = SOCKERRNO;
           failf(data, "Couldn't bind to interface '%s' with errno %d: %s",
-                iface, error, Curl_strerror(error, buffer, sizeof(buffer)));
+                iface, error, curlx_strerror(error, buffer, sizeof(buffer)));
           return CURLE_INTERFACE_FAILED;
         }
         break;
@@ -751,8 +761,8 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
       char buffer[STRERROR_LEN];
       data->state.errorbuf = FALSE;
       data->state.os_errno = error = SOCKERRNO;
-      failf(data, "Couldn't bind to '%s' with errno %d: %s",
-            host, error, Curl_strerror(error, buffer, sizeof(buffer)));
+      failf(data, "Couldn't bind to '%s' with errno %d: %s", host,
+            error, curlx_strerror(error, buffer, sizeof(buffer)));
       return CURLE_INTERFACE_FAILED;
     }
   }
@@ -790,10 +800,10 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
       infof(data, "Bind to local port %d failed, trying next", port - 1);
       /* We reuse/clobber the port variable here below */
       if(sock->sa_family == AF_INET)
-        si4->sin_port = ntohs(port);
+        si4->sin_port = htons(port);
 #ifdef USE_IPV6
       else
-        si6->sin6_port = ntohs(port);
+        si6->sin6_port = htons(port);
 #endif
     }
     else
@@ -803,7 +813,7 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
     char buffer[STRERROR_LEN];
     data->state.os_errno = error = SOCKERRNO;
     failf(data, "bind failed with errno %d: %s",
-          error, Curl_strerror(error, buffer, sizeof(buffer)));
+          error, curlx_strerror(error, buffer, sizeof(buffer)));
   }
 
   return CURLE_INTERFACE_FAILED;
@@ -825,8 +835,8 @@ static bool verifyconnect(curl_socket_t sockfd, int *error)
    * In October 2003 we effectively nullified this function on Windows due to
    * problems with it using all CPU in multi-threaded cases.
    *
-   * In May 2004, we bring it back to offer more info back on connect failures.
-   * Gisle Vanem could reproduce the former problems with this function, but
+   * In May 2004, we brought it back to offer more info back on connect
+   * failures. We could reproduce the former problems with this function, but
    * could avoid them by adding this SleepEx() call below:
    *
    *    "I do not have Rational Quantify, but the hint from his post was
@@ -904,8 +914,8 @@ static CURLcode socket_connect_result(struct Curl_easy *data,
 #else
     {
       char buffer[STRERROR_LEN];
-      infof(data, "Immediate connect fail for %s: %s",
-            ipaddress, Curl_strerror(error, buffer, sizeof(buffer)));
+      infof(data, "Immediate connect fail for %s: %s", ipaddress,
+            curlx_strerror(error, buffer, sizeof(buffer)));
     }
 #endif
     data->state.os_errno = error;
@@ -1036,10 +1046,12 @@ static void cf_socket_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
   cf->ctx = NULL;
 }
 
-static CURLcode set_local_ip(struct Curl_cfilter *cf,
-                             struct Curl_easy *data)
+static void set_local_ip(struct Curl_cfilter *cf,
+                         struct Curl_easy *data)
 {
   struct cf_socket_ctx *ctx = cf->ctx;
+  ctx->ip.local_ip[0] = 0;
+  ctx->ip.local_port = -1;
 
 #ifdef HAVE_GETSOCKNAME
   if((ctx->sock != CURL_SOCKET_BAD) &&
@@ -1053,23 +1065,18 @@ static CURLcode set_local_ip(struct Curl_cfilter *cf,
     memset(&ssloc, 0, sizeof(ssloc));
     if(getsockname(ctx->sock, (struct sockaddr*) &ssloc, &slen)) {
       int error = SOCKERRNO;
-      failf(data, "getsockname() failed with errno %d: %s",
-            error, Curl_strerror(error, buffer, sizeof(buffer)));
-      return CURLE_FAILED_INIT;
+      infof(data, "getsockname() failed with errno %d: %s",
+            error, curlx_strerror(error, buffer, sizeof(buffer)));
     }
-    if(!Curl_addr2string((struct sockaddr*)&ssloc, slen,
-                         ctx->ip.local_ip, &ctx->ip.local_port)) {
-      failf(data, "ssloc inet_ntop() failed with errno %d: %s",
-            errno, Curl_strerror(errno, buffer, sizeof(buffer)));
-      return CURLE_FAILED_INIT;
+    else if(!Curl_addr2string((struct sockaddr*)&ssloc, slen,
+                              ctx->ip.local_ip, &ctx->ip.local_port)) {
+      infof(data, "ssloc inet_ntop() failed with errno %d: %s",
+            errno, curlx_strerror(errno, buffer, sizeof(buffer)));
     }
   }
 #else
   (void)data;
-  ctx->ip.local_ip[0] = 0;
-  ctx->ip.local_port = -1;
 #endif
-  return CURLE_OK;
 }
 
 static CURLcode set_remote_ip(struct Curl_cfilter *cf,
@@ -1086,7 +1093,7 @@ static CURLcode set_remote_ip(struct Curl_cfilter *cf,
     ctx->error = errno;
     /* malformed address or bug in inet_ntop, try next address */
     failf(data, "curl_sa_addr inet_ntop() failed with errno %d: %s",
-          errno, Curl_strerror(errno, buffer, sizeof(buffer)));
+          errno, curlx_strerror(errno, buffer, sizeof(buffer)));
     return CURLE_FAILED_INIT;
   }
   return CURLE_OK;
@@ -1332,7 +1339,8 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf,
   rc = SOCKET_WRITABLE(ctx->sock, 0);
 
   if(rc == 0) { /* no connection yet */
-    CURL_TRC_CF(data, cf, "not connected yet");
+    CURL_TRC_CF(data, cf, "not connected yet on fd=%" FMT_SOCKET_T,
+                ctx->sock);
     return CURLE_OK;
   }
   else if(rc == CURL_CSELECT_OUT || cf->conn->bits.tcp_fastopen) {
@@ -1342,7 +1350,7 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf,
       set_local_ip(cf, data);
       *done = TRUE;
       cf->connected = TRUE;
-      CURL_TRC_CF(data, cf, "connected");
+      CURL_TRC_CF(data, cf, "connected on fd=%" FMT_SOCKET_T, ctx->sock);
       return CURLE_OK;
     }
   }
@@ -1363,7 +1371,7 @@ out:
         infof(data, "connect to %s port %u from %s port %d failed: %s",
               ctx->ip.remote_ip, ctx->ip.remote_port,
               ctx->ip.local_ip, ctx->ip.local_port,
-              Curl_strerror(ctx->error, buffer, sizeof(buffer)));
+              curlx_strerror(ctx->error, buffer, sizeof(buffer)));
       }
 #endif
     }
@@ -1501,7 +1509,7 @@ static CURLcode cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data,
     else {
       char buffer[STRERROR_LEN];
       failf(data, "Send failure: %s",
-            Curl_strerror(sockerr, buffer, sizeof(buffer)));
+            curlx_strerror(sockerr, buffer, sizeof(buffer)));
       data->state.os_errno = sockerr;
       result = CURLE_SEND_ERROR;
     }
@@ -1569,7 +1577,7 @@ static CURLcode cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
     else {
       char buffer[STRERROR_LEN];
       failf(data, "Recv failure: %s",
-            Curl_strerror(sockerr, buffer, sizeof(buffer)));
+            curlx_strerror(sockerr, buffer, sizeof(buffer)));
       data->state.os_errno = sockerr;
       result = CURLE_RECV_ERROR;
     }
@@ -1764,6 +1772,11 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf,
   (void)data;
   (void)conn;
   DEBUGASSERT(transport == TRNSPRT_TCP);
+  if(!ai) {
+    result = CURLE_BAD_FUNCTION_ARGUMENT;
+    goto out;
+  }
+
   ctx = calloc(1, sizeof(*ctx));
   if(!ctx) {
     result = CURLE_OUT_OF_MEMORY;
@@ -1860,6 +1873,7 @@ static CURLcode cf_udp_connect(struct Curl_cfilter *cf,
     *done = TRUE;
     return CURLE_OK;
   }
+
   *done = FALSE;
   if(ctx->sock == CURL_SOCKET_BAD) {
     result = cf_socket_open(cf, data);
@@ -1876,10 +1890,6 @@ static CURLcode cf_udp_connect(struct Curl_cfilter *cf,
                   FMT_SOCKET_T " (%s:%d)",
                   ctx->sock, ctx->ip.local_ip, ctx->ip.local_port);
     }
-    else {
-      CURL_TRC_CF(data, cf, "cf_udp_connect(), opened socket=%"
-                  FMT_SOCKET_T " (unconnected)", ctx->sock);
-    }
     *done = TRUE;
     cf->connected = TRUE;
   }
@@ -2040,13 +2050,13 @@ static void cf_tcp_set_accepted_remote_ip(struct Curl_cfilter *cf,
   if(getpeername(ctx->sock, (struct sockaddr*) &ssrem, &plen)) {
     int error = SOCKERRNO;
     failf(data, "getpeername() failed with errno %d: %s",
-          error, Curl_strerror(error, buffer, sizeof(buffer)));
+          error, curlx_strerror(error, buffer, sizeof(buffer)));
     return;
   }
   if(!Curl_addr2string((struct sockaddr*)&ssrem, plen,
                        ctx->ip.remote_ip, &ctx->ip.remote_port)) {
     failf(data, "ssrem inet_ntop() failed with errno %d: %s",
-          errno, Curl_strerror(errno, buffer, sizeof(buffer)));
+          errno, curlx_strerror(errno, buffer, sizeof(buffer)));
     return;
   }
 #else
@@ -2061,6 +2071,7 @@ static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf,
                                       bool *done)
 {
   struct cf_socket_ctx *ctx = cf->ctx;
+  char errbuf[STRERROR_LEN];
 #ifdef USE_IPV6
   struct Curl_sockaddr_storage add;
 #else
@@ -2079,6 +2090,7 @@ static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf,
     return CURLE_OK;
   }
 
+  *done = FALSE;
   timeout_ms = cf_tcp_accept_timeleft(cf, data);
   if(timeout_ms < 0) {
     /* if a timeout was already reached, bail out */
@@ -2106,29 +2118,33 @@ static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf,
 
   if(!incoming) {
     CURL_TRC_CF(data, cf, "nothing heard from the server yet");
-    *done = FALSE;
     return CURLE_OK;
   }
 
-  if(!getsockname(ctx->sock, (struct sockaddr *) &add, &size)) {
-    size = sizeof(add);
+  size = sizeof(add);
 #ifdef HAVE_ACCEPT4
-    s_accepted = accept4(ctx->sock, (struct sockaddr *) &add, &size,
-                         SOCK_NONBLOCK | SOCK_CLOEXEC);
+  s_accepted = CURL_ACCEPT4(ctx->sock, (struct sockaddr *) &add, &size,
+                            SOCK_NONBLOCK | SOCK_CLOEXEC);
 #else
-    s_accepted = CURL_ACCEPT(ctx->sock, (struct sockaddr *) &add, &size);
+  s_accepted = CURL_ACCEPT(ctx->sock, (struct sockaddr *) &add, &size);
 #endif
-  }
 
   if(CURL_SOCKET_BAD == s_accepted) {
-    failf(data, "Error accept()ing server connect");
-    return CURLE_FTP_PORT_FAILED;
+    failf(data, "Error accept()ing server connect: %s",
+          curlx_strerror(SOCKERRNO, errbuf, sizeof(errbuf)));
+    return CURLE_FTP_ACCEPT_FAILED;
+  }
+#if !defined(HAVE_ACCEPT4) && defined(HAVE_FCNTL)
+  if((fcntl(s_accepted, F_SETFD, FD_CLOEXEC) < 0) ||
+     (curlx_nonblock(s_accepted, TRUE) < 0)) {
+    failf(data, "fcntl set CLOEXEC/NONBLOCK: %s",
+          curlx_strerror(SOCKERRNO, errbuf, sizeof(errbuf)));
+    Curl_socket_close(data, cf->conn, s_accepted);
+    return CURLE_FTP_ACCEPT_FAILED;
   }
-
-  infof(data, "Connection accepted from server");
-#ifndef HAVE_ACCEPT4
-  (void)curlx_nonblock(s_accepted, TRUE); /* enable non-blocking */
 #endif
+  infof(data, "Connection accepted from server");
+
   /* Replace any filter on SECONDARY with one listening on this socket */
   ctx->listening = FALSE;
   ctx->accepted = TRUE;

+ 7 - 6
lib/cf-socket.h

@@ -48,11 +48,12 @@ struct Curl_sockaddr_ex {
   int protocol;
   unsigned int addrlen;
   union {
-    struct sockaddr addr;
-    struct Curl_sockaddr_storage buff;
-  } _sa_ex_u;
+    struct sockaddr sa;
+    struct Curl_sockaddr_storage buf;
+  } addr;
 };
-#define curl_sa_addr _sa_ex_u.addr
+#define curl_sa_addr addr.sa
+#define curl_sa_addrbuf addr.buf
 
 /*
  * Parse interface option, and return the interface name and the host part.
@@ -80,7 +81,7 @@ int Curl_socket_close(struct Curl_easy *data, struct connectdata *conn,
 /* When you run a program that uses the Windows Sockets API, you may
    experience slow performance when you copy data to a TCP server.
 
-   https://support.microsoft.com/kb/823764
+   https://learn.microsoft.com/troubleshoot/windows-server/networking/slow-performance-copy-data-tcp-server-sockets-api
 
    Work-around: Make the Socket Send Buffer Size Larger Than the Program Send
    Buffer Size
@@ -157,7 +158,7 @@ CURLcode Curl_cf_socket_peek(struct Curl_cfilter *cf,
                              struct Curl_easy *data,
                              curl_socket_t *psock,
                              const struct Curl_sockaddr_ex **paddr,
-                             struct ip_quadruple *pip);
+                             struct ip_quadruple *pip) WARN_UNUSED_RESULT;
 
 extern struct Curl_cftype Curl_cft_tcp;
 extern struct Curl_cftype Curl_cft_udp;

+ 26 - 35
lib/cfilters.c

@@ -37,8 +37,7 @@
 #include "curlx/warnless.h"
 #include "curlx/strparse.h"
 
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
+/* The last 2 #include files should be in this order */
 #include "curl_memory.h"
 #include "memdebug.h"
 
@@ -383,29 +382,26 @@ void Curl_conn_cf_insert_after(struct Curl_cfilter *cf_at,
   *pnext = tail;
 }
 
-bool Curl_conn_cf_discard_sub(struct Curl_cfilter *cf,
-                              struct Curl_cfilter *discard,
-                              struct Curl_easy *data,
-                              bool destroy_always)
+bool Curl_conn_cf_discard(struct Curl_cfilter **pcf,
+                          struct Curl_easy *data)
 {
-  struct Curl_cfilter **pprev = &cf->next;
+  struct Curl_cfilter *cf = pcf ? *pcf : NULL;
   bool found = FALSE;
-
-  /* remove from sub-chain and destroy */
-  DEBUGASSERT(cf);
-  while(*pprev) {
-    if(*pprev == cf) {
-      *pprev = discard->next;
-      discard->next = NULL;
-      found = TRUE;
-      break;
+  if(cf) {
+    if(cf->conn) {
+      /* unlink if present in connection filter chain */
+      struct Curl_cfilter **pprev = &cf->conn->cfilter[cf->sockindex];
+      while(*pprev) {
+        if(*pprev == *pcf) {
+          *pprev = (*pcf)->next;
+          cf->next = NULL;
+          found = TRUE;
+          break;
+        }
+        pprev = &((*pprev)->next);
+      }
     }
-    pprev = &((*pprev)->next);
-  }
-  if(found || destroy_always) {
-    discard->next = NULL;
-    discard->cft->destroy(discard, data);
-    free(discard);
+    Curl_conn_cf_discard_chain(pcf, data);
   }
   return found;
 }
@@ -546,8 +542,9 @@ CURLcode Curl_conn_connect(struct Curl_easy *data,
       Curl_pollfds_reset(&cpfds);
       /* In general, we want to send after connect, wait on that. */
       if(sockfd != CURL_SOCKET_BAD)
-        Curl_pollset_set_out_only(data, &ps, sockfd);
-      result = Curl_conn_adjust_pollset(data, data->conn, &ps);
+        result = Curl_pollset_set_out_only(data, &ps, sockfd);
+      if(!result)
+        result = Curl_conn_adjust_pollset(data, data->conn, &ps);
       if(result)
         goto out;
       result = Curl_pollfds_add_ps(&cpfds, &ps);
@@ -936,13 +933,14 @@ Curl_conn_get_remote_addr(struct Curl_easy *data, int sockindex)
 
 void Curl_conn_forget_socket(struct Curl_easy *data, int sockindex)
 {
-  if(data->conn && CONN_SOCK_IDX_VALID(sockindex)) {
-    struct Curl_cfilter *cf = data->conn->cfilter[sockindex];
+  struct connectdata *conn = data->conn;
+  if(conn && CONN_SOCK_IDX_VALID(sockindex)) {
+    struct Curl_cfilter *cf = conn->cfilter[sockindex];
     if(cf)
       (void)Curl_conn_cf_cntrl(cf, data, TRUE,
                                CF_CTRL_FORGET_SOCKET, 0, NULL);
-    fake_sclose(data->conn->sock[sockindex]);
-    data->conn->sock[sockindex] = CURL_SOCKET_BAD;
+    fake_sclose(conn->sock[sockindex]);
+    conn->sock[sockindex] = CURL_SOCKET_BAD;
   }
 }
 
@@ -969,13 +967,6 @@ CURLcode Curl_conn_ev_data_setup(struct Curl_easy *data)
                       CF_CTRL_DATA_SETUP, 0, NULL);
 }
 
-CURLcode Curl_conn_ev_data_idle(struct Curl_easy *data)
-{
-  return cf_cntrl_all(data->conn, data, FALSE,
-                      CF_CTRL_DATA_IDLE, 0, NULL);
-}
-
-
 CURLcode Curl_conn_flush(struct Curl_easy *data, int sockindex)
 {
   if(!CONN_SOCK_IDX_VALID(sockindex))

+ 7 - 17
lib/cfilters.h

@@ -118,7 +118,7 @@ typedef CURLcode Curl_cft_conn_keep_alive(struct Curl_cfilter *cf,
  */
 /*      data event                          arg1       arg2     return */
 #define CF_CTRL_DATA_SETUP            4  /* 0          NULL     first fail */
-#define CF_CTRL_DATA_IDLE             5  /* 0          NULL     first fail */
+/* unused now                         5  */
 #define CF_CTRL_DATA_PAUSE            6  /* on/off     NULL     first fail */
 #define CF_CTRL_DATA_DONE             7  /* premature  NULL     ignored */
 #define CF_CTRL_DATA_DONE_SEND        8  /* 0          NULL     ignored */
@@ -297,16 +297,12 @@ void Curl_conn_cf_insert_after(struct Curl_cfilter *cf_at,
                                struct Curl_cfilter *cf_new);
 
 /**
- * Discard, e.g. remove and destroy `discard` iff
- * it still is in the filter chain below `cf`. If `discard`
- * is no longer found beneath `cf` return FALSE.
- * if `destroy_always` is TRUE, will call `discard`s destroy
- * function and free it even if not found in the subchain.
+ * Extract filter `*pcf` from its connection filter chain.
+ * Destroy `*pcf`, even if it was not part of the chain and NULL it.
+ * Returns TRUE of cf has been part of chain.
  */
-bool Curl_conn_cf_discard_sub(struct Curl_cfilter *cf,
-                              struct Curl_cfilter *discard,
-                              struct Curl_easy *data,
-                              bool destroy_always);
+bool Curl_conn_cf_discard(struct Curl_cfilter **pcf,
+                          struct Curl_easy *data);
 
 /**
  * Discard all cfilters starting with `*pcf` and clearing it afterwards.
@@ -517,7 +513,7 @@ CURLcode Curl_cf_send(struct Curl_easy *data, int sockindex,
 
 /**
  * Receive bytes from connection filter `cf` into `bufq`.
- * Convenience wrappter around `Curl_bufq_sipn()`,
+ * Convenience wrapper around `Curl_bufq_sipn()`,
  * so users do not have to implement a callback.
  */
 CURLcode Curl_cf_recv_bufq(struct Curl_cfilter *cf,
@@ -543,12 +539,6 @@ CURLcode Curl_cf_send_bufq(struct Curl_cfilter *cf,
  */
 CURLcode Curl_conn_ev_data_setup(struct Curl_easy *data);
 
-/**
- * Notify connection filters that now would be a good time to
- * perform any idle, e.g. time related, actions.
- */
-CURLcode Curl_conn_ev_data_idle(struct Curl_easy *data);
-
 /**
  * Notify connection filters that the transfer represented by `data`
  * is done with sending data (e.g. has uploaded everything).

+ 19 - 13
lib/conncache.c

@@ -45,8 +45,7 @@
 #include "curlx/strparse.h"
 #include "uint-table.h"
 
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
+/* The last 2 #include files should be in this order */
 #include "curl_memory.h"
 #include "memdebug.h"
 
@@ -79,7 +78,7 @@
 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 */
+  char dest[1]; /* destination of bundle, allocated to keep dest_len bytes */
 };
 
 
@@ -91,13 +90,13 @@ static void cpool_discard_conn(struct cpool *cpool,
 static struct cpool_bundle *cpool_bundle_create(const char *dest)
 {
   struct cpool_bundle *bundle;
-  size_t dest_len = strlen(dest);
+  size_t dest_len = strlen(dest) + 1;
 
-  bundle = calloc(1, sizeof(*bundle) + dest_len);
+  bundle = calloc(1, sizeof(*bundle) + dest_len - 1);
   if(!bundle)
     return NULL;
   Curl_llist_init(&bundle->conns, NULL);
-  bundle->dest_len = dest_len + 1;
+  bundle->dest_len = dest_len;
   memcpy(bundle->dest, dest, bundle->dest_len);
   return bundle;
 }
@@ -320,7 +319,7 @@ static struct connectdata *cpool_get_oldest_idle(struct cpool *cpool)
   struct connectdata *oldest_idle = NULL;
   struct cpool_bundle *bundle;
   struct curltime now;
-  timediff_t highscore =- 1;
+  timediff_t highscore = -1;
   timediff_t score;
 
   now = curlx_now();
@@ -532,12 +531,19 @@ static bool cpool_foreach(struct Curl_easy *data,
 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;
+  unsigned int maxconnects;
   struct connectdata *oldest_idle = NULL;
   struct cpool *cpool = cpool_get_instance(data);
   bool kept = TRUE;
 
+  if(!data->multi->maxconnects) {
+    unsigned int running = Curl_multi_xfers_running(data->multi);
+    maxconnects = (running <= UINT_MAX / 4) ? running * 4 : UINT_MAX;
+  }
+  else {
+    maxconnects = data->multi->maxconnects;
+  }
+
   conn->lastused = curlx_now(); /* it was used up until now */
   if(cpool && maxconnects) {
     /* may be called form a callback already under lock */
@@ -895,7 +901,7 @@ void Curl_cpool_print(struct cpool *cpool)
   if(!cpool)
     return;
 
-  fprintf(stderr, "=Bundle cache=\n");
+  curl_mfprintf(stderr, "=Bundle cache=\n");
 
   Curl_hash_start_iterate(cpool->dest2bundle, &iter);
 
@@ -906,15 +912,15 @@ void Curl_cpool_print(struct cpool *cpool)
 
     bundle = he->ptr;
 
-    fprintf(stderr, "%s -", he->key);
+    curl_mfprintf(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);
+      curl_mfprintf(stderr, " [%p %d]", (void *)conn, conn->refcount);
       curr = Curl_node_next(curr);
     }
-    fprintf(stderr, "\n");
+    curl_mfprintf(stderr, "\n");
 
     he = Curl_hash_next_element(&iter);
   }

+ 21 - 14
lib/connect.c

@@ -41,9 +41,6 @@
 #ifdef HAVE_NETDB_H
 #include <netdb.h>
 #endif
-#ifdef HAVE_FCNTL_H
-#include <fcntl.h>
-#endif
 #ifdef HAVE_ARPA_INET_H
 #include <arpa/inet.h>
 #endif
@@ -78,8 +75,7 @@
 #include "http_proxy.h"
 #include "socks.h"
 
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
+/* The last 2 #include files should be in this order */
 #include "curl_memory.h"
 #include "memdebug.h"
 
@@ -127,7 +123,7 @@ timediff_t Curl_timeleft(struct Curl_easy *data,
      before the connect timeout expires and we must acknowledge whichever
      timeout that is reached first. The total timeout is set per entire
      operation, while the connect timeout is set per connect. */
-  if(data->set.timeout <= 0 && !duringconnect)
+  if((!data->set.timeout || data->set.connect_only) && !duringconnect)
     return 0; /* no timeout in place or checked, return "no limit" */
 
   if(!nowp) {
@@ -135,9 +131,9 @@ timediff_t Curl_timeleft(struct Curl_easy *data,
     nowp = &now;
   }
 
-  if(data->set.timeout > 0) {
+  if(data->set.timeout) {
     timeleft_ms = data->set.timeout -
-                  curlx_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)
@@ -162,20 +158,21 @@ void Curl_shutdown_start(struct Curl_easy *data, int sockindex,
                          int timeout_ms, struct curltime *nowp)
 {
   struct curltime now;
+  struct connectdata *conn = data->conn;
 
-  DEBUGASSERT(data->conn);
+  DEBUGASSERT(conn);
   if(!nowp) {
     now = curlx_now();
     nowp = &now;
   }
-  data->conn->shutdown.start[sockindex] = *nowp;
-  data->conn->shutdown.timeout_ms = (timeout_ms > 0) ?
+  conn->shutdown.start[sockindex] = *nowp;
+  conn->shutdown.timeout_ms = (timeout_ms > 0) ?
     (timediff_t)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 > 0))
-    Curl_expire_ex(data, nowp, data->conn->shutdown.timeout_ms,
+  if(data->mid)
+    Curl_expire_ex(data, nowp, conn->shutdown.timeout_ms,
                    EXPIRE_SHUTDOWN);
 }
 
@@ -270,7 +267,7 @@ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
     case AF_UNIX:
       if(salen > (curl_socklen_t)sizeof(CURL_SA_FAMILY_T)) {
         su = (struct sockaddr_un*)sa;
-        msnprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path);
+        curl_msnprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path);
       }
       else
         addr[0] = 0; /* socket with no name */
@@ -621,3 +618,13 @@ out:
     Curl_resolv_unlink(data, &data->state.dns[sockindex]);
   return result;
 }
+
+void Curl_conn_set_multiplex(struct connectdata *conn)
+{
+  if(!conn->bits.multiplex) {
+    conn->bits.multiplex = TRUE;
+    if(conn->attached_multi) {
+      Curl_multi_connchanged(conn->attached_multi);
+    }
+  }
+}

+ 3 - 0
lib/connect.h

@@ -123,6 +123,9 @@ CURLcode Curl_conn_setup(struct Curl_easy *data,
                          struct Curl_dns_entry *dns,
                          int ssl_mode);
 
+/* Set conn to allow multiplexing. */
+void Curl_conn_set_multiplex(struct connectdata *conn);
+
 extern struct Curl_cftype Curl_cft_setup;
 
 #endif /* HEADER_CURL_CONNECT_H */

+ 1 - 3
lib/content_encoding.c

@@ -51,10 +51,8 @@
 #include "sendf.h"
 #include "http.h"
 #include "content_encoding.h"
-#include "strdup.h"
 
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
+/* The last 2 #include files should be in this order */
 #include "curl_memory.h"
 #include "memdebug.h"
 

+ 40 - 29
lib/cookie.c

@@ -80,17 +80,16 @@ Example set of cookies:
 #include "slist.h"
 #include "share.h"
 #include "strcase.h"
+#include "curl_fopen.h"
 #include "curl_get_line.h"
 #include "curl_memrchr.h"
 #include "parsedate.h"
 #include "rename.h"
-#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"
+/* The last 2 #include files should be in this order */
 #include "curl_memory.h"
 #include "memdebug.h"
 
@@ -751,7 +750,6 @@ parse_cookie_header(struct Curl_easy *data,
   if(!co->name)
     return CERR_BAD;
 
-  data->req.setcookies++;
   return CERR_OK;
 }
 
@@ -1141,6 +1139,9 @@ Curl_cookie_add(struct Curl_easy *data,
   if(co->expires && (co->expires < ci->next_expiration))
     ci->next_expiration = co->expires;
 
+  if(httpheader)
+    data->req.setcookies++;
+
   return co;
 fail:
   freecookie(co);
@@ -1195,7 +1196,7 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
       if(!strcmp(file, "-"))
         fp = stdin;
       else {
-        fp = fopen(file, "rb");
+        fp = curlx_fopen(file, "rb");
         if(!fp)
           infof(data, "WARNING: failed to open cookie file \"%s\"", file);
         else
@@ -1206,19 +1207,27 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
     ci->running = FALSE; /* this is not running, this is init */
     if(fp) {
       struct dynbuf buf;
+      bool eof = FALSE;
+      CURLcode result;
       curlx_dyn_init(&buf, MAX_COOKIE_LINE);
-      while(Curl_get_line(&buf, fp)) {
-        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;
-          curlx_str_passblanks(&lineptr);
-        }
+      do {
+        result = Curl_get_line(&buf, fp, &eof);
+        if(!result) {
+          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;
+            curlx_str_passblanks(&lineptr);
+          }
 
-        Curl_cookie_add(data, ci, headerline, TRUE, lineptr, NULL, NULL, TRUE);
-      }
+          (void)Curl_cookie_add(data, ci, headerline, TRUE, lineptr, NULL,
+                                NULL, TRUE);
+          /* File reading cookie failures are not propagated back to the
+             caller because there is no way to do that */
+        }
+      } while(!result && !eof);
       curlx_dyn_free(&buf); /* free the line buffer */
 
       /*
@@ -1228,7 +1237,7 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
       remove_expired(ci);
 
       if(handle)
-        fclose(handle);
+        curlx_fclose(handle);
     }
     data->state.cookie_engine = TRUE;
   }
@@ -1479,7 +1488,7 @@ void Curl_cookie_cleanup(struct CookieInfo *ci)
  */
 static char *get_netscape_format(const struct Cookie *co)
 {
-  return aprintf(
+  return curl_maprintf(
     "%s"     /* httponly preamble */
     "%s%s\t" /* domain */
     "%s\t"   /* tailmatch */
@@ -1575,7 +1584,7 @@ static CURLcode cookie_output(struct Curl_easy *data,
         error = CURLE_OUT_OF_MEMORY;
         goto error;
       }
-      fprintf(out, "%s\n", format_ptr);
+      curl_mfprintf(out, "%s\n", format_ptr);
       free(format_ptr);
     }
 
@@ -1583,10 +1592,9 @@ static CURLcode cookie_output(struct Curl_easy *data,
   }
 
   if(!use_stdout) {
-    fclose(out);
+    curlx_fclose(out);
     out = NULL;
     if(tempstore && Curl_rename(tempstore, filename)) {
-      unlink(tempstore);
       error = CURLE_WRITE_ERROR;
       goto error;
     }
@@ -1602,8 +1610,11 @@ static CURLcode cookie_output(struct Curl_easy *data,
 
 error:
   if(out && !use_stdout)
-    fclose(out);
-  free(tempstore);
+    curlx_fclose(out);
+  if(tempstore) {
+    unlink(tempstore);
+    free(tempstore);
+  }
   return error;
 }
 
@@ -1658,18 +1669,18 @@ void Curl_flush_cookies(struct Curl_easy *data, bool cleanup)
 {
   CURLcode res;
 
-  if(data->set.str[STRING_COOKIEJAR]) {
-    Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
-
+  Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
+  /* only save the cookie file if a transfer was started (data->state.url is
+     set), as otherwise the cookies were not completely initialized and there
+     might be cookie files that weren't loaded so saving the file is the wrong
+     thing. */
+  if(data->set.str[STRING_COOKIEJAR] && data->state.url) {
     /* if we have a destination file for all the cookies to get dumped to */
     res = cookie_output(data, data->cookies, data->set.str[STRING_COOKIEJAR]);
     if(res)
       infof(data, "WARNING: failed to save cookies in %s: %s",
             data->set.str[STRING_COOKIEJAR], curl_easy_strerror(res));
   }
-  else {
-    Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
-  }
 
   if(cleanup && (!data->share || (data->cookies != data->share->cookies))) {
     Curl_cookie_cleanup(data->cookies);

+ 1 - 2
lib/cshutdn.c

@@ -42,8 +42,7 @@
 #include "select.h"
 #include "curlx/strparse.h"
 
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
+/* The last 2 #include files should be in this order */
 #include "curl_memory.h"
 #include "memdebug.h"
 

+ 6 - 2
lib/curl_addrinfo.c

@@ -53,8 +53,8 @@
 #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"
+
+/* The last 2 #include files should be in this order */
 #include "curl_memory.h"
 #include "memdebug.h"
 
@@ -507,9 +507,11 @@ curl_dbg_freeaddrinfo(struct addrinfo *freethis,
     if(env)
       r_freeaddrinfo(freethis);
     else
+      /* !checksrc! disable BANNEDFUNC 1 */
       freeaddrinfo(freethis);
   }
 #else
+  /* !checksrc! disable BANNEDFUNC 1 */
   freeaddrinfo(freethis);
 #endif
 }
@@ -540,8 +542,10 @@ curl_dbg_getaddrinfo(const char *hostname,
   if(env)
     res = r_getaddrinfo(hostname, service, hints, result);
   else
+    /* !checksrc! disable BANNEDFUNC 1 */
     res = getaddrinfo(hostname, service, hints, result);
 #else
+  /* !checksrc! disable BANNEDFUNC 1 */
   int res = getaddrinfo(hostname, service, hints, result);
 #endif
   if(res == 0)

+ 15 - 15
lib/curl_config.h.cmake

@@ -148,7 +148,7 @@
 /* disables SMTP */
 #cmakedefine CURL_DISABLE_SMTP 1
 
-/* disabled WebSockets */
+/* disabled WebSocket */
 #cmakedefine CURL_DISABLE_WEBSOCKETS 1
 
 /* disables use of socketpair for curl_multi_poll */
@@ -315,15 +315,12 @@
 /* if you have the gssapi libraries */
 #cmakedefine HAVE_GSSAPI 1
 
-/* Define to 1 if you have the <gssapi/gssapi_generic.h> header file. */
-#cmakedefine HAVE_GSSAPI_GSSAPI_GENERIC_H 1
-
-/* Define to 1 if you have the <gssapi/gssapi.h> header file. */
-#cmakedefine HAVE_GSSAPI_GSSAPI_H 1
-
 /* if you have the GNU gssapi libraries */
 #cmakedefine HAVE_GSSGNU 1
 
+/* MIT Kerberos version */
+#cmakedefine CURL_KRB5_VERSION ${CURL_KRB5_VERSION}
+
 /* Define to 1 if you have the <ifaddrs.h> header file. */
 #cmakedefine HAVE_IFADDRS_H 1
 
@@ -421,9 +418,6 @@
 /* Define to 1 if you have the <net/if.h> header file. */
 #cmakedefine HAVE_NET_IF_H 1
 
-/* if you have an old MIT gssapi library, lacking GSS_C_NT_HOSTBASED_SERVICE */
-#cmakedefine HAVE_OLD_GSSMIT 1
-
 /* Define to 1 if you have the `pipe' function. */
 #cmakedefine HAVE_PIPE 1
 
@@ -679,6 +673,9 @@ ${SIZEOF_TIME_T_CODE}
 /* if mbedTLS is enabled */
 #cmakedefine USE_MBEDTLS 1
 
+/* if mbedTLS <4 has the mbedtls_des_crypt_ecb function. */
+#cmakedefine HAVE_MBEDTLS_DES_CRYPT_ECB 1
+
 /* if Rustls is enabled */
 #cmakedefine USE_RUSTLS 1
 
@@ -706,9 +703,6 @@ ${SIZEOF_TIME_T_CODE}
 /* if libssh2 is in use */
 #cmakedefine USE_LIBSSH2 1
 
-/* if wolfssh is in use */
-#cmakedefine USE_WOLFSSH 1
-
 /* if libpsl is in use */
 #cmakedefine USE_LIBPSL 1
 
@@ -791,6 +785,9 @@ ${SIZEOF_TIME_T_CODE}
 /* to enable Apple IDN */
 #cmakedefine USE_APPLE_IDN 1
 
+/* to enable Apple OS-native certificate verification */
+#cmakedefine USE_APPLE_SECTRUST 1
+
 /* Define to 1 if OpenSSL has the SSL_CTX_set_srp_username function. */
 #cmakedefine HAVE_OPENSSL_SRP 1
 
@@ -807,7 +804,10 @@ ${SIZEOF_TIME_T_CODE}
 #cmakedefine USE_ECH 1
 
 /* Define to 1 if you have the wolfSSL_CTX_GenerateEchConfig function. */
-#cmakedefine HAVE_WOLFSSL_CTX_GENERATEECHCONFIG
+#cmakedefine HAVE_WOLFSSL_CTX_GENERATEECHCONFIG 1
 
 /* Define to 1 if you have the SSL_set1_ech_config_list function. */
-#cmakedefine HAVE_SSL_SET1_ECH_CONFIG_LIST
+#cmakedefine HAVE_SSL_SET1_ECH_CONFIG_LIST 1
+
+/* Define to 1 if OpenSSL has the DES_ecb_encrypt function. */
+#cmakedefine HAVE_DES_ECB_ENCRYPT 1

+ 0 - 68
lib/curl_des.c

@@ -1,68 +0,0 @@
-/***************************************************************************
- *                                  _   _ ____  _
- *  Project                     ___| | | |  _ \| |
- *                             / __| | | | |_) | |
- *                            | (__| |_| |  _ <| |___
- *                             \___|\___/|_| \_\_____|
- *
- * Copyright (C) Steve Holme, <[email protected]>.
- *
- * 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_CURL_NTLM_CORE) && \
-  (defined(USE_GNUTLS) ||          \
-   defined(USE_OS400CRYPTO) ||     \
-   defined(USE_WIN32_CRYPTO))
-
-#include "curl_des.h"
-
-/*
- * Curl_des_set_odd_parity()
- *
- * This is used to apply odd parity to the given byte array. It is typically
- * used by when a cryptography engine does not have its own version.
- *
- * The function is a port of the Java based oddParity() function over at:
- *
- * https://davenport.sourceforge.net/ntlm.html
- *
- * Parameters:
- *
- * bytes       [in/out] - The data whose parity bits are to be adjusted for
- *                        odd parity.
- * len         [out]    - The length of the data.
- */
-void Curl_des_set_odd_parity(unsigned char *bytes, size_t len)
-{
-  size_t i;
-
-  for(i = 0; i < len; i++) {
-    unsigned char b = bytes[i];
-
-    bool needs_parity = (((b >> 7) ^ (b >> 6) ^ (b >> 5) ^
-                          (b >> 4) ^ (b >> 3) ^ (b >> 2) ^
-                          (b >> 1)) & 0x01) == 0;
-
-    if(needs_parity)
-      bytes[i] |= 0x01;
-    else
-      bytes[i] &= 0xfe;
-  }
-}
-
-#endif

+ 12 - 13
lib/fopen.c → lib/curl_fopen.c

@@ -27,15 +27,11 @@
 #if !defined(CURL_DISABLE_COOKIES) || !defined(CURL_DISABLE_ALTSVC) ||  \
   !defined(CURL_DISABLE_HSTS)
 
-#ifdef HAVE_FCNTL_H
-#include <fcntl.h>
-#endif
-
 #include "urldata.h"
 #include "rand.h"
-#include "fopen.h"
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
+#include "curl_fopen.h"
+
+/* The last 2 #include files should be in this order */
 #include "curl_memory.h"
 #include "memdebug.h"
 
@@ -102,11 +98,12 @@ CURLcode Curl_fopen(struct Curl_easy *data, const char *filename,
   char *dir = NULL;
   *tempname = NULL;
 
-  *fh = fopen(filename, FOPEN_WRITETEXT);
+  *fh = curlx_fopen(filename, FOPEN_WRITETEXT);
   if(!*fh)
     goto fail;
   if(
 #ifdef UNDER_CE
+     /* !checksrc! disable BANNEDFUNC 1 */
      stat(filename, &sb) == -1
 #else
      fstat(fileno(*fh), &sb) == -1
@@ -114,7 +111,7 @@ CURLcode Curl_fopen(struct Curl_easy *data, const char *filename,
      || !S_ISREG(sb.st_mode)) {
     return CURLE_OK;
   }
-  fclose(*fh);
+  curlx_fclose(*fh);
   *fh = NULL;
 
   result = Curl_rand_alnum(data, randbuf, sizeof(randbuf));
@@ -125,7 +122,7 @@ CURLcode Curl_fopen(struct Curl_easy *data, const char *filename,
   if(dir) {
     /* The temp filename should not end up too long for the target file
        system */
-    tempstore = aprintf("%s%s.tmp", dir, randbuf);
+    tempstore = curl_maprintf("%s%s.tmp", dir, randbuf);
     free(dir);
   }
 
@@ -137,14 +134,16 @@ CURLcode Curl_fopen(struct Curl_easy *data, const char *filename,
   result = CURLE_WRITE_ERROR;
 #if (defined(ANDROID) || defined(__ANDROID__)) && \
   (defined(__i386__) || defined(__arm__))
-  fd = open(tempstore, O_WRONLY | O_CREAT | O_EXCL, (mode_t)(0600|sb.st_mode));
+  fd = curlx_open(tempstore, O_WRONLY | O_CREAT | O_EXCL,
+                  (mode_t)(0600 | sb.st_mode));
 #else
-  fd = open(tempstore, O_WRONLY | O_CREAT | O_EXCL, 0600|sb.st_mode);
+  fd = curlx_open(tempstore, O_WRONLY | O_CREAT | O_EXCL,
+                  0600 | sb.st_mode);
 #endif
   if(fd == -1)
     goto fail;
 
-  *fh = fdopen(fd, FOPEN_WRITETEXT);
+  *fh = curlx_fdopen(fd, FOPEN_WRITETEXT);
   if(!*fh)
     goto fail;
 

+ 2 - 0
lib/fopen.h → lib/curl_fopen.h

@@ -24,6 +24,8 @@
  *
  ***************************************************************************/
 
+#include "curlx/fopen.h"
+
 CURLcode Curl_fopen(struct Curl_easy *data, const char *filename,
                     FILE **fh, char **tempname);
 

+ 21 - 41
lib/curl_get_line.c

@@ -32,63 +32,43 @@
 /* The last #include file should be: */
 #include "memdebug.h"
 
-static int appendnl(struct dynbuf *buf)
-{
-  CURLcode result = curlx_dyn_addn(buf, "\n", 1);
-  if(result)
-    /* too long line or out of memory */
-    return 0; /* error */
-  return 1; /* all good */
-}
+#define appendnl(b)                             \
+  curlx_dyn_addn(buf, "\n", 1)
 
 /*
- * Curl_get_line() makes sure to only return complete whole lines that end
- * newlines.
+ * Curl_get_line() returns only complete whole lines that end with newline.
+ * When 'eof' is set TRUE, the last line has been read.
  */
-int Curl_get_line(struct dynbuf *buf, FILE *input)
+CURLcode Curl_get_line(struct dynbuf *buf, FILE *input, bool *eof)
 {
   CURLcode result;
   char buffer[128];
   curlx_dyn_reset(buf);
   while(1) {
-    char *b = fgets(buffer, sizeof(buffer), input);
     size_t rlen;
+    char *b = fgets(buffer, sizeof(buffer), input);
 
-    if(b) {
-      rlen = strlen(b);
-
-      if(!rlen)
-        break;
+    *eof = feof(input);
 
+    rlen = b ? strlen(b) : 0;
+    if(rlen) {
       result = curlx_dyn_addn(buf, b, rlen);
       if(result)
         /* too long line or out of memory */
-        return 0; /* error */
-
-      else if(b[rlen-1] == '\n')
-        /* end of the line */
-        return 1; /* all good */
-
-      else if(feof(input))
-        /* append a newline */
-        return appendnl(buf);
-    }
-    else {
-      rlen = curlx_dyn_len(buf);
-      if(rlen) {
-        b = curlx_dyn_ptr(buf);
-
-        if(b[rlen-1] != '\n')
-          /* append a newline */
-          return appendnl(buf);
-
-        return 1; /* all good */
-      }
-      else
-        break;
+        return result;
     }
+    /* now check the full line */
+    rlen = curlx_dyn_len(buf);
+    b = curlx_dyn_ptr(buf);
+    if(rlen && (b[rlen-1] == '\n'))
+      /* LF at end of the line */
+      return CURLE_OK; /* all good */
+    if(*eof)
+      /* append a newline */
+      return appendnl(buf);
+    /* otherwise get next line to append */
   }
-  return 0;
+  /* UNREACHABLE */
 }
 
 #endif /* if not disabled */

+ 1 - 1
lib/curl_get_line.h

@@ -27,6 +27,6 @@
 #include "curlx/dynbuf.h"
 
 /* Curl_get_line() returns complete lines that end with a newline. */
-int Curl_get_line(struct dynbuf *buf, FILE *input);
+CURLcode Curl_get_line(struct dynbuf *buf, FILE *input, bool *eof);
 
 #endif /* HEADER_CURL_GET_LINE_H */

+ 41 - 21
lib/curl_gssapi.c

@@ -29,8 +29,29 @@
 #include "curl_gssapi.h"
 #include "sendf.h"
 
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
+#ifdef DEBUGBUILD
+#if defined(HAVE_GSSGNU) || !defined(_WIN32)
+/* To avoid memdebug macro replacement, wrap the name in parentheses to call
+   the original version. It is freed via the GSS API gss_release_buffer(). */
+#define Curl_gss_alloc (malloc)
+#define Curl_gss_free  (free)
+#define CURL_GSS_STUB
+/* For correctness this would be required for all platforms, not only Windows,
+   but, as of v1.22.1, MIT Kerberos uses a special allocator only for Windows,
+   and the availability of 'gssapi/gssapi_alloc.h' is difficult to detect,
+   because GSS headers are not versioned, and there is also no other macro to
+   indicate 1.18+ vs. previous versions. On Windows we can use 'GSS_S_BAD_MIC'.
+ */
+#elif defined(_WIN32) && defined(GSS_S_BAD_MIC) /* MIT Kerberos 1.15+ */
+/* MIT Kerberos 1.10+ (Windows), 1.18+ (all platforms), missing from GNU GSS */
+#include <gssapi/gssapi_alloc.h>
+#define Curl_gss_alloc gssalloc_malloc
+#define Curl_gss_free  gssalloc_free
+#define CURL_GSS_STUB
+#endif
+#endif /* DEBUGBUILD */
+
+/* The last 2 #include files should be in this order */
 #include "curl_memory.h"
 #include "memdebug.h"
 
@@ -52,7 +73,7 @@ gss_OID_desc Curl_krb5_mech_oid CURL_ALIGN8 = {
   9, CURL_UNCONST("\x2a\x86\x48\x86\xf7\x12\x01\x02\x02")
 };
 
-#ifdef DEBUGBUILD
+#ifdef CURL_GSS_STUB
 enum min_err_code {
   STUB_GSS_OK = 0,
   STUB_GSS_NO_MEMORY,
@@ -213,9 +234,7 @@ stub_gss_init_sec_context(OM_uint32 *min,
     ctx->flags = req_flags;
   }
 
-  /* To avoid memdebug macro replacement, wrap the name in parentheses to call
-     the original version. It is freed via the GSS API gss_release_buffer(). */
-  token = (malloc)(length);
+  token = Curl_gss_alloc(length);
   if(!token) {
     free(ctx);
     *min = STUB_GSS_NO_MEMORY;
@@ -230,29 +249,30 @@ stub_gss_init_sec_context(OM_uint32 *min,
     major_status = gss_display_name(&minor_status, target_name,
                                     &target_desc, &name_type);
     if(GSS_ERROR(major_status)) {
-      (free)(token);
+      Curl_gss_free(token);
       free(ctx);
       *min = STUB_GSS_NO_MEMORY;
       return GSS_S_FAILURE;
     }
 
     if(strlen(creds) + target_desc.length + 5 >= sizeof(ctx->creds)) {
-      (free)(token);
+      Curl_gss_free(token);
       free(ctx);
       *min = STUB_GSS_NO_MEMORY;
       return GSS_S_FAILURE;
     }
 
     /* Token format: creds:target:type:padding */
-    used = msnprintf(token, length, "%s:%.*s:%d:", creds,
-                     (int)target_desc.length, (const char *)target_desc.value,
-                     ctx->sent);
+    used = curl_msnprintf(token, length, "%s:%.*s:%d:", creds,
+                          (int)target_desc.length,
+                          (const char *)target_desc.value,
+                          ctx->sent);
 
     gss_release_buffer(&minor_status, &target_desc);
   }
 
   if(used >= length) {
-    (free)(token);
+    Curl_gss_free(token);
     free(ctx);
     *min = STUB_GSS_NO_MEMORY;
     return GSS_S_FAILURE;
@@ -294,7 +314,7 @@ stub_gss_delete_sec_context(OM_uint32 *min,
 
   return GSS_S_COMPLETE;
 }
-#endif /* DEBUGBUILD */
+#endif /* CURL_GSS_STUB */
 
 OM_uint32 Curl_gss_init_sec_context(struct Curl_easy *data,
                                     OM_uint32 *minor_status,
@@ -313,7 +333,7 @@ OM_uint32 Curl_gss_init_sec_context(struct Curl_easy *data,
     req_flags |= GSS_C_MUTUAL_FLAG;
 
   if(data->set.gssapi_delegation & CURLGSSAPI_DELEGATION_POLICY_FLAG) {
-#ifdef GSS_C_DELEG_POLICY_FLAG
+#ifdef GSS_C_DELEG_POLICY_FLAG  /* MIT Kerberos 1.8+, missing from GNU GSS */
     req_flags |= GSS_C_DELEG_POLICY_FLAG;
 #else
     infof(data, "WARNING: support for CURLGSSAPI_DELEGATION_POLICY_FLAG not "
@@ -324,7 +344,7 @@ OM_uint32 Curl_gss_init_sec_context(struct Curl_easy *data,
   if(data->set.gssapi_delegation & CURLGSSAPI_DELEGATION_FLAG)
     req_flags |= GSS_C_DELEG_FLAG;
 
-#ifdef DEBUGBUILD
+#ifdef CURL_GSS_STUB
   if(getenv("CURL_STUB_GSS_CREDS"))
     return stub_gss_init_sec_context(minor_status,
                                      GSS_C_NO_CREDENTIAL, /* cred_handle */
@@ -339,7 +359,7 @@ OM_uint32 Curl_gss_init_sec_context(struct Curl_easy *data,
                                      output_token,
                                      ret_flags,
                                      NULL /* time_rec */);
-#endif /* DEBUGBUILD */
+#endif /* CURL_GSS_STUB */
 
   return gss_init_sec_context(minor_status,
                               GSS_C_NO_CREDENTIAL, /* cred_handle */
@@ -360,12 +380,12 @@ OM_uint32 Curl_gss_delete_sec_context(OM_uint32 *min,
                                       gss_ctx_id_t *context,
                                       gss_buffer_t output_token)
 {
-#ifdef DEBUGBUILD
+#ifdef CURL_GSS_STUB
   if(getenv("CURL_STUB_GSS_CREDS"))
     return stub_gss_delete_sec_context(min,
                                      (struct stub_gss_ctx_id_t_desc **)context,
                                      output_token);
-#endif /* DEBUGBUILD */
+#endif /* CURL_GSS_STUB */
 
   return gss_delete_sec_context(min, context, output_token);
 }
@@ -387,9 +407,9 @@ static size_t display_gss_error(OM_uint32 status, int type,
                                   &status_string);
     if(maj_stat == GSS_S_COMPLETE && status_string.length > 0) {
       if(GSS_LOG_BUFFER_LEN > len + status_string.length + 3) {
-        len += msnprintf(buf + len, GSS_LOG_BUFFER_LEN - len,
-                         "%.*s. ", (int)status_string.length,
-                         (char *)status_string.value);
+        len += curl_msnprintf(buf + len, GSS_LOG_BUFFER_LEN - len,
+                              "%.*s. ", (int)status_string.length,
+                              (char *)status_string.value);
       }
     }
     gss_release_buffer(&min_stat, &status_string);

+ 5 - 6
lib/curl_gssapi.h

@@ -28,6 +28,11 @@
 #include "urldata.h"
 
 #ifdef HAVE_GSSAPI
+
+#ifdef GSS_C_CHANNEL_BOUND_FLAG  /* MIT Kerberos 1.19+, missing from GNU GSS */
+#define CURL_GSSAPI_HAS_CHANNEL_BINDING
+#endif
+
 extern gss_OID_desc Curl_spnego_mech_oid;
 extern gss_OID_desc Curl_krb5_mech_oid;
 
@@ -51,12 +56,6 @@ OM_uint32 Curl_gss_delete_sec_context(OM_uint32 *min,
 void Curl_gss_log_error(struct Curl_easy *data, const char *prefix,
                         OM_uint32 major, OM_uint32 minor);
 
-/* Provide some definitions missing in old headers */
-#ifdef HAVE_OLD_GSSMIT
-#define GSS_C_NT_HOSTBASED_SERVICE gss_nt_service_name
-#define NCOMPAT 1
-#endif
-
 /* Define our privacy and integrity protection values */
 #define GSSAUTH_P_NONE      1
 #define GSSAUTH_P_INTEGRITY 2

+ 1 - 19
lib/curl_mem_undef.h

@@ -29,27 +29,9 @@
 #undef calloc
 #undef realloc
 #undef free
-#undef send
-#undef recv
-
 #ifdef _WIN32
-#undef _tcsdup
-#endif
-
-#undef socket
-#ifdef HAVE_ACCEPT4
-#undef accept4
-#endif
-#ifdef HAVE_SOCKETPAIR
-#undef socketpair
-#endif
-
-#undef fopen
-#ifdef CURL_FOPEN
-#define fopen(fname, mode) CURL_FOPEN(fname, mode)
+#undef Curl_tcsdup
 #endif
-#undef fdopen
-#undef fclose
 
 #undef HEADER_CURL_MEMORY_H
 #undef HEADER_CURL_MEMDEBUG_H

+ 3 - 3
lib/curl_memory.h

@@ -77,11 +77,11 @@
 #define free(ptr) Curl_cfree(ptr)
 
 #ifdef _WIN32
-#undef _tcsdup
+#undef Curl_tcsdup
 #ifdef UNICODE
-#define _tcsdup(ptr) Curl_wcsdup(ptr)
+#define Curl_tcsdup(ptr) Curl_wcsdup(ptr)
 #else
-#define _tcsdup(ptr) Curl_cstrdup(ptr)
+#define Curl_tcsdup(ptr) Curl_cstrdup(ptr)
 #endif
 #endif /* _WIN32 */
 

+ 76 - 39
lib/curl_ntlm_core.c

@@ -38,10 +38,9 @@
    1. USE_OPENSSL
    2. USE_WOLFSSL
    3. USE_GNUTLS
-   4. -
-   5. USE_MBEDTLS
-   6. USE_OS400CRYPTO
-   7. USE_WIN32_CRYPTO
+   4. USE_MBEDTLS
+   5. USE_OS400CRYPTO
+   6. USE_WIN32_CRYPTO
 
    This ensures that:
    - the same SSL branch gets activated throughout this source
@@ -51,23 +50,25 @@
      in NTLM type-3 messages.
  */
 
-#ifdef USE_OPENSSL
-  #include <openssl/opensslconf.h>
-  #if !defined(OPENSSL_NO_DES) && !defined(OPENSSL_NO_DEPRECATED_3_0)
-    #define USE_OPENSSL_DES
-  #endif
-#elif defined(USE_WOLFSSL)
-  #include <wolfssl/options.h>
-  #ifndef NO_DES3
-    #define USE_OPENSSL_DES
-  #endif
+#ifdef USE_MBEDTLS
+#include <mbedtls/version.h>
+#if MBEDTLS_VERSION_NUMBER < 0x03020000
+  #error "mbedTLS 3.2.0 or later required"
+#endif
+#endif
+
+#if defined(USE_OPENSSL) && defined(HAVE_DES_ECB_ENCRYPT)
+  #define USE_OPENSSL_DES
+#elif defined(USE_WOLFSSL) && defined(HAVE_WOLFSSL_DES_ECB_ENCRYPT)
+  #define USE_OPENSSL_DES
+#elif defined(USE_MBEDTLS) && defined(HAVE_MBEDTLS_DES_CRYPT_ECB)
+  #define USE_MBEDTLS_DES
 #endif
 
 #ifdef USE_OPENSSL_DES
 
 #ifdef USE_OPENSSL
 #  include <openssl/des.h>
-#  include <openssl/md5.h>
 #  include <openssl/ssl.h>
 #  include <openssl/rand.h>
 #  ifdef OPENSSL_IS_AWSLC  /* for versions 1.2.0 to 1.30.1 */
@@ -75,8 +76,8 @@
 #  endif
 #  define DESKEY(x) &x
 #else
+#  include <wolfssl/options.h>
 #  include <wolfssl/openssl/des.h>
-#  include <wolfssl/openssl/md5.h>
 #  include <wolfssl/openssl/ssl.h>
 #  include <wolfssl/openssl/rand.h>
 #  ifdef OPENSSL_COEXIST
@@ -96,18 +97,20 @@
 #elif defined(USE_GNUTLS)
 
 #  include <nettle/des.h>
+#  define USE_CURL_DES_SET_ODD_PARITY
 
-#elif defined(USE_MBEDTLS)
+#elif defined(USE_MBEDTLS_DES)
 
 #  include <mbedtls/des.h>
 
 #elif defined(USE_OS400CRYPTO)
 #  include "cipher.mih"  /* mih/cipher */
+#  define USE_CURL_DES_SET_ODD_PARITY
 #elif defined(USE_WIN32_CRYPTO)
 #  include <wincrypt.h>
+#  define USE_CURL_DES_SET_ODD_PARITY
 #else
 #  error "cannot compile NTLM support without a crypto library with DES."
-#  define CURL_NTLM_NOT_SUPPORTED
 #endif
 
 #include "urldata.h"
@@ -117,14 +120,50 @@
 #include "curl_hmac.h"
 #include "curlx/warnless.h"
 #include "curl_endian.h"
-#include "curl_des.h"
 #include "curl_md4.h"
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
+
+/* The last 2 #include files should be in this order */
 #include "curl_memory.h"
 #include "memdebug.h"
 
-#ifndef CURL_NTLM_NOT_SUPPORTED
+#ifdef USE_CURL_DES_SET_ODD_PARITY
+/*
+ * curl_des_set_odd_parity()
+ *
+ * Copyright (C) Steve Holme, <[email protected]>
+ *
+ * This is used to apply odd parity to the given byte array. It is typically
+ * used by when a cryptography engine does not have its own version.
+ *
+ * The function is a port of the Java based oddParity() function over at:
+ *
+ * https://davenport.sourceforge.net/ntlm.html
+ *
+ * Parameters:
+ *
+ * bytes       [in/out] - The data whose parity bits are to be adjusted for
+ *                        odd parity.
+ * len         [out]    - The length of the data.
+ */
+static void curl_des_set_odd_parity(unsigned char *bytes, size_t len)
+{
+  size_t i;
+
+  for(i = 0; i < len; i++) {
+    unsigned char b = bytes[i];
+
+    bool needs_parity = (((b >> 7) ^ (b >> 6) ^ (b >> 5) ^
+                          (b >> 4) ^ (b >> 3) ^ (b >> 2) ^
+                          (b >> 1)) & 0x01) == 0;
+
+    if(needs_parity)
+      bytes[i] |= 0x01;
+    else
+      bytes[i] &= 0xfe;
+  }
+}
+#endif /* USE_CURL_DES_SET_ODD_PARITY */
+
 /*
 * Turns a 56-bit key into being 64-bit wide.
 */
@@ -139,7 +178,6 @@ static void extend_key_56_to_64(const unsigned char *key_56, char *key)
   key[6] = (char)(((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6));
   key[7] = (char) ((key_56[6] << 1) & 0xFF);
 }
-#endif
 
 #ifdef USE_OPENSSL_DES
 /*
@@ -172,13 +210,13 @@ static void setup_des_key(const unsigned char *key_56,
   extend_key_56_to_64(key_56, key);
 
   /* Set the key parity to odd */
-  Curl_des_set_odd_parity((unsigned char *) key, sizeof(key));
+  curl_des_set_odd_parity((unsigned char *) key, sizeof(key));
 
   /* Set the key */
   des_set_key(des, (const uint8_t *) key);
 }
 
-#elif defined(USE_MBEDTLS)
+#elif defined(USE_MBEDTLS_DES)
 
 static bool encrypt_des(const unsigned char *in, unsigned char *out,
                         const unsigned char *key_56)
@@ -214,7 +252,7 @@ static bool encrypt_des(const unsigned char *in, unsigned char *out,
   extend_key_56_to_64(key_56, ctl.Crypto_Key);
 
   /* Set the key parity to odd */
-  Curl_des_set_odd_parity((unsigned char *) ctl.Crypto_Key, ctl.Data_Len);
+  curl_des_set_odd_parity((unsigned char *) ctl.Crypto_Key, ctl.Data_Len);
 
   /* Perform the encryption */
   _CIPHER((_SPCPTR *) &out, &ctl, (_SPCPTR *) &in);
@@ -252,7 +290,7 @@ static bool encrypt_des(const unsigned char *in, unsigned char *out,
   extend_key_56_to_64(key_56, blob.key);
 
   /* Set the key parity to odd */
-  Curl_des_set_odd_parity((unsigned char *) blob.key, sizeof(blob.key));
+  curl_des_set_odd_parity((unsigned char *) blob.key, sizeof(blob.key));
 
   /* Import the key */
   if(!CryptImportKey(hprov, (BYTE *) &blob, sizeof(blob), 0, 0, &hkey)) {
@@ -305,7 +343,7 @@ void Curl_ntlm_core_lm_resp(const unsigned char *keys,
   des_encrypt(&des, 8, results + 8, plaintext);
   setup_des_key(keys + 14, &des);
   des_encrypt(&des, 8, results + 16, plaintext);
-#elif defined(USE_MBEDTLS) || defined(USE_OS400CRYPTO) ||       \
+#elif defined(USE_MBEDTLS_DES) || defined(USE_OS400CRYPTO) ||   \
   defined(USE_WIN32_CRYPTO)
   encrypt_des(plaintext, results, keys);
   encrypt_des(plaintext, results + 8, keys + 7);
@@ -324,11 +362,9 @@ CURLcode Curl_ntlm_core_mk_lm_hash(const char *password,
                                    unsigned char *lmbuffer /* 21 bytes */)
 {
   unsigned char pw[14];
-#ifndef CURL_NTLM_NOT_SUPPORTED
   static const unsigned char magic[] = {
     0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25 /* i.e. KGS!@#$% */
   };
-#endif
   size_t len = CURLMIN(strlen(password), 14);
 
   Curl_strntoupper((char *)pw, password, len);
@@ -353,7 +389,7 @@ CURLcode Curl_ntlm_core_mk_lm_hash(const char *password,
     des_encrypt(&des, 8, lmbuffer, magic);
     setup_des_key(pw + 7, &des);
     des_encrypt(&des, 8, lmbuffer + 8, magic);
-#elif defined(USE_MBEDTLS) || defined(USE_OS400CRYPTO) ||       \
+#elif defined(USE_MBEDTLS_DES) || defined(USE_OS400CRYPTO) ||   \
   defined(USE_WIN32_CRYPTO)
     encrypt_des(magic, lmbuffer, pw);
     encrypt_des(magic, lmbuffer + 8, pw + 7);
@@ -567,14 +603,15 @@ CURLcode Curl_ntlm_core_mk_ntlmv2_resp(unsigned char *ntlmv2hash,
     return CURLE_OUT_OF_MEMORY;
 
   /* Create the BLOB structure */
-  msnprintf((char *)ptr + HMAC_MD5_LENGTH, NTLMv2_BLOB_LEN,
-            "%c%c%c%c"           /* NTLMv2_BLOB_SIGNATURE */
-            "%c%c%c%c"           /* Reserved = 0 */
-            "%c%c%c%c%c%c%c%c",  /* Timestamp */
-            NTLMv2_BLOB_SIGNATURE[0], NTLMv2_BLOB_SIGNATURE[1],
-            NTLMv2_BLOB_SIGNATURE[2], NTLMv2_BLOB_SIGNATURE[3],
-            0, 0, 0, 0,
-            LONGQUARTET(tw.dwLowDateTime), LONGQUARTET(tw.dwHighDateTime));
+  curl_msnprintf((char *)ptr + HMAC_MD5_LENGTH, NTLMv2_BLOB_LEN,
+                 "%c%c%c%c"           /* NTLMv2_BLOB_SIGNATURE */
+                 "%c%c%c%c"           /* Reserved = 0 */
+                 "%c%c%c%c%c%c%c%c",  /* Timestamp */
+                 NTLMv2_BLOB_SIGNATURE[0], NTLMv2_BLOB_SIGNATURE[1],
+                 NTLMv2_BLOB_SIGNATURE[2], NTLMv2_BLOB_SIGNATURE[3],
+                 0, 0, 0, 0,
+                 LONGQUARTET(tw.dwLowDateTime),
+                 LONGQUARTET(tw.dwHighDateTime));
 
   memcpy(ptr + 32, challenge_client, 8);
   if(ntlm->target_info_len)

+ 0 - 27
lib/curl_printf.h

@@ -24,8 +24,6 @@
  *
  ***************************************************************************/
 
-#include <curl/mprintf.h>
-
 #define MERR_OK        0
 #define MERR_MEM       1
 #define MERR_TOO_LARGE 2
@@ -36,29 +34,4 @@ 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
-# undef vprintf
-# undef vfprintf
-# undef mvsnprintf
-# undef aprintf
-# undef vaprintf
-# define printf curl_mprintf
-# define fprintf curl_mfprintf
-# define msnprintf curl_msnprintf
-# define vprintf curl_mvprintf
-# define vfprintf curl_mvfprintf
-# define mvsnprintf curl_mvsnprintf
-# define aprintf curl_maprintf
-# define vaprintf curl_mvaprintf
-
-#endif /* BUILDING_LIBCURL */
 #endif /* HEADER_CURL_PRINTF_H */

+ 4 - 5
lib/curl_rtmp.c

@@ -37,8 +37,7 @@
 #include <curl/curl.h>
 #include <librtmp/rtmp.h>
 
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
+/* The last 2 #include files should be in this order */
 #include "curl_memory.h"
 #include "memdebug.h"
 
@@ -386,9 +385,9 @@ void Curl_rtmp_version(char *version, size_t len)
   else
     suff[0] = '\0';
 
-  msnprintf(version, len, "librtmp/%d.%d%s",
-            RTMP_LIB_VERSION >> 16, (RTMP_LIB_VERSION >> 8) & 0xff,
-            suff);
+  curl_msnprintf(version, len, "librtmp/%d.%d%s",
+                 RTMP_LIB_VERSION >> 16, (RTMP_LIB_VERSION >> 8) & 0xff,
+                 suff);
 }
 
 #endif  /* USE_LIBRTMP */

+ 5 - 4
lib/curl_sasl.c

@@ -43,7 +43,6 @@
 #include "urldata.h"
 
 #include "curlx/base64.h"
-#include "curl_md5.h"
 #include "vauth/vauth.h"
 #include "cfilters.h"
 #include "vtls/vtls.h"
@@ -51,8 +50,8 @@
 #include "curl_sasl.h"
 #include "curlx/warnless.h"
 #include "sendf.h"
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
+
+/* The last 2 #include files should be in this order */
 #include "curl_memory.h"
 #include "memdebug.h"
 
@@ -812,7 +811,9 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
 
   case SASL_CANCEL:
     /* Remove the offending mechanism from the supported list */
-    sasl->authmechs ^= sasl->authused;
+    sasl->authmechs &= (unsigned short)~sasl->authused;
+    sasl->authused = SASL_AUTH_NONE;
+    sasl->curmech = NULL;
 
     /* Start an alternative SASL authentication */
     return Curl_sasl_start(sasl, data, sasl->force_ir, progress);

+ 32 - 16
lib/curl_setup.h

@@ -46,7 +46,7 @@
    fail. Fixed in 14.2.0_1. Disable the workaround if the fix is detected. */
 #if defined(__APPLE__) && !defined(__clang__) && defined(__GNUC__) && \
   defined(__has_attribute)
-#  if !defined(__has_feature)
+#  if !defined(__has_feature)  /* Keep this PP check separate from others */
 #    define availability curl_pp_attribute_disabled
 #  elif !__has_feature(attribute_availability)
 #    define availability curl_pp_attribute_disabled
@@ -506,15 +506,6 @@
 #    endif
 #    define LSEEK_ERROR                  (long)-1
 #  endif
-#  ifndef UNDER_CE
-     int curlx_win32_stat(const char *path, struct_stat *buffer);
-     int curlx_win32_open(const char *filename, int oflag, ...);
-     FILE *curlx_win32_fopen(const char *filename, const char *mode);
-#    define stat(fname, stp)           curlx_win32_stat(fname, stp)
-#    define open                       curlx_win32_open
-#    define CURL_FOPEN(fname, mode)    curlx_win32_fopen(fname, mode)
-#    define fopen(fname, mode)         CURL_FOPEN(fname, mode)
-#  endif
 #elif defined(__DJGPP__)
    /* Requires DJGPP 2.04 */
 #  include <unistd.h>
@@ -765,8 +756,9 @@
 
 /* Single point where USE_NTLM definition might be defined */
 #ifndef CURL_DISABLE_NTLM
-#  if defined(USE_OPENSSL) || defined(USE_MBEDTLS) ||                   \
+#  if (defined(USE_OPENSSL) && defined(HAVE_DES_ECB_ENCRYPT)) ||        \
   defined(USE_GNUTLS) ||                                                \
+  (defined(USE_MBEDTLS) && defined(HAVE_MBEDTLS_DES_CRYPT_ECB)) ||      \
   defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) ||              \
   (defined(USE_WOLFSSL) && defined(HAVE_WOLFSSL_DES_ECB_ENCRYPT))
 #    define USE_CURL_NTLM_CORE
@@ -776,7 +768,7 @@
 #  endif
 #endif
 
-#if defined(USE_LIBSSH2) || defined(USE_LIBSSH) || defined(USE_WOLFSSH)
+#if defined(USE_LIBSSH2) || defined(USE_LIBSSH)
 #define USE_SSH
 #endif
 
@@ -959,6 +951,10 @@ endings either CRLF or LF so 't' is appropriate.
 
 #define CURL_ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
 
+/* Buffer size for error messages retrieved via
+   curlx_strerror() and Curl_sspi_strerror() */
+#define STRERROR_LEN 256
+
 #ifndef CURL_DID_MEMORY_FUNC_TYPEDEFS /* only if not already done */
 /*
  * The following memory function replacement typedef's are COPIED from
@@ -988,6 +984,8 @@ extern curl_calloc_callback Curl_ccalloc;
 #define Curl_safefree(ptr) \
   do { free((ptr)); (ptr) = NULL;} while(0)
 
+#include <curl/curl.h> /* for CURL_EXTERN, mprintf.h */
+
 #ifdef CURLDEBUG
 #ifdef __clang__
 #  define ALLOC_FUNC         __attribute__((__malloc__))
@@ -1012,8 +1010,6 @@ extern curl_calloc_callback Curl_ccalloc;
 #  define ALLOC_SIZE2(n, s)
 #endif
 
-#include <curl/curl.h> /* for CURL_EXTERN */
-
 extern FILE *curl_dbg_logfile;
 
 /* memory functions */
@@ -1083,9 +1079,21 @@ CURL_EXTERN ALLOC_FUNC
   curl_dbg_getaddrinfo(host, serv, hint, res, __LINE__, __FILE__)
 #define CURL_FREEADDRINFO(data) \
   curl_dbg_freeaddrinfo(data, __LINE__, __FILE__)
-
+#define CURL_SOCKET(domain,type,protocol) \
+  curl_dbg_socket((int)domain, type, protocol, __LINE__, __FILE__)
+#ifdef HAVE_SOCKETPAIR
+#define CURL_SOCKETPAIR(domain,type,protocol,socket_vector) \
+  curl_dbg_socketpair((int)domain, type, protocol, socket_vector, \
+                      __LINE__, __FILE__)
+#endif
 #define CURL_ACCEPT(sock,addr,len) \
   curl_dbg_accept(sock, addr, len, __LINE__, __FILE__)
+#ifdef HAVE_ACCEPT4
+#define CURL_ACCEPT4(sock,addr,len,flags) \
+  curl_dbg_accept4(sock, addr, len, flags, __LINE__, __FILE__)
+#endif
+#define CURL_SEND(a,b,c,d) curl_dbg_send(a,b,c,d, __LINE__, __FILE__)
+#define CURL_RECV(a,b,c,d) curl_dbg_recv(a,b,c,d, __LINE__, __FILE__)
 
 #else /* !CURLDEBUG */
 
@@ -1094,8 +1102,16 @@ CURL_EXTERN ALLOC_FUNC
 
 #define CURL_GETADDRINFO getaddrinfo
 #define CURL_FREEADDRINFO freeaddrinfo
-
+#define CURL_SOCKET socket
+#ifdef HAVE_SOCKETPAIR
+#define CURL_SOCKETPAIR socketpair
+#endif
 #define CURL_ACCEPT accept
+#ifdef HAVE_ACCEPT4
+#define CURL_ACCEPT4 accept4
+#endif
+#define CURL_SEND send
+#define CURL_RECV recv
 
 #endif /* CURLDEBUG */
 

+ 2 - 2
lib/curl_sspi.c

@@ -138,7 +138,7 @@ CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp,
   }
 
   /* Setup the identity's user and length */
-  dup_user.tchar_ptr = _tcsdup(user.tchar_ptr);
+  dup_user.tchar_ptr = Curl_tcsdup(user.tchar_ptr);
   if(!dup_user.tchar_ptr) {
     curlx_unicodefree(useranddomain.tchar_ptr);
     return CURLE_OUT_OF_MEMORY;
@@ -165,7 +165,7 @@ CURLcode Curl_create_sspi_identity(const char *userp, const 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);
+  dup_passwd.tchar_ptr = Curl_tcsdup(passwd.tchar_ptr);
   if(!dup_passwd.tchar_ptr) {
     curlx_unicodefree(passwd.tchar_ptr);
     return CURLE_OUT_OF_MEMORY;

+ 10 - 61
lib/curl_threads.c

@@ -26,12 +26,8 @@
 
 #include <curl/curl.h>
 
-#ifdef USE_THREADS_POSIX
-#  ifdef HAVE_PTHREAD_H
-#    include <pthread.h>
-#  endif
-#elif defined(USE_THREADS_WIN32)
-#  include <process.h>
+#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)
+#include <pthread.h>
 #endif
 
 #include "curl_threads.h"
@@ -64,14 +60,18 @@ curl_thread_t Curl_thread_create(CURL_THREAD_RETURN_T
 {
   curl_thread_t t = malloc(sizeof(pthread_t));
   struct Curl_actual_call *ac = malloc(sizeof(struct Curl_actual_call));
+  int rc;
   if(!(ac && t))
     goto err;
 
   ac->func = func;
   ac->arg = arg;
 
-  if(pthread_create(t, NULL, curl_thread_create_thunk, ac) != 0)
+  rc = pthread_create(t, NULL, curl_thread_create_thunk, ac);
+  if(rc) {
+    CURL_SETERRNO(rc);
     goto err;
+  }
 
   return t;
 
@@ -100,61 +100,19 @@ int Curl_thread_join(curl_thread_t *hnd)
   return ret;
 }
 
-/* do not use pthread_cancel if:
- * - pthread_cancel seems to be absent
- * - on FreeBSD, as we see hangers in CI testing
- * - this is a -fsanitize=thread build
- *   (clang sanitizer reports false positive when functions to not return)
- */
-#if defined(PTHREAD_CANCEL_ENABLE) && !defined(__FreeBSD__)
-#if defined(__has_feature)
-#  if !__has_feature(thread_sanitizer)
-#define USE_PTHREAD_CANCEL
-#  endif
-#else /* __has_feature */
-#define USE_PTHREAD_CANCEL
-#endif /* !__has_feature */
-#endif /* PTHREAD_CANCEL_ENABLE && !__FreeBSD__ */
-
-int Curl_thread_cancel(curl_thread_t *hnd)
-{
-  (void)hnd;
-  if(*hnd != curl_thread_t_null)
-#ifdef USE_PTHREAD_CANCEL
-    return pthread_cancel(**hnd);
-#else
-    return 1; /* not supported */
-#endif
-  return 0;
-}
-
 #elif defined(USE_THREADS_WIN32)
 
 curl_thread_t Curl_thread_create(CURL_THREAD_RETURN_T
                                  (CURL_STDCALL *func) (void *), void *arg)
 {
-#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(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 UNDER_CE
+  curl_thread_t t = CreateThread(NULL, 0, func, arg, 0, NULL);
+  if(!t) {
     DWORD gle = GetLastError();
     /* !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;
@@ -170,7 +128,7 @@ void Curl_thread_destroy(curl_thread_t *hnd)
 
 int Curl_thread_join(curl_thread_t *hnd)
 {
-#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < _WIN32_WINNT_VISTA)
+#ifdef UNDER_CE
   int ret = (WaitForSingleObject(*hnd, INFINITE) == WAIT_OBJECT_0);
 #else
   int ret = (WaitForSingleObjectEx(*hnd, INFINITE, FALSE) == WAIT_OBJECT_0);
@@ -178,16 +136,7 @@ int Curl_thread_join(curl_thread_t *hnd)
 
   Curl_thread_destroy(hnd);
 
-
   return ret;
 }
 
-int Curl_thread_cancel(curl_thread_t *hnd)
-{
-  if(*hnd != curl_thread_t_null) {
-    return 1; /* not supported */
-  }
-  return 0;
-}
-
 #endif /* USE_THREADS_* */

+ 3 - 25
lib/curl_threads.h

@@ -26,6 +26,7 @@
 #include "curl_setup.h"
 
 #ifdef USE_THREADS_POSIX
+#  define CURL_THREAD_RETURN_T   unsigned int
 #  define CURL_STDCALL
 #  define curl_mutex_t           pthread_mutex_t
 #  define curl_thread_t          pthread_t *
@@ -35,7 +36,8 @@
 #  define Curl_mutex_release(m)  pthread_mutex_unlock(m)
 #  define Curl_mutex_destroy(m)  pthread_mutex_destroy(m)
 #elif defined(USE_THREADS_WIN32)
-#  define CURL_STDCALL           __stdcall
+#  define CURL_THREAD_RETURN_T   DWORD
+#  define CURL_STDCALL           WINAPI
 #  define curl_mutex_t           CRITICAL_SECTION
 #  define curl_thread_t          HANDLE
 #  define curl_thread_t_null     (HANDLE)0
@@ -47,14 +49,6 @@
 #  define Curl_mutex_acquire(m)  EnterCriticalSection(m)
 #  define Curl_mutex_release(m)  LeaveCriticalSection(m)
 #  define Curl_mutex_destroy(m)  DeleteCriticalSection(m)
-#else
-#  define CURL_STDCALL
-#endif
-
-#if defined(CURL_WINDOWS_UWP) || defined(UNDER_CE)
-#define CURL_THREAD_RETURN_T DWORD
-#else
-#define CURL_THREAD_RETURN_T unsigned int
 #endif
 
 #if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32)
@@ -66,22 +60,6 @@ void Curl_thread_destroy(curl_thread_t *hnd);
 
 int Curl_thread_join(curl_thread_t *hnd);
 
-int Curl_thread_cancel(curl_thread_t *hnd);
-
-#if defined(USE_THREADS_POSIX) && defined(PTHREAD_CANCEL_ENABLE)
-#define Curl_thread_push_cleanup(a,b)   pthread_cleanup_push(a,b)
-#define Curl_thread_pop_cleanup()       pthread_cleanup_pop(0)
-#define Curl_thread_enable_cancel()     \
-    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL)
-#define Curl_thread_disable_cancel()     \
-    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL)
-#else
-#define Curl_thread_push_cleanup(a,b)   ((void)a,(void)b)
-#define Curl_thread_pop_cleanup()       Curl_nop_stmt
-#define Curl_thread_enable_cancel()     Curl_nop_stmt
-#define Curl_thread_disable_cancel()    Curl_nop_stmt
-#endif
-
 #endif /* USE_THREADS_POSIX || USE_THREADS_WIN32 */
 
 #endif /* HEADER_CURL_THREADS_H */

+ 47 - 27
lib/curl_trc.c

@@ -47,8 +47,7 @@
 #include "vtls/vtls.h"
 #include "vquic/vquic.h"
 
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
+/* The last 2 #include files should be in this order */
 #include "curl_memory.h"
 #include "memdebug.h"
 
@@ -102,14 +101,14 @@ static size_t trc_print_ids(struct Curl_easy *data, char *buf, size_t maxlen)
                    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);
+      return curl_msnprintf(buf, maxlen, CURL_TRC_FMT_IDSDC, data->id, cid);
     else
-      return msnprintf(buf, maxlen, CURL_TRC_FMT_IDSD, data->id);
+      return curl_msnprintf(buf, maxlen, CURL_TRC_FMT_IDSD, data->id);
   }
   else if(cid >= 0)
-    return msnprintf(buf, maxlen, CURL_TRC_FMT_IDSC, cid);
+    return curl_msnprintf(buf, maxlen, CURL_TRC_FMT_IDSC, cid);
   else {
-    return msnprintf(buf, maxlen, "[x-x] ");
+    return curl_msnprintf(buf, maxlen, "[x-x] ");
   }
 }
 
@@ -143,8 +142,8 @@ void Curl_debug(struct Curl_easy *data, curl_infotype type,
 
       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 += curl_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);
@@ -189,7 +188,7 @@ void Curl_failf(struct Curl_easy *data, const char *fmt, ...)
     size_t len;
     char error[CURL_ERROR_SIZE + 2];
     va_start(ap, fmt);
-    len = mvsnprintf(error, CURL_ERROR_SIZE, fmt, ap);
+    len = curl_mvsnprintf(error, CURL_ERROR_SIZE, fmt, ap);
 
     if(data->set.errorbuffer && !data->state.errorbuf) {
       strcpy(data->set.errorbuffer, error);
@@ -220,15 +219,15 @@ static void trc_infof(struct Curl_easy *data,
   if(CURL_TRC_IDS(data))
     len += trc_print_ids(data, buf + len, TRC_LINE_MAX - len);
   if(feat)
-    len += msnprintf(buf + len, TRC_LINE_MAX - len, "[%s] ", feat->name);
+    len += curl_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);
+      len += curl_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);
+      len += curl_msnprintf(buf + len, TRC_LINE_MAX - len, "[%s] ", opt_id);
   }
-  len += mvsnprintf(buf + len, TRC_LINE_MAX - len, fmt, ap);
+  len += curl_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);
 }
@@ -272,6 +271,10 @@ struct curl_trc_feat Curl_trc_feat_dns = {
   "DNS",
   CURL_LOG_LVL_NONE,
 };
+struct curl_trc_feat Curl_trc_feat_timer = {
+  "TIMER",
+  CURL_LOG_LVL_NONE,
+};
 
 static const char * const Curl_trc_timer_names[]={
   "100_TIMEOUT",
@@ -291,24 +294,37 @@ static const char * const Curl_trc_timer_names[]={
   "SHUTDOWN",
 };
 
-const char *Curl_trc_timer_name(int tid)
+static const char *trc_timer_name(int tid)
 {
   if((tid >= 0) && ((size_t)tid < CURL_ARRAYSIZE(Curl_trc_timer_names)))
     return Curl_trc_timer_names[(size_t)tid];
   return "UNKNOWN?";
 }
 
-void Curl_trc_multi_timeouts(struct Curl_easy *data)
+void Curl_trc_timer(struct Curl_easy *data, int tid, const char *fmt, ...)
 {
-  struct Curl_llist_node *e = Curl_llist_head(&data->state.timeoutlist);
-  if(e) {
-    struct curltime now = curlx_now();
-    while(e) {
-      struct time_node *n = Curl_node_elem(e);
-      e = Curl_node_next(e);
-      CURL_TRC_M(data, "[TIMEOUT] %s expires in %" FMT_TIMEDIFF_T "ns",
-                 CURL_TIMER_NAME(n->eid),
-                 curlx_timediff_us(n->time, now));
+  DEBUGASSERT(!strchr(fmt, '\n'));
+  if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_timer)) {
+    const char *tname = trc_timer_name(tid);
+    va_list ap;
+    va_start(ap, fmt);
+    trc_infof(data, &Curl_trc_feat_timer, tname, 0, fmt, ap);
+    va_end(ap);
+  }
+}
+
+void Curl_trc_easy_timers(struct Curl_easy *data)
+{
+  if(CURL_TRC_TIMER_is_verbose(data)) {
+    struct Curl_llist_node *e = Curl_llist_head(&data->state.timeoutlist);
+    if(e) {
+      struct curltime now = curlx_now();
+      while(e) {
+        struct time_node *n = Curl_node_elem(e);
+        e = Curl_node_next(e);
+        CURL_TRC_TIMER(data, n->eid, "expires in %" FMT_TIMEDIFF_T "ns",
+                       curlx_timediff_us(n->time, now));
+      }
     }
   }
 }
@@ -476,6 +492,7 @@ static struct trc_feat_def trc_feats[] = {
   { &Curl_trc_feat_read,      TRC_CT_NONE },
   { &Curl_trc_feat_write,     TRC_CT_NONE },
   { &Curl_trc_feat_dns,       TRC_CT_NETWORK },
+  { &Curl_trc_feat_timer,     TRC_CT_NETWORK },
 #ifndef CURL_DISABLE_FTP
   { &Curl_trc_feat_ftp,       TRC_CT_PROTOCOL },
 #endif
@@ -640,8 +657,6 @@ void Curl_trc_cf_infof(struct Curl_easy *data, const struct Curl_cfilter *cf,
   (void)data; (void)cf; (void)fmt;
 }
 
-struct curl_trc_feat;
-
 void Curl_trc_multi(struct Curl_easy *data, const char *fmt, ...)
 {
   (void)data; (void)fmt;
@@ -657,6 +672,11 @@ void Curl_trc_dns(struct Curl_easy *data, const char *fmt, ...)
   (void)data; (void)fmt;
 }
 
+void Curl_trc_timer(struct Curl_easy *data, int tid, const char *fmt, ...)
+{
+  (void)data; (void)tid; (void)fmt;
+}
+
 void Curl_trc_read(struct Curl_easy *data, const char *fmt, ...)
 {
   (void)data; (void)fmt;

+ 22 - 13
lib/curl_trc.h

@@ -86,7 +86,7 @@ void Curl_trc_multi(struct Curl_easy *data,
                     const char *fmt, ...) CURL_PRINTF(2, 3);
 const char *Curl_trc_mstate_name(int state);
 const char *Curl_trc_timer_name(int tid);
-void Curl_trc_multi_timeouts(struct Curl_easy *data);
+void Curl_trc_easy_timers(struct Curl_easy *data);
 
 void Curl_trc_write(struct Curl_easy *data,
                     const char *fmt, ...) CURL_PRINTF(2, 3);
@@ -94,6 +94,13 @@ 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);
+void Curl_trc_timer(struct Curl_easy *data, int tid,
+                    const char *fmt, ...) CURL_PRINTF(3, 4);
+
+struct curl_trc_feat {
+  const char *name;
+  int log_level;
+};
 
 #ifndef CURL_DISABLE_FTP
 extern struct curl_trc_feat Curl_trc_feat_ftp;
@@ -118,6 +125,10 @@ void Curl_trc_ws(struct Curl_easy *data,
 
 #define CURL_TRC_M_is_verbose(data) \
   Curl_trc_ft_is_verbose(data, &Curl_trc_feat_multi)
+#define CURL_TRC_DNS_is_verbose(data) \
+  Curl_trc_ft_is_verbose(data, &Curl_trc_feat_dns)
+#define CURL_TRC_TIMER_is_verbose(data) \
+  Curl_trc_ft_is_verbose(data, &Curl_trc_feat_timer)
 
 #if defined(CURL_HAVE_C99) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
 #define infof(data, ...) \
@@ -136,8 +147,11 @@ void Curl_trc_ws(struct Curl_easy *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)) \
+  do { if(CURL_TRC_DNS_is_verbose(data)) \
          Curl_trc_dns(data, __VA_ARGS__); } while(0)
+#define CURL_TRC_TIMER(data, tid, ...) \
+  do { if(CURL_TRC_TIMER_is_verbose(data)) \
+         Curl_trc_timer(data, tid, __VA_ARGS__); } while(0)
 
 #ifndef CURL_DISABLE_FTP
 #define CURL_TRC_FTP(data, ...) \
@@ -168,6 +182,7 @@ void Curl_trc_ws(struct Curl_easy *data,
 #define CURL_TRC_WRITE Curl_trc_write
 #define CURL_TRC_READ  Curl_trc_read
 #define CURL_TRC_DNS   Curl_trc_dns
+#define CURL_TRC_TIMER Curl_trc_timer
 
 #ifndef CURL_DISABLE_FTP
 #define CURL_TRC_FTP   Curl_trc_ftp
@@ -184,11 +199,6 @@ void Curl_trc_ws(struct Curl_easy *data,
 
 #endif /* !CURL_HAVE_C99 */
 
-struct curl_trc_feat {
-  const char *name;
-  int log_level;
-};
-
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
 /* informational messages enabled */
 
@@ -196,6 +206,7 @@ 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;
+extern struct curl_trc_feat Curl_trc_feat_timer;
 
 #define Curl_trc_is_verbose(data) \
             ((data) && (data)->set.verbose && \
@@ -208,10 +219,9 @@ extern struct curl_trc_feat Curl_trc_feat_dns;
             (Curl_trc_is_verbose(data) && \
              (ft)->log_level >= CURL_LOG_LVL_INFO)
 #define CURL_MSTATE_NAME(s)  Curl_trc_mstate_name((int)(s))
-#define CURL_TIMER_NAME(t)   Curl_trc_timer_name((int)(t))
-#define CURL_TRC_M_TIMEOUTS(data) \
-  do { if(CURL_TRC_M_is_verbose(data)) \
-         Curl_trc_multi_timeouts(data); } while(0)
+#define CURL_TRC_EASY_TIMERS(data) \
+  do { if(CURL_TRC_TIMER_is_verbose(data)) \
+         Curl_trc_easy_timers(data); } while(0)
 
 #else /* CURL_DISABLE_VERBOSE_STRINGS */
 /* All informational messages are not compiled in for size savings */
@@ -220,8 +230,7 @@ extern struct curl_trc_feat Curl_trc_feat_dns;
 #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), "-")
-#define CURL_TIMER_NAME(x)            ((void)(x), "-")
-#define CURL_TRC_M_TIMEOUTS(x)        Curl_nop_stmt
+#define CURL_TRC_EASY_TIMERS(x)       Curl_nop_stmt
 
 #endif /* !CURL_DISABLE_VERBOSE_STRINGS */
 

+ 5 - 7
lib/curlx/base64.c

@@ -182,12 +182,12 @@ static CURLcode base64_encode(const char *table64,
   *outlen = 0;
 
   if(!insize)
-    insize = strlen(inputbuff);
+    return CURLE_OK;
 
-#if SIZEOF_SIZE_T == 4
-  if(insize > UINT_MAX/4)
-    return CURLE_OUT_OF_MEMORY;
-#endif
+  /* safety precaution */
+  DEBUGASSERT(insize <= CURL_MAX_BASE64_INPUT);
+  if(insize > CURL_MAX_BASE64_INPUT)
+    return CURLE_TOO_LARGE;
 
   base64data = output = malloc((insize + 2) / 3 * 4 + 1);
   if(!output)
@@ -240,8 +240,6 @@ static CURLcode base64_encode(const char *table64,
  * encoded data. Size of encoded data is returned in variable pointed by
  * outlen.
  *
- * 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.
  *

+ 4 - 0
lib/curlx/base64.h

@@ -33,4 +33,8 @@ CURLcode curlx_base64_decode(const char *src,
 
 extern const char Curl_base64encdec[];
 
+/* maximum input length acceptable to base64 encode, here to catch and prevent
+   mistakes */
+#define CURL_MAX_BASE64_INPUT 16000000
+
 #endif /* HEADER_CURL_BASE64_H */

+ 6 - 0
lib/curlx/curlx.h

@@ -58,12 +58,18 @@
 #include "version_win32.h"
 /* provides curlx_verify_windows_version() */
 
+#include "strerr.h"
+/* The curlx_strerror() function */
+
 #include "strparse.h"
 /* The curlx_str_* parsing functions */
 
 #include "dynbuf.h"
 /* The curlx_dyn_* functions */
 
+#include "fopen.h"
+/* The curlx_f* functions */
+
 #include "base64.h"
 #include "timeval.h"
 #include "timediff.h"

+ 324 - 0
lib/curlx/fopen.c

@@ -0,0 +1,324 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <[email protected]>, 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 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 "fopen.h"
+
+int curlx_fseek(void *stream, curl_off_t offset, int whence)
+{
+#if defined(_WIN32) && defined(USE_WIN32_LARGE_FILES)
+  return _fseeki64(stream, (__int64)offset, whence);
+#elif defined(HAVE_FSEEKO) && defined(HAVE_DECL_FSEEKO)
+  return fseeko(stream, (off_t)offset, whence);
+#else
+  if(offset > LONG_MAX)
+    return -1;
+  return fseek(stream, (long)offset, whence);
+#endif
+}
+
+#if defined(_WIN32) && !defined(UNDER_CE)
+
+#include "multibyte.h"
+
+/* declare GetFullPathNameW for mingw-w64 UWP builds targeting old windows */
+#if defined(CURL_WINDOWS_UWP) && defined(__MINGW32__) && \
+  (_WIN32_WINNT < _WIN32_WINNT_WIN10)
+WINBASEAPI DWORD WINAPI GetFullPathNameW(LPCWSTR, DWORD, LPWSTR, LPWSTR *);
+#endif
+
+/* Fix excessive paths (paths that exceed MAX_PATH length of 260).
+ *
+ * This is a helper function to fix paths that would exceed the MAX_PATH
+ * limitation check done by Windows APIs. It does so by normalizing the passed
+ * in filename or path 'in' to its full canonical path, and if that path is
+ * longer than MAX_PATH then setting 'out' to "\\?\" prefix + that full path.
+ *
+ * For example 'in' filename255chars in current directory C:\foo\bar is
+ * fixed as \\?\C:\foo\bar\filename255chars for 'out' which will tell Windows
+ * it is ok to access that filename even though the actual full path is longer
+ * than 260 chars.
+ *
+ * For non-Unicode builds this function may fail sometimes because only the
+ * Unicode versions of some Windows API functions can access paths longer than
+ * MAX_PATH, for example GetFullPathNameW which is used in this function. When
+ * the full path is then converted from Unicode to multibyte that fails if any
+ * directories in the path contain characters not in the current codepage.
+ */
+static bool fix_excessive_path(const TCHAR *in, TCHAR **out)
+{
+  size_t needed, count;
+  const wchar_t *in_w;
+  wchar_t *fbuf = NULL;
+
+  /* MS documented "approximate" limit for the maximum path length */
+  const size_t max_path_len = 32767;
+
+#ifndef _UNICODE
+  wchar_t *ibuf = NULL;
+  char *obuf = NULL;
+#endif
+
+  *out = NULL;
+
+  /* skip paths already normalized */
+  if(!_tcsncmp(in, _T("\\\\?\\"), 4))
+    goto cleanup;
+
+#ifndef _UNICODE
+  /* convert multibyte input to unicode */
+  needed = mbstowcs(NULL, in, 0);
+  if(needed == (size_t)-1 || needed >= max_path_len)
+    goto cleanup;
+  ++needed; /* for NUL */
+  ibuf = (malloc)(needed * sizeof(wchar_t));
+  if(!ibuf)
+    goto cleanup;
+  count = mbstowcs(ibuf, in, needed);
+  if(count == (size_t)-1 || count >= needed)
+    goto cleanup;
+  in_w = ibuf;
+#else
+  in_w = in;
+#endif
+
+  /* GetFullPathNameW returns the normalized full path in unicode. It converts
+     forward slashes to backslashes, processes .. to remove directory segments,
+     etc. Unlike GetFullPathNameA it can process paths that exceed MAX_PATH. */
+  needed = (size_t)GetFullPathNameW(in_w, 0, NULL, NULL);
+  if(!needed || needed > max_path_len)
+    goto cleanup;
+  /* skip paths that are not excessive and do not need modification */
+  if(needed <= MAX_PATH)
+    goto cleanup;
+  fbuf = (malloc)(needed * sizeof(wchar_t));
+  if(!fbuf)
+    goto cleanup;
+  count = (size_t)GetFullPathNameW(in_w, (DWORD)needed, fbuf, NULL);
+  if(!count || count >= needed)
+    goto cleanup;
+
+  /* prepend \\?\ or \\?\UNC\ to the excessively long path.
+   *
+   * c:\longpath            --->    \\?\c:\longpath
+   * \\.\c:\longpath        --->    \\?\c:\longpath
+   * \\?\c:\longpath        --->    \\?\c:\longpath  (unchanged)
+   * \\server\c$\longpath   --->    \\?\UNC\server\c$\longpath
+   *
+   * https://learn.microsoft.com/dotnet/standard/io/file-path-formats
+   */
+  if(!wcsncmp(fbuf, L"\\\\?\\", 4))
+    ; /* do nothing */
+  else if(!wcsncmp(fbuf, L"\\\\.\\", 4))
+    fbuf[2] = '?';
+  else if(!wcsncmp(fbuf, L"\\\\.", 3) || !wcsncmp(fbuf, L"\\\\?", 3)) {
+    /* Unexpected, not UNC. The formatting doc doesn't allow this AFAICT. */
+    goto cleanup;
+  }
+  else {
+    wchar_t *temp;
+
+    if(!wcsncmp(fbuf, L"\\\\", 2)) {
+      /* "\\?\UNC\" + full path without "\\" + null */
+      needed = 8 + (count - 2) + 1;
+      if(needed > max_path_len)
+        goto cleanup;
+
+      temp = (malloc)(needed * sizeof(wchar_t));
+      if(!temp)
+        goto cleanup;
+
+      wcsncpy(temp, L"\\\\?\\UNC\\", 8);
+      wcscpy(temp + 8, fbuf + 2);
+    }
+    else {
+      /* "\\?\" + full path + null */
+      needed = 4 + count + 1;
+      if(needed > max_path_len)
+        goto cleanup;
+
+      temp = (malloc)(needed * sizeof(wchar_t));
+      if(!temp)
+        goto cleanup;
+
+      wcsncpy(temp, L"\\\\?\\", 4);
+      wcscpy(temp + 4, fbuf);
+    }
+
+    (free)(fbuf);
+    fbuf = temp;
+  }
+
+#ifndef _UNICODE
+  /* convert unicode full path to multibyte output */
+  needed = wcstombs(NULL, fbuf, 0);
+  if(needed == (size_t)-1 || needed >= max_path_len)
+    goto cleanup;
+  ++needed; /* for NUL */
+  obuf = (malloc)(needed);
+  if(!obuf)
+    goto cleanup;
+  count = wcstombs(obuf, fbuf, needed);
+  if(count == (size_t)-1 || count >= needed)
+    goto cleanup;
+  *out = obuf;
+  obuf = NULL;
+#else
+  *out = fbuf;
+  fbuf = NULL;
+#endif
+
+cleanup:
+  (free)(fbuf);
+#ifndef _UNICODE
+  (free)(ibuf);
+  (free)(obuf);
+#endif
+  return *out ? true : false;
+}
+
+int curlx_win32_open(const char *filename, int oflag, ...)
+{
+  int pmode = 0;
+  int result = -1;
+  TCHAR *fixed = NULL;
+  const TCHAR *target = NULL;
+
+#ifdef _UNICODE
+  wchar_t *filename_w = curlx_convert_UTF8_to_wchar(filename);
+#endif
+
+  va_list param;
+  va_start(param, oflag);
+  if(oflag & O_CREAT)
+    pmode = va_arg(param, int);
+  va_end(param);
+
+#ifdef _UNICODE
+  if(filename_w) {
+    if(fix_excessive_path(filename_w, &fixed))
+      target = fixed;
+    else
+      target = filename_w;
+    result = _wopen(target, oflag, pmode);
+    curlx_unicodefree(filename_w);
+  }
+  else
+    /* !checksrc! disable ERRNOVAR 1 */
+    CURL_SETERRNO(EINVAL);
+#else
+  if(fix_excessive_path(filename, &fixed))
+    target = fixed;
+  else
+    target = filename;
+  result = _open(target, oflag, pmode);
+#endif
+
+  (free)(fixed);
+  return result;
+}
+
+FILE *curlx_win32_fopen(const char *filename, const char *mode)
+{
+  FILE *result = NULL;
+  TCHAR *fixed = NULL;
+  const TCHAR *target = NULL;
+
+#ifdef _UNICODE
+  wchar_t *filename_w = curlx_convert_UTF8_to_wchar(filename);
+  wchar_t *mode_w = curlx_convert_UTF8_to_wchar(mode);
+  if(filename_w && mode_w) {
+    if(fix_excessive_path(filename_w, &fixed))
+      target = fixed;
+    else
+      target = filename_w;
+    result = _wfopen(target, mode_w);
+  }
+  else
+    /* !checksrc! disable ERRNOVAR 1 */
+    CURL_SETERRNO(EINVAL);
+  curlx_unicodefree(filename_w);
+  curlx_unicodefree(mode_w);
+#else
+  if(fix_excessive_path(filename, &fixed))
+    target = fixed;
+  else
+    target = filename;
+  /* !checksrc! disable BANNEDFUNC 1 */
+  result = fopen(target, mode);
+#endif
+
+  (free)(fixed);
+  return result;
+}
+
+int curlx_win32_stat(const char *path, struct_stat *buffer)
+{
+  int result = -1;
+  TCHAR *fixed = NULL;
+  const TCHAR *target = NULL;
+
+#ifdef _UNICODE
+  wchar_t *path_w = curlx_convert_UTF8_to_wchar(path);
+  if(path_w) {
+    if(fix_excessive_path(path_w, &fixed))
+      target = fixed;
+    else
+      target = path_w;
+#ifndef USE_WIN32_LARGE_FILES
+    result = _wstat(target, buffer);
+#else
+    result = _wstati64(target, buffer);
+#endif
+    curlx_unicodefree(path_w);
+  }
+  else
+    /* !checksrc! disable ERRNOVAR 1 */
+    CURL_SETERRNO(EINVAL);
+#else
+  if(fix_excessive_path(path, &fixed))
+    target = fixed;
+  else
+    target = path;
+#ifndef USE_WIN32_LARGE_FILES
+  result = _stat(target, buffer);
+#else
+  result = _stati64(target, buffer);
+#endif
+#endif
+
+  (free)(fixed);
+  return result;
+}
+
+#endif /* _WIN32 && !UNDER_CE */

+ 60 - 0
lib/curlx/fopen.h

@@ -0,0 +1,60 @@
+#ifndef HEADER_CURLX_FOPEN_H
+#define HEADER_CURLX_FOPEN_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <[email protected]>, 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 "multibyte.h"
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>  /* for open() and attributes */
+#endif
+
+int curlx_fseek(void *stream, curl_off_t offset, int whence);
+
+#if defined(_WIN32) && !defined(UNDER_CE)
+FILE *curlx_win32_fopen(const char *filename, const char *mode);
+int curlx_win32_stat(const char *path, struct_stat *buffer);
+int curlx_win32_open(const char *filename, int oflag, ...);
+#define CURLX_FOPEN_LOW(fname, mode) curlx_win32_fopen(fname, mode)
+#define curlx_stat(fname, stp)       curlx_win32_stat(fname, stp)
+#define curlx_open                   curlx_win32_open
+#else
+#define CURLX_FOPEN_LOW              fopen
+#define curlx_stat(fname, stp)       stat(fname, stp)
+#define curlx_open                   open
+#endif
+
+#ifdef CURLDEBUG
+#define curlx_fopen(file,mode)  curl_dbg_fopen(file,mode,__LINE__,__FILE__)
+#define curlx_fdopen(file,mode) curl_dbg_fdopen(file,mode,__LINE__,__FILE__)
+#define curlx_fclose(file)      curl_dbg_fclose(file,__LINE__,__FILE__)
+#else
+#define curlx_fopen             CURLX_FOPEN_LOW
+#define curlx_fdopen            fdopen
+#define curlx_fclose            fclose
+#endif
+
+#endif /* HEADER_CURLX_FOPEN_H */

+ 0 - 3
lib/curlx/inet_ntop.c

@@ -16,9 +16,6 @@
  *
  * SPDX-License-Identifier: ISC
  */
-/*
- * Original code by Paul Vixie. "curlified" by Gisle Vanem.
- */
 
 #include "../curl_setup.h"
 

+ 0 - 273
lib/curlx/multibyte.c

@@ -84,277 +84,4 @@ 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)
-WINBASEAPI DWORD WINAPI GetFullPathNameW(LPCWSTR, DWORD, LPWSTR, LPWSTR *);
-#endif
-
-/* Fix excessive paths (paths that exceed MAX_PATH length of 260).
- *
- * This is a helper function to fix paths that would exceed the MAX_PATH
- * limitation check done by Windows APIs. It does so by normalizing the passed
- * in filename or path 'in' to its full canonical path, and if that path is
- * longer than MAX_PATH then setting 'out' to "\\?\" prefix + that full path.
- *
- * For example 'in' filename255chars in current directory C:\foo\bar is
- * fixed as \\?\C:\foo\bar\filename255chars for 'out' which will tell Windows
- * it is ok to access that filename even though the actual full path is longer
- * than 260 chars.
- *
- * For non-Unicode builds this function may fail sometimes because only the
- * Unicode versions of some Windows API functions can access paths longer than
- * MAX_PATH, for example GetFullPathNameW which is used in this function. When
- * the full path is then converted from Unicode to multibyte that fails if any
- * directories in the path contain characters not in the current codepage.
- */
-static bool fix_excessive_path(const TCHAR *in, TCHAR **out)
-{
-  size_t needed, count;
-  const wchar_t *in_w;
-  wchar_t *fbuf = NULL;
-
-  /* MS documented "approximate" limit for the maximum path length */
-  const size_t max_path_len = 32767;
-
-#ifndef _UNICODE
-  wchar_t *ibuf = NULL;
-  char *obuf = NULL;
-#endif
-
-  *out = NULL;
-
-  /* skip paths already normalized */
-  if(!_tcsncmp(in, _T("\\\\?\\"), 4))
-    goto cleanup;
-
-#ifndef _UNICODE
-  /* convert multibyte input to unicode */
-  needed = mbstowcs(NULL, in, 0);
-  if(needed == (size_t)-1 || needed >= max_path_len)
-    goto cleanup;
-  ++needed; /* for NUL */
-  ibuf = (malloc)(needed * sizeof(wchar_t));
-  if(!ibuf)
-    goto cleanup;
-  count = mbstowcs(ibuf, in, needed);
-  if(count == (size_t)-1 || count >= needed)
-    goto cleanup;
-  in_w = ibuf;
-#else
-  in_w = in;
-#endif
-
-  /* GetFullPathNameW returns the normalized full path in unicode. It converts
-     forward slashes to backslashes, processes .. to remove directory segments,
-     etc. Unlike GetFullPathNameA it can process paths that exceed MAX_PATH. */
-  needed = (size_t)GetFullPathNameW(in_w, 0, NULL, NULL);
-  if(!needed || needed > max_path_len)
-    goto cleanup;
-  /* skip paths that are not excessive and do not need modification */
-  if(needed <= MAX_PATH)
-    goto cleanup;
-  fbuf = (malloc)(needed * sizeof(wchar_t));
-  if(!fbuf)
-    goto cleanup;
-  count = (size_t)GetFullPathNameW(in_w, (DWORD)needed, fbuf, NULL);
-  if(!count || count >= needed)
-    goto cleanup;
-
-  /* prepend \\?\ or \\?\UNC\ to the excessively long path.
-   *
-   * c:\longpath            --->    \\?\c:\longpath
-   * \\.\c:\longpath        --->    \\?\c:\longpath
-   * \\?\c:\longpath        --->    \\?\c:\longpath  (unchanged)
-   * \\server\c$\longpath   --->    \\?\UNC\server\c$\longpath
-   *
-   * https://learn.microsoft.com/en-us/dotnet/standard/io/file-path-formats
-   */
-  if(!wcsncmp(fbuf, L"\\\\?\\", 4))
-    ; /* do nothing */
-  else if(!wcsncmp(fbuf, L"\\\\.\\", 4))
-    fbuf[2] = '?';
-  else if(!wcsncmp(fbuf, L"\\\\.", 3) || !wcsncmp(fbuf, L"\\\\?", 3)) {
-    /* Unexpected, not UNC. The formatting doc doesn't allow this AFAICT. */
-    goto cleanup;
-  }
-  else {
-    wchar_t *temp;
-
-    if(!wcsncmp(fbuf, L"\\\\", 2)) {
-      /* "\\?\UNC\" + full path without "\\" + null */
-      needed = 8 + (count - 2) + 1;
-      if(needed > max_path_len)
-        goto cleanup;
-
-      temp = (malloc)(needed * sizeof(wchar_t));
-      if(!temp)
-        goto cleanup;
-
-      wcsncpy(temp, L"\\\\?\\UNC\\", 8);
-      wcscpy(temp + 8, fbuf + 2);
-    }
-    else {
-      /* "\\?\" + full path + null */
-      needed = 4 + count + 1;
-      if(needed > max_path_len)
-        goto cleanup;
-
-      temp = (malloc)(needed * sizeof(wchar_t));
-      if(!temp)
-        goto cleanup;
-
-      wcsncpy(temp, L"\\\\?\\", 4);
-      wcscpy(temp + 4, fbuf);
-    }
-
-    (free)(fbuf);
-    fbuf = temp;
-  }
-
-#ifndef _UNICODE
-  /* convert unicode full path to multibyte output */
-  needed = wcstombs(NULL, fbuf, 0);
-  if(needed == (size_t)-1 || needed >= max_path_len)
-    goto cleanup;
-  ++needed; /* for NUL */
-  obuf = (malloc)(needed);
-  if(!obuf)
-    goto cleanup;
-  count = wcstombs(obuf, fbuf, needed);
-  if(count == (size_t)-1 || count >= needed)
-    goto cleanup;
-  *out = obuf;
-  obuf = NULL;
-#else
-  *out = fbuf;
-  fbuf = NULL;
-#endif
-
-cleanup:
-  (free)(fbuf);
-#ifndef _UNICODE
-  (free)(ibuf);
-  (free)(obuf);
-#endif
-  return *out ? true : false;
-}
-
-int curlx_win32_open(const char *filename, int oflag, ...)
-{
-  int pmode = 0;
-  int result = -1;
-  TCHAR *fixed = NULL;
-  const TCHAR *target = NULL;
-
-#ifdef _UNICODE
-  wchar_t *filename_w = curlx_convert_UTF8_to_wchar(filename);
-#endif
-
-  va_list param;
-  va_start(param, oflag);
-  if(oflag & O_CREAT)
-    pmode = va_arg(param, int);
-  va_end(param);
-
-#ifdef _UNICODE
-  if(filename_w) {
-    if(fix_excessive_path(filename_w, &fixed))
-      target = fixed;
-    else
-      target = filename_w;
-    result = _wopen(target, oflag, pmode);
-    curlx_unicodefree(filename_w);
-  }
-  else
-    /* !checksrc! disable ERRNOVAR 1 */
-    CURL_SETERRNO(EINVAL);
-#else
-  if(fix_excessive_path(filename, &fixed))
-    target = fixed;
-  else
-    target = filename;
-  result = _open(target, oflag, pmode);
-#endif
-
-  (free)(fixed);
-  return result;
-}
-
-FILE *curlx_win32_fopen(const char *filename, const char *mode)
-{
-  FILE *result = NULL;
-  TCHAR *fixed = NULL;
-  const TCHAR *target = NULL;
-
-#ifdef _UNICODE
-  wchar_t *filename_w = curlx_convert_UTF8_to_wchar(filename);
-  wchar_t *mode_w = curlx_convert_UTF8_to_wchar(mode);
-  if(filename_w && mode_w) {
-    if(fix_excessive_path(filename_w, &fixed))
-      target = fixed;
-    else
-      target = filename_w;
-    result = _wfopen(target, mode_w);
-  }
-  else
-    /* !checksrc! disable ERRNOVAR 1 */
-    CURL_SETERRNO(EINVAL);
-  curlx_unicodefree(filename_w);
-  curlx_unicodefree(mode_w);
-#else
-  if(fix_excessive_path(filename, &fixed))
-    target = fixed;
-  else
-    target = filename;
-  result = (fopen)(target, mode);
-#endif
-
-  (free)(fixed);
-  return result;
-}
-
-int curlx_win32_stat(const char *path, struct_stat *buffer)
-{
-  int result = -1;
-  TCHAR *fixed = NULL;
-  const TCHAR *target = NULL;
-
-#ifdef _UNICODE
-  wchar_t *path_w = curlx_convert_UTF8_to_wchar(path);
-  if(path_w) {
-    if(fix_excessive_path(path_w, &fixed))
-      target = fixed;
-    else
-      target = path_w;
-#ifndef USE_WIN32_LARGE_FILES
-    result = _wstat(target, buffer);
-#else
-    result = _wstati64(target, buffer);
-#endif
-    curlx_unicodefree(path_w);
-  }
-  else
-    /* !checksrc! disable ERRNOVAR 1 */
-    CURL_SETERRNO(EINVAL);
-#else
-  if(fix_excessive_path(path, &fixed))
-    target = fixed;
-  else
-    target = path;
-#ifndef USE_WIN32_LARGE_FILES
-  result = _stat(target, buffer);
-#else
-  result = _stati64(target, buffer);
-#endif
-#endif
-
-  (free)(fixed);
-  return result;
-}
-
-#endif /* UNDER_CE */
-
 #endif /* _WIN32 */

+ 361 - 0
lib/curlx/strerr.c

@@ -0,0 +1,361 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <[email protected]>, 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_STRERROR_R
+#  if (!defined(HAVE_POSIX_STRERROR_R) && \
+       !defined(HAVE_GLIBC_STRERROR_R)) || \
+      (defined(HAVE_POSIX_STRERROR_R) && defined(HAVE_GLIBC_STRERROR_R))
+#    error "strerror_r MUST be either POSIX, glibc style"
+#  endif
+#endif
+
+#include <curl/curl.h>
+
+#ifndef WITHOUT_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 /* !WITHOUT_LIBCURL */
+
+#include "winapi.h"
+#include "strerr.h"
+/* The last 2 #include files should be in this order */
+#include "../curl_memory.h"
+#include "../memdebug.h"
+
+#ifdef USE_WINSOCK
+/* This is a helper function for curlx_strerror that converts Winsock error
+ * codes (WSAGetLastError) to error messages.
+ * Returns NULL if no error message was found for error code.
+ */
+static const char *
+get_winsock_error(int err, char *buf, size_t len)
+{
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+  const char *p;
+  size_t alen;
+#endif
+
+  if(!len)
+    return NULL;
+
+  *buf = '\0';
+
+#ifdef CURL_DISABLE_VERBOSE_STRINGS
+  (void)err;
+  return NULL;
+#else
+  switch(err) {
+  case WSAEINTR:
+    p = "Call interrupted";
+    break;
+  case WSAEBADF:
+    p = "Bad file";
+    break;
+  case WSAEACCES:
+    p = "Bad access";
+    break;
+  case WSAEFAULT:
+    p = "Bad argument";
+    break;
+  case WSAEINVAL:
+    p = "Invalid arguments";
+    break;
+  case WSAEMFILE:
+    p = "Out of file descriptors";
+    break;
+  case WSAEWOULDBLOCK:
+    p = "Call would block";
+    break;
+  case WSAEINPROGRESS:
+  case WSAEALREADY:
+    p = "Blocking call in progress";
+    break;
+  case WSAENOTSOCK:
+    p = "Descriptor is not a socket";
+    break;
+  case WSAEDESTADDRREQ:
+    p = "Need destination address";
+    break;
+  case WSAEMSGSIZE:
+    p = "Bad message size";
+    break;
+  case WSAEPROTOTYPE:
+    p = "Bad protocol";
+    break;
+  case WSAENOPROTOOPT:
+    p = "Protocol option is unsupported";
+    break;
+  case WSAEPROTONOSUPPORT:
+    p = "Protocol is unsupported";
+    break;
+  case WSAESOCKTNOSUPPORT:
+    p = "Socket is unsupported";
+    break;
+  case WSAEOPNOTSUPP:
+    p = "Operation not supported";
+    break;
+  case WSAEAFNOSUPPORT:
+    p = "Address family not supported";
+    break;
+  case WSAEPFNOSUPPORT:
+    p = "Protocol family not supported";
+    break;
+  case WSAEADDRINUSE:
+    p = "Address already in use";
+    break;
+  case WSAEADDRNOTAVAIL:
+    p = "Address not available";
+    break;
+  case WSAENETDOWN:
+    p = "Network down";
+    break;
+  case WSAENETUNREACH:
+    p = "Network unreachable";
+    break;
+  case WSAENETRESET:
+    p = "Network has been reset";
+    break;
+  case WSAECONNABORTED:
+    p = "Connection was aborted";
+    break;
+  case WSAECONNRESET:
+    p = "Connection was reset";
+    break;
+  case WSAENOBUFS:
+    p = "No buffer space";
+    break;
+  case WSAEISCONN:
+    p = "Socket is already connected";
+    break;
+  case WSAENOTCONN:
+    p = "Socket is not connected";
+    break;
+  case WSAESHUTDOWN:
+    p = "Socket has been shut down";
+    break;
+  case WSAETOOMANYREFS:
+    p = "Too many references";
+    break;
+  case WSAETIMEDOUT:
+    p = "Timed out";
+    break;
+  case WSAECONNREFUSED:
+    p = "Connection refused";
+    break;
+  case WSAELOOP:
+    p = "Loop??";
+    break;
+  case WSAENAMETOOLONG:
+    p = "Name too long";
+    break;
+  case WSAEHOSTDOWN:
+    p = "Host down";
+    break;
+  case WSAEHOSTUNREACH:
+    p = "Host unreachable";
+    break;
+  case WSAENOTEMPTY:
+    p = "Not empty";
+    break;
+  case WSAEPROCLIM:
+    p = "Process limit reached";
+    break;
+  case WSAEUSERS:
+    p = "Too many users";
+    break;
+  case WSAEDQUOT:
+    p = "Bad quota";
+    break;
+  case WSAESTALE:
+    p = "Something is stale";
+    break;
+  case WSAEREMOTE:
+    p = "Remote error";
+    break;
+  case WSAEDISCON:
+    p = "Disconnected";
+    break;
+    /* Extended Winsock errors */
+  case WSASYSNOTREADY:
+    p = "Winsock library is not ready";
+    break;
+  case WSANOTINITIALISED:
+    p = "Winsock library not initialised";
+    break;
+  case WSAVERNOTSUPPORTED:
+    p = "Winsock version not supported";
+    break;
+
+    /* getXbyY() errors (already handled in herrmsg):
+     * Authoritative Answer: Host not found */
+  case WSAHOST_NOT_FOUND:
+    p = "Host not found";
+    break;
+
+    /* Non-Authoritative: Host not found, or SERVERFAIL */
+  case WSATRY_AGAIN:
+    p = "Host not found, try again";
+    break;
+
+    /* Non recoverable errors, FORMERR, REFUSED, NOTIMP */
+  case WSANO_RECOVERY:
+    p = "Unrecoverable error in call to nameserver";
+    break;
+
+    /* Valid name, no data record of requested type */
+  case WSANO_DATA:
+    p = "No data record of requested type";
+    break;
+
+  default:
+    return NULL;
+  }
+  alen = strlen(p);
+  if(alen < len)
+    strcpy(buf, p);
+  return buf;
+#endif
+}
+#endif /* USE_WINSOCK */
+
+/*
+ * Our thread-safe and smart strerror() replacement.
+ *
+ * The 'err' argument passed in to this function MUST be a true errno number
+ * as reported on this system. We do no range checking on the number before
+ * we pass it to the "number-to-message" conversion function and there might
+ * be systems that do not do proper range checking in there themselves.
+ *
+ * We do not do range checking (on systems other than Windows) since there is
+ * no good reliable and portable way to do it.
+ *
+ * On Windows different types of error codes overlap. This function has an
+ * order of preference when trying to match error codes:
+ * CRT (errno), Winsock (WSAGetLastError), Windows API (GetLastError).
+ *
+ * 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 curlx_winapi_strerror if the error code is definitely Windows API.
+ */
+const char *curlx_strerror(int err, char *buf, size_t buflen)
+{
+#ifdef _WIN32
+  DWORD old_win_err = GetLastError();
+#endif
+  int old_errno = errno;
+  char *p;
+
+  if(!buflen)
+    return NULL;
+
+#ifndef _WIN32
+  DEBUGASSERT(err >= 0);
+#endif
+
+  *buf = '\0';
+
+#ifdef _WIN32
+#ifndef UNDER_CE
+  /* 'sys_nerr' is the maximum errno number, it is not widely portable */
+  if(err >= 0 && err < sys_nerr)
+    SNPRINTF(buf, buflen, "%s", sys_errlist[err]);
+  else
+#endif
+  {
+    if(
+#ifdef USE_WINSOCK
+      !get_winsock_error(err, buf, buflen) &&
+#endif
+      !curlx_get_winapi_error((DWORD)err, buf, buflen))
+      SNPRINTF(buf, buflen, "Unknown error %d (%#x)", err, err);
+  }
+#else /* !_WIN32 */
+
+#if defined(HAVE_STRERROR_R) && defined(HAVE_POSIX_STRERROR_R)
+  /*
+   * The POSIX-style strerror_r() may set errno to ERANGE if insufficient
+   * storage is supplied via 'strerrbuf' and 'buflen' to hold the generated
+   * message string, or EINVAL if 'errnum' is not a valid error number.
+   */
+  if(strerror_r(err, buf, buflen) &&
+     buflen > sizeof("Unknown error ") + 20) {
+    if(buf[0] == '\0')
+      SNPRINTF(buf, buflen, "Unknown error %d", err);
+  }
+#elif defined(HAVE_STRERROR_R) && defined(HAVE_GLIBC_STRERROR_R)
+  /*
+   * The glibc-style strerror_r() only *might* use the buffer we pass to
+   * the function, but it always returns the error message as a pointer,
+   * so we must copy that string unconditionally (if non-NULL).
+   */
+  {
+    char buffer[256];
+    char *msg = strerror_r(err, buffer, sizeof(buffer));
+    if(msg && buflen > 1)
+      SNPRINTF(buf, buflen, "%s", msg);
+    else if(buflen > sizeof("Unknown error ") + 20)
+      SNPRINTF(buf, buflen, "Unknown error %d", err);
+  }
+#else
+  {
+    /* !checksrc! disable BANNEDFUNC 1 */
+    const char *msg = strerror(err);
+    if(msg && buflen > 1)
+      SNPRINTF(buf, buflen, "%s", msg);
+    else if(buflen > sizeof("Unknown error ") + 20)
+      SNPRINTF(buf, buflen, "Unknown error %d", err);
+  }
+#endif
+
+#endif /* _WIN32 */
+
+  /* strip trailing '\r\n' or '\n'. */
+  p = strrchr(buf, '\n');
+  if(p && (p - buf) >= 2)
+    *p = '\0';
+  p = strrchr(buf, '\r');
+  if(p && (p - buf) >= 1)
+    *p = '\0';
+
+  if(errno != old_errno)
+    CURL_SETERRNO(old_errno);
+
+#ifdef _WIN32
+  if(old_win_err != GetLastError())
+    SetLastError(old_win_err);
+#endif
+
+  return buf;
+}

+ 5 - 15
lib/curl_des.h → lib/curlx/strerr.h

@@ -1,5 +1,5 @@
-#ifndef HEADER_CURL_DES_H
-#define HEADER_CURL_DES_H
+#ifndef HEADER_CURL_STRERR_H
+#define HEADER_CURL_STRERR_H
 /***************************************************************************
  *                                  _   _ ____  _
  *  Project                     ___| | | |  _ \| |
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) Steve Holme, <[email protected]>.
+ * Copyright (C) Daniel Stenberg, <[email protected]>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -24,16 +24,6 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+const char *curlx_strerror(int err, char *buf, size_t buflen);
 
-#if defined(USE_CURL_NTLM_CORE) && \
-  (defined(USE_GNUTLS) ||          \
-   defined(USE_OS400CRYPTO) ||     \
-   defined(USE_WIN32_CRYPTO))
-
-/* Applies odd parity to the given byte array */
-void Curl_des_set_odd_parity(unsigned char *bytes, size_t length);
-
-#endif
-
-#endif /* HEADER_CURL_DES_H */
+#endif /* HEADER_CURL_STRERR_H */

+ 8 - 1
lib/curlx/version_win32.c

@@ -134,8 +134,15 @@ bool curlx_verify_windows_version(const unsigned int majorVersion,
   static bool onetime = TRUE; /* safe because first call is during init */
 
   if(onetime) {
+#if defined(__clang__) && __clang_major__ >= 16
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wcast-function-type-strict"
+#endif
     pRtlVerifyVersionInfo = CURLX_FUNCTION_CAST(RTLVERIFYVERSIONINFO_FN,
-      (GetProcAddress(GetModuleHandleA("ntdll"), "RtlVerifyVersionInfo")));
+      GetProcAddress(GetModuleHandleA("ntdll"), "RtlVerifyVersionInfo"));
+#if defined(__clang__) && __clang_major__ >= 16
+#pragma clang diagnostic pop
+#endif
     onetime = FALSE;
   }
 

+ 9 - 16
lib/curlx/winapi.c

@@ -25,12 +25,12 @@
 
 /*
  * curlx_winapi_strerror:
- * Variant of Curl_strerror if the error code is definitely Windows API.
+ * Variant of curlx_strerror if the error code is definitely Windows API.
  */
 #ifdef _WIN32
 #include "winapi.h"
 
-#ifdef BUILDING_LIBCURL
+#ifndef WITHOUT_LIBCURL
 #include <curl/mprintf.h>
 #define SNPRINTF curl_msnprintf
 #else
@@ -38,19 +38,17 @@
 
 /* adjust for old MSVC */
 #if defined(_MSC_VER) && (_MSC_VER < 1900)
-# define SNPRINTF _snprintf
+#define SNPRINTF _snprintf
 #else
 #define SNPRINTF snprintf
 #endif
+#endif /* !WITHOUT_LIBCURL */
 
-#endif /* !BUILDING_LIBCURL */
-
-#ifdef _WIN32
-/* This is a helper function for Curl_strerror that converts Windows API error
+/* This is a helper function for curlx_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)
+const char *curlx_get_winapi_error(DWORD err, char *buf, size_t buflen)
 {
   char *p;
   wchar_t wbuf[256];
@@ -66,7 +64,7 @@ const char *curlx_get_winapi_error(int err, char *buf, size_t buflen)
      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,
+                     FORMAT_MESSAGE_IGNORE_INSERTS), NULL, err,
                     LANG_NEUTRAL, wbuf, CURL_ARRAYSIZE(wbuf), NULL)) {
     size_t written = wcstombs(buf, wbuf, buflen - 1);
     if(written != (size_t)-1)
@@ -86,13 +84,10 @@ const char *curlx_get_winapi_error(int err, char *buf, size_t buflen)
 
   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)
@@ -101,14 +96,14 @@ const char *curlx_winapi_strerror(DWORD err, char *buf, size_t buflen)
   *buf = '\0';
 
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
-  if(!curlx_get_winapi_error((int)err, buf, buflen)) {
+  if(!curlx_get_winapi_error(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);
+    SNPRINTF(buf, buflen, "Unknown error %lu (0x%08lx)", err, err);
 #if defined(__GNUC__) && __GNUC__ >= 7
 #pragma GCC diagnostic pop
 #endif
@@ -125,10 +120,8 @@ const char *curlx_winapi_strerror(DWORD err, char *buf, size_t buflen)
   if(errno != old_errno)
     CURL_SETERRNO(old_errno);
 
-#ifdef _WIN32
   if(old_win_err != GetLastError())
     SetLastError(old_win_err);
-#endif
 
   return buf;
 }

+ 1 - 1
lib/curlx/winapi.h

@@ -26,7 +26,7 @@
 
 #ifdef _WIN32
 #define WINAPI_ERROR_LEN 100
-const char *curlx_get_winapi_error(int err, char *buf, size_t buflen);
+const char *curlx_get_winapi_error(DWORD err, char *buf, size_t buflen);
 const char *curlx_winapi_strerror(DWORD err, char *buf, size_t buflen);
 #endif
 

+ 3 - 3
lib/cw-out.c

@@ -35,8 +35,7 @@
 #include "cw-out.h"
 #include "cw-pause.h"
 
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
+/* The last 2 #include files should be in this order */
 #include "curl_memory.h"
 #include "memdebug.h"
 
@@ -303,6 +302,7 @@ static CURLcode cw_out_buf_flush(struct cw_out_ctx *ctx,
                               &consumed);
     if(result && (result != CURLE_AGAIN))
       return result;
+    result = CURLE_OK;
 
     if(consumed) {
       if(consumed == curlx_dyn_len(&cwbuf->b)) {
@@ -404,7 +404,7 @@ static CURLcode cw_out_do_write(struct cw_out_ctx *ctx,
     /* still have buffered data, append and flush */
     result = cw_out_append(ctx, data, otype, buf, blen);
     if(result)
-      return result;
+      goto out;
     result = cw_out_flush_chain(ctx, data, &ctx->buf, flush_all);
     if(result)
       goto out;

+ 1 - 2
lib/cw-pause.c

@@ -34,8 +34,7 @@
 #include "sendf.h"
 #include "cw-pause.h"
 
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
+/* The last 2 #include files should be in this order */
 #include "curl_memory.h"
 #include "memdebug.h"
 

+ 3 - 3
lib/dict.c

@@ -59,9 +59,9 @@
 #include "escape.h"
 #include "progress.h"
 #include "dict.h"
-#include "curl_printf.h"
+
+/* The last 2 #include files should be: */
 #include "curl_memory.h"
-/* The last #include file should be: */
 #include "memdebug.h"
 
 
@@ -144,7 +144,7 @@ static CURLcode sendf(struct Curl_easy *data, const char *fmt, ...)
   char *sptr;
   va_list ap;
   va_start(ap, fmt);
-  s = vaprintf(fmt, ap); /* returns an allocated string */
+  s = curl_mvaprintf(fmt, ap); /* returns an allocated string */
   va_end(ap);
   if(!s)
     return CURLE_OUT_OF_MEMORY; /* failure */

+ 1 - 2
lib/dllmain.c

@@ -28,8 +28,7 @@
 #include <openssl/crypto.h>
 #endif
 
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
+/* The last 2 #include files should be in this order */
 #include "curl_memory.h"
 #include "memdebug.h"
 

+ 28 - 25
lib/doh.c

@@ -41,8 +41,7 @@
 #include "escape.h"
 #include "urlapi-int.h"
 
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
+/* The last 2 #include files should be in this order */
 #include "curl_memory.h"
 #include "memdebug.h"
 
@@ -377,6 +376,9 @@ static CURLcode doh_probe_run(struct Curl_easy *data,
      options should be added to check doh proxy insecure separately,
      CURLOPT_DOH_PROXY_SSL_VERIFYHOST and CURLOPT_DOH_PROXY_SSL_VERIFYPEER.
      */
+  doh->set.ssl.custom_cafile = data->set.ssl.custom_cafile;
+  doh->set.ssl.custom_capath = data->set.ssl.custom_capath;
+  doh->set.ssl.custom_cablob = data->set.ssl.custom_cablob;
   if(data->set.str[STRING_SSL_CAFILE]) {
     ERROR_CHECK_SETOPT(CURLOPT_CAINFO,
                        data->set.str[STRING_SSL_CAFILE]);
@@ -456,6 +458,7 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
 
   DEBUGASSERT(conn);
   DEBUGASSERT(!data->state.async.doh);
+  DEBUGASSERT(hostname && hostname[0]);
   if(data->state.async.doh)
     Curl_doh_cleanup(data);
 
@@ -510,7 +513,7 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
     /* Only use HTTPS RR for HTTP(S) transfers */
     char *qname = NULL;
     if(port != PORT_HTTPS) {
-      qname = aprintf("_%d._https.%s", port, hostname);
+      qname = curl_maprintf("_%d._https.%s", port, hostname);
       if(!qname)
         goto error;
     }
@@ -759,7 +762,7 @@ UNITTEST DOHcode doh_resp_decode(const unsigned char *doh,
 
   ancount = doh_get16bit(doh, 6);
   while(ancount) {
-    unsigned short class;
+    unsigned short dnsclass;
     unsigned int ttl;
 
     rc = doh_skipqname(doh, dohlen, &index);
@@ -779,8 +782,8 @@ UNITTEST DOHcode doh_resp_decode(const unsigned char *doh,
 
     if(dohlen < (index + 2))
       return DOH_DNS_OUT_OF_RANGE;
-    class = doh_get16bit(doh, index);
-    if(DNS_CLASS_IN != class)
+    dnsclass = doh_get16bit(doh, index);
+    if(DNS_CLASS_IN != dnsclass)
       return DOH_DNS_UNEXPECTED_CLASS; /* unsupported */
     index += 2;
 
@@ -816,7 +819,7 @@ UNITTEST DOHcode doh_resp_decode(const unsigned char *doh,
     if(dohlen < (index + 8))
       return DOH_DNS_OUT_OF_RANGE;
 
-    index += 2 + 2 + 4; /* type, class and ttl */
+    index += 2 + 2 + 4; /* type, dnsclass and ttl */
 
     if(dohlen < (index + 2))
       return DOH_DNS_OUT_OF_RANGE;
@@ -838,7 +841,7 @@ UNITTEST DOHcode doh_resp_decode(const unsigned char *doh,
     if(dohlen < (index + 8))
       return DOH_DNS_OUT_OF_RANGE;
 
-    index += 2 + 2 + 4; /* type, class and ttl */
+    index += 2 + 2 + 4; /* type, dnsclass and ttl */
 
     if(dohlen < (index + 2))
       return DOH_DNS_OUT_OF_RANGE;
@@ -887,8 +890,9 @@ static void doh_show(struct Curl_easy *data,
       len = sizeof(buffer) - len;
       for(j = 0; j < 16; j += 2) {
         size_t l;
-        msnprintf(ptr, len, "%s%02x%02x", j ? ":" : "", d->addr[i].ip.v6[j],
-                  d->addr[i].ip.v6[j + 1]);
+        curl_msnprintf(ptr, len, "%s%02x%02x", j ? ":" : "",
+                       d->addr[i].ip.v6[j],
+                       d->addr[i].ip.v6[j + 1]);
         l = strlen(ptr);
         len -= l;
         ptr += l;
@@ -1062,7 +1066,7 @@ UNITTEST void de_cleanup(struct dohentry *d)
  * @return is 1 for success, error otherwise
  *
  * The encoding here is defined in
- * https://tools.ietf.org/html/rfc1035#section-3.1
+ * https://datatracker.ietf.org/doc/html/rfc1035#section-3.1
  *
  * The input buffer pointer will be modified so it points to
  * just after the end of the DNS name encoding on output. (And
@@ -1215,8 +1219,9 @@ UNITTEST void doh_print_httpsrr(struct Curl_easy *data,
 CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
                               struct Curl_dns_entry **dnsp)
 {
-  CURLcode result;
+  CURLcode result = CURLE_OK;
   struct doh_probes *dohp = data->state.async.doh;
+  struct dohentry de;
   *dnsp = NULL; /* defaults to no response */
   if(!dohp)
     return CURLE_OUT_OF_MEMORY;
@@ -1229,7 +1234,6 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
   }
   else if(!dohp->pending) {
     DOHcode rc[DOH_SLOT_COUNT];
-    struct dohentry de;
     int slot;
 
     /* Clear any result the might still be there */
@@ -1261,17 +1265,14 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
       struct Curl_dns_entry *dns;
       struct Curl_addrinfo *ai;
 
-
       if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_dns)) {
         CURL_TRC_DNS(data, "hostname: %s", dohp->host);
         doh_show(data, &de);
       }
 
       result = doh2ai(&de, dohp->host, dohp->port, &ai);
-      if(result) {
-        de_cleanup(&de);
-        return result;
-      }
+      if(result)
+        goto error;
 
       /* we got a response, create a dns entry. */
       dns = Curl_dnscache_mk_entry(data, ai, dohp->host, 0, dohp->port, FALSE);
@@ -1284,7 +1285,8 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
                                            de.https_rrs->len, &hrr);
           if(result) {
             infof(data, "Failed to decode HTTPS RR");
-            return result;
+            Curl_resolv_unlink(data, &dns);
+            goto error;
           }
           infof(data, "Some HTTPS RR to process");
 # ifdef DEBUGBUILD
@@ -1302,14 +1304,15 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
 
     /* All done */
     data->state.async.done = TRUE;
-    de_cleanup(&de);
-    Curl_doh_cleanup(data);
-    return result;
-
   } /* !dohp->pending */
+  else
+    /* wait for pending DoH transactions to complete */
+    return CURLE_OK;
 
-  /* else wait for pending DoH transactions to complete */
-  return CURLE_OK;
+error:
+  de_cleanup(&de);
+  Curl_doh_cleanup(data);
+  return result;
 }
 
 void Curl_doh_close(struct Curl_easy *data)

+ 2 - 2
lib/dynhds.c

@@ -26,12 +26,12 @@
 #include "dynhds.h"
 #include "strcase.h"
 
-/* The last 3 #include files should be in this order */
 #ifdef USE_NGHTTP2
 #include <stdint.h>
 #include <nghttp2/nghttp2.h>
 #endif /* USE_NGHTTP2 */
-#include "curl_printf.h"
+
+/* The last 2 #include files should be in this order */
 #include "curl_memory.h"
 #include "memdebug.h"
 

+ 28 - 25
lib/easy.c

@@ -80,8 +80,7 @@
 
 #include "easy_lock.h"
 
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
+/* The last 2 #include files should be in this order */
 #include "curl_memory.h"
 #include "memdebug.h"
 
@@ -157,42 +156,42 @@ static CURLcode global_init(long flags, bool memoryfuncs)
   }
 
   if(Curl_trc_init()) {
-    DEBUGF(fprintf(stderr, "Error: Curl_trc_init failed\n"));
+    DEBUGF(curl_mfprintf(stderr, "Error: Curl_trc_init failed\n"));
     goto fail;
   }
 
   if(!Curl_ssl_init()) {
-    DEBUGF(fprintf(stderr, "Error: Curl_ssl_init failed\n"));
+    DEBUGF(curl_mfprintf(stderr, "Error: Curl_ssl_init failed\n"));
     goto fail;
   }
 
   if(!Curl_vquic_init()) {
-    DEBUGF(fprintf(stderr, "Error: Curl_vquic_init failed\n"));
+    DEBUGF(curl_mfprintf(stderr, "Error: Curl_vquic_init failed\n"));
     goto fail;
   }
 
   if(Curl_win32_init(flags)) {
-    DEBUGF(fprintf(stderr, "Error: win32_init failed\n"));
+    DEBUGF(curl_mfprintf(stderr, "Error: win32_init failed\n"));
     goto fail;
   }
 
   if(Curl_amiga_init()) {
-    DEBUGF(fprintf(stderr, "Error: Curl_amiga_init failed\n"));
+    DEBUGF(curl_mfprintf(stderr, "Error: Curl_amiga_init failed\n"));
     goto fail;
   }
 
   if(Curl_macos_init()) {
-    DEBUGF(fprintf(stderr, "Error: Curl_macos_init failed\n"));
+    DEBUGF(curl_mfprintf(stderr, "Error: Curl_macos_init failed\n"));
     goto fail;
   }
 
   if(Curl_async_global_init()) {
-    DEBUGF(fprintf(stderr, "Error: resolver_global_init failed\n"));
+    DEBUGF(curl_mfprintf(stderr, "Error: resolver_global_init failed\n"));
     goto fail;
   }
 
   if(Curl_ssh_init()) {
-    DEBUGF(fprintf(stderr, "Error: Curl_ssh_init failed\n"));
+    DEBUGF(curl_mfprintf(stderr, "Error: Curl_ssh_init failed\n"));
     goto fail;
   }
 
@@ -361,7 +360,7 @@ CURL *curl_easy_init(void)
     result = global_init(CURL_GLOBAL_DEFAULT, TRUE);
     if(result) {
       /* something in the global init failed, return nothing */
-      DEBUGF(fprintf(stderr, "Error: curl_global_init failed\n"));
+      DEBUGF(curl_mfprintf(stderr, "Error: curl_global_init failed\n"));
       global_init_unlock();
       return NULL;
     }
@@ -371,7 +370,7 @@ CURL *curl_easy_init(void)
   /* We use curl_open() with undefined URL so far */
   result = Curl_open(&data);
   if(result) {
-    DEBUGF(fprintf(stderr, "Error: Curl_open failed\n"));
+    DEBUGF(curl_mfprintf(stderr, "Error: Curl_open failed\n"));
     return NULL;
   }
 
@@ -407,7 +406,7 @@ static int events_timer(CURLM *multi,    /* multi handle */
   struct events *ev = userp;
   (void)multi;
 #if DEBUG_EV_POLL
-  fprintf(stderr, "events_timer: set timeout %ldms\n", timeout_ms);
+  curl_mfprintf(stderr, "events_timer: set timeout %ldms\n", timeout_ms);
 #endif
   ev->ms = timeout_ms;
   ev->msbump = TRUE;
@@ -559,7 +558,7 @@ static unsigned int populate_fds(struct pollfd *fds, struct events *ev)
     f->events = m->socket.events;
     f->revents = 0;
 #if DEBUG_EV_POLL
-    fprintf(stderr, "poll() %d check socket %d\n", numfds, f->fd);
+    curl_mfprintf(stderr, "poll() %d check socket %d\n", numfds, f->fd);
 #endif
     f++;
     numfds++;
@@ -579,19 +578,19 @@ static CURLcode poll_fds(struct events *ev,
   if(numfds) {
     /* wait for activity or timeout */
 #if DEBUG_EV_POLL
-    fprintf(stderr, "poll(numfds=%u, timeout=%ldms)\n", numfds, ev->ms);
+    curl_mfprintf(stderr, "poll(numfds=%u, timeout=%ldms)\n", numfds, ev->ms);
 #endif
     *pollrc = Curl_poll(fds, numfds, ev->ms);
 #if DEBUG_EV_POLL
-    fprintf(stderr, "poll(numfds=%u, timeout=%ldms) -> %d\n",
-            numfds, ev->ms, *pollrc);
+    curl_mfprintf(stderr, "poll(numfds=%u, timeout=%ldms) -> %d\n",
+                  numfds, ev->ms, *pollrc);
 #endif
     if(*pollrc < 0)
       return CURLE_UNRECOVERABLE_POLL;
   }
   else {
 #if DEBUG_EV_POLL
-    fprintf(stderr, "poll, but no fds, wait timeout=%ldms\n", ev->ms);
+    curl_mfprintf(stderr, "poll, but no fds, wait timeout=%ldms\n", ev->ms);
 #endif
     *pollrc = 0;
     if(ev->ms > 0)
@@ -629,7 +628,7 @@ static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev)
     if(!pollrc) {
       /* timeout! */
       ev->ms = 0;
-      /* fprintf(stderr, "call curl_multi_socket_action(TIMEOUT)\n"); */
+      /* curl_mfprintf(stderr, "call curl_multi_socket_action(TIMEOUT)\n"); */
       mcode = curl_multi_socket_action(multi, CURL_SOCKET_TIMEOUT, 0,
                                        &ev->running_handles);
     }
@@ -658,8 +657,8 @@ static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev)
         timediff_t timediff = curlx_timediff(curlx_now(), before);
         if(timediff > 0) {
 #if DEBUG_EV_POLL
-        fprintf(stderr, "poll timeout %ldms not updated, decrease by "
-                "time spent %ldms\n", ev->ms, (long)timediff);
+        curl_mfprintf(stderr, "poll timeout %ldms not updated, decrease by "
+                      "time spent %ldms\n", ev->ms, (long)timediff);
 #endif
           if(timediff > ev->ms)
             ev->ms = 0;
@@ -877,12 +876,16 @@ void curl_easy_cleanup(CURL *ptr)
  * information from a performed transfer and similar.
  */
 #undef curl_easy_getinfo
-CURLcode curl_easy_getinfo(CURL *data, CURLINFO info, ...)
+CURLcode curl_easy_getinfo(CURL *easy, CURLINFO info, ...)
 {
+  struct Curl_easy *data = easy;
   va_list arg;
   void *paramp;
   CURLcode result;
 
+  if(!GOOD_EASY_HANDLE(data))
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+
   va_start(arg, info);
   paramp = va_arg(arg, void *);
 
@@ -1106,7 +1109,7 @@ void curl_easy_reset(CURL *d)
   /* zero out UserDefined data: */
   Curl_freeset(data);
   memset(&data->set, 0, sizeof(struct UserDefined));
-  (void)Curl_init_userdefined(data);
+  Curl_init_userdefined(data);
 
   /* zero out Progress data: */
   memset(&data->progress, 0, sizeof(struct Progress));
@@ -1116,7 +1119,6 @@ void curl_easy_reset(CURL *d)
 
   data->progress.hide = TRUE;
   data->state.current_speed = -1; /* init to negative == impossible */
-  data->state.retrycount = 0;     /* reset the retry counter */
   data->state.recent_conn_id = -1; /* clear remembered connection id */
 
   /* zero out authentication data: */
@@ -1163,7 +1165,8 @@ CURLcode curl_easy_pause(CURL *d, int action)
   send_paused = Curl_xfer_send_is_paused(data);
   send_paused_new = (action & CURLPAUSE_SEND);
 
-  if(send_paused != send_paused_new) {
+  if((send_paused != send_paused_new) ||
+     (send_paused_new != Curl_creader_is_paused(data))) {
     changed = TRUE;
     result = Curl_1st_err(result, Curl_xfer_pause_send(data, send_paused_new));
   }

+ 6 - 8
lib/easy_lock.h

@@ -45,20 +45,18 @@
 #define curl_simple_lock atomic_int
 #define CURL_SIMPLE_LOCK_INIT 0
 
-/* a clang-thing */
-#ifndef __has_builtin
-#define __has_builtin(x) 0
-#endif
-
 #ifndef __INTEL_COMPILER
 /* The Intel compiler tries to look like GCC *and* clang *and* lies in its
    __has_builtin() function, so override it. */
 
 /* if GCC on i386/x86_64 or if the built-in is present */
-#if ( (defined(__GNUC__) && !defined(__clang__)) &&     \
-      (defined(__i386__) || defined(__x86_64__))) ||    \
-  __has_builtin(__builtin_ia32_pause)
+#if (defined(__GNUC__) && !defined(__clang__)) &&     \
+    (defined(__i386__) || defined(__x86_64__))
 #define HAVE_BUILTIN_IA32_PAUSE
+#elif defined(__has_builtin)  /* Keep this PP check separate from others */
+#if __has_builtin(__builtin_ia32_pause)
+#define HAVE_BUILTIN_IA32_PAUSE
+#endif
 #endif
 
 #endif

+ 2 - 3
lib/escape.c

@@ -34,11 +34,10 @@ struct Curl_easy;
 #include "urldata.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"
+
+/* The last 2 #include files should be in this order */
 #include "curl_memory.h"
 #include "memdebug.h"
 

+ 2 - 3
lib/fake_addrinfo.c

@@ -31,8 +31,7 @@
 #include <stdlib.h>
 #include <ares.h>
 
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
+/* The last 2 #include files should be in this order */
 #include "curl_memory.h"
 #include "memdebug.h"
 
@@ -178,7 +177,7 @@ int r_getaddrinfo(const char *node,
     if(env) {
       rc = ares_set_servers_ports_csv(channel, env);
       if(rc) {
-        fprintf(stderr, "ares_set_servers_ports_csv failed: %d", rc);
+        curl_mfprintf(stderr, "ares_set_servers_ports_csv failed: %d", rc);
         /* Cleanup */
         ares_destroy(channel);
         return EAI_MEMORY; /* we can't run */

+ 20 - 24
lib/file.c

@@ -46,10 +46,6 @@
 #include <sys/param.h>
 #endif
 
-#ifdef HAVE_FCNTL_H
-#include <fcntl.h>
-#endif
-
 #ifdef HAVE_SYS_TYPES_H
 #include <sys/types.h>
 #endif
@@ -65,15 +61,15 @@
 #include "escape.h"
 #include "file.h"
 #include "speedcheck.h"
-#include "getinfo.h"
 #include "multiif.h"
 #include "transfer.h"
 #include "url.h"
 #include "parsedate.h" /* for the week day and month names */
+#include "curlx/fopen.h"
 #include "curlx/warnless.h"
 #include "curl_range.h"
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
+
+/* The last 2 #include files should be in this order */
 #include "curl_memory.h"
 #include "memdebug.h"
 
@@ -237,7 +233,7 @@ static CURLcode file_connect(struct Curl_easy *data, bool *done)
       return CURLE_URL_MALFORMAT;
     }
 
-  fd = open(actual_path, O_RDONLY|CURL_O_BINARY);
+  fd = curlx_open(actual_path, O_RDONLY | CURL_O_BINARY);
   file->path = actual_path;
 #else
   if(memchr(real_path, 0, real_path_len)) {
@@ -261,16 +257,16 @@ static CURLcode file_connect(struct Curl_easy *data, bool *done)
     extern int __unix_path_semantics;
     if(strchr(real_path + 1, ':')) {
       /* Amiga absolute path */
-      fd = open(real_path + 1, O_RDONLY);
+      fd = curlx_open(real_path + 1, O_RDONLY);
       file->path++;
     }
     else if(__unix_path_semantics) {
       /* -lunix fallback */
-      fd = open(real_path, O_RDONLY);
+      fd = curlx_open(real_path, O_RDONLY);
     }
   }
   #else
-  fd = open(real_path, O_RDONLY);
+  fd = curlx_open(real_path, O_RDONLY);
   file->path = real_path;
   #endif
 #endif
@@ -349,9 +345,9 @@ static CURLcode file_upload(struct Curl_easy *data,
 
 #if (defined(ANDROID) || defined(__ANDROID__)) && \
   (defined(__i386__) || defined(__arm__))
-  fd = open(file->path, mode, (mode_t)data->set.new_file_perms);
+  fd = curlx_open(file->path, mode, (mode_t)data->set.new_file_perms);
 #else
-  fd = open(file->path, mode, data->set.new_file_perms);
+  fd = curlx_open(file->path, mode, data->set.new_file_perms);
 #endif
   if(fd < 0) {
     failf(data, "cannot open %s for writing", file->path);
@@ -491,8 +487,8 @@ static CURLcode file_do(struct Curl_easy *data, bool *done)
     static const char accept_ranges[]= { "Accept-ranges: bytes\r\n" };
     if(expected_size >= 0) {
       headerlen =
-        msnprintf(header, sizeof(header), "Content-Length: %" FMT_OFF_T "\r\n",
-                  expected_size);
+        curl_msnprintf(header, sizeof(header),
+                       "Content-Length: %" FMT_OFF_T "\r\n", expected_size);
       result = Curl_client_write(data, CLIENTWRITE_HEADER, header, headerlen);
       if(result)
         return result;
@@ -510,15 +506,15 @@ static CURLcode file_do(struct Curl_easy *data, bool *done)
 
     /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
     headerlen =
-      msnprintf(header, sizeof(header),
-                "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
-                Curl_wkday[tm->tm_wday ? tm->tm_wday-1 : 6],
-                tm->tm_mday,
-                Curl_month[tm->tm_mon],
-                tm->tm_year + 1900,
-                tm->tm_hour,
-                tm->tm_min,
-                tm->tm_sec);
+      curl_msnprintf(header, sizeof(header),
+                     "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
+                     Curl_wkday[tm->tm_wday ? tm->tm_wday-1 : 6],
+                     tm->tm_mday,
+                     Curl_month[tm->tm_mon],
+                     tm->tm_year + 1900,
+                     tm->tm_hour,
+                     tm->tm_min,
+                     tm->tm_sec);
     result = Curl_client_write(data, CLIENTWRITE_HEADER, header, headerlen);
     if(!result)
       /* end of headers */

+ 0 - 1
lib/fileinfo.c

@@ -26,7 +26,6 @@
 
 #ifndef CURL_DISABLE_FTP
 
-#include "strdup.h"
 #include "fileinfo.h"
 #include "curl_memory.h"
 /* The last #include file should be: */

+ 11 - 17
lib/formdata.c

@@ -37,9 +37,10 @@ struct Curl_easy;
 #include "sendf.h"
 #include "strdup.h"
 #include "rand.h"
+#include "curlx/fopen.h"
 #include "curlx/warnless.h"
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
+
+/* The last 2 #include files should be in this order */
 #include "curl_memory.h"
 #include "memdebug.h"
 
@@ -771,20 +772,6 @@ static CURLcode setname(curl_mimepart *part, const char *name, size_t len)
   return res;
 }
 
-/* wrap call to fseeko so it matches the calling convention of callback */
-static int fseeko_wrapper(void *stream, curl_off_t offset, int whence)
-{
-#if defined(_WIN32) && defined(USE_WIN32_LARGE_FILES)
-  return _fseeki64(stream, (__int64)offset, whence);
-#elif defined(HAVE_FSEEKO) && defined(HAVE_DECL_FSEEKO)
-  return fseeko(stream, (off_t)offset, whence);
-#else
-  if(offset > LONG_MAX)
-    return -1;
-  return fseek(stream, (long)offset, whence);
-#endif
-}
-
 /*
  * Curl_getformdata() converts a linked list of "meta data" into a mime
  * structure. The input list is in 'post', while the output is stored in
@@ -868,10 +855,17 @@ CURLcode Curl_getformdata(CURL *data,
                particular, freopen(stdin) by the caller is not guaranteed
                to result as expected. This feature has been kept for backward
                compatibility: use of "-" pseudo filename should be avoided. */
+#if defined(__clang__) && __clang_major__ >= 16
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wcast-function-type-strict"
+#endif
             result = curl_mime_data_cb(part, (curl_off_t) -1,
                                        (curl_read_callback) fread,
-                                       fseeko_wrapper,
+                                       curlx_fseek,
                                        NULL, (void *) stdin);
+#if defined(__clang__) && __clang_major__ >= 16
+#pragma clang diagnostic pop
+#endif
           }
           else
             result = curl_mime_filedata(part, file->contents);

+ 191 - 227
lib/ftp.c

@@ -53,13 +53,11 @@
 #include "fileinfo.h"
 #include "ftplistparser.h"
 #include "curl_range.h"
-#include "curl_krb5.h"
 #include "strcase.h"
 #include "vtls/vtls.h"
 #include "cfilters.h"
 #include "cf-socket.h"
 #include "connect.h"
-#include "strerror.h"
 #include "curlx/inet_ntop.h"
 #include "curlx/inet_pton.h"
 #include "select.h"
@@ -72,9 +70,10 @@
 #include "http_proxy.h"
 #include "socks.h"
 #include "strdup.h"
+#include "curlx/strerr.h"
 #include "curlx/strparse.h"
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
+
+/* The last 2 #include files should be in this order */
 #include "curl_memory.h"
 #include "memdebug.h"
 
@@ -142,11 +141,11 @@ static const char * const ftp_state_names[]={
 #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
+static void ftp_state_low(struct Curl_easy *data,
+                          struct ftp_conn *ftpc,
+                          ftpstate newstate
 #ifdef DEBUGBUILD
-                       , int lineno
+                          , int lineno
 #endif
   )
 {
@@ -172,9 +171,9 @@ static void _ftp_state(struct Curl_easy *data,
 
 /* Local API functions */
 #ifndef DEBUGBUILD
-#define ftp_state(x,y,z) _ftp_state(x,y,z)
+#define ftp_state(x,y,z) ftp_state_low(x,y,z)
 #else /* !DEBUGBUILD */
-#define ftp_state(x,y,z) _ftp_state(x,y,z,__LINE__)
+#define ftp_state(x,y,z) ftp_state_low(x,y,z,__LINE__)
 #endif /* DEBUGBUILD */
 
 static CURLcode ftp_sendquote(struct Curl_easy *data,
@@ -272,7 +271,7 @@ const struct Curl_handler Curl_handler_ftp = {
   CURLPROTO_FTP,                   /* family */
   PROTOPT_DUAL | PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD |
   PROTOPT_NOURLQUERY | PROTOPT_PROXY_AS_HTTP |
-  PROTOPT_WILDCARD /* flags */
+  PROTOPT_WILDCARD | PROTOPT_SSL_REUSE /* flags */
 };
 
 
@@ -335,7 +334,6 @@ static void freedirs(struct ftp_conn *ftpc)
   ftpc->dirdepth = 0;
   Curl_safefree(ftpc->rawpath);
   ftpc->file = NULL;
-  Curl_safefree(ftpc->newhost);
 }
 
 #ifdef CURL_PREFER_LF_LINEENDS
@@ -430,6 +428,10 @@ static const struct Curl_cwtype ftp_cw_lc = {
 };
 
 #endif /* CURL_PREFER_LF_LINEENDS */
+
+static CURLcode getftpresponse(struct Curl_easy *data, ssize_t *nread,
+                               int *ftpcode);
+
 /***********************************************************************
  *
  * ftp_check_ctrl_on_data_wait()
@@ -446,11 +448,14 @@ static CURLcode ftp_check_ctrl_on_data_wait(struct Curl_easy *data,
   bool response = FALSE;
 
   /* First check whether there is a cached response from server */
-  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);
-    return CURLE_FTP_ACCEPT_FAILED;
+  if(curlx_dyn_len(&pp->recvbuf)) {
+    const char *l = curlx_dyn_ptr(&pp->recvbuf);
+    if(!ISDIGIT(*l) || (*l > '3')) {
+      /* Data connection could not be established, let's return */
+      infof(data, "There is negative response in cache while serv connect");
+      (void)getftpresponse(data, &nread, &ftpcode);
+      return CURLE_FTP_ACCEPT_FAILED;
+    }
   }
 
   if(pp->overflow)
@@ -476,13 +481,14 @@ static CURLcode ftp_check_ctrl_on_data_wait(struct Curl_easy *data,
     infof(data, "Ctrl conn has data while waiting for data conn");
     if(pp->overflow > 3) {
       const char *r = curlx_dyn_ptr(&pp->recvbuf);
+      size_t len = curlx_dyn_len(&pp->recvbuf);
 
-      DEBUGASSERT((pp->overflow + pp->nfinal) <=
-                  curlx_dyn_len(&pp->recvbuf));
+      DEBUGASSERT((pp->overflow + pp->nfinal) <= curlx_dyn_len(&pp->recvbuf));
       /* move over the most recently handled response line */
       r += pp->nfinal;
+      len -= pp->nfinal;
 
-      if(LASTLINE(r)) {
+      if((len > 3) && LASTLINE(r)) {
         curl_off_t status;
         if(!curlx_str_number(&r, &status, 999) && (status == 226)) {
           /* funny timing situation where we get the final message on the
@@ -495,7 +501,7 @@ static CURLcode ftp_check_ctrl_on_data_wait(struct Curl_easy *data,
       }
     }
 
-    (void)Curl_GetFTPResponse(data, &nread, &ftpcode);
+    (void)getftpresponse(data, &nread, &ftpcode);
 
     infof(data, "FTP code: %03d", ftpcode);
 
@@ -527,7 +533,7 @@ static CURLcode ftp_initiate_transfer(struct Curl_easy *data,
   if(result || !connected)
     return result;
 
-  if(ftpc->state_saved == FTP_STOR) {
+  if(data->state.upload) {
     /* 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);
@@ -542,7 +548,7 @@ static CURLcode ftp_initiate_transfer(struct Curl_easy *data,
   }
   else {
     /* FTP download, shutdown, do not ignore errors */
-    Curl_xfer_setup_recv(data, SECONDARYSOCKET, ftpc->retr_size_saved);
+    Curl_xfer_setup_recv(data, SECONDARYSOCKET, data->req.size);
     Curl_xfer_set_shutdown(data, TRUE, FALSE);
   }
 
@@ -577,28 +583,6 @@ static CURLcode ftp_readresp(struct Curl_easy *data,
   int code;
   CURLcode result = Curl_pp_readresp(data, sockindex, pp, &code, size);
   DEBUGASSERT(ftpcodep);
-#ifdef HAVE_GSSAPI
-  {
-    struct connectdata *conn = data->conn;
-    char * const buf = curlx_dyn_ptr(&ftpc->pp.recvbuf);
-
-    /* handle the security-oriented responses 6xx ***/
-    switch(code) {
-    case 631:
-      code = Curl_sec_read_msg(data, conn, buf, PROT_SAFE);
-      break;
-    case 632:
-      code = Curl_sec_read_msg(data, conn, buf, PROT_PRIVATE);
-      break;
-    case 633:
-      code = Curl_sec_read_msg(data, conn, buf, PROT_CONFIDENTIAL);
-      break;
-    default:
-      /* normal ftp stuff we pass through! */
-      break;
-    }
-  }
-#endif
 
   /* store the latest code for later retrieval, except during shutdown */
   if(!ftpc->shutdown)
@@ -625,14 +609,14 @@ static CURLcode ftp_readresp(struct Curl_easy *data,
 /* --- parse FTP server responses --- */
 
 /*
- * Curl_GetFTPResponse() is a BLOCKING function to read the full response
- * from a server after a command.
+ * getftpresponse() is a BLOCKING function to read the full response from a
+ * server after a command.
  *
  */
-
-CURLcode Curl_GetFTPResponse(struct Curl_easy *data,
-                             ssize_t *nreadp, /* return number of bytes read */
-                             int *ftpcodep) /* return the ftp-code */
+static CURLcode getftpresponse(struct Curl_easy *data,
+                               ssize_t *nreadp, /* return number of bytes
+                                                   read */
+                               int *ftpcodep) /* return the ftp-code */
 {
   /*
    * We cannot read just one byte per read() and then go back to select() as
@@ -650,7 +634,7 @@ CURLcode Curl_GetFTPResponse(struct Curl_easy *data,
   int cache_skip = 0;
   DEBUGASSERT(ftpcodep);
 
-  CURL_TRC_FTP(data, "getFTPResponse start");
+  CURL_TRC_FTP(data, "getftpresponse start");
   *nreadp = 0;
   *ftpcodep = 0; /* 0 for errors */
 
@@ -733,7 +717,7 @@ CURLcode Curl_GetFTPResponse(struct Curl_easy *data,
   } /* while there is buffer left and loop is requested */
 
   pp->pending_resp = FALSE;
-  CURL_TRC_FTP(data, "getFTPResponse -> result=%d, nread=%zd, ftpcode=%d",
+  CURL_TRC_FTP(data, "getftpresponse -> result=%d, nread=%zd, ftpcode=%d",
                result, *nreadp, *ftpcodep);
 
   return result;
@@ -1026,7 +1010,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
     sslen = sizeof(ss);
     if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
       failf(data, "getsockname() failed: %s",
-            Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
+            curlx_strerror(SOCKERRNO, buffer, sizeof(buffer)));
       goto out;
     }
     switch(sa->sa_family) {
@@ -1073,7 +1057,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
   }
   if(!ai) {
     failf(data, "socket failure: %s",
-          Curl_strerror(error, buffer, sizeof(buffer)));
+          curlx_strerror(error, buffer, sizeof(buffer)));
     goto out;
   }
   CURL_TRC_FTP(data, "[%s] ftp_state_use_port(), opened socket",
@@ -1100,12 +1084,12 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
          * the control connection instead and restart the port loop
          */
         infof(data, "bind(port=%hu) on non-local address failed: %s", port,
-              Curl_strerror(error, buffer, sizeof(buffer)));
+              curlx_strerror(error, buffer, sizeof(buffer)));
 
         sslen = sizeof(ss);
         if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
           failf(data, "getsockname() failed: %s",
-                Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
+                curlx_strerror(SOCKERRNO, buffer, sizeof(buffer)));
           goto out;
         }
         port = port_min;
@@ -1114,28 +1098,30 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
       }
       if(error != SOCKEADDRINUSE && error != SOCKEACCES) {
         failf(data, "bind(port=%hu) failed: %s", port,
-              Curl_strerror(error, buffer, sizeof(buffer)));
+              curlx_strerror(error, buffer, sizeof(buffer)));
         goto out;
       }
     }
     else
       break;
 
+    /* check if port is the maximum value here, because it might be 0xffff and
+       then the increment below will wrap the 16 bit counter */
+    if(port == port_max) {
+      /* maybe all ports were in use already */
+      failf(data, "bind() failed, ran out of ports");
+      goto out;
+    }
     port++;
   }
 
-  /* maybe all ports were in use already */
-  if(port > port_max) {
-    failf(data, "bind() failed, we ran out of ports");
-    goto out;
-  }
 
   /* get the name again after the bind() so that we can extract the
      port number it uses now */
   sslen = sizeof(ss);
   if(getsockname(portsock, sa, &sslen)) {
     failf(data, "getsockname() failed: %s",
-          Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
+          curlx_strerror(SOCKERRNO, buffer, sizeof(buffer)));
     goto out;
   }
   CURL_TRC_FTP(data, "[%s] ftp_state_use_port(), socket bound to port %d",
@@ -1145,7 +1131,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
 
   if(listen(portsock, 1)) {
     failf(data, "socket failure: %s",
-          Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
+          curlx_strerror(SOCKERRNO, buffer, sizeof(buffer)));
     goto out;
   }
   CURL_TRC_FTP(data, "[%s] ftp_state_use_port(), listening on %d",
@@ -1222,7 +1208,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
         source++;
       }
       *dest = 0;
-      msnprintf(dest, 20, ",%d,%d", (int)(port >> 8), (int)(port & 0xff));
+      curl_msnprintf(dest, 20, ",%d,%d", (int)(port >> 8), (int)(port & 0xff));
 
       result = Curl_pp_sendf(data, &ftpc->pp, "%s %s", mode[fcmd], target);
       if(result) {
@@ -1435,12 +1421,12 @@ static CURLcode ftp_state_list(struct Curl_easy *data,
     }
   }
 
-  cmd = aprintf("%s%s%.*s",
-                data->set.str[STRING_CUSTOMREQUEST] ?
-                data->set.str[STRING_CUSTOMREQUEST] :
-                (data->state.list_only ? "NLST" : "LIST"),
-                lstArg ? " " : "",
-                lstArglen, lstArg ? lstArg : "");
+  cmd = curl_maprintf("%s%s%.*s",
+                      data->set.str[STRING_CUSTOMREQUEST] ?
+                      data->set.str[STRING_CUSTOMREQUEST] :
+                      (data->state.list_only ? "NLST" : "LIST"),
+                      lstArg ? " " : "",
+                      lstArglen, lstArg ? lstArg : "");
 
   if(!cmd)
     return CURLE_OUT_OF_MEMORY;
@@ -1784,11 +1770,12 @@ static CURLcode ftp_epsv_disable(struct Curl_easy *data,
 }
 
 
-static char *control_address_dup(struct Curl_easy *data,
-                                 struct connectdata *conn)
+static CURLcode ftp_control_addr_dup(struct Curl_easy *data,
+                                     char **newhostp)
 {
-    struct ip_quadruple ipquad;
-    bool is_ipv6;
+  struct connectdata *conn = data->conn;
+  struct ip_quadruple ipquad;
+  bool is_ipv6;
 
   /* Returns the control connection IP address.
      If a proxy tunnel is used, returns the original hostname instead, because
@@ -1796,11 +1783,19 @@ static char *control_address_dup(struct Curl_easy *data,
      not the ftp host. */
 #ifndef CURL_DISABLE_PROXY
   if(conn->bits.tunnel_proxy || conn->bits.socksproxy)
-    return strdup(conn->host.name);
+    *newhostp = strdup(conn->host.name);
+  else
 #endif
-  if(!Curl_conn_get_ip_info(data, conn, FIRSTSOCKET, &is_ipv6, &ipquad))
-    return strdup(ipquad.remote_ip);
-  return NULL;
+  if(!Curl_conn_get_ip_info(data, conn, FIRSTSOCKET, &is_ipv6, &ipquad) &&
+     *ipquad.remote_ip)
+    *newhostp = strdup(ipquad.remote_ip);
+  else {
+    /* failed to get the remote_ip of the DATA connection */
+    failf(data, "unable to get peername of DATA connection");
+    *newhostp = NULL;
+    return CURLE_FTP_CANT_GET_HOST;
+  }
+  return *newhostp ? CURLE_OK : CURLE_OUT_OF_MEMORY;
 }
 
 static bool match_pasv_6nums(const char *p,
@@ -1830,12 +1825,11 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
   struct Curl_dns_entry *dns = NULL;
   unsigned short connectport; /* the local port connect() should use! */
   struct pingpong *pp = &ftpc->pp;
+  char *newhost = NULL;
+  unsigned short newport = 0;
   char *str =
     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);
-
   if((ftpc->count1 == 0) &&
      (ftpcode == 229)) {
     /* positive EPSV response */
@@ -1852,10 +1846,10 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
           failf(data, "Illegal port number in EPSV reply");
           return CURLE_FTP_WEIRD_PASV_REPLY;
         }
-        ftpc->newport = (unsigned short)num;
-        ftpc->newhost = control_address_dup(data, conn);
-        if(!ftpc->newhost)
-          return CURLE_OUT_OF_MEMORY;
+        newport = (unsigned short)num;
+        result = ftp_control_addr_dup(data, &newhost);
+        if(result)
+          return result;
       }
       else
         ptr = NULL;
@@ -1897,15 +1891,17 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
       infof(data, "Skip %u.%u.%u.%u for data connection, reuse %s instead",
             ip[0], ip[1], ip[2], ip[3],
             conn->host.name);
-      ftpc->newhost = control_address_dup(data, conn);
+      result = ftp_control_addr_dup(data, &newhost);
+      if(result)
+        return result;
     }
     else
-      ftpc->newhost = aprintf("%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
+      newhost = curl_maprintf("%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
 
-    if(!ftpc->newhost)
+    if(!newhost)
       return CURLE_OUT_OF_MEMORY;
 
-    ftpc->newport = (unsigned short)(((ip[4] << 8) + ip[5]) & 0xffff);
+    newport = (unsigned short)(((ip[4] << 8) + ip[5]) & 0xffff);
   }
   else if(ftpc->count1 == 0) {
     /* EPSV failed, move on to PASV */
@@ -1929,7 +1925,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
     result = Curl_conn_get_ip_info(data, data->conn, FIRSTSOCKET,
                                    &is_ipv6, &ipquad);
     if(result)
-      return result;
+      goto error;
 
     (void)Curl_resolv_blocking(data, host_name, ipquad.remote_port,
                                is_ipv6 ? CURL_IPRESOLVE_V6 : CURL_IPRESOLVE_V4,
@@ -1939,31 +1935,31 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
 
     if(!dns) {
       failf(data, "cannot resolve proxy host %s:%hu", host_name, connectport);
-      return CURLE_COULDNT_RESOLVE_PROXY;
+      result = CURLE_COULDNT_RESOLVE_PROXY;
+      goto error;
     }
   }
   else
 #endif
   {
     /* normal, direct, ftp connection */
-    DEBUGASSERT(ftpc->newhost);
+    DEBUGASSERT(newhost);
 
     /* postponed address resolution in case of tcp fastopen */
-    if(conn->bits.tcp_fastopen && !conn->bits.reuse && !ftpc->newhost[0]) {
-      free(ftpc->newhost);
-      ftpc->newhost = control_address_dup(data, conn);
-      if(!ftpc->newhost)
-        return CURLE_OUT_OF_MEMORY;
+    if(conn->bits.tcp_fastopen && !conn->bits.reuse && !newhost[0]) {
+      free(newhost);
+      result = ftp_control_addr_dup(data, &newhost);
+      if(result)
+        goto error;
     }
 
-    (void)Curl_resolv_blocking(data, ftpc->newhost, ftpc->newport,
-                               conn->ip_version, &dns);
-    connectport = ftpc->newport; /* we connect to the remote port */
+    (void)Curl_resolv_blocking(data, newhost, newport, conn->ip_version, &dns);
+    connectport = newport; /* we connect to the remote port */
 
     if(!dns) {
-      failf(data, "cannot resolve new host %s:%hu",
-            ftpc->newhost, connectport);
-      return CURLE_FTP_CANT_GET_HOST;
+      failf(data, "cannot resolve new host %s:%hu", newhost, connectport);
+      result = CURLE_FTP_CANT_GET_HOST;
+      goto error;
     }
   }
 
@@ -1972,10 +1968,12 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
                            CURL_CF_SSL_ENABLE : CURL_CF_SSL_DISABLE);
 
   if(result) {
-    if(ftpc->count1 == 0 && ftpcode == 229)
+    if(ftpc->count1 == 0 && ftpcode == 229) {
+      free(newhost);
       return ftp_epsv_disable(data, ftpc, conn);
+    }
 
-    return result;
+    goto error;
   }
 
 
@@ -1987,17 +1985,21 @@ 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, dns->addr, ftpc->newhost, connectport);
+    ftp_pasv_verbose(data, dns->addr, newhost, connectport);
 
   free(conn->secondaryhostname);
-  conn->secondary_port = ftpc->newport;
-  conn->secondaryhostname = strdup(ftpc->newhost);
-  if(!conn->secondaryhostname)
-    return CURLE_OUT_OF_MEMORY;
+  conn->secondary_port = newport;
+  conn->secondaryhostname = strdup(newhost);
+  if(!conn->secondaryhostname) {
+    result = CURLE_OUT_OF_MEMORY;
+    goto error;
+  }
 
   conn->bits.do_more = TRUE;
   ftp_state(data, ftpc, FTP_STOP); /* this phase is completed */
 
+error:
+  free(newhost);
   return result;
 }
 
@@ -2106,9 +2108,9 @@ static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data,
       if(ftp_213_date(resp, &year, &month, &day, &hour, &minute, &second)) {
         /* we have a time, reformat it */
         char timebuf[24];
-        msnprintf(timebuf, sizeof(timebuf),
-                  "%04d%02d%02d %02d:%02d:%02d GMT",
-                  year, month, day, hour, minute, second);
+        curl_msnprintf(timebuf, sizeof(timebuf),
+                       "%04d%02d%02d %02d:%02d:%02d GMT",
+                       year, month, day, hour, minute, second);
         /* now, convert this into a time() value: */
         if(!Curl_getdate_capped(timebuf, &data->info.filetime))
           showtime = TRUE;
@@ -2141,15 +2143,16 @@ static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data,
 
         /* format: "Tue, 15 Nov 1994 12:45:26" */
         headerbuflen =
-          msnprintf(headerbuf, sizeof(headerbuf),
-                    "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
-                    Curl_wkday[tm->tm_wday ? tm->tm_wday-1 : 6],
-                    tm->tm_mday,
-                    Curl_month[tm->tm_mon],
-                    tm->tm_year + 1900,
-                    tm->tm_hour,
-                    tm->tm_min,
-                    tm->tm_sec);
+          curl_msnprintf(headerbuf, sizeof(headerbuf),
+                         "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d "
+                         "GMT\r\n",
+                         Curl_wkday[tm->tm_wday ? tm->tm_wday-1 : 6],
+                         tm->tm_mday,
+                         Curl_month[tm->tm_mon],
+                         tm->tm_year + 1900,
+                         tm->tm_hour,
+                         tm->tm_min,
+                         tm->tm_sec);
         result = client_write_header(data, headerbuf, headerbuflen);
         if(result)
           return result;
@@ -2336,7 +2339,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];
-    const char *fdigit = memchr(start, '\r', len);
+    const char *fdigit = memchr(start, '\r', len - 4);
     if(fdigit) {
       fdigit--;
       if(*fdigit == '\n')
@@ -2362,8 +2365,9 @@ static CURLcode ftp_state_size_resp(struct Curl_easy *data,
 #ifdef CURL_FTP_HTTPSTYLE_HEAD
     if(filesize != -1) {
       char clbuf[128];
-      int clbuflen = msnprintf(clbuf, sizeof(clbuf),
-                "Content-Length: %" FMT_OFF_T "\r\n", filesize);
+      int clbuflen = curl_msnprintf(clbuf, sizeof(clbuf),
+                                    "Content-Length: %" FMT_OFF_T "\r\n",
+                                    filesize);
       result = client_write_header(data, clbuf, clbuflen);
       if(result)
         return result;
@@ -2424,19 +2428,16 @@ 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)
+                                    int ftpcode)
 {
   CURLcode result = CURLE_OK;
 
   if(ftpcode >= 400) {
     failf(data, "Failed FTP upload: %0d", ftpcode);
     ftp_state(data, ftpc, FTP_STOP);
-    /* oops, we never close the sockets! */
     return CURLE_UPLOAD_FAILED;
   }
 
-  ftpc->state_saved = instate;
-
   /* PORT means we are now awaiting the server to connect to us. */
   if(data->set.ftp_use_port) {
     bool connected;
@@ -2485,7 +2486,7 @@ static CURLcode ftp_state_get_resp(struct Curl_easy *data,
       E:
       125 Data connection already open; Transfer starting. */
 
-    curl_off_t size = -1; /* default unknown size */
+    data->req.size = -1; /* default unknown size */
 
 
     /*
@@ -2508,49 +2509,35 @@ static CURLcode ftp_state_get_resp(struct Curl_easy *data,
        * those cases only confuses us.
        *
        * Example D above makes this parsing a little tricky */
-      const char *bytes;
-      char *buf = curlx_dyn_ptr(&ftpc->pp.recvbuf);
-      bytes = strstr(buf, " bytes");
-      if(bytes) {
-        long in = (long)(--bytes-buf);
-        /* this is a hint there is size information in there! ;-) */
-        while(--in) {
-          /* scan for the left parenthesis and break there */
-          if('(' == *bytes)
-            break;
-          /* skip only digits */
-          if(!ISDIGIT(*bytes)) {
-            bytes = NULL;
+      size_t len = curlx_dyn_len(&ftpc->pp.recvbuf);
+      if(len >= 7) { /* "1 bytes" is 7 characters */
+        size_t i;
+        for(i = 0; i < len - 7; i++) {
+          curl_off_t what;
+          char *buf = curlx_dyn_ptr(&ftpc->pp.recvbuf);
+          const char *c = &buf[i];
+          if(!curlx_str_number(&c, &what, CURL_OFF_T_MAX) &&
+             !curlx_str_single(&c, ' ') &&
+             !strncmp(c, "bytes", 5)) {
+            data->req.size = what;
             break;
           }
-          /* one more estep backwards */
-          bytes--;
-        }
-        /* if we have nothing but digits: */
-        if(bytes) {
-          ++bytes;
-          /* get the number! */
-          if(curlx_str_number(&bytes, &size, CURL_OFF_T_MAX))
-            size = 1;
         }
       }
     }
     else if(ftp->downloadsize > -1)
-      size = ftp->downloadsize;
+      data->req.size = ftp->downloadsize;
 
-    if(size > data->req.maxdownload && data->req.maxdownload > 0)
-      size = data->req.size = data->req.maxdownload;
+    if(data->req.size > data->req.maxdownload && data->req.maxdownload > 0)
+      data->req.size = data->req.maxdownload;
     else if((instate != FTP_LIST) && (data->state.prefer_ascii))
-      size = -1; /* kludge for servers that understate ASCII mode file size */
+      data->req.size = -1; /* for servers that understate ASCII mode file
+                              size */
 
     infof(data, "Maxdownload = %" FMT_OFF_T, data->req.maxdownload);
 
     if(instate != FTP_LIST)
-      infof(data, "Getting file with size: %" FMT_OFF_T, size);
-
-    /* FTP download: */
-    ftpc->state_saved = instate;
-    ftpc->retr_size_saved = size;
+      infof(data, "Getting file with size: %" FMT_OFF_T, data->req.size);
 
     if(data->set.ftp_use_port) {
       bool connected;
@@ -2629,7 +2616,7 @@ static CURLcode ftp_state_user_resp(struct Curl_easy *data,
     /* 331 Password required for ...
        (the server requires to send the user's password too) */
     result = Curl_pp_sendf(data, &ftpc->pp, "PASS %s",
-                           data->conn->passwd ? data->conn->passwd : "");
+                           data->conn->passwd);
     if(!result)
       ftp_state(data, ftpc, FTP_PASS);
   }
@@ -2812,25 +2799,6 @@ static CURLcode ftp_wait_resp(struct Curl_easy *data,
     return CURLE_WEIRD_SERVER_REPLY;
   }
 
-  /* We have received a 220 response fine, now we proceed. */
-#ifdef HAVE_GSSAPI
-  if(data->set.krb) {
-    /* If not anonymous login, try a secure login. Note that this
-       procedure is still BLOCKING. */
-
-    Curl_sec_request_prot(conn, "private");
-    /* We set private first as default, in case the line below fails to
-       set a valid level */
-    Curl_sec_request_prot(conn, data->set.str[STRING_KRB_LEVEL]);
-
-    if(Curl_sec_login(data, conn)) {
-      failf(data, "secure login failed");
-      return CURLE_WEIRD_SERVER_REPLY;
-    }
-    infof(data, "Authentication successful");
-  }
-#endif
-
   if(data->set.use_ssl && !conn->bits.ftp_use_control_ssl) {
     /* We do not have an SSL/TLS control connection yet, but FTPS is
        requested. Try an FTPS connection now */
@@ -3169,7 +3137,7 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data,
     break;
 
   case FTP_STOR:
-    result = ftp_state_stor_resp(data, ftpc, ftpcode, ftpc->state);
+    result = ftp_state_stor_resp(data, ftpc, ftpcode);
     break;
 
   case FTP_QUIT:
@@ -3396,14 +3364,8 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
      * data has been transferred. This happens when doing through NATs etc that
      * abandon old silent connections.
      */
-    timediff_t old_time = pp->response_time;
-
-    pp->response_time = 60*1000; /* give it only a minute for now */
     pp->response = curlx_now(); /* timeout relative now */
-
-    result = Curl_GetFTPResponse(data, &nread, &ftpcode);
-
-    pp->response_time = old_time; /* set this back to previous value */
+    result = getftpresponse(data, &nread, &ftpcode);
 
     if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) {
       failf(data, "control connection looks dead");
@@ -3494,7 +3456,6 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
  *
  * BLOCKING
  */
-
 static
 CURLcode ftp_sendquote(struct Curl_easy *data,
                        struct ftp_conn *ftpc,
@@ -3525,7 +3486,7 @@ CURLcode ftp_sendquote(struct Curl_easy *data,
       result = Curl_pp_sendf(data, &ftpc->pp, "%s", cmd);
       if(!result) {
         pp->response = curlx_now(); /* timeout relative now */
-        result = Curl_GetFTPResponse(data, &nread, &ftpcode);
+        result = getftpresponse(data, &nread, &ftpcode);
       }
       if(result)
         return result;
@@ -3617,7 +3578,6 @@ ftp_pasv_verbose(struct Curl_easy *data,
  * (which basically is only for when PASV is being sent to retry a failed
  * EPSV).
  */
-
 static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
 {
   struct connectdata *conn = data->conn;
@@ -3632,6 +3592,9 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
 
   if(!ftpc || !ftp)
     return CURLE_FAILED_INIT;
+
+  *completep = 0; /* default to stay in the state */
+
   /* 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:
@@ -3650,7 +3613,6 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
         /* this is a EPSV connect failing, try PASV instead */
         return ftp_epsv_disable(data, ftpc, conn);
       }
-      *completep = (int)complete;
       return result;
     }
   }
@@ -3781,7 +3743,6 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
  * This is the actual DO function for FTP. Get a file/directory according to
  * the options previously setup.
  */
-
 static
 CURLcode ftp_perform(struct Curl_easy *data,
                      struct ftp_conn *ftpc,
@@ -3971,7 +3932,7 @@ static CURLcode wc_statemach(struct Curl_easy *data,
       struct Curl_llist_node *head = Curl_llist_head(&wildcard->filelist);
       struct curl_fileinfo *finfo = Curl_node_elem(head);
 
-      char *tmp_path = aprintf("%s%s", wildcard->path, finfo->filename);
+      char *tmp_path = curl_maprintf("%s%s", wildcard->path, finfo->filename);
       if(!tmp_path)
         return CURLE_OUT_OF_MEMORY;
 
@@ -4197,6 +4158,8 @@ static size_t numof_slashes(const char *str)
   return num;
 }
 
+#define FTP_MAX_DIR_DEPTH 1000
+
 /***********************************************************************
  *
  * ftp_parse_url_path()
@@ -4270,7 +4233,7 @@ CURLcode ftp_parse_url_path(struct Curl_easy *data,
       /* number of entries to allocate for the 'dirs' array */
       size_t dirAlloc = numof_slashes(rawPath);
 
-      if(dirAlloc >= 1000)
+      if(dirAlloc >= FTP_MAX_DIR_DEPTH)
         /* suspiciously deep dir hierarchy */
         return CURLE_URL_MALFORMAT;
 
@@ -4463,10 +4426,38 @@ static void ftp_conn_dtor(void *key, size_t klen, void *entry)
   free(ftpc);
 }
 
+static void type_url_check(struct Curl_easy *data, struct FTP *ftp)
+{
+  size_t len = strlen(ftp->path);
+  /* FTP URLs support an extension like ";type=<typecode>" that
+   * we will try to get now! */
+  if((len >= 7) && !memcmp(&ftp->path[len - 7], ";type=", 6)) {
+    char *type = &ftp->path[len - 7];
+    char command = Curl_raw_toupper(type[6]);
+
+    *type = 0; /* cut it off */
+
+    switch(command) {
+    case 'A': /* ASCII mode */
+      data->state.prefer_ascii = TRUE;
+      break;
+
+    case 'D': /* directory mode */
+      data->state.list_only = TRUE;
+      break;
+
+    case 'I': /* binary mode */
+    default:
+      /* switch off ASCII */
+      data->state.prefer_ascii = FALSE;
+      break;
+    }
+  }
+}
+
 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;
@@ -4501,34 +4492,7 @@ static CURLcode ftp_setup_connection(struct Curl_easy *data,
 
   ftp->path = &data->state.up.path[1]; /* do not include the initial slash */
 
-  /* FTP URLs support an extension like ";type=<typecode>" that
-   * we will try to get now! */
-  type = strstr(ftp->path, ";type=");
-
-  if(!type)
-    type = strstr(conn->host.rawalloc, ";type=");
-
-  if(type) {
-    char command;
-    *type = 0;                     /* it was in the middle of the hostname */
-    command = Curl_raw_toupper(type[6]);
-
-    switch(command) {
-    case 'A': /* ASCII mode */
-      data->state.prefer_ascii = TRUE;
-      break;
-
-    case 'D': /* directory mode */
-      data->state.list_only = TRUE;
-      break;
-
-    case 'I': /* binary mode */
-    default:
-      /* switch off ASCII */
-      data->state.prefer_ascii = FALSE;
-      break;
-    }
-  }
+  type_url_check(data, ftp);
 
   /* get some initial data into the ftp struct */
   ftp->transfer = PPTRANSFER_BODY;

Some files were not shown because too many files changed in this diff